From 758e4dba8130fe7824c04bbdfd3c9bfb3668803c Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 26 Apr 2021 13:07:00 -0700 Subject: [PATCH] topology: pre-process-dapm: add support for route objects DAPM route objects such as: Object.Base.route."1" { source "dai.SSP.0.dai.capture" sink "buffer.2.1" } will be converted to: SectionGraph."Endpoint.route.1" { index 0 lines [ "dai.SSP.0.capture, , buffer.2.1" ] } If the source/sink names are references to objects within a parent pipeline object, the index attribute value can be skipped and it will be populated when the object is pre-processed Object.Pipeline.volume-capture."1" { Object.Base.route."1" { source "pga..0" sink "buffer..0" } } The reference pga..0 will need to be resolved to get the widget name pga.1.0 and buffer..0 will be resolved to buffer.1.0 before creating the SectionGraph as follows: SectionGraph."volume-capture.1.route.1" { index 2 lines [ "pga.1.0, , buffer.1.0" ] } Signed-off-by: Ranjani Sridharan Signed-off-by: Jaroslav Kysela --- topology/pre-process-dapm.c | 287 ++++++++++++++++++++++++++++++++++ topology/pre-process-object.c | 1 + topology/pre-processor.h | 2 + 3 files changed, 290 insertions(+) diff --git a/topology/pre-process-dapm.c b/topology/pre-process-dapm.c index 63dd1fb..d2b3b7a 100644 --- a/topology/pre-process-dapm.c +++ b/topology/pre-process-dapm.c @@ -129,3 +129,290 @@ int tplg_build_bytes_control(struct tplg_pre_processor *tplg_pp, snd_config_t *o { return tplg_build_control(tplg_pp, obj_cfg, parent, "bytes"); } + +/* + * Widget names for pipeline endpoints can be of the following type: + * "class. ex: pga.0.1, buffer.1.1 etc + * Optionally, the index argument for a widget can be omitted and will be substituted with + * the index from the route: ex: pga..0, host..playback etc + */ +static int tplg_pp_get_widget_name(struct tplg_pre_processor *tplg_pp, + const char *string, long index, char **widget) +{ + snd_config_iterator_t i, next; + snd_config_t *temp_cfg, *child, *class_cfg, *n; + char *class_name, *args, *widget_name; + int ret; + + /* get class name */ + args = strchr(string, '.'); + class_name = calloc(1, strlen(string) - strlen(args) + 1); + if (!class_name) + return -ENOMEM; + + snprintf(class_name, strlen(string) - strlen(args) + 1, "%s", string); + + /* create config with Widget class type */ + ret = snd_config_make(&temp_cfg, "Widget", SND_CONFIG_TYPE_COMPOUND); + if (ret < 0) { + free(class_name); + return ret; + } + + /* create config with class name and add it to the Widget config */ + ret = tplg_config_make_add(&child, class_name, SND_CONFIG_TYPE_COMPOUND, temp_cfg); + if (ret < 0) { + free(class_name); + return ret; + } + + /* get class definition for widget */ + class_cfg = tplg_class_lookup(tplg_pp, temp_cfg); + snd_config_delete(temp_cfg); + if (!class_cfg) { + free(class_name); + return -EINVAL; + } + + /* get constructor for class */ + ret = snd_config_search(class_cfg, "attributes.constructor", &temp_cfg); + if (ret < 0) { + SNDERR("No arguments in class for widget %s\n", string); + free(class_name); + return ret; + } + + widget_name = strdup(class_name); + free(class_name); + if (!widget_name) + return -ENOMEM; + + /* construct widget name using the constructor argument values */ + snd_config_for_each(i, next, temp_cfg) { + const char *id; + char *arg, *remaining, *temp; + + n = snd_config_iterator_entry(i); + if (snd_config_get_string(n, &id) < 0) + continue; + + if (!args) { + SNDERR("insufficient arugments for widget %s\n", string); + return -EINVAL; + } + + remaining = strchr(args + 1, '.'); + if (remaining) { + arg = calloc(1, strlen(args + 1) - strlen(remaining) + 1); + if (!arg) { + ret = -ENOMEM; + goto err; + } + snprintf(arg, strlen(args + 1) - strlen(remaining) + 1, "%s", args + 1); + } else { + arg = calloc(1, strlen(args + 1) + 1); + if (!arg) { + ret = -ENOMEM; + goto err; + } + + snprintf(arg, strlen(args + 1) + 1, "%s", args + 1); + } + + /* if no index provided, substitue with route index */ + if (!strcmp(arg, "") && !strcmp(id, "index")) { + free(arg); + arg = tplg_snprintf("%ld", index); + if (!arg) { + ret = -ENOMEM; + free(arg); + goto err; + } + } + + temp = tplg_snprintf("%s.%s", widget_name, arg); + if (!temp) { + ret = -ENOMEM; + free(arg); + goto err; + } + + free(widget_name); + widget_name = temp; + free(arg); + if (remaining) + args = remaining; + else + args = NULL; + } + + *widget = widget_name; + return 0; + +err: + free(widget_name); + return ret; +} + +int tplg_build_dapm_route_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, + snd_config_t *parent) +{ + snd_config_t *top, *obj, *cfg, *route, *child, *parent_obj; + const char *name, *wname; + const char *parent_name = "Endpoint"; + char *src_widget_name, *sink_widget_name, *line_str, *route_name; + const char *control = ""; + long index = 0; + int ret; + + obj = tplg_object_get_instance_config(tplg_pp, obj_cfg); + + ret = snd_config_get_id(obj, &name); + if (ret < 0) + return -EINVAL; + + /* endpoint connections at the top-level conf have no parent */ + if (parent) { + parent_obj = tplg_object_get_instance_config(tplg_pp, parent); + + ret = snd_config_get_id(parent_obj, &parent_name); + if (ret < 0) + return -EINVAL; + } + + tplg_pp_debug("Building DAPM route object: '%s' ...", name); + + ret = snd_config_search(tplg_pp->output_cfg, "SectionGraph", &top); + if (ret < 0) { + ret = tplg_config_make_add(&top, "SectionGraph", + SND_CONFIG_TYPE_COMPOUND, tplg_pp->output_cfg); + if (ret < 0) { + SNDERR("Error creating 'SectionGraph' config\n"); + return ret; + } + } + + /* get route index */ + ret = snd_config_search(obj, "index", &cfg); + if (ret >= 0) { + ret = snd_config_get_integer(cfg, &index); + if (ret < 0) { + SNDERR("Invalid index route %s\n", name); + return ret; + } + } + + /* get source widget name */ + ret = snd_config_search(obj, "source", &cfg); + if (ret < 0) { + SNDERR("No source for route %s\n", name); + return ret; + } + + ret = snd_config_get_string(cfg, &wname); + if (ret < 0) { + SNDERR("Invalid name for source in route %s\n", name); + return ret; + } + + ret = tplg_pp_get_widget_name(tplg_pp, wname, index, &src_widget_name); + if (ret < 0) { + SNDERR("error getting widget name for %s\n", wname); + return ret; + } + + /* get sink widget name */ + ret = snd_config_search(obj, "sink", &cfg); + if (ret < 0) { + SNDERR("No sink for route %s\n", name); + free(src_widget_name); + return ret; + } + + ret = snd_config_get_string(cfg, &wname); + if (ret < 0) { + SNDERR("Invalid name for sink in route %s\n", name); + free(src_widget_name); + return ret; + } + + ret = tplg_pp_get_widget_name(tplg_pp, wname, index, &sink_widget_name); + if (ret < 0) { + SNDERR("error getting widget name for %s\n", wname); + free(src_widget_name); + return ret; + } + + /* get control name */ + ret = snd_config_search(obj, "control", &cfg); + if (ret >= 0) { + ret = snd_config_get_string(cfg, &control); + if (ret < 0) { + SNDERR("Invalid control name for route %s\n", name); + goto err; + } + } + + /* add route */ + route_name = tplg_snprintf("%s.%s", parent_name, name); + if (!route_name) { + ret = -ENOMEM; + goto err; + } + + ret = snd_config_make(&route, route_name, SND_CONFIG_TYPE_COMPOUND); + free(route_name); + if (ret < 0) { + SNDERR("Error creating route config for %s %d\n", name, ret); + goto err; + } + + ret = snd_config_add(top, route); + if (ret < 0) { + SNDERR("Error adding route config for %s %d\n", name, ret); + goto err; + } + + /* add index */ + ret = tplg_config_make_add(&child, "index", SND_CONFIG_TYPE_INTEGER, route); + if (ret < 0) { + SNDERR("Error creating index config for %s\n", name); + goto err; + } + + ret = snd_config_set_integer(child, index); + if (ret < 0) { + SNDERR("Error setting index config for %s\n", name); + goto err; + } + + /* add lines */ + ret = tplg_config_make_add(&cfg, "lines", SND_CONFIG_TYPE_COMPOUND, route); + if (ret < 0) { + SNDERR("Error creating lines config for %s\n", name); + goto err; + } + + /* add route string */ + ret = tplg_config_make_add(&child, "0", SND_CONFIG_TYPE_STRING, cfg); + if (ret < 0) { + SNDERR("Error creating lines config for %s\n", name); + goto err; + } + + line_str = tplg_snprintf("%s, %s, %s", src_widget_name, control, sink_widget_name); + if (!line_str) { + ret = -ENOMEM; + goto err; + } + + /* set the line string */ + ret = snd_config_set_string(child, line_str); + free(line_str); + if (ret < 0) + SNDERR("Error creating lines config for %s\n", name); +err: + free(src_widget_name); + free(sink_widget_name); + return ret; +} diff --git a/topology/pre-process-object.c b/topology/pre-process-object.c index 8e49be8..1c8988d 100644 --- a/topology/pre-process-object.c +++ b/topology/pre-process-object.c @@ -899,6 +899,7 @@ const struct build_function_map object_build_map[] = { {"Base", "extops", "extops" ,&tplg_build_ops_object, &ops_config}, {"Base", "channel", "channel", &tplg_build_channel_object, &channel_config}, {"Base", "VendorToken", "SectionVendorTokens", &tplg_build_vendor_token_object, NULL}, + {"Base", "route", "SectionGraph", &tplg_build_dapm_route_object, NULL}, {"Widget", "", "SectionWidget", &tplg_build_generic_object, &widget_config}, {"Control", "mixer", "SectionControlMixer", &tplg_build_mixer_control, &mixer_control_config}, diff --git a/topology/pre-processor.h b/topology/pre-processor.h index e89fad6..9c31107 100644 --- a/topology/pre-processor.h +++ b/topology/pre-processor.h @@ -65,6 +65,8 @@ int tplg_build_mixer_control(struct tplg_pre_processor *tplg_pp, snd_config_t *o snd_config_t *parent); int tplg_build_bytes_control(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, snd_config_t *parent); +int tplg_build_dapm_route_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, + snd_config_t *parent); int tplg_parent_update(struct tplg_pre_processor *tplg_pp, snd_config_t *parent, const char *section_name, const char *item_name);