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