• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2   Copyright(c) 2021 Intel Corporation
3   All rights reserved.
4 
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of version 2 of the GNU General Public License as
7   published by the Free Software Foundation.
8 
9   This program is distributed in the hope that it will be useful, but
10   WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   General Public License for more details.
13 
14   You should have received a copy of the GNU General Public License
15   along with this program; if not, write to the Free Software
16   Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17   The full GNU General Public License is included in this distribution
18   in the file called LICENSE.GPL.
19 */
20 #include <errno.h>
21 #include <stdio.h>
22 #include <alsa/asoundlib.h>
23 #include "topology.h"
24 #include "pre-processor.h"
25 
tplg_build_base_object(struct tplg_pre_processor * tplg_pp,snd_config_t * obj_cfg,snd_config_t * parent,bool skip_name)26 int tplg_build_base_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
27 			   snd_config_t *parent, bool skip_name)
28 {
29 	snd_config_t *top, *parent_obj, *cfg, *dest;
30 	const char *parent_name;
31 
32 	/* find parent section config */
33 	top = tplg_object_get_section(tplg_pp, parent);
34 	if (!top)
35 		return -EINVAL;
36 
37 	parent_obj = tplg_object_get_instance_config(tplg_pp, parent);
38 
39 	/* get parent name */
40 	parent_name = tplg_object_get_name(tplg_pp, parent_obj);
41 	if (!parent_name)
42 		return 0;
43 
44 	/* find parent config with name */
45 	dest = tplg_find_config(top, parent_name);
46 	if (!dest) {
47 		SNDERR("Cannot find parent config %s\n", parent_name);
48 		return -EINVAL;
49 	}
50 
51 	/* build config from template and add to parent */
52 	return tplg_build_object_from_template(tplg_pp, obj_cfg, &cfg, dest, skip_name);
53 }
54 
tplg_build_scale_object(struct tplg_pre_processor * tplg_pp,snd_config_t * obj_cfg,snd_config_t * parent)55 int tplg_build_scale_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
56 			      snd_config_t *parent)
57 {
58 	return tplg_build_base_object(tplg_pp, obj_cfg, parent, true);
59 }
60 
tplg_build_ops_object(struct tplg_pre_processor * tplg_pp,snd_config_t * obj_cfg,snd_config_t * parent)61 int tplg_build_ops_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
62 			      snd_config_t *parent)
63 {
64 	return tplg_build_base_object(tplg_pp, obj_cfg, parent, false);
65 }
66 
tplg_build_channel_object(struct tplg_pre_processor * tplg_pp,snd_config_t * obj_cfg,snd_config_t * parent)67 int tplg_build_channel_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
68 			      snd_config_t *parent)
69 {
70 	return tplg_build_base_object(tplg_pp, obj_cfg, parent, false);
71 }
72 
tplg_build_text_object(struct tplg_pre_processor * tplg_pp,snd_config_t * obj_cfg,snd_config_t * parent)73 int tplg_build_text_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
74 			   snd_config_t *parent)
75 {
76 	snd_config_t *cfg;
77 	const char *name;
78 	int ret;
79 
80 	cfg = tplg_object_get_instance_config(tplg_pp, obj_cfg);
81 
82 	name = tplg_object_get_name(tplg_pp, cfg);
83 	if (!name)
84 		return -EINVAL;
85 
86 	ret = tplg_build_object_from_template(tplg_pp, obj_cfg, &cfg, NULL, false);
87 	if (ret < 0)
88 		return ret;
89 
90 	return tplg_parent_update(tplg_pp, parent, "texts", name);
91 }
92 
tplg_build_tlv_object(struct tplg_pre_processor * tplg_pp,snd_config_t * obj_cfg,snd_config_t * parent)93 int tplg_build_tlv_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
94 			      snd_config_t *parent)
95 {
96 	snd_config_t *cfg;
97 	const char *name;
98 	int ret;
99 
100 	cfg = tplg_object_get_instance_config(tplg_pp, obj_cfg);
101 
102 	name = tplg_object_get_name(tplg_pp, cfg);
103 	if (!name)
104 		return -EINVAL;
105 
106 	ret = tplg_build_object_from_template(tplg_pp, obj_cfg, &cfg, NULL, false);
107 	if (ret < 0)
108 		return ret;
109 
110 	return tplg_parent_update(tplg_pp, parent, "tlv", name);
111 }
112 
tplg_build_control(struct tplg_pre_processor * tplg_pp,snd_config_t * obj_cfg,snd_config_t * parent,char * type)113 static int tplg_build_control(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
114 			      snd_config_t *parent, char *type)
115 {
116 	snd_config_t *cfg, *obj;
117 	const char *name;
118 	int ret;
119 
120 	obj = tplg_object_get_instance_config(tplg_pp, obj_cfg);
121 
122 	/* get control name */
123 	ret = snd_config_search(obj, "name", &cfg);
124 	if (ret < 0)
125 		return 0;
126 
127 	ret = snd_config_get_string(cfg, &name);
128 	if (ret < 0)
129 		return ret;
130 
131 	ret = tplg_build_object_from_template(tplg_pp, obj_cfg, &cfg, NULL, false);
132 	if (ret < 0)
133 		return ret;
134 
135 	ret = tplg_add_object_data(tplg_pp, obj_cfg, cfg, NULL);
136 	if (ret < 0)
137 		SNDERR("Failed to add data section for %s\n", name);
138 
139 	return tplg_parent_update(tplg_pp, parent, type, name);
140 }
141 
tplg_build_mixer_control(struct tplg_pre_processor * tplg_pp,snd_config_t * obj_cfg,snd_config_t * parent)142 int tplg_build_mixer_control(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
143 			      snd_config_t *parent)
144 {
145 	return tplg_build_control(tplg_pp, obj_cfg, parent, "mixer");
146 }
147 
tplg_build_bytes_control(struct tplg_pre_processor * tplg_pp,snd_config_t * obj_cfg,snd_config_t * parent)148 int tplg_build_bytes_control(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
149 			      snd_config_t *parent)
150 {
151 	return tplg_build_control(tplg_pp, obj_cfg, parent, "bytes");
152 }
153 
tplg_build_enum_control(struct tplg_pre_processor * tplg_pp,snd_config_t * obj_cfg,snd_config_t * parent)154 int tplg_build_enum_control(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
155 			     snd_config_t *parent)
156 {
157 	return tplg_build_control(tplg_pp, obj_cfg, parent, "enum");
158 }
159 
160 /*
161  * Widget names for pipeline endpoints can be of the following type:
162  * "class.<constructor args separated by .> ex: pga.0.1, buffer.1.1 etc
163  * Optionally, the index argument for a widget can be omitted and will be substituted with
164  * the index from the route: ex: pga..0, host..playback etc
165  */
tplg_pp_get_widget_name(struct tplg_pre_processor * tplg_pp,const char * string,long index,char ** widget)166 static int tplg_pp_get_widget_name(struct tplg_pre_processor *tplg_pp,
167 				      const char *string, long index, char **widget)
168 {
169 	snd_config_iterator_t i, next;
170 	snd_config_t *temp_cfg, *child, *class_cfg, *n;
171 	char *class_name, *args, *widget_name;
172 	int ret;
173 
174 	/* get class name */
175 	args = strchr(string, '.');
176 	if (!args) {
177 		SNDERR("Error getting class name for %s\n", string);
178 		return -EINVAL;
179 	}
180 
181 	class_name = calloc(1, strlen(string) - strlen(args) + 1);
182 	if (!class_name)
183 		return -ENOMEM;
184 
185 	snprintf(class_name, strlen(string) - strlen(args) + 1, "%s", string);
186 
187 	/* create config with Widget class type */
188 	ret = snd_config_make(&temp_cfg, "Widget", SND_CONFIG_TYPE_COMPOUND);
189 	if (ret < 0) {
190 		free(class_name);
191 		return ret;
192 	}
193 
194 	/* create config with class name and add it to the Widget config */
195 	ret = tplg_config_make_add(&child, class_name, SND_CONFIG_TYPE_COMPOUND, temp_cfg);
196 	if (ret < 0) {
197 		free(class_name);
198 		return ret;
199 	}
200 
201 	/* get class definition for widget */
202 	class_cfg = tplg_class_lookup(tplg_pp, temp_cfg);
203 	snd_config_delete(temp_cfg);
204 	if (!class_cfg) {
205 		free(class_name);
206 		return -EINVAL;
207 	}
208 
209 	/* get constructor for class */
210 	ret = snd_config_search(class_cfg, "attributes.constructor", &temp_cfg);
211 	if (ret < 0) {
212 		SNDERR("No arguments in class for widget %s\n", string);
213 		free(class_name);
214 		return ret;
215 	}
216 
217 	widget_name = strdup(class_name);
218 	free(class_name);
219 	if (!widget_name)
220 		return -ENOMEM;
221 
222 	/* construct widget name using the constructor argument values */
223 	snd_config_for_each(i, next, temp_cfg) {
224 		const char *id;
225 		char *arg, *remaining, *temp;
226 
227 		n = snd_config_iterator_entry(i);
228 		if (snd_config_get_string(n, &id) < 0)
229 			continue;
230 
231 		if (!args) {
232 			SNDERR("insufficient arugments for widget %s\n", string);
233 			ret = -EINVAL;
234 			goto err;
235 		}
236 
237 		remaining = strchr(args + 1, '.');
238 		if (remaining) {
239 			arg = calloc(1, strlen(args + 1) - strlen(remaining) + 1);
240 			if (!arg) {
241 				ret = -ENOMEM;
242 				goto err;
243 			}
244 			snprintf(arg, strlen(args + 1) - strlen(remaining) + 1, "%s", args + 1);
245 		} else {
246 			arg = calloc(1, strlen(args + 1) + 1);
247 			if (!arg) {
248 				ret = -ENOMEM;
249 				goto err;
250 			}
251 
252 			snprintf(arg, strlen(args + 1) + 1, "%s", args + 1);
253 		}
254 
255 		/* if no index provided, substitue with route index */
256 		if (!strcmp(arg, "") && !strcmp(id, "index")) {
257 			free(arg);
258 			arg = tplg_snprintf("%ld", index);
259 			if (!arg) {
260 				ret = -ENOMEM;
261 				free(arg);
262 				goto err;
263 			}
264 		}
265 
266 		temp = tplg_snprintf("%s.%s", widget_name, arg);
267 		if (!temp) {
268 			ret = -ENOMEM;
269 			free(arg);
270 			goto err;
271 		}
272 
273 		free(widget_name);
274 		widget_name = temp;
275 		free(arg);
276 		if (remaining)
277 			args = remaining;
278 		else
279 			args = NULL;
280 	}
281 
282 	*widget = widget_name;
283 	return 0;
284 
285 err:
286 	free(widget_name);
287 	return ret;
288 }
289 
tplg_build_dapm_route_object(struct tplg_pre_processor * tplg_pp,snd_config_t * obj_cfg,snd_config_t * parent)290 int tplg_build_dapm_route_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
291 			      snd_config_t *parent)
292 {
293 	snd_config_t *top, *obj, *cfg, *route, *child, *parent_obj;
294 	const char *name, *wname;
295 	const char *parent_name = "Endpoint";
296 	char *src_widget_name, *sink_widget_name, *line_str, *route_name;
297 	const char *control = "";
298 	long index = 0;
299 	int ret;
300 
301 	obj = tplg_object_get_instance_config(tplg_pp, obj_cfg);
302 
303 	ret = snd_config_get_id(obj, &name);
304 	if (ret < 0)
305 		return -EINVAL;
306 
307 	/* endpoint connections at the top-level conf have no parent */
308 	if (parent) {
309 		parent_obj = tplg_object_get_instance_config(tplg_pp, parent);
310 
311 		ret = snd_config_get_id(parent_obj, &parent_name);
312 		if (ret < 0)
313 			return -EINVAL;
314 	}
315 
316 	tplg_pp_debug("Building DAPM route object: '%s' ...", name);
317 
318 	ret = snd_config_search(tplg_pp->output_cfg, "SectionGraph", &top);
319 	if (ret < 0) {
320 		ret = tplg_config_make_add(&top, "SectionGraph",
321 					  SND_CONFIG_TYPE_COMPOUND, tplg_pp->output_cfg);
322 		if (ret < 0) {
323 			SNDERR("Error creating 'SectionGraph' config\n");
324 			return ret;
325 		}
326 	}
327 
328 	/* get route index */
329 	ret = snd_config_search(obj, "index", &cfg);
330 	if (ret >= 0) {
331 		ret = snd_config_get_integer(cfg, &index);
332 		if (ret < 0) {
333 			SNDERR("Invalid index route %s\n", name);
334 			return ret;
335 		}
336 	}
337 
338 	/* get source widget name */
339 	ret = snd_config_search(obj, "source", &cfg);
340 	if (ret < 0) {
341 		SNDERR("No source for route %s\n", name);
342 		return ret;
343 	}
344 
345 	ret = snd_config_get_string(cfg, &wname);
346 	if (ret < 0) {
347 		SNDERR("Invalid name for source in route %s\n", name);
348 		return ret;
349 	}
350 
351 	ret = tplg_pp_get_widget_name(tplg_pp, wname, index, &src_widget_name);
352 	if (ret < 0) {
353 		SNDERR("error getting widget name for %s\n", wname);
354 		return ret;
355 	}
356 
357 	/* get sink widget name */
358 	ret = snd_config_search(obj, "sink", &cfg);
359 	if (ret < 0) {
360 		SNDERR("No sink for route %s\n", name);
361 		free(src_widget_name);
362 		return ret;
363 	}
364 
365 	ret = snd_config_get_string(cfg, &wname);
366 	if (ret < 0) {
367 		SNDERR("Invalid name for sink in route %s\n", name);
368 		free(src_widget_name);
369 		return ret;
370 	}
371 
372 	ret = tplg_pp_get_widget_name(tplg_pp, wname, index, &sink_widget_name);
373 	if (ret < 0) {
374 		SNDERR("error getting widget name for %s\n", wname);
375 		free(src_widget_name);
376 		return ret;
377 	}
378 
379 	/* get control name */
380 	ret = snd_config_search(obj, "control", &cfg);
381 	if (ret >= 0) {
382 		ret = snd_config_get_string(cfg, &control);
383 		if (ret < 0) {
384 			SNDERR("Invalid control name for route %s\n", name);
385 			goto err;
386 		}
387 	}
388 
389 	/* add route */
390 	route_name = tplg_snprintf("%s.%s", parent_name, name);
391 	if (!route_name) {
392 		ret = -ENOMEM;
393 		goto err;
394 	}
395 
396 	ret = snd_config_make(&route, route_name, SND_CONFIG_TYPE_COMPOUND);
397 	free(route_name);
398 	if (ret < 0) {
399 		SNDERR("Error creating route config for %s %d\n", name, ret);
400 		goto err;
401 	}
402 
403 	ret = snd_config_add(top, route);
404 	if (ret < 0) {
405 		SNDERR("Error adding route config for %s %d\n", name, ret);
406 		goto err;
407 	}
408 
409 	/* add index */
410 	ret = tplg_config_make_add(&child, "index", SND_CONFIG_TYPE_INTEGER, route);
411 	if (ret < 0) {
412 		SNDERR("Error creating index config for %s\n", name);
413 		goto err;
414 	}
415 
416 	ret = snd_config_set_integer(child, index);
417 	if (ret < 0) {
418 		SNDERR("Error setting index config for %s\n", name);
419 		goto err;
420 	}
421 
422 	/* add lines */
423 	ret = tplg_config_make_add(&cfg, "lines", SND_CONFIG_TYPE_COMPOUND, route);
424 	if (ret < 0) {
425 		SNDERR("Error creating lines config for %s\n", name);
426 		goto err;
427 	}
428 
429 	/* add route string */
430 	ret = tplg_config_make_add(&child, "0", SND_CONFIG_TYPE_STRING, cfg);
431 	if (ret < 0) {
432 		SNDERR("Error creating lines config for %s\n", name);
433 		goto err;
434 	}
435 
436 	line_str = tplg_snprintf("%s, %s, %s", sink_widget_name, control, src_widget_name);
437 	if (!line_str) {
438 		ret = -ENOMEM;
439 		goto err;
440 	}
441 
442 	/* set the line string */
443 	ret = snd_config_set_string(child, line_str);
444 	free(line_str);
445 	if (ret < 0)
446 		SNDERR("Error creating lines config for %s\n", name);
447 err:
448 	free(src_widget_name);
449 	free(sink_widget_name);
450 	return ret;
451 }
452