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