1 /*
2 Copyright(c) 2014-2015 Intel Corporation
3 All rights reserved.
4
5 This library is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as
7 published by the Free Software Foundation; either version 2.1 of
8 the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
14
15 Authors: Mengdong Lin <mengdong.lin@intel.com>
16 Yao Jin <yao.jin@intel.com>
17 Liam Girdwood <liam.r.girdwood@linux.intel.com>
18 */
19
20 #include "tplg_local.h"
21
22 #define RATE(v) [SND_PCM_RATE_##v] = #v
23
24 static const char *const snd_pcm_rate_names[] = {
25 RATE(5512),
26 RATE(8000),
27 RATE(11025),
28 RATE(16000),
29 RATE(22050),
30 RATE(32000),
31 RATE(44100),
32 RATE(48000),
33 RATE(64000),
34 RATE(88200),
35 RATE(96000),
36 RATE(176400),
37 RATE(192000),
38 RATE(CONTINUOUS),
39 RATE(KNOT),
40 };
41
lookup_pcm_dai_stream(struct list_head * base,const char * id)42 struct tplg_elem *lookup_pcm_dai_stream(struct list_head *base, const char* id)
43 {
44 struct list_head *pos;
45 struct tplg_elem *elem;
46 struct snd_soc_tplg_pcm *pcm;
47
48 list_for_each(pos, base) {
49
50 elem = list_entry(pos, struct tplg_elem, list);
51 if (elem->type != SND_TPLG_TYPE_PCM)
52 return NULL;
53
54 pcm = elem->pcm;
55
56 if (pcm && !strcmp(pcm->dai_name, id))
57 return elem;
58 }
59
60 return NULL;
61 }
62
63 /* copy referenced caps to the parent (pcm or be dai) */
copy_stream_caps(const char * id ATTRIBUTE_UNUSED,struct snd_soc_tplg_stream_caps * caps,struct tplg_elem * ref_elem)64 static void copy_stream_caps(const char *id ATTRIBUTE_UNUSED,
65 struct snd_soc_tplg_stream_caps *caps,
66 struct tplg_elem *ref_elem)
67 {
68 struct snd_soc_tplg_stream_caps *ref_caps = ref_elem->stream_caps;
69
70 tplg_dbg("Copy pcm caps (%ld bytes) from '%s' to '%s'",
71 sizeof(*caps), ref_elem->id, id);
72
73 *caps = *ref_caps;
74 }
75
76 /* find and copy the referenced stream caps */
tplg_build_stream_caps(snd_tplg_t * tplg,const char * id,int index,struct snd_soc_tplg_stream_caps * caps)77 static int tplg_build_stream_caps(snd_tplg_t *tplg,
78 const char *id, int index,
79 struct snd_soc_tplg_stream_caps *caps)
80 {
81 struct tplg_elem *ref_elem = NULL;
82 unsigned int i;
83
84 for (i = 0; i < 2; i++) {
85 ref_elem = tplg_elem_lookup(&tplg->pcm_caps_list,
86 caps[i].name, SND_TPLG_TYPE_STREAM_CAPS, index);
87
88 if (ref_elem != NULL)
89 copy_stream_caps(id, &caps[i], ref_elem);
90 }
91
92 return 0;
93 }
94
95 /* build a PCM (FE DAI & DAI link) element */
build_pcm(snd_tplg_t * tplg,struct tplg_elem * elem)96 static int build_pcm(snd_tplg_t *tplg, struct tplg_elem *elem)
97 {
98 struct tplg_ref *ref;
99 struct list_head *base, *pos;
100 int err;
101
102 err = tplg_build_stream_caps(tplg, elem->id, elem->index,
103 elem->pcm->caps);
104 if (err < 0)
105 return err;
106
107 /* merge private data from the referenced data elements */
108 base = &elem->ref_list;
109 list_for_each(pos, base) {
110
111 ref = list_entry(pos, struct tplg_ref, list);
112 if (ref->type == SND_TPLG_TYPE_DATA) {
113 err = tplg_copy_data(tplg, elem, ref);
114 if (err < 0)
115 return err;
116 }
117 if (!ref->elem) {
118 SNDERR("cannot find '%s' referenced by"
119 " PCM '%s'", ref->id, elem->id);
120 return -EINVAL;
121 }
122 }
123
124 return 0;
125 }
126
127 /* build all PCM (FE DAI & DAI link) elements */
tplg_build_pcms(snd_tplg_t * tplg,unsigned int type)128 int tplg_build_pcms(snd_tplg_t *tplg, unsigned int type)
129 {
130 struct list_head *base, *pos;
131 struct tplg_elem *elem;
132 int err = 0;
133
134 base = &tplg->pcm_list;
135 list_for_each(pos, base) {
136
137 elem = list_entry(pos, struct tplg_elem, list);
138 if (elem->type != type) {
139 SNDERR("invalid elem '%s'", elem->id);
140 return -EINVAL;
141 }
142
143 err = build_pcm(tplg, elem);
144 if (err < 0)
145 return err;
146
147 /* add PCM to manifest */
148 tplg->manifest.pcm_elems++;
149 }
150
151 return 0;
152 }
153
154 /* build a physical DAI */
tplg_build_dai(snd_tplg_t * tplg,struct tplg_elem * elem)155 static int tplg_build_dai(snd_tplg_t *tplg, struct tplg_elem *elem)
156 {
157 struct tplg_ref *ref;
158 struct list_head *base, *pos;
159 int err = 0;
160
161 /* get playback & capture stream caps */
162 err = tplg_build_stream_caps(tplg, elem->id, elem->index,
163 elem->dai->caps);
164 if (err < 0)
165 return err;
166
167 /* get private data */
168 base = &elem->ref_list;
169 list_for_each(pos, base) {
170
171 ref = list_entry(pos, struct tplg_ref, list);
172
173 if (ref->type == SND_TPLG_TYPE_DATA) {
174 err = tplg_copy_data(tplg, elem, ref);
175 if (err < 0)
176 return err;
177 }
178 }
179
180 /* add DAI to manifest */
181 tplg->manifest.dai_elems++;
182
183 return 0;
184 }
185
186 /* build physical DAIs*/
tplg_build_dais(snd_tplg_t * tplg,unsigned int type)187 int tplg_build_dais(snd_tplg_t *tplg, unsigned int type)
188 {
189 struct list_head *base, *pos;
190 struct tplg_elem *elem;
191 int err = 0;
192
193 base = &tplg->dai_list;
194 list_for_each(pos, base) {
195
196 elem = list_entry(pos, struct tplg_elem, list);
197 if (elem->type != type) {
198 SNDERR("invalid elem '%s'", elem->id);
199 return -EINVAL;
200 }
201
202 err = tplg_build_dai(tplg, elem);
203 if (err < 0)
204 return err;
205 }
206
207 return 0;
208 }
209
tplg_build_stream_cfg(snd_tplg_t * tplg,struct snd_soc_tplg_stream * stream,int num_streams,int index)210 static int tplg_build_stream_cfg(snd_tplg_t *tplg,
211 struct snd_soc_tplg_stream *stream,
212 int num_streams, int index)
213 {
214 struct snd_soc_tplg_stream *strm;
215 struct tplg_elem *ref_elem;
216 int i;
217
218 for (i = 0; i < num_streams; i++) {
219 strm = stream + i;
220 ref_elem = tplg_elem_lookup(&tplg->pcm_config_list,
221 strm->name, SND_TPLG_TYPE_STREAM_CONFIG, index);
222
223 if (ref_elem && ref_elem->stream_cfg)
224 *strm = *ref_elem->stream_cfg;
225 }
226
227 return 0;
228 }
229
build_link(snd_tplg_t * tplg,struct tplg_elem * elem)230 static int build_link(snd_tplg_t *tplg, struct tplg_elem *elem)
231 {
232 struct snd_soc_tplg_link_config *link = elem->link;
233 struct tplg_ref *ref;
234 struct list_head *base, *pos;
235 int num_hw_configs = 0, err = 0;
236
237 err = tplg_build_stream_cfg(tplg, link->stream,
238 link->num_streams, elem->index);
239 if (err < 0)
240 return err;
241
242 /* hw configs & private data */
243 base = &elem->ref_list;
244 list_for_each(pos, base) {
245
246 ref = list_entry(pos, struct tplg_ref, list);
247
248 switch (ref->type) {
249 case SND_TPLG_TYPE_HW_CONFIG:
250 ref->elem = tplg_elem_lookup(&tplg->hw_cfg_list,
251 ref->id, SND_TPLG_TYPE_HW_CONFIG, elem->index);
252 if (!ref->elem) {
253 SNDERR("cannot find HW config '%s'"
254 " referenced by link '%s'",
255 ref->id, elem->id);
256 return -EINVAL;
257 }
258
259 memcpy(&link->hw_config[num_hw_configs],
260 ref->elem->hw_cfg,
261 sizeof(struct snd_soc_tplg_hw_config));
262 num_hw_configs++;
263 break;
264
265 case SND_TPLG_TYPE_DATA: /* merge private data */
266 err = tplg_copy_data(tplg, elem, ref);
267 if (err < 0)
268 return err;
269 link = elem->link; /* realloc */
270 break;
271
272 default:
273 break;
274 }
275 }
276
277 /* add link to manifest */
278 tplg->manifest.dai_link_elems++;
279
280 return 0;
281 }
282
283 /* build physical DAI link configurations */
tplg_build_links(snd_tplg_t * tplg,unsigned int type)284 int tplg_build_links(snd_tplg_t *tplg, unsigned int type)
285 {
286 struct list_head *base, *pos;
287 struct tplg_elem *elem;
288 int err = 0;
289
290 switch (type) {
291 case SND_TPLG_TYPE_LINK:
292 case SND_TPLG_TYPE_BE:
293 base = &tplg->be_list;
294 break;
295 case SND_TPLG_TYPE_CC:
296 base = &tplg->cc_list;
297 break;
298 default:
299 return -EINVAL;
300 }
301
302 list_for_each(pos, base) {
303
304 elem = list_entry(pos, struct tplg_elem, list);
305 err = build_link(tplg, elem);
306 if (err < 0)
307 return err;
308 }
309
310 return 0;
311 }
312
split_format(struct snd_soc_tplg_stream_caps * caps,char * str)313 static int split_format(struct snd_soc_tplg_stream_caps *caps, char *str)
314 {
315 char *s = NULL;
316 snd_pcm_format_t format;
317 int i = 0;
318
319 s = strtok(str, ",");
320 while ((s != NULL) && (i < SND_SOC_TPLG_MAX_FORMATS)) {
321 format = snd_pcm_format_value(s);
322 if (format == SND_PCM_FORMAT_UNKNOWN) {
323 SNDERR("unsupported stream format %s", s);
324 return -EINVAL;
325 }
326
327 caps->formats |= 1ull << format;
328 s = strtok(NULL, ", ");
329 i++;
330 }
331
332 return 0;
333 }
334
get_rate_value(const char * name)335 static int get_rate_value(const char* name)
336 {
337 int rate;
338 for (rate = 0; rate <= SND_PCM_RATE_LAST; rate++) {
339 if (snd_pcm_rate_names[rate] &&
340 strcasecmp(name, snd_pcm_rate_names[rate]) == 0) {
341 return rate;
342 }
343 }
344
345 return SND_PCM_RATE_UNKNOWN;
346 }
347
get_rate_name(int rate)348 static const char *get_rate_name(int rate)
349 {
350 if (rate >= 0 && rate <= SND_PCM_RATE_LAST)
351 return snd_pcm_rate_names[rate];
352 return NULL;
353 }
354
split_rate(struct snd_soc_tplg_stream_caps * caps,char * str)355 static int split_rate(struct snd_soc_tplg_stream_caps *caps, char *str)
356 {
357 char *s = NULL;
358 snd_pcm_rates_t rate;
359 int i = 0;
360
361 s = strtok(str, ",");
362 while (s) {
363 rate = get_rate_value(s);
364
365 if (rate == SND_PCM_RATE_UNKNOWN) {
366 SNDERR("unsupported stream rate %s", s);
367 return -EINVAL;
368 }
369
370 caps->rates |= 1 << rate;
371 s = strtok(NULL, ", ");
372 i++;
373 }
374
375 return 0;
376 }
377
parse_unsigned(snd_config_t * n,void * dst)378 static int parse_unsigned(snd_config_t *n, void *dst)
379 {
380 int ival;
381
382 if (tplg_get_integer(n, &ival, 0) < 0)
383 return -EINVAL;
384
385 unaligned_put32(dst, ival);
386 #if TPLG_DEBUG
387 {
388 const char *id;
389 if (snd_config_get_id(n, &id) >= 0)
390 tplg_dbg("\t\t%s: %d", id, ival);
391 }
392 #endif
393 return 0;
394 }
395
396 /* Parse pcm stream capabilities */
tplg_parse_stream_caps(snd_tplg_t * tplg,snd_config_t * cfg,void * private ATTRIBUTE_UNUSED)397 int tplg_parse_stream_caps(snd_tplg_t *tplg,
398 snd_config_t *cfg,
399 void *private ATTRIBUTE_UNUSED)
400 {
401 struct snd_soc_tplg_stream_caps *sc;
402 struct tplg_elem *elem;
403 snd_config_iterator_t i, next;
404 snd_config_t *n;
405 const char *id, *val;
406 char *s;
407 int err;
408
409 elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_STREAM_CAPS);
410 if (!elem)
411 return -ENOMEM;
412
413 sc = elem->stream_caps;
414 sc->size = elem->size;
415 snd_strlcpy(sc->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
416
417 tplg_dbg(" PCM Capabilities: %s", elem->id);
418
419 snd_config_for_each(i, next, cfg) {
420 n = snd_config_iterator_entry(i);
421 if (snd_config_get_id(n, &id) < 0)
422 continue;
423
424 /* skip comments */
425 if (strcmp(id, "comment") == 0)
426 continue;
427 if (id[0] == '#')
428 continue;
429
430 if (strcmp(id, "formats") == 0) {
431 if (snd_config_get_string(n, &val) < 0)
432 return -EINVAL;
433
434 s = strdup(val);
435 if (s == NULL)
436 return -ENOMEM;
437
438 err = split_format(sc, s);
439 free(s);
440
441 if (err < 0)
442 return err;
443
444 tplg_dbg("\t\t%s: %s", id, val);
445 continue;
446 }
447
448 if (strcmp(id, "rates") == 0) {
449 if (snd_config_get_string(n, &val) < 0)
450 return -EINVAL;
451
452 s = strdup(val);
453 if (!s)
454 return -ENOMEM;
455
456 err = split_rate(sc, s);
457 free(s);
458
459 if (err < 0)
460 return err;
461
462 tplg_dbg("\t\t%s: %s", id, val);
463 continue;
464 }
465
466 if (strcmp(id, "rate_min") == 0) {
467 if (parse_unsigned(n, &sc->rate_min))
468 return -EINVAL;
469 continue;
470 }
471
472 if (strcmp(id, "rate_max") == 0) {
473 if (parse_unsigned(n, &sc->rate_max))
474 return -EINVAL;
475 continue;
476 }
477
478 if (strcmp(id, "channels_min") == 0) {
479 if (parse_unsigned(n, &sc->channels_min))
480 return -EINVAL;
481 continue;
482 }
483
484 if (strcmp(id, "channels_max") == 0) {
485 if (parse_unsigned(n, &sc->channels_max))
486 return -EINVAL;
487 continue;
488 }
489
490 if (strcmp(id, "periods_min") == 0) {
491 if (parse_unsigned(n, &sc->periods_min))
492 return -EINVAL;
493 continue;
494 }
495
496 if (strcmp(id, "periods_max") == 0) {
497 if (parse_unsigned(n, &sc->periods_max))
498 return -EINVAL;
499 continue;
500 }
501
502 if (strcmp(id, "period_size_min") == 0) {
503 if (parse_unsigned(n, &sc->period_size_min))
504 return -EINVAL;
505 continue;
506 }
507
508 if (strcmp(id, "period_size_max") == 0) {
509 if (parse_unsigned(n, &sc->period_size_max))
510 return -EINVAL;
511 continue;
512 }
513
514 if (strcmp(id, "buffer_size_min") == 0) {
515 if (parse_unsigned(n, &sc->buffer_size_min))
516 return -EINVAL;
517 continue;
518 }
519
520 if (strcmp(id, "buffer_size_max") == 0) {
521 if (parse_unsigned(n, &sc->buffer_size_max))
522 return -EINVAL;
523 continue;
524 }
525
526 if (strcmp(id, "sig_bits") == 0) {
527 if (parse_unsigned(n, &sc->sig_bits))
528 return -EINVAL;
529 continue;
530 }
531
532 }
533
534 return 0;
535 }
536
537 /* save stream caps */
tplg_save_stream_caps(snd_tplg_t * tplg ATTRIBUTE_UNUSED,struct tplg_elem * elem,struct tplg_buf * dst,const char * pfx)538 int tplg_save_stream_caps(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
539 struct tplg_elem *elem,
540 struct tplg_buf *dst, const char *pfx)
541 {
542 struct snd_soc_tplg_stream_caps *sc = elem->stream_caps;
543 const char *s;
544 unsigned int i;
545 int err, first;
546
547 err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
548 if (err >= 0 && sc->formats) {
549 err = tplg_save_printf(dst, pfx, "\tformats '");
550 first = 1;
551 for (i = 0; err >= 0 && i <= SND_PCM_FORMAT_LAST; i++) {
552 if (sc->formats & (1ULL << i)) {
553 s = snd_pcm_format_name(i);
554 err = tplg_save_printf(dst, NULL, "%s%s",
555 !first ? ", " : "", s);
556 first = 0;
557 }
558 }
559 if (err >= 0)
560 err = tplg_save_printf(dst, NULL, "'\n");
561 }
562 if (err >= 0 && sc->rates) {
563 err = tplg_save_printf(dst, pfx, "\trates '");
564 first = 1;
565 for (i = 0; err >= 0 && i <= SND_PCM_RATE_LAST; i++) {
566 if (sc->rates & (1ULL << i)) {
567 s = get_rate_name(i);
568 err = tplg_save_printf(dst, NULL, "%s%s",
569 !first ? ", " : "", s);
570 first = 0;
571 }
572 }
573 if (err >= 0)
574 err = tplg_save_printf(dst, NULL, "'\n");
575 }
576 if (err >= 0 && sc->rate_min)
577 err = tplg_save_printf(dst, pfx, "\trate_min %u\n",
578 sc->rate_min);
579 if (err >= 0 && sc->rate_max)
580 err = tplg_save_printf(dst, pfx, "\trate_max %u\n",
581 sc->rate_max);
582 if (err >= 0 && sc->channels_min)
583 err = tplg_save_printf(dst, pfx, "\tchannels_min %u\n",
584 sc->channels_min);
585 if (err >= 0 && sc->channels_max)
586 err = tplg_save_printf(dst, pfx, "\tchannels_max %u\n",
587 sc->channels_max);
588 if (err >= 0 && sc->periods_min)
589 err = tplg_save_printf(dst, pfx, "\tperiods_min %u\n",
590 sc->periods_min);
591 if (err >= 0 && sc->periods_max)
592 err = tplg_save_printf(dst, pfx, "\tperiods_max %u\n",
593 sc->periods_max);
594 if (err >= 0 && sc->period_size_min)
595 err = tplg_save_printf(dst, pfx, "\tperiod_size_min %u\n",
596 sc->period_size_min);
597 if (err >= 0 && sc->period_size_max)
598 err = tplg_save_printf(dst, pfx, "\tperiod_size_max %u\n",
599 sc->period_size_max);
600 if (err >= 0 && sc->buffer_size_min)
601 err = tplg_save_printf(dst, pfx, "\tbuffer_size_min %u\n",
602 sc->buffer_size_min);
603 if (err >= 0 && sc->buffer_size_max)
604 err = tplg_save_printf(dst, pfx, "\tbuffer_size_max %u\n",
605 sc->buffer_size_max);
606 if (err >= 0 && sc->sig_bits)
607 err = tplg_save_printf(dst, pfx, "\tsig_bits %u\n",
608 sc->sig_bits);
609 if (err >= 0)
610 err = tplg_save_printf(dst, pfx, "}\n");
611 return err;
612 }
613
614 /* Parse the caps and config of a pcm stream */
tplg_parse_streams(snd_tplg_t * tplg ATTRIBUTE_UNUSED,snd_config_t * cfg,void * private)615 static int tplg_parse_streams(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
616 snd_config_t *cfg, void *private)
617 {
618 snd_config_iterator_t i, next;
619 snd_config_t *n;
620 struct tplg_elem *elem = private;
621 struct snd_soc_tplg_pcm *pcm;
622 struct snd_soc_tplg_dai *dai;
623 void *playback, *capture;
624 struct snd_soc_tplg_stream_caps *caps;
625 const char *id, *value;
626 int stream;
627
628 snd_config_get_id(cfg, &id);
629
630 tplg_dbg("\t%s:", id);
631
632 switch (elem->type) {
633 case SND_TPLG_TYPE_PCM:
634 pcm = elem->pcm;
635 playback = &pcm->playback;
636 capture = &pcm->capture;
637 caps = pcm->caps;
638 break;
639
640 case SND_TPLG_TYPE_DAI:
641 dai = elem->dai;
642 playback = &dai->playback;
643 capture = &dai->capture;
644 caps = dai->caps;
645 break;
646
647 default:
648 return -EINVAL;
649 }
650
651 if (strcmp(id, "playback") == 0) {
652 stream = SND_SOC_TPLG_STREAM_PLAYBACK;
653 unaligned_put32(playback, 1);
654 } else if (strcmp(id, "capture") == 0) {
655 stream = SND_SOC_TPLG_STREAM_CAPTURE;
656 unaligned_put32(capture, 1);
657 } else
658 return -EINVAL;
659
660 snd_config_for_each(i, next, cfg) {
661
662 n = snd_config_iterator_entry(i);
663
664 /* get id */
665 if (snd_config_get_id(n, &id) < 0)
666 continue;
667
668 if (strcmp(id, "capabilities") == 0) {
669 if (snd_config_get_string(n, &value) < 0)
670 continue;
671 /* store stream caps name, to find and merge
672 * the caps in building phase.
673 */
674 snd_strlcpy(caps[stream].name, value,
675 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
676
677 tplg_dbg("\t\t%s\n\t\t\t%s", id, value);
678 continue;
679 }
680 }
681
682 return 0;
683 }
684
685 /* Save the caps and config of a pcm stream */
tplg_save_streams(snd_tplg_t * tplg ATTRIBUTE_UNUSED,struct tplg_elem * elem,struct tplg_buf * dst,const char * pfx)686 int tplg_save_streams(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
687 struct tplg_elem *elem,
688 struct tplg_buf *dst, const char *pfx)
689 {
690 static const char *stream_ids[2] = {
691 "playback",
692 "capture"
693 };
694 static unsigned int stream_types[2] = {
695 SND_SOC_TPLG_STREAM_PLAYBACK,
696 SND_SOC_TPLG_STREAM_CAPTURE
697 };
698 struct snd_soc_tplg_stream_caps *caps;
699 unsigned int streams[2], stream;
700 const char *s;
701 int err;
702
703 switch (elem->type) {
704 case SND_TPLG_TYPE_PCM:
705 streams[0] = elem->pcm->playback;
706 streams[1] = elem->pcm->capture;
707 caps = elem->pcm->caps;
708 break;
709 case SND_TPLG_TYPE_DAI:
710 streams[0] = elem->dai->playback;
711 streams[1] = elem->dai->capture;
712 caps = elem->dai->caps;
713 break;
714 default:
715 return -EINVAL;
716 }
717
718 for (stream = 0; stream < 2; stream++) {
719 if (streams[stream] == 0)
720 continue;
721 if (!caps)
722 continue;
723 s = caps[stream_types[stream]].name;
724 if (s[0] == '\0')
725 continue;
726 err = tplg_save_printf(dst, pfx, "pcm.%s {\n", stream_ids[stream]);
727 if (err < 0)
728 return err;
729 err = tplg_save_printf(dst, pfx, "\tcapabilities '%s'\n", s);
730 if (err < 0)
731 return err;
732 err = tplg_save_printf(dst, pfx, "}\n");
733 if (err < 0)
734 return err;
735 }
736
737 return 0;
738 }
739
740 /* Parse name and id of a front-end DAI (ie. cpu dai of a FE DAI link) */
tplg_parse_fe_dai(snd_tplg_t * tplg ATTRIBUTE_UNUSED,snd_config_t * cfg,void * private)741 static int tplg_parse_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
742 snd_config_t *cfg, void *private)
743 {
744 struct tplg_elem *elem = private;
745 struct snd_soc_tplg_pcm *pcm = elem->pcm;
746 snd_config_iterator_t i, next;
747 snd_config_t *n;
748 const char *id;
749 unsigned int dai_id;
750
751 snd_config_get_id(cfg, &id);
752 tplg_dbg("\t\tFE DAI %s:", id);
753 snd_strlcpy(pcm->dai_name, id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
754
755 snd_config_for_each(i, next, cfg) {
756
757 n = snd_config_iterator_entry(i);
758
759 /* get id */
760 if (snd_config_get_id(n, &id) < 0)
761 continue;
762
763 if (strcmp(id, "id") == 0) {
764 if (tplg_get_unsigned(n, &dai_id, 0)) {
765 SNDERR("invalid fe dai ID");
766 return -EINVAL;
767 }
768
769 unaligned_put32(&pcm->dai_id, dai_id);
770 tplg_dbg("\t\t\tindex: %d", dai_id);
771 }
772 }
773
774 return 0;
775 }
776
777 /* Save the caps and config of a pcm stream */
tplg_save_fe_dai(snd_tplg_t * tplg ATTRIBUTE_UNUSED,struct tplg_elem * elem,struct tplg_buf * dst,const char * pfx)778 int tplg_save_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
779 struct tplg_elem *elem,
780 struct tplg_buf *dst, const char *pfx)
781 {
782 struct snd_soc_tplg_pcm *pcm = elem->pcm;
783 int err = 0;
784
785 if (strlen(pcm->dai_name))
786 err = tplg_save_printf(dst, pfx, "dai.'%s'.id %u\n", pcm->dai_name, pcm->dai_id);
787 else if (pcm->dai_id > 0)
788 err = tplg_save_printf(dst, pfx, "dai.0.id %u\n", pcm->dai_id);
789 return err;
790 }
791
792 /* parse a flag bit of the given mask */
parse_flag(snd_config_t * n,unsigned int mask_in,void * mask,void * flags)793 static int parse_flag(snd_config_t *n, unsigned int mask_in,
794 void *mask, void *flags)
795 {
796 int ret;
797
798 ret = snd_config_get_bool(n);
799 if (ret < 0)
800 return ret;
801
802 unaligned_put32(mask, unaligned_get32(mask) | mask_in);
803 if (ret)
804 unaligned_put32(flags, unaligned_get32(flags) | mask_in);
805 else
806 unaligned_put32(flags, unaligned_get32(flags) & (~mask_in));
807
808 return 0;
809 }
810
save_flags(unsigned int flags,unsigned int mask,struct tplg_buf * dst,const char * pfx)811 static int save_flags(unsigned int flags, unsigned int mask,
812 struct tplg_buf *dst, const char *pfx)
813 {
814 static unsigned int flag_masks[4] = {
815 SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES,
816 SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS,
817 SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS,
818 SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP,
819 };
820 static const char *flag_ids[4] = {
821 "symmetric_rates",
822 "symmetric_channels",
823 "symmetric_sample_bits",
824 "ignore_suspend",
825 };
826 unsigned int i;
827 int err = 0;
828
829 for (i = 0; err >= 0 && i < ARRAY_SIZE(flag_masks); i++) {
830 if (mask & flag_masks[i]) {
831 unsigned int v = (flags & flag_masks[i]) ? 1 : 0;
832 err = tplg_save_printf(dst, pfx, "%s %u\n",
833 flag_ids[i], v);
834 }
835 }
836 return err;
837 }
838
839 /* Parse PCM (for front end DAI & DAI link) in text conf file */
tplg_parse_pcm(snd_tplg_t * tplg,snd_config_t * cfg,void * private ATTRIBUTE_UNUSED)840 int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg,
841 void *private ATTRIBUTE_UNUSED)
842 {
843 struct snd_soc_tplg_pcm *pcm;
844 struct tplg_elem *elem;
845 snd_config_iterator_t i, next;
846 snd_config_t *n;
847 const char *id;
848 int err, ival;
849
850 elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_PCM);
851 if (!elem)
852 return -ENOMEM;
853
854 pcm = elem->pcm;
855 pcm->size = elem->size;
856 snd_strlcpy(pcm->pcm_name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
857
858 tplg_dbg(" PCM: %s", elem->id);
859
860 snd_config_for_each(i, next, cfg) {
861
862 n = snd_config_iterator_entry(i);
863 if (snd_config_get_id(n, &id) < 0)
864 continue;
865
866 /* skip comments */
867 if (strcmp(id, "comment") == 0)
868 continue;
869 if (id[0] == '#')
870 continue;
871
872 if (strcmp(id, "id") == 0) {
873 if (parse_unsigned(n, &pcm->pcm_id))
874 return -EINVAL;
875 continue;
876 }
877
878 if (strcmp(id, "pcm") == 0) {
879 err = tplg_parse_compound(tplg, n,
880 tplg_parse_streams, elem);
881 if (err < 0)
882 return err;
883 continue;
884 }
885
886 if (strcmp(id, "compress") == 0) {
887 ival = snd_config_get_bool(n);
888 if (ival < 0)
889 return -EINVAL;
890
891 pcm->compress = ival;
892
893 tplg_dbg("\t%s: %d", id, ival);
894 continue;
895 }
896
897 if (strcmp(id, "dai") == 0) {
898 err = tplg_parse_compound(tplg, n,
899 tplg_parse_fe_dai, elem);
900 if (err < 0)
901 return err;
902 continue;
903 }
904
905 /* flags */
906 if (strcmp(id, "symmetric_rates") == 0) {
907 err = parse_flag(n,
908 SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES,
909 &pcm->flag_mask, &pcm->flags);
910 if (err < 0)
911 return err;
912 continue;
913 }
914
915 if (strcmp(id, "symmetric_channels") == 0) {
916 err = parse_flag(n,
917 SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS,
918 &pcm->flag_mask, &pcm->flags);
919 if (err < 0)
920 return err;
921 continue;
922 }
923
924 if (strcmp(id, "symmetric_sample_bits") == 0) {
925 err = parse_flag(n,
926 SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS,
927 &pcm->flag_mask, &pcm->flags);
928 if (err < 0)
929 return err;
930 continue;
931 }
932
933 if (strcmp(id, "ignore_suspend") == 0) {
934 err = parse_flag(n,
935 SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP,
936 &pcm->flag_mask, &pcm->flags);
937 if (err < 0)
938 return err;
939 continue;
940 }
941
942 /* private data */
943 if (strcmp(id, "data") == 0) {
944 err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
945 if (err < 0)
946 return err;
947 continue;
948 }
949 }
950
951 return 0;
952 }
953
954 /* save PCM */
tplg_save_pcm(snd_tplg_t * tplg ATTRIBUTE_UNUSED,struct tplg_elem * elem,struct tplg_buf * dst,const char * pfx)955 int tplg_save_pcm(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
956 struct tplg_elem *elem,
957 struct tplg_buf *dst, const char *pfx)
958 {
959 struct snd_soc_tplg_pcm *pcm = elem->pcm;
960 char pfx2[16];
961 int err;
962
963 snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
964 err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
965 if (err >= 0 && elem->index)
966 err = tplg_save_printf(dst, pfx, "\tindex %u\n",
967 elem->index);
968 if (err >= 0 && pcm->pcm_id)
969 err = tplg_save_printf(dst, pfx, "\tid %u\n",
970 pcm->pcm_id);
971 if (err >= 0 && pcm->compress)
972 err = tplg_save_printf(dst, pfx, "\tcompress 1\n");
973 snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
974 if (err >= 0)
975 err = tplg_save_fe_dai(tplg, elem, dst, pfx2);
976 if (err >= 0)
977 err = tplg_save_streams(tplg, elem, dst, pfx2);
978 if (err >= 0)
979 err = save_flags(pcm->flags, pcm->flag_mask, dst, pfx);
980 if (err >= 0)
981 err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA,
982 "data", dst, pfx2);
983 if (err >= 0)
984 err = tplg_save_printf(dst, pfx, "}\n");
985 return err;
986 }
987
988 /* Parse physical DAI */
tplg_parse_dai(snd_tplg_t * tplg,snd_config_t * cfg,void * private ATTRIBUTE_UNUSED)989 int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg,
990 void *private ATTRIBUTE_UNUSED)
991 {
992 struct snd_soc_tplg_dai *dai;
993 struct tplg_elem *elem;
994 snd_config_iterator_t i, next;
995 snd_config_t *n;
996 const char *id;
997 int err;
998
999 elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_DAI);
1000 if (!elem)
1001 return -ENOMEM;
1002
1003 dai = elem->dai;
1004 dai->size = elem->size;
1005 snd_strlcpy(dai->dai_name, elem->id,
1006 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1007
1008 tplg_dbg(" DAI: %s", elem->id);
1009
1010 snd_config_for_each(i, next, cfg) {
1011
1012 n = snd_config_iterator_entry(i);
1013 if (snd_config_get_id(n, &id) < 0)
1014 continue;
1015
1016 /* skip comments */
1017 if (strcmp(id, "comment") == 0)
1018 continue;
1019 if (id[0] == '#')
1020 continue;
1021
1022 if (strcmp(id, "id") == 0) {
1023 if (parse_unsigned(n, &dai->dai_id))
1024 return -EINVAL;
1025 continue;
1026 }
1027
1028 if (strcmp(id, "playback") == 0) {
1029 if (parse_unsigned(n, &dai->playback))
1030 return -EINVAL;
1031 continue;
1032 }
1033
1034
1035 if (strcmp(id, "capture") == 0) {
1036 if (parse_unsigned(n, &dai->capture))
1037 return -EINVAL;
1038 continue;
1039 }
1040
1041
1042 /* stream capabilities */
1043 if (strcmp(id, "pcm") == 0) {
1044 err = tplg_parse_compound(tplg, n,
1045 tplg_parse_streams, elem);
1046 if (err < 0)
1047 return err;
1048 continue;
1049 }
1050
1051 /* flags */
1052 if (strcmp(id, "symmetric_rates") == 0) {
1053 err = parse_flag(n,
1054 SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES,
1055 &dai->flag_mask, &dai->flags);
1056 if (err < 0)
1057 return err;
1058 continue;
1059 }
1060
1061 if (strcmp(id, "symmetric_channels") == 0) {
1062 err = parse_flag(n,
1063 SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS,
1064 &dai->flag_mask, &dai->flags);
1065 if (err < 0)
1066 return err;
1067 continue;
1068 }
1069
1070 if (strcmp(id, "symmetric_sample_bits") == 0) {
1071 err = parse_flag(n,
1072 SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS,
1073 &dai->flag_mask, &dai->flags);
1074 if (err < 0)
1075 return err;
1076 continue;
1077 }
1078
1079 if (strcmp(id, "ignore_suspend") == 0) {
1080 err = parse_flag(n,
1081 SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP,
1082 &dai->flag_mask, &dai->flags);
1083 if (err < 0)
1084 return err;
1085 continue;
1086 }
1087
1088 /* private data */
1089 if (strcmp(id, "data") == 0) {
1090 err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
1091 if (err < 0)
1092 return err;
1093 continue;
1094 }
1095 }
1096
1097 return 0;
1098 }
1099
1100 /* save DAI */
tplg_save_dai(snd_tplg_t * tplg ATTRIBUTE_UNUSED,struct tplg_elem * elem,struct tplg_buf * dst,const char * pfx)1101 int tplg_save_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
1102 struct tplg_elem *elem,
1103 struct tplg_buf *dst, const char *pfx)
1104 {
1105 struct snd_soc_tplg_dai *dai = elem->dai;
1106 char pfx2[16];
1107 int err;
1108
1109 if (!dai)
1110 return 0;
1111 snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
1112 err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
1113 if (err >= 0 && elem->index)
1114 err = tplg_save_printf(dst, pfx, "\tindex %u\n",
1115 elem->index);
1116 if (err >= 0 && dai->dai_id)
1117 err = tplg_save_printf(dst, pfx, "\tid %u\n",
1118 dai->dai_id);
1119 if (err >= 0 && dai->playback)
1120 err = tplg_save_printf(dst, pfx, "\tplayback %u\n",
1121 dai->playback);
1122 if (err >= 0 && dai->capture)
1123 err = tplg_save_printf(dst, pfx, "\tcapture %u\n",
1124 dai->capture);
1125 if (err >= 0)
1126 err = tplg_save_streams(tplg, elem, dst, pfx2);
1127 if (err >= 0)
1128 err = save_flags(dai->flags, dai->flag_mask, dst, pfx);
1129 if (err >= 0)
1130 err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA,
1131 "data", dst, pfx2);
1132 if (err >= 0)
1133 err = tplg_save_printf(dst, pfx, "}\n");
1134 return err;
1135 }
1136
1137 /* parse physical link runtime supported HW configs in text conf file */
parse_hw_config_refs(snd_tplg_t * tplg ATTRIBUTE_UNUSED,snd_config_t * cfg,struct tplg_elem * elem)1138 static int parse_hw_config_refs(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
1139 snd_config_t *cfg,
1140 struct tplg_elem *elem)
1141 {
1142 struct snd_soc_tplg_link_config *link = elem->link;
1143 int err;
1144
1145 err = tplg_parse_refs(cfg, elem, SND_TPLG_TYPE_HW_CONFIG);
1146 if (err < 0)
1147 return err;
1148 link->num_hw_configs = err;
1149 return 0;
1150 }
1151
1152 /* Parse a physical link element in text conf file */
tplg_parse_link(snd_tplg_t * tplg,snd_config_t * cfg,void * private ATTRIBUTE_UNUSED)1153 int tplg_parse_link(snd_tplg_t *tplg, snd_config_t *cfg,
1154 void *private ATTRIBUTE_UNUSED)
1155 {
1156 struct snd_soc_tplg_link_config *link;
1157 struct tplg_elem *elem;
1158 snd_config_iterator_t i, next;
1159 snd_config_t *n;
1160 const char *id, *val = NULL;
1161 int err;
1162
1163 elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_BE);
1164 if (!elem)
1165 return -ENOMEM;
1166
1167 link = elem->link;
1168 link->size = elem->size;
1169 snd_strlcpy(link->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1170
1171 tplg_dbg(" Link: %s", elem->id);
1172
1173 snd_config_for_each(i, next, cfg) {
1174
1175 n = snd_config_iterator_entry(i);
1176 if (snd_config_get_id(n, &id) < 0)
1177 continue;
1178
1179 /* skip comments */
1180 if (strcmp(id, "comment") == 0)
1181 continue;
1182 if (id[0] == '#')
1183 continue;
1184
1185 if (strcmp(id, "id") == 0) {
1186 if (parse_unsigned(n, &link->id))
1187 return -EINVAL;
1188 continue;
1189 }
1190
1191 if (strcmp(id, "stream_name") == 0) {
1192 if (snd_config_get_string(n, &val) < 0)
1193 return -EINVAL;
1194
1195 snd_strlcpy(link->stream_name, val,
1196 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1197 tplg_dbg("\t%s: %s", id, val);
1198 continue;
1199 }
1200
1201 if (strcmp(id, "hw_configs") == 0) {
1202 err = parse_hw_config_refs(tplg, n, elem);
1203 if (err < 0)
1204 return err;
1205 continue;
1206 }
1207
1208 if (strcmp(id, "default_hw_conf_id") == 0) {
1209 if (parse_unsigned(n, &link->default_hw_config_id))
1210 return -EINVAL;
1211 continue;
1212 }
1213
1214 /* flags */
1215 if (strcmp(id, "symmetric_rates") == 0) {
1216 err = parse_flag(n,
1217 SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES,
1218 &link->flag_mask, &link->flags);
1219 if (err < 0)
1220 return err;
1221 continue;
1222 }
1223
1224 if (strcmp(id, "symmetric_channels") == 0) {
1225 err = parse_flag(n,
1226 SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS,
1227 &link->flag_mask, &link->flags);
1228 if (err < 0)
1229 return err;
1230 continue;
1231 }
1232
1233 if (strcmp(id, "symmetric_sample_bits") == 0) {
1234 err = parse_flag(n,
1235 SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS,
1236 &link->flag_mask, &link->flags);
1237 if (err < 0)
1238 return err;
1239 continue;
1240 }
1241
1242 if (strcmp(id, "ignore_suspend") == 0) {
1243 err = parse_flag(n,
1244 SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP,
1245 &link->flag_mask, &link->flags);
1246 if (err < 0)
1247 return err;
1248 continue;
1249 }
1250
1251 /* private data */
1252 if (strcmp(id, "data") == 0) {
1253 err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
1254 if (err < 0)
1255 return err;
1256 continue;
1257 }
1258 }
1259
1260 return 0;
1261 }
1262
1263 /* save physical link */
tplg_save_link(snd_tplg_t * tplg ATTRIBUTE_UNUSED,struct tplg_elem * elem,struct tplg_buf * dst,const char * pfx)1264 int tplg_save_link(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
1265 struct tplg_elem *elem,
1266 struct tplg_buf *dst, const char *pfx)
1267 {
1268 struct snd_soc_tplg_link_config *link = elem->link;
1269 char pfx2[16];
1270 int err;
1271
1272 if (!link)
1273 return 0;
1274 snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
1275 err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
1276 if (err >= 0 && elem->index)
1277 err = tplg_save_printf(dst, pfx, "\tindex %u\n",
1278 elem->index);
1279 if (err >= 0 && link->id)
1280 err = tplg_save_printf(dst, pfx, "\tid %u\n",
1281 link->id);
1282 if (err >= 0 && link->stream_name[0])
1283 err = tplg_save_printf(dst, pfx, "\tstream_name '%s'\n",
1284 link->stream_name);
1285 if (err >= 0 && link->default_hw_config_id)
1286 err = tplg_save_printf(dst, pfx, "\tdefault_hw_conf_id %u\n",
1287 link->default_hw_config_id);
1288 if (err >= 0)
1289 err = save_flags(link->flags, link->flag_mask, dst, pfx);
1290 if (err >= 0)
1291 err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_HW_CONFIG,
1292 "hw_configs", dst, pfx2);
1293 if (err >= 0)
1294 err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA,
1295 "data", dst, pfx2);
1296 if (err >= 0)
1297 err = tplg_save_printf(dst, pfx, "}\n");
1298 return err;
1299 }
1300
1301 /* Parse cc */
tplg_parse_cc(snd_tplg_t * tplg,snd_config_t * cfg,void * private ATTRIBUTE_UNUSED)1302 int tplg_parse_cc(snd_tplg_t *tplg, snd_config_t *cfg,
1303 void *private ATTRIBUTE_UNUSED)
1304 {
1305 struct snd_soc_tplg_link_config *link;
1306 struct tplg_elem *elem;
1307 snd_config_iterator_t i, next;
1308 snd_config_t *n;
1309 const char *id;
1310
1311 elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_CC);
1312 if (!elem)
1313 return -ENOMEM;
1314
1315 link = elem->link;
1316 link->size = elem->size;
1317
1318 tplg_dbg(" CC: %s", elem->id);
1319
1320 snd_config_for_each(i, next, cfg) {
1321
1322 n = snd_config_iterator_entry(i);
1323 if (snd_config_get_id(n, &id) < 0)
1324 continue;
1325
1326 /* skip comments */
1327 if (strcmp(id, "comment") == 0)
1328 continue;
1329 if (id[0] == '#')
1330 continue;
1331
1332 if (strcmp(id, "id") == 0) {
1333 if (parse_unsigned(n, &link->id))
1334 return -EINVAL;
1335 continue;
1336 }
1337
1338 }
1339
1340 return 0;
1341 }
1342
1343 /* save CC */
tplg_save_cc(snd_tplg_t * tplg ATTRIBUTE_UNUSED,struct tplg_elem * elem,struct tplg_buf * dst,const char * pfx)1344 int tplg_save_cc(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
1345 struct tplg_elem *elem,
1346 struct tplg_buf *dst, const char *pfx)
1347 {
1348 struct snd_soc_tplg_link_config *link = elem->link;
1349 char pfx2[16];
1350 int err;
1351
1352 if (!link)
1353 return 0;
1354 snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
1355 err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
1356 if (err >= 0 && elem->index)
1357 err = tplg_save_printf(dst, pfx, "\tindex %u\n",
1358 elem->index);
1359 if (err >= 0 && link->id)
1360 err = tplg_save_printf(dst, pfx, "\tid %u\n",
1361 link->id);
1362 if (err >= 0)
1363 err = tplg_save_printf(dst, pfx, "}\n");
1364 return err;
1365 }
1366
1367 #ifndef DOC_HIDDEN
1368 struct audio_hw_format {
1369 unsigned int type;
1370 const char *name;
1371 };
1372 #endif /* DOC_HIDDEN */
1373
1374 static struct audio_hw_format audio_hw_formats[] = {
1375 {
1376 .type = SND_SOC_DAI_FORMAT_I2S,
1377 .name = "I2S",
1378 },
1379 {
1380 .type = SND_SOC_DAI_FORMAT_RIGHT_J,
1381 .name = "RIGHT_J",
1382 },
1383 {
1384 .type = SND_SOC_DAI_FORMAT_LEFT_J,
1385 .name = "LEFT_J",
1386 },
1387 {
1388 .type = SND_SOC_DAI_FORMAT_DSP_A,
1389 .name = "DSP_A",
1390 },
1391 {
1392 .type = SND_SOC_DAI_FORMAT_DSP_B,
1393 .name = "DSP_B",
1394 },
1395 {
1396 .type = SND_SOC_DAI_FORMAT_AC97,
1397 .name = "AC97",
1398 },
1399 {
1400 .type = SND_SOC_DAI_FORMAT_PDM,
1401 .name = "PDM",
1402 },
1403 };
1404
get_audio_hw_format(const char * val)1405 static int get_audio_hw_format(const char *val)
1406 {
1407 unsigned int i;
1408
1409 if (val[0] == '\0')
1410 return -EINVAL;
1411
1412 for (i = 0; i < ARRAY_SIZE(audio_hw_formats); i++)
1413 if (strcasecmp(audio_hw_formats[i].name, val) == 0)
1414 return audio_hw_formats[i].type;
1415
1416 SNDERR("invalid audio HW format %s", val);
1417 return -EINVAL;
1418 }
1419
get_audio_hw_format_name(unsigned int type)1420 static const char *get_audio_hw_format_name(unsigned int type)
1421 {
1422 unsigned int i;
1423
1424 for (i = 0; i < ARRAY_SIZE(audio_hw_formats); i++)
1425 if (audio_hw_formats[i].type == type)
1426 return audio_hw_formats[i].name;
1427 return NULL;
1428 }
1429
tplg_parse_hw_config(snd_tplg_t * tplg,snd_config_t * cfg,void * private ATTRIBUTE_UNUSED)1430 int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg,
1431 void *private ATTRIBUTE_UNUSED)
1432 {
1433
1434 struct snd_soc_tplg_hw_config *hw_cfg;
1435 struct tplg_elem *elem;
1436 snd_config_iterator_t i, next;
1437 snd_config_t *n;
1438 const char *id, *val = NULL;
1439 int ret, ival;
1440 bool provider_legacy;
1441
1442 elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_HW_CONFIG);
1443 if (!elem)
1444 return -ENOMEM;
1445
1446 hw_cfg = elem->hw_cfg;
1447 hw_cfg->size = elem->size;
1448
1449 tplg_dbg(" Link HW config: %s", elem->id);
1450
1451 snd_config_for_each(i, next, cfg) {
1452
1453 n = snd_config_iterator_entry(i);
1454 if (snd_config_get_id(n, &id) < 0)
1455 continue;
1456
1457 /* skip comments */
1458 if (strcmp(id, "comment") == 0)
1459 continue;
1460 if (id[0] == '#')
1461 continue;
1462
1463 if (strcmp(id, "id") == 0) {
1464 if (parse_unsigned(n, &hw_cfg->id))
1465 return -EINVAL;
1466 continue;
1467 }
1468
1469 if (strcmp(id, "format") == 0 ||
1470 strcmp(id, "fmt") == 0) {
1471 if (snd_config_get_string(n, &val) < 0)
1472 return -EINVAL;
1473
1474 ret = get_audio_hw_format(val);
1475 if (ret < 0)
1476 return ret;
1477 hw_cfg->fmt = ret;
1478 continue;
1479 }
1480
1481 provider_legacy = false;
1482 if (strcmp(id, "bclk_master") == 0) {
1483 SNDERR("deprecated option %s, please use 'bclk'", id);
1484 provider_legacy = true;
1485 }
1486
1487 if (provider_legacy ||
1488 strcmp(id, "bclk") == 0) {
1489
1490 if (snd_config_get_string(n, &val) < 0)
1491 return -EINVAL;
1492
1493 if (!strcmp(val, "master")) {
1494 /* For backwards capability,
1495 * "master" == "codec is slave"
1496 */
1497 SNDERR("deprecated bclk value '%s'", val);
1498
1499 hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CC;
1500 } else if (!strcmp(val, "codec_slave")) {
1501 SNDERR("deprecated bclk value '%s', use 'codec_consumer'", val);
1502
1503 hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CC;
1504 } else if (!strcmp(val, "codec_consumer")) {
1505 hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CC;
1506 } else if (!strcmp(val, "codec_master")) {
1507 SNDERR("deprecated bclk value '%s', use 'codec_provider", val);
1508
1509 hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CP;
1510 } else if (!strcmp(val, "codec_provider")) {
1511 hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CP;
1512 }
1513 continue;
1514 }
1515
1516 if (strcmp(id, "bclk_freq") == 0 ||
1517 strcmp(id, "bclk_rate") == 0) {
1518 if (parse_unsigned(n, &hw_cfg->bclk_rate))
1519 return -EINVAL;
1520 continue;
1521 }
1522
1523 if (strcmp(id, "bclk_invert") == 0 ||
1524 strcmp(id, "invert_bclk") == 0) {
1525 ival = snd_config_get_bool(n);
1526 if (ival < 0)
1527 return -EINVAL;
1528
1529 hw_cfg->invert_bclk = ival;
1530 continue;
1531 }
1532
1533 provider_legacy = false;
1534 if (strcmp(id, "fsync_master") == 0) {
1535 SNDERR("deprecated option %s, please use 'fsync'", id);
1536 provider_legacy = true;
1537 }
1538
1539 if (provider_legacy ||
1540 strcmp(id, "fsync") == 0) {
1541
1542 if (snd_config_get_string(n, &val) < 0)
1543 return -EINVAL;
1544
1545 if (!strcmp(val, "master")) {
1546 /* For backwards capability,
1547 * "master" == "codec is slave"
1548 */
1549 SNDERR("deprecated fsync value '%s'", val);
1550
1551 hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CC;
1552 } else if (!strcmp(val, "codec_slave")) {
1553 SNDERR("deprecated fsync value '%s', use 'codec_consumer'", val);
1554
1555 hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CC;
1556 } else if (!strcmp(val, "codec_consumer")) {
1557 hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CC;
1558 } else if (!strcmp(val, "codec_master")) {
1559 SNDERR("deprecated fsync value '%s', use 'codec_provider'", val);
1560
1561 hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CP;
1562 } else if (!strcmp(val, "codec_provider")) {
1563 hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CP;
1564 }
1565 continue;
1566 }
1567
1568 if (strcmp(id, "fsync_invert") == 0 ||
1569 strcmp(id, "invert_fsync") == 0) {
1570 ival = snd_config_get_bool(n);
1571 if (ival < 0)
1572 return -EINVAL;
1573
1574 hw_cfg->invert_fsync = ival;
1575 continue;
1576 }
1577
1578 if (strcmp(id, "fsync_freq") == 0 ||
1579 strcmp(id, "fsync_rate") == 0) {
1580 if (parse_unsigned(n, &hw_cfg->fsync_rate))
1581 return -EINVAL;
1582 continue;
1583 }
1584
1585 if (strcmp(id, "mclk_freq") == 0 ||
1586 strcmp(id, "mclk_rate") == 0) {
1587 if (parse_unsigned(n, &hw_cfg->mclk_rate))
1588 return -EINVAL;
1589 continue;
1590 }
1591
1592 if (strcmp(id, "mclk") == 0 ||
1593 strcmp(id, "mclk_direction") == 0) {
1594 if (snd_config_get_string(n, &val) < 0)
1595 return -EINVAL;
1596
1597 if (!strcmp(val, "master")) {
1598 /* For backwards capability,
1599 * "master" == "for codec, mclk is input"
1600 */
1601 SNDERR("deprecated mclk value '%s'", val);
1602
1603 hw_cfg->mclk_direction = SND_SOC_TPLG_MCLK_CI;
1604 } else if (!strcmp(val, "codec_mclk_in")) {
1605 hw_cfg->mclk_direction = SND_SOC_TPLG_MCLK_CI;
1606 } else if (!strcmp(val, "codec_mclk_out")) {
1607 hw_cfg->mclk_direction = SND_SOC_TPLG_MCLK_CO;
1608 }
1609 continue;
1610 }
1611
1612 if (strcmp(id, "pm_gate_clocks") == 0 ||
1613 strcmp(id, "clock_gated") == 0) {
1614 ival = snd_config_get_bool(n);
1615 if (ival < 0)
1616 return -EINVAL;
1617
1618 if (ival)
1619 hw_cfg->clock_gated =
1620 SND_SOC_TPLG_DAI_CLK_GATE_GATED;
1621 else
1622 hw_cfg->clock_gated =
1623 SND_SOC_TPLG_DAI_CLK_GATE_CONT;
1624 continue;
1625 }
1626
1627 if (strcmp(id, "tdm_slots") == 0) {
1628 if (parse_unsigned(n, &hw_cfg->tdm_slots))
1629 return -EINVAL;
1630 continue;
1631 }
1632
1633 if (strcmp(id, "tdm_slot_width") == 0) {
1634 if (parse_unsigned(n, &hw_cfg->tdm_slot_width))
1635 return -EINVAL;
1636 continue;
1637 }
1638
1639 if (strcmp(id, "tx_slots") == 0) {
1640 if (parse_unsigned(n, &hw_cfg->tx_slots))
1641 return -EINVAL;
1642 continue;
1643 }
1644
1645 if (strcmp(id, "rx_slots") == 0) {
1646 if (parse_unsigned(n, &hw_cfg->rx_slots))
1647 return -EINVAL;
1648 continue;
1649 }
1650
1651 if (strcmp(id, "tx_channels") == 0) {
1652 if (parse_unsigned(n, &hw_cfg->tx_channels))
1653 return -EINVAL;
1654 continue;
1655 }
1656
1657 if (strcmp(id, "rx_channels") == 0) {
1658 if (parse_unsigned(n, &hw_cfg->rx_channels))
1659 return -EINVAL;
1660 continue;
1661 }
1662
1663 }
1664
1665 return 0;
1666 }
1667
1668 /* save hw config */
tplg_save_hw_config(snd_tplg_t * tplg ATTRIBUTE_UNUSED,struct tplg_elem * elem,struct tplg_buf * dst,const char * pfx)1669 int tplg_save_hw_config(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
1670 struct tplg_elem *elem,
1671 struct tplg_buf *dst, const char *pfx)
1672 {
1673 struct snd_soc_tplg_hw_config *hc = elem->hw_cfg;
1674 int err;
1675
1676 err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
1677 if (err >= 0 && hc->id)
1678 err = tplg_save_printf(dst, pfx, "\tid %u\n",
1679 hc->id);
1680 if (err >= 0 && hc->fmt)
1681 err = tplg_save_printf(dst, pfx, "\tformat '%s'\n",
1682 get_audio_hw_format_name(hc->fmt));
1683 if (err >= 0 && hc->bclk_provider)
1684 err = tplg_save_printf(dst, pfx, "\tbclk '%s'\n",
1685 hc->bclk_provider == SND_SOC_TPLG_BCLK_CC ?
1686 "codec_consumer" : "codec_provider");
1687 if (err >= 0 && hc->bclk_rate)
1688 err = tplg_save_printf(dst, pfx, "\tbclk_freq %u\n",
1689 hc->bclk_rate);
1690 if (err >= 0 && hc->invert_bclk)
1691 err = tplg_save_printf(dst, pfx, "\tbclk_invert 1\n");
1692 if (err >= 0 && hc->fsync_provider)
1693 err = tplg_save_printf(dst, pfx, "\tfsync_provider '%s'\n",
1694 hc->fsync_provider == SND_SOC_TPLG_FSYNC_CC ?
1695 "codec_consumer" : "codec_provider");
1696 if (err >= 0 && hc->fsync_rate)
1697 err = tplg_save_printf(dst, pfx, "\tfsync_freq %u\n",
1698 hc->fsync_rate);
1699 if (err >= 0 && hc->invert_fsync)
1700 err = tplg_save_printf(dst, pfx, "\tfsync_invert 1\n");
1701 if (err >= 0 && hc->mclk_rate)
1702 err = tplg_save_printf(dst, pfx, "\tmclk_freq %u\n",
1703 hc->mclk_rate);
1704 if (err >= 0 && hc->mclk_direction)
1705 err = tplg_save_printf(dst, pfx, "\tmclk '%s'\n",
1706 hc->mclk_direction == SND_SOC_TPLG_MCLK_CI ?
1707 "codec_mclk_in" : "codec_mclk_out");
1708 if (err >= 0 && hc->clock_gated)
1709 err = tplg_save_printf(dst, pfx, "\tpm_gate_clocks 1\n");
1710 if (err >= 0 && hc->tdm_slots)
1711 err = tplg_save_printf(dst, pfx, "\ttdm_slots %u\n",
1712 hc->tdm_slots);
1713 if (err >= 0 && hc->tdm_slot_width)
1714 err = tplg_save_printf(dst, pfx, "\ttdm_slot_width %u\n",
1715 hc->tdm_slot_width);
1716 if (err >= 0 && hc->tx_slots)
1717 err = tplg_save_printf(dst, pfx, "\ttx_slots %u\n",
1718 hc->tx_slots);
1719 if (err >= 0 && hc->rx_slots)
1720 err = tplg_save_printf(dst, pfx, "\trx_slots %u\n",
1721 hc->rx_slots);
1722 if (err >= 0 && hc->tx_channels)
1723 err = tplg_save_printf(dst, pfx, "\ttx_channels %u\n",
1724 hc->tx_channels);
1725 if (err >= 0 && hc->rx_channels)
1726 err = tplg_save_printf(dst, pfx, "\trx_channels %u\n",
1727 hc->rx_channels);
1728 if (err >= 0)
1729 err = tplg_save_printf(dst, pfx, "}\n");
1730 return err;
1731 }
1732
1733 /* copy stream object */
tplg_add_stream_object(struct snd_soc_tplg_stream * strm,struct snd_tplg_stream_template * strm_tpl)1734 static void tplg_add_stream_object(struct snd_soc_tplg_stream *strm,
1735 struct snd_tplg_stream_template *strm_tpl)
1736 {
1737 snd_strlcpy(strm->name, strm_tpl->name,
1738 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1739 strm->format = strm_tpl->format;
1740 strm->rate = strm_tpl->rate;
1741 strm->period_bytes = strm_tpl->period_bytes;
1742 strm->buffer_bytes = strm_tpl->buffer_bytes;
1743 strm->channels = strm_tpl->channels;
1744 }
1745
tplg_add_stream_caps(snd_tplg_t * tplg,struct snd_tplg_stream_caps_template * caps_tpl)1746 static int tplg_add_stream_caps(snd_tplg_t *tplg,
1747 struct snd_tplg_stream_caps_template *caps_tpl)
1748 {
1749 struct snd_soc_tplg_stream_caps *caps;
1750 struct tplg_elem *elem;
1751
1752 elem = tplg_elem_new_common(tplg, NULL, caps_tpl->name,
1753 SND_TPLG_TYPE_STREAM_CAPS);
1754 if (!elem)
1755 return -ENOMEM;
1756
1757 caps = elem->stream_caps;
1758
1759 snd_strlcpy(caps->name, caps_tpl->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1760
1761 caps->formats = caps_tpl->formats;
1762 caps->rates = caps_tpl->rates;
1763 caps->rate_min = caps_tpl->rate_min;
1764 caps->rate_max = caps_tpl->rate_max;
1765 caps->channels_min = caps_tpl->channels_min;
1766 caps->channels_max = caps_tpl->channels_max;
1767 caps->periods_min = caps_tpl->periods_min;
1768 caps->periods_max = caps_tpl->periods_max;
1769 caps->period_size_min = caps_tpl->period_size_min;
1770 caps->period_size_max = caps_tpl->period_size_max;
1771 caps->buffer_size_min = caps_tpl->buffer_size_min;
1772 caps->buffer_size_max = caps_tpl->buffer_size_max;
1773 caps->sig_bits = caps_tpl->sig_bits;
1774 return 0;
1775 }
1776
1777 /* Add a PCM element (FE DAI & DAI link) from C API */
tplg_add_pcm_object(snd_tplg_t * tplg,snd_tplg_obj_template_t * t)1778 int tplg_add_pcm_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
1779 {
1780 struct snd_tplg_pcm_template *pcm_tpl = t->pcm;
1781 struct snd_soc_tplg_private *priv;
1782 struct snd_soc_tplg_pcm *pcm;
1783 struct tplg_elem *elem;
1784 int ret, i;
1785
1786 tplg_dbg("PCM: %s, DAI %s", pcm_tpl->pcm_name, pcm_tpl->dai_name);
1787
1788 if (pcm_tpl->num_streams > SND_SOC_TPLG_STREAM_CONFIG_MAX)
1789 return -EINVAL;
1790
1791 elem = tplg_elem_new_common(tplg, NULL, pcm_tpl->pcm_name,
1792 SND_TPLG_TYPE_PCM);
1793 if (!elem)
1794 return -ENOMEM;
1795
1796 pcm = elem->pcm;
1797 pcm->size = elem->size;
1798
1799 snd_strlcpy(pcm->pcm_name, pcm_tpl->pcm_name,
1800 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1801 snd_strlcpy(pcm->dai_name, pcm_tpl->dai_name,
1802 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1803 pcm->pcm_id = pcm_tpl->pcm_id;
1804 pcm->dai_id = pcm_tpl->dai_id;
1805 pcm->playback = pcm_tpl->playback;
1806 pcm->capture = pcm_tpl->capture;
1807 pcm->compress = pcm_tpl->compress;
1808
1809 for (i = 0; i < 2; i++) {
1810 if (!pcm_tpl->caps[i] || !pcm_tpl->caps[i]->name)
1811 continue;
1812 ret = tplg_add_stream_caps(tplg, pcm_tpl->caps[i]);
1813 if (ret < 0)
1814 return ret;
1815 snd_strlcpy(pcm->caps[i].name, pcm_tpl->caps[i]->name,
1816 sizeof(pcm->caps[i].name));
1817 }
1818
1819 pcm->flag_mask = pcm_tpl->flag_mask;
1820 pcm->flags = pcm_tpl->flags;
1821
1822 pcm->num_streams = pcm_tpl->num_streams;
1823 for (i = 0; i < pcm_tpl->num_streams; i++)
1824 tplg_add_stream_object(&pcm->stream[i], &pcm_tpl->stream[i]);
1825
1826 /* private data */
1827 priv = pcm_tpl->priv;
1828 if (priv && priv->size > 0) {
1829 ret = tplg_add_data(tplg, elem, priv,
1830 sizeof(*priv) + priv->size);
1831 if (ret < 0)
1832 return ret;
1833 }
1834
1835 return 0;
1836 }
1837
1838 /* Set link HW config from C API template */
set_link_hw_config(struct snd_soc_tplg_hw_config * cfg,struct snd_tplg_hw_config_template * tpl)1839 static int set_link_hw_config(struct snd_soc_tplg_hw_config *cfg,
1840 struct snd_tplg_hw_config_template *tpl)
1841 {
1842 unsigned int i;
1843
1844 cfg->size = sizeof(*cfg);
1845 cfg->id = tpl->id;
1846
1847 cfg->fmt = tpl->fmt;
1848 cfg->clock_gated = tpl->clock_gated;
1849 cfg->invert_bclk = tpl->invert_bclk;
1850 cfg->invert_fsync = tpl->invert_fsync;
1851 cfg->bclk_provider = tpl->bclk_provider;
1852 cfg->fsync_provider = tpl->fsync_provider;
1853 cfg->mclk_direction = tpl->mclk_direction;
1854 cfg->reserved = tpl->reserved;
1855 cfg->mclk_rate = tpl->mclk_rate;
1856 cfg->bclk_rate = tpl->bclk_rate;
1857 cfg->fsync_rate = tpl->fsync_rate;
1858
1859 cfg->tdm_slots = tpl->tdm_slots;
1860 cfg->tdm_slot_width = tpl->tdm_slot_width;
1861 cfg->tx_slots = tpl->tx_slots;
1862 cfg->rx_slots = tpl->rx_slots;
1863
1864 if (cfg->tx_channels > SND_SOC_TPLG_MAX_CHAN
1865 || cfg->rx_channels > SND_SOC_TPLG_MAX_CHAN)
1866 return -EINVAL;
1867
1868 cfg->tx_channels = tpl->tx_channels;
1869 for (i = 0; i < cfg->tx_channels; i++)
1870 cfg->tx_chanmap[i] = tpl->tx_chanmap[i];
1871
1872 cfg->rx_channels = tpl->rx_channels;
1873 for (i = 0; i < cfg->rx_channels; i++)
1874 cfg->rx_chanmap[i] = tpl->rx_chanmap[i];
1875
1876 return 0;
1877 }
1878
1879 /* Add a physical DAI link element from C API */
tplg_add_link_object(snd_tplg_t * tplg,snd_tplg_obj_template_t * t)1880 int tplg_add_link_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
1881 {
1882 struct snd_tplg_link_template *link_tpl = t->link;
1883 struct snd_soc_tplg_link_config *link;
1884 struct snd_soc_tplg_private *priv;
1885 struct tplg_elem *elem;
1886 unsigned int i;
1887 int ret;
1888
1889 if (t->type != SND_TPLG_TYPE_LINK && t->type != SND_TPLG_TYPE_BE
1890 && t->type != SND_TPLG_TYPE_CC)
1891 return -EINVAL;
1892
1893 elem = tplg_elem_new_common(tplg, NULL, link_tpl->name, t->type);
1894 if (!elem)
1895 return -ENOMEM;
1896
1897 tplg_dbg("Link: %s", link_tpl->name);
1898
1899 link = elem->link;
1900 link->size = elem->size;
1901
1902 /* ID and names */
1903 link->id = link_tpl->id;
1904 snd_strlcpy(link->name, link_tpl->name,
1905 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1906 snd_strlcpy(link->stream_name, link_tpl->stream_name,
1907 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1908
1909 /* stream configs */
1910 if (link_tpl->num_streams > SND_SOC_TPLG_STREAM_CONFIG_MAX)
1911 return -EINVAL;
1912 link->num_streams = link_tpl->num_streams;
1913 for (i = 0; i < link->num_streams; i++)
1914 tplg_add_stream_object(&link->stream[i], &link_tpl->stream[i]);
1915
1916 /* HW configs */
1917 if (link_tpl->num_hw_configs > SND_SOC_TPLG_HW_CONFIG_MAX)
1918 return -EINVAL;
1919 link->num_hw_configs = link_tpl->num_hw_configs;
1920 link->default_hw_config_id = link_tpl->default_hw_config_id;
1921 for (i = 0; i < link->num_hw_configs; i++)
1922 set_link_hw_config(&link->hw_config[i], &link_tpl->hw_config[i]);
1923
1924 /* flags */
1925 link->flag_mask = link_tpl->flag_mask;
1926 link->flags = link_tpl->flags;
1927
1928 /* private data */
1929 priv = link_tpl->priv;
1930 if (priv && priv->size > 0) {
1931 ret = tplg_add_data(tplg, elem, priv,
1932 sizeof(*priv) + priv->size);
1933 if (ret < 0)
1934 return ret;
1935 }
1936
1937 return 0;
1938 }
1939
tplg_add_dai_object(snd_tplg_t * tplg,snd_tplg_obj_template_t * t)1940 int tplg_add_dai_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
1941 {
1942 struct snd_tplg_dai_template *dai_tpl = t->dai;
1943 struct snd_soc_tplg_dai *dai;
1944 struct snd_soc_tplg_private *priv;
1945 struct tplg_elem *elem;
1946 int ret, i;
1947
1948 tplg_dbg("DAI %s", dai_tpl->dai_name);
1949
1950 elem = tplg_elem_new_common(tplg, NULL, dai_tpl->dai_name,
1951 SND_TPLG_TYPE_DAI);
1952 if (!elem)
1953 return -ENOMEM;
1954
1955 dai = elem->dai;
1956 dai->size = elem->size;
1957
1958 snd_strlcpy(dai->dai_name, dai_tpl->dai_name,
1959 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1960 dai->dai_id = dai_tpl->dai_id;
1961
1962 /* stream caps */
1963 dai->playback = dai_tpl->playback;
1964 dai->capture = dai_tpl->capture;
1965
1966 for (i = 0; i < 2; i++) {
1967 if (!dai_tpl->caps[i] || !dai_tpl->caps[i]->name)
1968 continue;
1969 ret = tplg_add_stream_caps(tplg, dai_tpl->caps[i]);
1970 if (ret < 0)
1971 return ret;
1972 snd_strlcpy(dai->caps[i].name, dai_tpl->caps[i]->name,
1973 sizeof(dai->caps[i].name));
1974 }
1975
1976 /* flags */
1977 dai->flag_mask = dai_tpl->flag_mask;
1978 dai->flags = dai_tpl->flags;
1979
1980 /* private data */
1981 priv = dai_tpl->priv;
1982 if (priv && priv->size > 0) {
1983 ret = tplg_add_data(tplg, elem, priv,
1984 sizeof(*priv) + priv->size);
1985 if (ret < 0)
1986 return ret;
1987 }
1988
1989 return 0;
1990 }
1991
1992 /* decode pcm from the binary input */
tplg_decode_pcm(snd_tplg_t * tplg,size_t pos,struct snd_soc_tplg_hdr * hdr,void * bin,size_t size)1993 int tplg_decode_pcm(snd_tplg_t *tplg,
1994 size_t pos,
1995 struct snd_soc_tplg_hdr *hdr,
1996 void *bin, size_t size)
1997 {
1998 struct snd_soc_tplg_pcm *pcm;
1999 snd_tplg_obj_template_t t;
2000 struct snd_tplg_pcm_template *pt;
2001 struct snd_tplg_stream_caps_template caps[2], *cap;
2002 struct snd_tplg_stream_template *stream;
2003 unsigned int i;
2004 size_t asize;
2005 int err;
2006
2007 err = tplg_decode_template(tplg, pos, hdr, &t);
2008 if (err < 0)
2009 return err;
2010
2011 asize = sizeof(*pt) + SND_SOC_TPLG_STREAM_CONFIG_MAX * sizeof(*stream);
2012 pt = alloca(asize);
2013
2014 next:
2015 memset(pt, 0, asize);
2016 pcm = bin;
2017
2018 if (size < sizeof(*pcm)) {
2019 SNDERR("pcm: small size %d", size);
2020 return -EINVAL;
2021 }
2022 if (sizeof(*pcm) != pcm->size) {
2023 SNDERR("pcm: unknown element size %d (expected %zd)",
2024 pcm->size, sizeof(*pcm));
2025 return -EINVAL;
2026 }
2027 if (pcm->num_streams > SND_SOC_TPLG_STREAM_CONFIG_MAX) {
2028 SNDERR("pcm: wrong number of streams %d", pcm->num_streams);
2029 return -EINVAL;
2030 }
2031 if (sizeof(*pcm) + pcm->priv.size > size) {
2032 SNDERR("pcm: wrong private data size %d", pcm->priv.size);
2033 return -EINVAL;
2034 }
2035
2036 tplg_log(tplg, 'D', pos, "pcm: size %d private size %d streams %d",
2037 pcm->size, pcm->priv.size, pcm->num_streams);
2038
2039 pt->pcm_name = pcm->pcm_name;
2040 tplg_log(tplg, 'D', pos, "pcm: pcm_name '%s'", pt->pcm_name);
2041 pt->dai_name = pcm->dai_name;
2042 tplg_log(tplg, 'D', pos, "pcm: dai_name '%s'", pt->dai_name);
2043 pt->pcm_id = pcm->pcm_id;
2044 pt->dai_id = pcm->dai_id;
2045 tplg_log(tplg, 'D', pos, "pcm: pcm_id %d dai_id %d", pt->pcm_id, pt->dai_id);
2046 pt->playback = pcm->playback;
2047 pt->capture = pcm->capture;
2048 pt->compress = pcm->compress;
2049 tplg_log(tplg, 'D', pos, "pcm: playback %d capture %d compress %d",
2050 pt->playback, pt->capture, pt->compress);
2051 pt->num_streams = pcm->num_streams;
2052 pt->flag_mask = pcm->flag_mask;
2053 pt->flags = pcm->flags;
2054 for (i = 0; i < pcm->num_streams; i++) {
2055 stream = &pt->stream[i];
2056 if (pcm->stream[i].size != sizeof(pcm->stream[0])) {
2057 SNDERR("pcm: unknown stream structure size %d",
2058 pcm->stream[i].size);
2059 return -EINVAL;
2060 }
2061 stream->name = pcm->stream[i].name;
2062 tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_pcm, stream[i]),
2063 "stream %d: '%s'", i, stream->name);
2064 stream->format = pcm->stream[i].format;
2065 stream->rate = pcm->stream[i].rate;
2066 stream->period_bytes = pcm->stream[i].period_bytes;
2067 stream->buffer_bytes = pcm->stream[i].buffer_bytes;
2068 stream->channels = pcm->stream[i].channels;
2069 }
2070 for (i = 0; i < 2; i++) {
2071 if (i == 0 && !pcm->playback)
2072 continue;
2073 if (i == 1 && !pcm->capture)
2074 continue;
2075 cap = &caps[i];
2076 pt->caps[i] = cap;
2077 if (pcm->caps[i].size != sizeof(pcm->caps[0])) {
2078 SNDERR("pcm: unknown caps structure size %d",
2079 pcm->caps[i].size);
2080 return -EINVAL;
2081 }
2082 cap->name = pcm->caps[i].name;
2083 tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_pcm, caps[i]),
2084 "caps %d: '%s'", i, cap->name);
2085 cap->formats = pcm->caps[i].formats;
2086 cap->rates = pcm->caps[i].rates;
2087 cap->rate_min = pcm->caps[i].rate_min;
2088 cap->rate_max = pcm->caps[i].rate_max;
2089 cap->channels_min = pcm->caps[i].channels_min;
2090 cap->channels_max = pcm->caps[i].channels_max;
2091 cap->periods_min = pcm->caps[i].periods_min;
2092 cap->periods_max = pcm->caps[i].periods_max;
2093 cap->period_size_min = pcm->caps[i].period_size_min;
2094 cap->period_size_max = pcm->caps[i].period_size_max;
2095 cap->buffer_size_min = pcm->caps[i].buffer_size_min;
2096 cap->buffer_size_max = pcm->caps[i].buffer_size_max;
2097 cap->sig_bits = pcm->caps[i].sig_bits;
2098 }
2099
2100 tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_pcm, priv),
2101 "pcm: private start");
2102 pt->priv = &pcm->priv;
2103
2104 bin += sizeof(*pcm) + pcm->priv.size;
2105 size -= sizeof(*pcm) + pcm->priv.size;
2106 pos += sizeof(*pcm) + pcm->priv.size;
2107
2108 t.pcm = pt;
2109 err = snd_tplg_add_object(tplg, &t);
2110 if (err < 0)
2111 return err;
2112
2113 if (size > 0)
2114 goto next;
2115
2116 return 0;
2117 }
2118
2119 /* decode dai from the binary input */
tplg_decode_dai(snd_tplg_t * tplg ATTRIBUTE_UNUSED,size_t pos ATTRIBUTE_UNUSED,struct snd_soc_tplg_hdr * hdr ATTRIBUTE_UNUSED,void * bin ATTRIBUTE_UNUSED,size_t size ATTRIBUTE_UNUSED)2120 int tplg_decode_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
2121 size_t pos ATTRIBUTE_UNUSED,
2122 struct snd_soc_tplg_hdr *hdr ATTRIBUTE_UNUSED,
2123 void *bin ATTRIBUTE_UNUSED,
2124 size_t size ATTRIBUTE_UNUSED)
2125 {
2126 SNDERR("not implemented");
2127 return -ENXIO;
2128 }
2129
2130 /* decode cc from the binary input */
tplg_decode_cc(snd_tplg_t * tplg ATTRIBUTE_UNUSED,size_t pos ATTRIBUTE_UNUSED,struct snd_soc_tplg_hdr * hdr ATTRIBUTE_UNUSED,void * bin ATTRIBUTE_UNUSED,size_t size ATTRIBUTE_UNUSED)2131 int tplg_decode_cc(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
2132 size_t pos ATTRIBUTE_UNUSED,
2133 struct snd_soc_tplg_hdr *hdr ATTRIBUTE_UNUSED,
2134 void *bin ATTRIBUTE_UNUSED,
2135 size_t size ATTRIBUTE_UNUSED)
2136 {
2137 SNDERR("not implemented");
2138 return -ENXIO;
2139 }
2140
2141 /* decode link from the binary input */
tplg_decode_link(snd_tplg_t * tplg,size_t pos,struct snd_soc_tplg_hdr * hdr,void * bin,size_t size)2142 int tplg_decode_link(snd_tplg_t *tplg,
2143 size_t pos,
2144 struct snd_soc_tplg_hdr *hdr,
2145 void *bin, size_t size)
2146 {
2147 struct snd_soc_tplg_link_config *link;
2148 snd_tplg_obj_template_t t;
2149 struct snd_tplg_link_template lt;
2150 struct snd_tplg_stream_template streams[SND_SOC_TPLG_STREAM_CONFIG_MAX];
2151 struct snd_tplg_stream_template *stream;
2152 struct snd_tplg_hw_config_template hws[SND_SOC_TPLG_HW_CONFIG_MAX];
2153 struct snd_tplg_hw_config_template *hw;
2154 unsigned int i, j;
2155 int err;
2156
2157 err = tplg_decode_template(tplg, pos, hdr, &t);
2158 if (err < 0)
2159 return err;
2160
2161 next:
2162 memset(<, 0, sizeof(lt));
2163 memset(streams, 0, sizeof(streams));
2164 memset(hws, 0, sizeof(hws));
2165 link = bin;
2166
2167 if (size < sizeof(*link)) {
2168 SNDERR("link: small size %d", size);
2169 return -EINVAL;
2170 }
2171 if (sizeof(*link) != link->size) {
2172 SNDERR("link: unknown element size %d (expected %zd)",
2173 link->size, sizeof(*link));
2174 return -EINVAL;
2175 }
2176 if (link->num_streams > SND_SOC_TPLG_STREAM_CONFIG_MAX) {
2177 SNDERR("link: wrong number of streams %d", link->num_streams);
2178 return -EINVAL;
2179 }
2180 if (link->num_hw_configs > SND_SOC_TPLG_HW_CONFIG_MAX) {
2181 SNDERR("link: wrong number of streams %d", link->num_streams);
2182 return -EINVAL;
2183 }
2184 if (sizeof(*link) + link->priv.size > size) {
2185 SNDERR("link: wrong private data size %d", link->priv.size);
2186 return -EINVAL;
2187 }
2188
2189 tplg_log(tplg, 'D', pos, "link: size %d private size %d streams %d "
2190 "hw_configs %d",
2191 link->size, link->priv.size, link->num_streams,
2192 link->num_hw_configs);
2193
2194 lt.id = link->id;
2195 lt.name = link->name;
2196 tplg_log(tplg, 'D', pos, "link: name '%s'", lt.name);
2197 lt.stream_name = link->stream_name;
2198 tplg_log(tplg, 'D', pos, "link: stream_name '%s'", lt.stream_name);
2199 lt.num_streams = link->num_streams;
2200 lt.num_hw_configs = link->num_hw_configs;
2201 lt.default_hw_config_id = link->default_hw_config_id;
2202 lt.flag_mask = link->flag_mask;
2203 lt.flags = link->flags;
2204 for (i = 0; i < link->num_streams; i++) {
2205 stream = &streams[i];
2206 if (link->stream[i].size != sizeof(link->stream[0])) {
2207 SNDERR("link: unknown stream structure size %d",
2208 link->stream[i].size);
2209 return -EINVAL;
2210 }
2211 stream->name = link->stream[i].name;
2212 tplg_log(tplg, 'D',
2213 pos + offsetof(struct snd_soc_tplg_link_config, stream[i]),
2214 "stream %d: '%s'", i, stream->name);
2215 stream->format = link->stream[i].format;
2216 stream->rate = link->stream[i].rate;
2217 stream->period_bytes = link->stream[i].period_bytes;
2218 stream->buffer_bytes = link->stream[i].buffer_bytes;
2219 stream->channels = link->stream[i].channels;
2220 }
2221 lt.stream = streams;
2222 for (i = 0; i < link->num_hw_configs; i++) {
2223 hw = &hws[i];
2224 if (link->hw_config[i].size != sizeof(link->hw_config[0])) {
2225 SNDERR("link: unknown hw_config structure size %d",
2226 link->hw_config[i].size);
2227 return -EINVAL;
2228 }
2229 hw->id = link->hw_config[i].id;
2230 hw->fmt = link->hw_config[i].fmt;
2231 hw->clock_gated = link->hw_config[i].clock_gated;
2232 hw->invert_bclk = link->hw_config[i].invert_bclk;
2233 hw->invert_fsync = link->hw_config[i].invert_fsync;
2234 hw->bclk_provider = link->hw_config[i].bclk_provider;
2235 hw->fsync_provider = link->hw_config[i].fsync_provider;
2236 hw->mclk_direction = link->hw_config[i].mclk_direction;
2237 hw->mclk_rate = link->hw_config[i].mclk_rate;
2238 hw->bclk_rate = link->hw_config[i].bclk_rate;
2239 hw->fsync_rate = link->hw_config[i].fsync_rate;
2240 hw->tdm_slots = link->hw_config[i].tdm_slots;
2241 hw->tdm_slot_width = link->hw_config[i].tdm_slot_width;
2242 hw->tx_slots = link->hw_config[i].tx_slots;
2243 hw->rx_slots = link->hw_config[i].rx_slots;
2244 hw->tx_channels = link->hw_config[i].tx_channels;
2245 if (hw->tx_channels > SND_SOC_TPLG_MAX_CHAN) {
2246 SNDERR("link: wrong tx channels %d", hw->tx_channels);
2247 return -EINVAL;
2248 }
2249 for (j = 0; j < hw->tx_channels; j++)
2250 hw->tx_chanmap[j] = link->hw_config[i].tx_chanmap[j];
2251 hw->rx_channels = link->hw_config[i].rx_channels;
2252 if (hw->rx_channels > SND_SOC_TPLG_MAX_CHAN) {
2253 SNDERR("link: wrong rx channels %d", hw->tx_channels);
2254 return -EINVAL;
2255 }
2256 for (j = 0; j < hw->rx_channels; j++)
2257 hw->rx_chanmap[j] = link->hw_config[i].rx_chanmap[j];
2258 }
2259 lt.hw_config = hws;
2260
2261 tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_pcm, priv),
2262 "link: private start");
2263 lt.priv = &link->priv;
2264
2265 bin += sizeof(*link) + link->priv.size;
2266 size -= sizeof(*link) + link->priv.size;
2267 pos += sizeof(*link) + link->priv.size;
2268
2269 t.link = <
2270 err = snd_tplg_add_object(tplg, &t);
2271 if (err < 0)
2272 return err;
2273
2274 if (size > 0)
2275 goto next;
2276
2277 return 0;
2278 }
2279