Skip to content

Commit

Permalink
topology: pre-processor: Introduce a new feature for subtree copies
Browse files Browse the repository at this point in the history
Introduce a new kyword "Subtreecopy" for extneding existing conf nodes
with additional nodes. This feature is useful for extending previous
pipeline class definitions with the addition of one or more widgets
without having to duplicate everything in the new class definition.

For example: Consider a pipeline class definition as below. Note that
only the widgets & routes are shown here.

Class.Pipeline.mixout-gain-dai-copier-playback {
	Object.Widget {
		mixout."1" {}
		dai-copier."1" {}
		gain."1" {}
		pipeline."1" {}
	}

	Object.Base {
		!route [
			{
				source mixout.$index.1
				sink	gain.$index.1
			}
		]
	}
}

If we want to extend this pipeline with the addition of an eqiir/eqfir,
we can create a Subtreecopy node with type extend as follows:

Subtreecopy.mixout-gain-eqiir-eqfir-dai-copier-playback {
	source "Class.Pipeline.mixout-gain-dai-copier-playback"
	target "Class.Pipeline"
	type extend

	Object.Widget {
		eqiir.1 {}
		eqfir.1 {}
	}

	Object.Base {
		!route [
			{
				source gain.$index.1
				sink   eqiir.$index.1
			}
			{
				source eqiir.$index.1
				sink   eqfir.$index.1
			}
		]
	}
}

But if we want to modify an existing pipeline class while modifying the
order of widgets and/or inserting new widgets, we should use the type
"override" instead. This allows for adding new widgets to the list of
widgets in the base class definition while also allowing overriding the
routes to allow inserting the new widgets and reordering the widgets in
the base class. For example, if we want to add a drc widget between the
gain and the eqiir modules in the above class, we can do the following:

Subtreecopy.mixout-efx-dai-copier-playback {
	source "Class.Pipeline.mixout-gain-eqiir-eqfir-dai-copier-playback"
	target "Class.Pipeline"
	type override

	Object.Widget {
		drc.1 {}
	}

	Object.Base {
		!route [
			{
				source mixout.$index.1
				sink	gain.$index.1
			}
			{
				source gain.$index.1
				sink	drc.$index.1
			}
			{
				source	drc.$index.1
				sink	eqiir.$index.1
			}
			{
				source	eqiir.$index.1
				sink	eqfir.$index.1
			}
		]
	}
}

Note that the routes array contains all the routes in the new subtreecopy definition.

This subtreecopy feature can also be used to copy normal config blocks
without having the need to duplicate information.

For example, if all the widgets in a pipeline support the same audio
formats, we can define it as follows:

Class.Pipeline.dai-copier-eqiir-module-copier-capture {
	formats {
		num_input_audio_formats 2
		num_output_audio_formats 2

		Object.Base.input_audio_format [
			{
				in_bit_depth		32
				in_valid_bit_depth	32
			}
			{
				in_channels		4
				in_bit_depth		32
				in_valid_bit_depth	32
				in_ch_cfg	$CHANNEL_CONFIG_3_POINT_1
				in_ch_map	$CHANNEL_MAP_3_POINT_1
			}
		]

		Object.Base.output_audio_format [
			{
				out_bit_depth		32
				out_valid_bit_depth	32
			}
			{
				out_channels		4
				out_bit_depth		32
				out_valid_bit_depth	32
				out_ch_cfg	$CHANNEL_CONFIG_3_POINT_1
				out_ch_map	$CHANNEL_MAP_3_POINT_1
			}
		]
	}

	Object.Widget {
		dai-copier."1" {
			type dai_out
			num_output_pins 1

			Subtreecopy.formats {
				source "Class.Pipeline.dai-copier-eqiir-module-copier-capture.formats"
				type override
			}
		}

		eqiir."1" {
			Subtreecopy.formats {
				source "Class.Pipeline.dai-copier-eqiir-module-copier-capture.formats"
				type override
			}

			Object.Control.bytes."1" {
				IncludeByKey.DMIC0_DAI_EQIIR {
					"passthrough"		"include/components/eqiir/passthrough.conf"
					"highpass_40hz_0db"	"include/components/eqiir/highpass_40hz_0db_48khz.conf"
					"highpass_40hz_20db"	"include/components/eqiir/highpass_40hz_20db_48khz.conf"
				}
			}
		}

		module-copier."2" {
			Subtreecopy.formats {
				source "Class.Pipeline.dai-copier-eqiir-module-copier-capture.formats"
				type override
			}
		}

		pipeline."1" {
			priority	0
			lp_mode		0
		}
	}
}

