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