• 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_tlv_object(struct tplg_pre_processor * tplg_pp,snd_config_t * obj_cfg,snd_config_t * parent)73 int tplg_build_tlv_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, "tlv", name);
91 }
92 
tplg_build_control(struct tplg_pre_processor * tplg_pp,snd_config_t * obj_cfg,snd_config_t * parent,char * type)93 static int tplg_build_control(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
94 			      snd_config_t *parent, char *type)
95 {
96 	snd_config_t *cfg, *obj;
97 	const char *name;
98 	int ret;
99 
100 	obj = tplg_object_get_instance_config(tplg_pp, obj_cfg);
101 
102 	/* get control name */
103 	ret = snd_config_search(obj, "name", &cfg);
104 	if (ret < 0)
105 		return 0;
106 
107 	ret = snd_config_get_string(cfg, &name);
108 	if (ret < 0)
109 		return ret;
110 
111 	ret = tplg_build_object_from_template(tplg_pp, obj_cfg, &cfg, NULL, false);
112 	if (ret < 0)
113 		return ret;
114 
115 	ret = tplg_add_object_data(tplg_pp, obj_cfg, cfg, NULL);
116 	if (ret < 0)
117 		SNDERR("Failed to add data section for %s\n", name);
118 
119 	return tplg_parent_update(tplg_pp, parent, type, name);
120 }
121 
tplg_build_mixer_control(struct tplg_pre_processor * tplg_pp,snd_config_t * obj_cfg,snd_config_t * parent)122 int tplg_build_mixer_control(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
123 			      snd_config_t *parent)
124 {
125 	return tplg_build_control(tplg_pp, obj_cfg, parent, "mixer");
126 }
127 
tplg_build_bytes_control(struct tplg_pre_processor * tplg_pp,snd_config_t * obj_cfg,snd_config_t * parent)128 int tplg_build_bytes_control(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
129 			      snd_config_t *parent)
130 {
131 	return tplg_build_control(tplg_pp, obj_cfg, parent, "bytes");
132 }
133 
134 /*
135  * Widget names for pipeline endpoints can be of the following type:
136  * "class.<constructor args separated by .> ex: pga.0.1, buffer.1.1 etc
137  * Optionally, the index argument for a widget can be omitted and will be substituted with
138  * the index from the route: ex: pga..0, host..playback etc
139  */
tplg_pp_get_widget_name(struct tplg_pre_processor * tplg_pp,const char * string,long index,char ** widget)140 static int tplg_pp_get_widget_name(struct tplg_pre_processor *tplg_pp,
141 				      const char *string, long index, char **widget)
142 {
143 	snd_config_iterator_t i, next;
144 	snd_config_t *temp_cfg, *child, *class_cfg, *n;
145 	char *class_name, *args, *widget_name;
146 	int ret;
147 
148 	/* get class name */
149 	args = strchr(string, '.');
150 	if (!args) {
151 		SNDERR("Error getting class name for %s\n", string);
152 		return -EINVAL;
153 	}
154 
155 	class_name = calloc(1, strlen(string) - strlen(args) + 1);
156 	if (!class_name)
157 		return -ENOMEM;
158 
159 	snprintf(class_name, strlen(string) - strlen(args) + 1, "%s", string);
160 
161 	/* create config with Widget class type */
162 	ret = snd_config_make(&temp_cfg, "Widget", SND_CONFIG_TYPE_COMPOUND);
163 	if (ret < 0) {
164 		free(class_name);
165 		return ret;
166 	}
167 
168 	/* create config with class name and add it to the Widget config */
169 	ret = tplg_config_make_add(&child, class_name, SND_CONFIG_TYPE_COMPOUND, temp_cfg);
170 	if (ret < 0) {
171 		free(class_name);
172 		return ret;
173 	}
174 
175 	/* get class definition for widget */
176 	class_cfg = tplg_class_lookup(tplg_pp, temp_cfg);
177 	snd_config_delete(temp_cfg);
178 	if (!class_cfg) {
179 		free(class_name);
180 		return -EINVAL;
181 	}
182 
183 	/* get constructor for class */
184 	ret = snd_config_search(class_cfg, "attributes.constructor", &temp_cfg);
185 	if (ret < 0) {
186 		SNDERR("No arguments in class for widget %s\n", string);
187 		free(class_name);
188 		return ret;
189 	}
190 
191 	widget_name = strdup(class_name);
192 	free(class_name);
193 	if (!widget_name)
194 		return -ENOMEM;
195 
196 	/* construct widget name using the constructor argument values */
197 	snd_config_for_each(i, next, temp_cfg) {
198 		const char *id;
199 		char *arg, *remaining, *temp;
200 
201 		n = snd_config_iterator_entry(i);
202 		if (snd_config_get_string(n, &id) < 0)
203 			continue;
204 
205 		if (!args) {
206 			SNDERR("insufficient arugments for widget %s\n", string);
207 			ret = -EINVAL;
208 			goto err;
209 		}
210 
211 		remaining = strchr(args + 1, '.');
212 		if (remaining) {
213 			arg = calloc(1, strlen(args + 1) - strlen(remaining) + 1);
214 			if (!arg) {
215 				ret = -ENOMEM;
216 				goto err;
217 			}
218 			snprintf(arg, strlen(args + 1) - strlen(remaining) + 1, "%s", args + 1);
219 		} else {
220 			arg = calloc(1, strlen(args + 1) + 1);
221 			if (!arg) {
222 				ret = -ENOMEM;
223 				goto err;
224 			}
225 
226 			snprintf(arg, strlen(args + 1) + 1, "%s", args + 1);
227 		}
228 
229 		/* if no index provided, substitue with route index */
230 		if (!strcmp(arg, "") && !strcmp(id, "index")) {
231 			free(arg);
232 			arg = tplg_snprintf("%ld", index);
233 			if (!arg) {
234 				ret = -ENOMEM;
235 				free(arg);
236 				goto err;
237 			}
238 		}
239 
240 		temp = tplg_snprintf("%s.%s", widget_name, arg);
241 		if (!temp) {
242 			ret = -ENOMEM;
243 			free(arg);
244 			goto err;
245 		}
246 
247 		free(widget_name);
248 		widget_name = temp;
249 		free(arg);
250 		if (remaining)
251 			args = remaining;
252 		else
253 			args = NULL;
254 	}
255 
256 	*widget = widget_name;
257 	return 0;
258 
259 err:
260 	free(widget_name);
261 	return ret;
262 }
263 
tplg_build_dapm_route_object(struct tplg_pre_processor * tplg_pp,snd_config_t * obj_cfg,snd_config_t * parent)264 int tplg_build_dapm_route_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
265 			      snd_config_t *parent)
266 {
267 	snd_config_t *top, *obj, *cfg, *route, *child, *parent_obj;
268 	const char *name, *wname;
269 	const char *parent_name = "Endpoint";
270 	char *src_widget_name, *sink_widget_name, *line_str, *route_name;
271 	const char *control = "";
272 	long index = 0;
273 	int ret;
274 
275 	obj = tplg_object_get_instance_config(tplg_pp, obj_cfg);
276 
277 	ret = snd_config_get_id(obj, &name);
278 	if (ret < 0)
279 		return -EINVAL;
280 
281 	/* endpoint connections at the top-level conf have no parent */
282 	if (parent) {
283 		parent_obj = tplg_object_get_instance_config(tplg_pp, parent);
284 
285 		ret = snd_config_get_id(parent_obj, &parent_name);
286 		if (ret < 0)
287 			return -EINVAL;
288 	}
289 
290 	tplg_pp_debug("Building DAPM route object: '%s' ...", name);
291 
292 	ret = snd_config_search(tplg_pp->output_cfg, "SectionGraph", &top);
293 	if (ret < 0) {
294 		ret = tplg_config_make_add(&top, "SectionGraph",
295 					  SND_CONFIG_TYPE_COMPOUND, tplg_pp->output_cfg);
296 		if (ret < 0) {
297 			SNDERR("Error creating 'SectionGraph' config\n");
298 			return ret;
299 		}
300 	}
301 
302 	/* get route index */
303 	ret = snd_config_search(obj, "index", &cfg);
304 	if (ret >= 0) {
305 		ret = snd_config_get_integer(cfg, &index);
306 		if (ret < 0) {
307 			SNDERR("Invalid index route %s\n", name);
308 			return ret;
309 		}
310 	}
311 
312 	/* get source widget name */
313 	ret = snd_config_search(obj, "source", &cfg);
314 	if (ret < 0) {
315 		SNDERR("No source for route %s\n", name);
316 		return ret;
317 	}
318 
319 	ret = snd_config_get_string(cfg, &wname);
320 	if (ret < 0) {
321 		SNDERR("Invalid name for source in route %s\n", name);
322 		return ret;
323 	}
324 
325 	ret = tplg_pp_get_widget_name(tplg_pp, wname, index, &src_widget_name);
326 	if (ret < 0) {
327 		SNDERR("error getting widget name for %s\n", wname);
328 		return ret;
329 	}
330 
331 	/* get sink widget name */
332 	ret = snd_config_search(obj, "sink", &cfg);
333 	if (ret < 0) {
334 		SNDERR("No sink for route %s\n", name);
335 		free(src_widget_name);
336 		return ret;
337 	}
338 
339 	ret = snd_config_get_string(cfg, &wname);
340 	if (ret < 0) {
341 		SNDERR("Invalid name for sink in route %s\n", name);
342 		free(src_widget_name);
343 		return ret;
344 	}
345 
346 	ret = tplg_pp_get_widget_name(tplg_pp, wname, index, &sink_widget_name);
347 	if (ret < 0) {
348 		SNDERR("error getting widget name for %s\n", wname);
349 		free(src_widget_name);
350 		return ret;
351 	}
352 
353 	/* get control name */
354 	ret = snd_config_search(obj, "control", &cfg);
355 	if (ret >= 0) {
356 		ret = snd_config_get_string(cfg, &control);
357 		if (ret < 0) {
358 			SNDERR("Invalid control name for route %s\n", name);
359 			goto err;
360 		}
361 	}
362 
363 	/* add route */
364 	route_name = tplg_snprintf("%s.%s", parent_name, name);
365 	if (!route_name) {
366 		ret = -ENOMEM;
367 		goto err;
368 	}
369 
370 	ret = snd_config_make(&route, route_name, SND_CONFIG_TYPE_COMPOUND);
371 	free(route_name);
372 	if (ret < 0) {
373 		SNDERR("Error creating route config for %s %d\n", name, ret);
374 		goto err;
375 	}
376 
377 	ret = snd_config_add(top, route);
378 	if (ret < 0) {
379 		SNDERR("Error adding route config for %s %d\n", name, ret);
380 		goto err;
381 	}
382 
383 	/* add index */
384 	ret = tplg_config_make_add(&child, "index", SND_CONFIG_TYPE_INTEGER, route);
385 	if (ret < 0) {
386 		SNDERR("Error creating index config for %s\n", name);
387 		goto err;
388 	}
389 
390 	ret = snd_config_set_integer(child, index);
391 	if (ret < 0) {
392 		SNDERR("Error setting index config for %s\n", name);
393 		goto err;
394 	}
395 
396 	/* add lines */
397 	ret = tplg_config_make_add(&cfg, "lines", SND_CONFIG_TYPE_COMPOUND, route);
398 	if (ret < 0) {
399 		SNDERR("Error creating lines config for %s\n", name);
400 		goto err;
401 	}
402 
403 	/* add route string */
404 	ret = tplg_config_make_add(&child, "0", SND_CONFIG_TYPE_STRING, cfg);
405 	if (ret < 0) {
406 		SNDERR("Error creating lines config for %s\n", name);
407 		goto err;
408 	}
409 
410 	line_str = tplg_snprintf("%s, %s, %s", sink_widget_name, control, src_widget_name);
411 	if (!line_str) {
412 		ret = -ENOMEM;
413 		goto err;
414 	}
415 
416 	/* set the line string */
417 	ret = snd_config_set_string(child, line_str);
418 	free(line_str);
419 	if (ret < 0)
420 		SNDERR("Error creating lines config for %s\n", name);
421 err:
422 	free(src_widget_name);
423 	free(sink_widget_name);
424 	return ret;
425 }
426 
tplg_get_sample_size_from_format(const char * format)427 static int tplg_get_sample_size_from_format(const char *format)
428 {
429 	if (!strcmp(format, "s32le") || !strcmp(format, "s24le") || !strcmp(format, "float"))
430 		return 4;
431 
432 	if (!strcmp(format, "s16le"))
433 		return 2;
434 
435 	SNDERR("Unsupported format: %s\n", format);
436 	return -EINVAL;
437 }
438 
tplg_update_buffer_auto_attr(struct tplg_pre_processor * tplg_pp,snd_config_t * buffer_cfg,snd_config_t * parent)439 int tplg_update_buffer_auto_attr(struct tplg_pre_processor *tplg_pp,
440 				 snd_config_t *buffer_cfg, snd_config_t *parent)
441 {
442 	snd_config_iterator_t i, next;
443 	snd_config_t *n, *pipeline_cfg, *child;
444 	const char *buffer_id, *format;
445 	long periods, channels, sample_size;
446 	long sched_period, rate, frames;
447 	long buffer_size;
448 	int err;
449 
450 	if (snd_config_get_id(buffer_cfg, &buffer_id) < 0)
451 		return -EINVAL;
452 
453 	if (!parent) {
454 		SNDERR("No parent for buffer %s\n", buffer_id);
455 		return -EINVAL;
456 	}
457 
458 	/* acquire attributes from buffer config */
459 	snd_config_for_each(i, next, buffer_cfg) {
460 		const char *id;
461 
462 		n = snd_config_iterator_entry(i);
463 		if (snd_config_get_id(n, &id) < 0)
464 			continue;
465 
466 		if (!strcmp(id, "periods")) {
467 			if (snd_config_get_integer(n, &periods)) {
468 				SNDERR("Invalid number of periods for buffer %s\n", buffer_id);
469 				return -EINVAL;
470 			}
471 		}
472 
473 		if (!strcmp(id, "channels")) {
474 			if (snd_config_get_integer(n, &channels)) {
475 				SNDERR("Invalid number of channels for buffer %s\n", buffer_id);
476 				return -EINVAL;
477 			}
478 		}
479 
480 		if (!strcmp(id, "format")) {
481 			if (snd_config_get_string(n, &format)) {
482 				SNDERR("Invalid format for buffer %s\n", buffer_id);
483 				return -EINVAL;
484 			}
485 		}
486 	}
487 
488 	pipeline_cfg = tplg_object_get_instance_config(tplg_pp, parent);
489 
490 	/* acquire some other attributes from parent pipeline config */
491 	snd_config_for_each(i, next, pipeline_cfg) {
492 		const char *id;
493 
494 		n = snd_config_iterator_entry(i);
495 		if (snd_config_get_id(n, &id) < 0)
496 			continue;
497 
498 		if (!strcmp(id, "period")) {
499 			if (snd_config_get_integer(n, &sched_period)) {
500 				SNDERR("Invalid period for buffer %s\n", buffer_id);
501 				return -EINVAL;
502 			}
503 		}
504 
505 		if (!strcmp(id, "rate")) {
506 			if (snd_config_get_integer(n, &rate)) {
507 				SNDERR("Invalid rate for buffer %s\n", buffer_id);
508 				return -EINVAL;
509 			}
510 		}
511 	}
512 
513 	/* calculate buffer size */
514 	sample_size = tplg_get_sample_size_from_format(format);
515 	if (sample_size < 0) {
516 		SNDERR("Invalid sample size value for %s\n", buffer_id);
517 		return sample_size;
518 	}
519 	frames = (rate * sched_period) / 1000000;
520 	buffer_size = periods * sample_size * channels * frames;
521 
522 	/* add size child config to buffer config */
523 	err = tplg_config_make_add(&child, "size", SND_CONFIG_TYPE_INTEGER, buffer_cfg);
524 	if (err < 0) {
525 		SNDERR("Error creating size config for %s\n", buffer_id);
526 		return err;
527 	}
528 
529 	err = snd_config_set_integer(child, buffer_size);
530 	if (err < 0)
531 		SNDERR("Error setting size config for %s\n", buffer_id);
532 
533 	return err;
534 }
535