The subtreecopy node in each widget ensures that the pipeline formats
are applied to all widgets without the need for duplicating them for
each widget.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
  • Loading branch information
ranj063 committed May 22, 2024
1 parent cc0bcef commit 3113646
Showing 1 changed file with 260 additions and 0 deletions.
260 changes: 260 additions & 0 deletions topology/pre-processor.c
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,260 @@ static int pre_process_arrays(struct tplg_pre_processor *tplg_pp, snd_config_t *
return 0;
}

static int pre_process_pipeline_class_copy(struct tplg_pre_processor *tplg_pp, snd_config_t *curr,
snd_config_t *source_tree, snd_config_t *target_tree,
bool override)
{
snd_config_t *tmp, *tmp1, *widget_cfg, *class_cfg;
const char *id;
int ret;

ret = snd_config_get_id(curr, &id);
if (ret < 0)
return ret;

/* class definition exists already, nothing to do */
ret = snd_config_search(target_tree, id, &class_cfg);
if (ret >= 0)
return ret;

/* copy source tree into a new temp node */
ret = snd_config_copy(&tmp, source_tree);
if(ret < 0)
return ret;

/* copy current node into a new temp node */
ret = snd_config_copy(&tmp1, curr);
if(ret < 0) {
snd_config_delete(tmp);
return ret;
}

/* merge the current tree with the source tree */
ret = snd_config_merge(tmp, tmp1, override);
if (ret < 0) {
SNDERR("Failed to merge source tree\n");
snd_config_delete(tmp);
snd_config_delete(tmp1);
return ret;
}

/* set the ID for the new config */
ret = snd_config_set_id(tmp, id);
if (ret < 0) {
snd_config_delete(tmp);
snd_config_delete(tmp1);
return ret;
}

if (!override)
goto out;

/*
* since a merge with override also deleted all widgets from the base class,
* copy them back into the new class config
*/
ret = snd_config_search(source_tree, "Object.Widget", &widget_cfg);
if (ret < 0)
goto out;

ret = snd_config_copy(&tmp1, widget_cfg);
if (ret < 0) {
snd_config_delete(tmp);
SNDERR("failed to copy widgets from the source tree for subtree %s\n", id);
return ret;
}

ret = snd_config_search(tmp, "Object.Widget", &widget_cfg);
if (ret < 0) {
snd_config_delete(tmp);
goto out;
}

ret = snd_config_merge(widget_cfg, tmp1, false);
if (ret < 0) {
snd_config_delete(tmp);
SNDERR("failed to merge widgets from the source tree for %s\n", id);
return ret;
}

out:
ret = snd_config_add(target_tree, tmp);
if (ret < 0)
snd_config_delete(tmp);

return ret;
}

static int pre_process_subtree_copy(struct tplg_pre_processor *tplg_pp, snd_config_t *curr,
snd_config_t *top)
{
snd_config_iterator_t i, next;
snd_config_t *subtrees;
int ret;

ret = snd_config_search(curr, "Subtreecopy", &subtrees);
if (ret < 0)
return 0;

snd_config_for_each(i, next, subtrees) {
snd_config_t *n, *source_cfg, *target_cfg, *type_cfg;
snd_config_t *source_tree, *target_tree, *tmp;
const char *id, *source_id, *type;
const char *target_id = NULL;
bool override = false;

n = snd_config_iterator_entry(i);
ret = snd_config_get_id(n, &id);
if (ret < 0) {
SNDERR("Failed to get ID for subtree copy\n");
return ret;
}

/* get the type of copy ex: override/extend */
ret = snd_config_search(n, "type", &type_cfg);
if (ret < 0) {
SNDERR("failed to get type for subtree %s\n", id);
return ret;
}

ret = snd_config_get_string(type_cfg, &type);
if (ret < 0) {
SNDERR("Invalid subtree copy type id for subtree %s\n", id);
return ret;
}

if (strcmp(type, "override") && strcmp(type, "extend")) {
SNDERR("Invalid value for sub tree copy type %s\n", type);
return ret;
}
if (!strcmp(type, "override"))
override = true;

ret = snd_config_search(n, "source", &source_cfg);
if (ret < 0) {
SNDERR("failed to get source config for subtree %s\n", id);
return ret;
}

/* if the target node is not set, the subtree will be copied to "top" */
ret = snd_config_search(n, "target", &target_cfg);
if (ret >= 0) {
ret = snd_config_get_string(target_cfg, &target_id);
if (ret < 0) {
SNDERR("Invalid source node id for subtree %s\n", id);
return ret;
}

ret = snd_config_search(top, target_id, &target_tree);
if (ret < 0) {
SNDERR("failed to get target tree %s\n", target_id);
return ret;
}
} else {
target_tree = curr;
}

/* get the source tree node */
ret = snd_config_get_string(source_cfg, &source_id);
if (ret < 0) {
SNDERR("Invalid source node id for subtree %s\n", id);
return ret;
}

ret = snd_config_search(top, source_id, &source_tree);
if (ret < 0) {
SNDERR("failed to get source tree %s\n", source_id);
return ret;
}

/* delete the source/target/type nodes */
ret = snd_config_delete(source_cfg);
if (ret < 0)
return ret;

ret = snd_config_delete(type_cfg);
if (ret < 0)
return ret;

/* handle pipeline class definition subtree copies */
if (target_id && !strcmp(target_id, "Class.Pipeline")) {
ret = snd_config_delete(target_cfg);
if (ret < 0)
return ret;

ret = pre_process_pipeline_class_copy(tplg_pp, n, source_tree, target_tree,
override);
if (ret < 0) {
SNDERR("Failed to preprocess pipeline subtree copy for %s\n", id);
return ret;
}
continue;
}

if (target_id) {
ret = snd_config_delete(target_cfg);
if (ret < 0)
return ret;
}

/* handle all other subtree copies */
ret = snd_config_copy(&tmp, source_tree);
if (ret < 0) {
SNDERR("failed to copy source tree for subtreecopy %s\n", id);
return ret;
}

ret = snd_config_merge(tmp, n, override);
if (ret < 0) {
snd_config_delete(tmp);
SNDERR("Failed to merge source tree w/ subtree %s\n", id);
return ret;
}

ret = snd_config_merge(target_tree, tmp, override);
if (ret < 0) {
snd_config_delete(tmp);
SNDERR("Failed to merge subtree w/ target for %s\n", id);
return ret;
}
}

/* all subtree copies have been processed, remove the node */
snd_config_delete(subtrees);

return 0;
}

static int pre_process_subtree_copies(struct tplg_pre_processor *tplg_pp, snd_config_t *top,
snd_config_t *curr)
{
snd_config_iterator_t i, next;
int ret;

if (snd_config_get_type(curr) != SND_CONFIG_TYPE_COMPOUND)
return 0;

/* process subtreecopies at this node */
ret = pre_process_subtree_copy(tplg_pp, curr, top);
if (ret < 0)
return ret;

/* process subtreecopies at all child nodes */
snd_config_for_each(i, next, curr) {
snd_config_t *n;

n = snd_config_iterator_entry(i);

/* process subtreecopies at this node */
ret = pre_process_subtree_copies(tplg_pp, top, n);
if (ret < 0)
return ret;
}

return 0;
}

#endif /* version < 1.2.6 */

int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_size,
Expand Down Expand Up @@ -980,6 +1234,12 @@ int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_
fprintf(stderr, "Failed to process object arrays in input config\n");
goto err;
}

err = pre_process_subtree_copies(tplg_pp, tplg_pp->input_cfg, tplg_pp->input_cfg);
if (err < 0) {
SNDERR("Failed to process subtree copies in input config\n");
goto err;
}
#endif

err = pre_process_config(tplg_pp, tplg_pp->input_cfg);
Expand Down

0 comments on commit 3113646

Please sign in to comment.