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,void * dst)379 static int parse_unsigned(snd_config_t *n, void *dst)
380 {
381 int ival;
382
383 if (tplg_get_integer(n, &ival, 0) < 0)
384 return -EINVAL;
385
386 unaligned_put32(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, ival);
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,struct tplg_buf * dst,const char * pfx)539 int tplg_save_stream_caps(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
540 struct tplg_elem *elem,
541 struct tplg_buf *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 && sc->sig_bits)
608 err = tplg_save_printf(dst, pfx, "\tsig_bits %u\n",
609 sc->sig_bits);
610 if (err >= 0)
611 err = tplg_save_printf(dst, pfx, "}\n");
612 return err;
613 }
614
615 /* Parse the caps and config of a pcm stream */
tplg_parse_streams(snd_tplg_t * tplg ATTRIBUTE_UNUSED,snd_config_t * cfg,void * private)616 static int tplg_parse_streams(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
617 snd_config_t *cfg, void *private)
618 {
619 snd_config_iterator_t i, next;
620 snd_config_t *n;
621 struct tplg_elem *elem = private;
622 struct snd_soc_tplg_pcm *pcm;
623 struct snd_soc_tplg_dai *dai;
624 void *playback, *capture;
625 struct snd_soc_tplg_stream_caps *caps;
626 const char *id, *value;
627 int stream;
628
629 snd_config_get_id(cfg, &id);
630
631 tplg_dbg("\t%s:", id);
632
633 switch (elem->type) {
634 case SND_TPLG_TYPE_PCM:
635 pcm = elem->pcm;
636 playback = &pcm->playback;
637 capture = &pcm->capture;
638 caps = pcm->caps;
639 break;
640
641 case SND_TPLG_TYPE_DAI:
642 dai = elem->dai;
643 playback = &dai->playback;
644 capture = &dai->capture;
645 caps = dai->caps;
646 break;
647
648 default:
649 return -EINVAL;
650 }
651
652 if (strcmp(id, "playback") == 0) {
653 stream = SND_SOC_TPLG_STREAM_PLAYBACK;
654 unaligned_put32(playback, 1);
655 } else if (strcmp(id, "capture") == 0) {
656 stream = SND_SOC_TPLG_STREAM_CAPTURE;
657 unaligned_put32(capture, 1);
658 } else
659 return -EINVAL;
660
661 snd_config_for_each(i, next, cfg) {
662
663 n = snd_config_iterator_entry(i);
664
665 /* get id */
666 if (snd_config_get_id(n, &id) < 0)
667 continue;
668
669 if (strcmp(id, "capabilities") == 0) {
670 if (snd_config_get_string(n, &value) < 0)
671 continue;
672 /* store stream caps name, to find and merge
673 * the caps in building phase.
674 */
675 snd_strlcpy(caps[stream].name, value,
676 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
677
678 tplg_dbg("\t\t%s\n\t\t\t%s", id, value);
679 continue;
680 }
681 }
682
683 return 0;
684 }
685
686 /* 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)687 int tplg_save_streams(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
688 struct tplg_elem *elem,
689 struct tplg_buf *dst, const char *pfx)
690 {
691 static const char *stream_ids[2] = {
692 "playback",
693 "capture"
694 };
695 static unsigned int stream_types[2] = {
696 SND_SOC_TPLG_STREAM_PLAYBACK,
697 SND_SOC_TPLG_STREAM_CAPTURE
698 };
699 struct snd_soc_tplg_stream_caps *caps;
700 unsigned int streams[2], stream;
701 const char *s;
702 int err;
703
704 switch (elem->type) {
705 case SND_TPLG_TYPE_PCM:
706 streams[0] = elem->pcm->playback;
707 streams[1] = elem->pcm->capture;
708 caps = elem->pcm->caps;
709 break;
710 case SND_TPLG_TYPE_DAI:
711 streams[0] = elem->dai->playback;
712 streams[1] = elem->dai->capture;
713 caps = elem->dai->caps;
714 break;
715 default:
716 return -EINVAL;
717 }
718
719 for (stream = 0; stream < 2; stream++) {
720 if (streams[stream] == 0)
721 continue;
722 if (!caps)
723 continue;
724 s = caps[stream_types[stream]].name;
725 if (s[0] == '\0')
726 continue;
727 err = tplg_save_printf(dst, pfx, "pcm.%s {\n", stream_ids[stream]);
728 if (err < 0)
729 return err;
730 err = tplg_save_printf(dst, pfx, "\tcapabilities '%s'\n", s);
731 if (err < 0)
732 return err;
733 err = tplg_save_printf(dst, pfx, "}\n");
734 if (err < 0)
735 return err;
736 }
737
738 return 0;
739 }
740
741 /* 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)742 static int tplg_parse_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
743 snd_config_t *cfg, void *private)
744 {
745 struct tplg_elem *elem = private;
746 struct snd_soc_tplg_pcm *pcm = elem->pcm;
747 snd_config_iterator_t i, next;
748 snd_config_t *n;
749 const char *id;
750 unsigned int dai_id;
751
752 snd_config_get_id(cfg, &id);
753 tplg_dbg("\t\tFE DAI %s:", id);
754 snd_strlcpy(pcm->dai_name, id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
755
756 snd_config_for_each(i, next, cfg) {
757
758 n = snd_config_iterator_entry(i);
759
760 /* get id */
761 if (snd_config_get_id(n, &id) < 0)
762 continue;
763
764 if (strcmp(id, "id") == 0) {
765 if (tplg_get_unsigned(n, &dai_id, 0)) {
766 SNDERR("invalid fe dai ID");
767 return -EINVAL;
768 }
769
770 unaligned_put32(&pcm->dai_id, dai_id);
771 tplg_dbg("\t\t\tindex: %d", dai_id);
772 }
773 }
774
775 return 0;
776 }
777
778 /* 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)779 int tplg_save_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
780 struct tplg_elem *elem,
781 struct tplg_buf *dst, const char *pfx)
782 {
783 struct snd_soc_tplg_pcm *pcm = elem->pcm;
784 int err = 0;
785
786 if (strlen(pcm->dai_name))
787 err = tplg_save_printf(dst, pfx, "dai.'%s'.id %u\n", pcm->dai_name, pcm->dai_id);
788 else if (pcm->dai_id > 0)
789 err = tplg_save_printf(dst, pfx, "dai.0.id %u\n", pcm->dai_id);
790 return err;
791 }
792
793 /* parse a flag bit of the given mask */
parse_flag(snd_config_t * n,unsigned int mask_in,void * mask,void * flags)794 static int parse_flag(snd_config_t *n, unsigned int mask_in,
795 void *mask, void *flags)
796 {
797 int ret;
798
799 ret = snd_config_get_bool(n);
800 if (ret < 0)
801 return ret;
802
803 unaligned_put32(mask, unaligned_get32(mask) | mask_in);
804 if (ret)
805 unaligned_put32(flags, unaligned_get32(flags) | mask_in);
806 else
807 unaligned_put32(flags, unaligned_get32(flags) & (~mask_in));
808
809 return 0;
810 }
811
save_flags(unsigned int flags,unsigned int mask,struct tplg_buf * dst,const char * pfx)812 static int save_flags(unsigned int flags, unsigned int mask,
813 struct tplg_buf *dst, const char *pfx)
814 {
815 static unsigned int flag_masks[3] = {
816 SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES,
817 SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS,
818 SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS,
819 };
820 static const char *flag_ids[3] = {
821 "symmetric_rates",
822 "symmetric_channels",
823 "symmetric_sample_bits",
824 };
825 unsigned int i;
826 int err = 0;
827
828 for (i = 0; err >= 0 && i < ARRAY_SIZE(flag_masks); i++) {
829 if (mask & flag_masks[i]) {
830 unsigned int v = (flags & flag_masks[i]) ? 1 : 0;
831 err = tplg_save_printf(dst, pfx, "%s %u\n",
832 flag_ids[i], v);
833 }
834 }
835 return err;
836 }
837
838 /* 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)839 int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg,
840 void *private ATTRIBUTE_UNUSED)
841 {
842 struct snd_soc_tplg_pcm *pcm;
843 struct tplg_elem *elem;
844 snd_config_iterator_t i, next;
845 snd_config_t *n;
846 const char *id;
847 int err, ival;
848
849 elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_PCM);
850 if (!elem)
851 return -ENOMEM;
852
853 pcm = elem->pcm;
854 pcm->size = elem->size;
855 snd_strlcpy(pcm->pcm_name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
856
857 tplg_dbg(" PCM: %s", elem->id);
858
859 snd_config_for_each(i, next, cfg) {
860
861 n = snd_config_iterator_entry(i);
862 if (snd_config_get_id(n, &id) < 0)
863 continue;
864
865 /* skip comments */
866 if (strcmp(id, "comment") == 0)
867 continue;
868 if (id[0] == '#')
869 continue;
870
871 if (strcmp(id, "id") == 0) {
872 if (parse_unsigned(n, &pcm->pcm_id))
873 return -EINVAL;
874 continue;
875 }
876
877 if (strcmp(id, "pcm") == 0) {
878 err = tplg_parse_compound(tplg, n,
879 tplg_parse_streams, elem);
880 if (err < 0)
881 return err;
882 continue;
883 }
884
885 if (strcmp(id, "compress") == 0) {
886 ival = snd_config_get_bool(n);
887 if (ival < 0)
888 return -EINVAL;
889
890 pcm->compress = ival;
891
892 tplg_dbg("\t%s: %d", id, ival);
893 continue;
894 }
895
896 if (strcmp(id, "dai") == 0) {
897 err = tplg_parse_compound(tplg, n,
898 tplg_parse_fe_dai, elem);
899 if (err < 0)
900 return err;
901 continue;
902 }
903
904 /* flags */
905 if (strcmp(id, "symmetric_rates") == 0) {
906 err = parse_flag(n,
907 SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES,
908 &pcm->flag_mask, &pcm->flags);
909 if (err < 0)
910 return err;
911 continue;
912 }
913
914 if (strcmp(id, "symmetric_channels") == 0) {
915 err = parse_flag(n,
916 SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS,
917 &pcm->flag_mask, &pcm->flags);
918 if (err < 0)
919 return err;
920 continue;
921 }
922
923 if (strcmp(id, "symmetric_sample_bits") == 0) {
924 err = parse_flag(n,
925 SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS,
926 &pcm->flag_mask, &pcm->flags);
927 if (err < 0)
928 return err;
929 continue;
930 }
931
932 /* private data */
933 if (strcmp(id, "data") == 0) {
934 err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
935 if (err < 0)
936 return err;
937 continue;
938 }
939 }
940
941 return 0;
942 }
943
944 /* save PCM */
tplg_save_pcm(snd_tplg_t * tplg ATTRIBUTE_UNUSED,struct tplg_elem * elem,struct tplg_buf * dst,const char * pfx)945 int tplg_save_pcm(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
946 struct tplg_elem *elem,
947 struct tplg_buf *dst, const char *pfx)
948 {
949 struct snd_soc_tplg_pcm *pcm = elem->pcm;
950 char pfx2[16];
951 int err;
952
953 snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
954 err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
955 if (err >= 0 && elem->index)
956 err = tplg_save_printf(dst, pfx, "\tindex %u\n",
957 elem->index);
958 if (err >= 0 && pcm->pcm_id)
959 err = tplg_save_printf(dst, pfx, "\tid %u\n",
960 pcm->pcm_id);
961 if (err >= 0 && pcm->compress)
962 err = tplg_save_printf(dst, pfx, "\tcompress 1\n");
963 snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
964 if (err >= 0)
965 err = tplg_save_fe_dai(tplg, elem, dst, pfx2);
966 if (err >= 0)
967 err = tplg_save_streams(tplg, elem, dst, pfx2);
968 if (err >= 0)
969 err = save_flags(pcm->flags, pcm->flag_mask, dst, pfx);
970 if (err >= 0)
971 err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA,
972 "data", dst, pfx2);
973 if (err >= 0)
974 err = tplg_save_printf(dst, pfx, "}\n");
975 return err;
976 }
977
978 /* Parse physical DAI */
tplg_parse_dai(snd_tplg_t * tplg,snd_config_t * cfg,void * private ATTRIBUTE_UNUSED)979 int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg,
980 void *private ATTRIBUTE_UNUSED)
981 {
982 struct snd_soc_tplg_dai *dai;
983 struct tplg_elem *elem;
984 snd_config_iterator_t i, next;
985 snd_config_t *n;
986 const char *id;
987 int err;
988
989 elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_DAI);
990 if (!elem)
991 return -ENOMEM;
992
993 dai = elem->dai;
994 dai->size = elem->size;
995 snd_strlcpy(dai->dai_name, elem->id,
996 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
997
998 tplg_dbg(" DAI: %s", elem->id);
999
1000 snd_config_for_each(i, next, cfg) {
1001
1002 n = snd_config_iterator_entry(i);
1003 if (snd_config_get_id(n, &id) < 0)
1004 continue;
1005
1006 /* skip comments */
1007 if (strcmp(id, "comment") == 0)
1008 continue;
1009 if (id[0] == '#')
1010 continue;
1011
1012 if (strcmp(id, "id") == 0) {
1013 if (parse_unsigned(n, &dai->dai_id))
1014 return -EINVAL;
1015 continue;
1016 }
1017
1018 if (strcmp(id, "playback") == 0) {
1019 if (parse_unsigned(n, &dai->playback))
1020 return -EINVAL;
1021 continue;
1022 }
1023
1024
1025 if (strcmp(id, "capture") == 0) {
1026 if (parse_unsigned(n, &dai->capture))
1027 return -EINVAL;
1028 continue;
1029 }
1030
1031
1032 /* stream capabilities */
1033 if (strcmp(id, "pcm") == 0) {
1034 err = tplg_parse_compound(tplg, n,
1035 tplg_parse_streams, elem);
1036 if (err < 0)
1037 return err;
1038 continue;
1039 }
1040
1041 /* flags */
1042 if (strcmp(id, "symmetric_rates") == 0) {
1043 err = parse_flag(n,
1044 SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES,
1045 &dai->flag_mask, &dai->flags);
1046 if (err < 0)
1047 return err;
1048 continue;
1049 }
1050
1051 if (strcmp(id, "symmetric_channels") == 0) {
1052 err = parse_flag(n,
1053 SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS,
1054 &dai->flag_mask, &dai->flags);
1055 if (err < 0)
1056 return err;
1057 continue;
1058 }
1059
1060 if (strcmp(id, "symmetric_sample_bits") == 0) {
1061 err = parse_flag(n,
1062 SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS,
1063 &dai->flag_mask, &dai->flags);
1064 if (err < 0)
1065 return err;
1066 continue;
1067 }
1068
1069 /* private data */
1070 if (strcmp(id, "data") == 0) {
1071 err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
1072 if (err < 0)
1073 return err;
1074 continue;
1075 }
1076 }
1077
1078 return 0;
1079 }
1080
1081 /* save DAI */
tplg_save_dai(snd_tplg_t * tplg ATTRIBUTE_UNUSED,struct tplg_elem * elem,struct tplg_buf * dst,const char * pfx)1082 int tplg_save_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
1083 struct tplg_elem *elem,
1084 struct tplg_buf *dst, const char *pfx)
1085 {
1086 struct snd_soc_tplg_dai *dai = elem->dai;
1087 char pfx2[16];
1088 int err;
1089
1090 if (!dai)
1091 return 0;
1092 snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
1093 err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
1094 if (err >= 0 && elem->index)
1095 err = tplg_save_printf(dst, pfx, "\tindex %u\n",
1096 elem->index);
1097 if (err >= 0 && dai->dai_id)
1098 err = tplg_save_printf(dst, pfx, "\tid %u\n",
1099 dai->dai_id);
1100 if (err >= 0 && dai->playback)
1101 err = tplg_save_printf(dst, pfx, "\tplayback %u\n",
1102 dai->playback);
1103 if (err >= 0 && dai->capture)
1104 err = tplg_save_printf(dst, pfx, "\tcapture %u\n",
1105 dai->capture);
1106 if (err >= 0)
1107 err = tplg_save_streams(tplg, elem, dst, pfx2);
1108 if (err >= 0)
1109 err = save_flags(dai->flags, dai->flag_mask, dst, pfx);
1110 if (err >= 0)
1111 err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA,
1112 "data", dst, pfx2);
1113 if (err >= 0)
1114 err = tplg_save_printf(dst, pfx, "}\n");
1115 return err;
1116 }
1117
1118 /* 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)1119 static int parse_hw_config_refs(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
1120 snd_config_t *cfg,
1121 struct tplg_elem *elem)
1122 {
1123 struct snd_soc_tplg_link_config *link = elem->link;
1124 int err;
1125
1126 err = tplg_parse_refs(cfg, elem, SND_TPLG_TYPE_HW_CONFIG);
1127 if (err < 0)
1128 return err;
1129 link->num_hw_configs = err;
1130 return 0;
1131 }
1132
1133 /* Parse a physical link element in text conf file */
tplg_parse_link(snd_tplg_t * tplg,snd_config_t * cfg,void * private ATTRIBUTE_UNUSED)1134 int tplg_parse_link(snd_tplg_t *tplg, snd_config_t *cfg,
1135 void *private ATTRIBUTE_UNUSED)
1136 {
1137 struct snd_soc_tplg_link_config *link;
1138 struct tplg_elem *elem;
1139 snd_config_iterator_t i, next;
1140 snd_config_t *n;
1141 const char *id, *val = NULL;
1142 int err;
1143
1144 elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_BE);
1145 if (!elem)
1146 return -ENOMEM;
1147
1148 link = elem->link;
1149 link->size = elem->size;
1150 snd_strlcpy(link->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1151
1152 tplg_dbg(" Link: %s", elem->id);
1153
1154 snd_config_for_each(i, next, cfg) {
1155
1156 n = snd_config_iterator_entry(i);
1157 if (snd_config_get_id(n, &id) < 0)
1158 continue;
1159
1160 /* skip comments */
1161 if (strcmp(id, "comment") == 0)
1162 continue;
1163 if (id[0] == '#')
1164 continue;
1165
1166 if (strcmp(id, "id") == 0) {
1167 if (parse_unsigned(n, &link->id))
1168 return -EINVAL;
1169 continue;
1170 }
1171
1172 if (strcmp(id, "stream_name") == 0) {
1173 if (snd_config_get_string(n, &val) < 0)
1174 return -EINVAL;
1175
1176 snd_strlcpy(link->stream_name, val,
1177 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1178 tplg_dbg("\t%s: %s", id, val);
1179 continue;
1180 }
1181
1182 if (strcmp(id, "hw_configs") == 0) {
1183 err = parse_hw_config_refs(tplg, n, elem);
1184 if (err < 0)
1185 return err;
1186 continue;
1187 }
1188
1189 if (strcmp(id, "default_hw_conf_id") == 0) {
1190 if (parse_unsigned(n, &link->default_hw_config_id))
1191 return -EINVAL;
1192 continue;
1193 }
1194
1195 /* flags */
1196 if (strcmp(id, "symmetric_rates") == 0) {
1197 err = parse_flag(n,
1198 SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES,
1199 &link->flag_mask, &link->flags);
1200 if (err < 0)
1201 return err;
1202 continue;
1203 }
1204
1205 if (strcmp(id, "symmetric_channels") == 0) {
1206 err = parse_flag(n,
1207 SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS,
1208 &link->flag_mask, &link->flags);
1209 if (err < 0)
1210 return err;
1211 continue;
1212 }
1213
1214 if (strcmp(id, "symmetric_sample_bits") == 0) {
1215 err = parse_flag(n,
1216 SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS,
1217 &link->flag_mask, &link->flags);
1218 if (err < 0)
1219 return err;
1220 continue;
1221 }
1222
1223 /* private data */
1224 if (strcmp(id, "data") == 0) {
1225 err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
1226 if (err < 0)
1227 return err;
1228 continue;
1229 }
1230 }
1231
1232 return 0;
1233 }
1234
1235 /* save physical link */
tplg_save_link(snd_tplg_t * tplg ATTRIBUTE_UNUSED,struct tplg_elem * elem,struct tplg_buf * dst,const char * pfx)1236 int tplg_save_link(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
1237 struct tplg_elem *elem,
1238 struct tplg_buf *dst, const char *pfx)
1239 {
1240 struct snd_soc_tplg_link_config *link = elem->link;
1241 char pfx2[16];
1242 int err;
1243
1244 if (!link)
1245 return 0;
1246 snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
1247 err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
1248 if (err >= 0 && elem->index)
1249 err = tplg_save_printf(dst, pfx, "\tindex %u\n",
1250 elem->index);
1251 if (err >= 0 && link->id)
1252 err = tplg_save_printf(dst, pfx, "\tid %u\n",
1253 link->id);
1254 if (err >= 0 && link->stream_name[0])
1255 err = tplg_save_printf(dst, pfx, "\tstream_name '%s'\n",
1256 link->stream_name);
1257 if (err >= 0 && link->default_hw_config_id)
1258 err = tplg_save_printf(dst, pfx, "\tdefault_hw_conf_id %u\n",
1259 link->default_hw_config_id);
1260 if (err >= 0)
1261 err = save_flags(link->flags, link->flag_mask, dst, pfx);
1262 if (err >= 0)
1263 err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_HW_CONFIG,
1264 "hw_configs", dst, pfx2);
1265 if (err >= 0)
1266 err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA,
1267 "data", dst, pfx2);
1268 if (err >= 0)
1269 err = tplg_save_printf(dst, pfx, "}\n");
1270 return err;
1271 }
1272
1273 /* Parse cc */
tplg_parse_cc(snd_tplg_t * tplg,snd_config_t * cfg,void * private ATTRIBUTE_UNUSED)1274 int tplg_parse_cc(snd_tplg_t *tplg, snd_config_t *cfg,
1275 void *private ATTRIBUTE_UNUSED)
1276 {
1277 struct snd_soc_tplg_link_config *link;
1278 struct tplg_elem *elem;
1279 snd_config_iterator_t i, next;
1280 snd_config_t *n;
1281 const char *id;
1282
1283 elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_CC);
1284 if (!elem)
1285 return -ENOMEM;
1286
1287 link = elem->link;
1288 link->size = elem->size;
1289
1290 tplg_dbg(" CC: %s", elem->id);
1291
1292 snd_config_for_each(i, next, cfg) {
1293
1294 n = snd_config_iterator_entry(i);
1295 if (snd_config_get_id(n, &id) < 0)
1296 continue;
1297
1298 /* skip comments */
1299 if (strcmp(id, "comment") == 0)
1300 continue;
1301 if (id[0] == '#')
1302 continue;
1303
1304 if (strcmp(id, "id") == 0) {
1305 if (parse_unsigned(n, &link->id))
1306 return -EINVAL;
1307 continue;
1308 }
1309
1310 }
1311
1312 return 0;
1313 }
1314
1315 /* save CC */
tplg_save_cc(snd_tplg_t * tplg ATTRIBUTE_UNUSED,struct tplg_elem * elem,struct tplg_buf * dst,const char * pfx)1316 int tplg_save_cc(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
1317 struct tplg_elem *elem,
1318 struct tplg_buf *dst, const char *pfx)
1319 {
1320 struct snd_soc_tplg_link_config *link = elem->link;
1321 char pfx2[16];
1322 int err;
1323
1324 if (!link)
1325 return 0;
1326 snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
1327 err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
1328 if (err >= 0 && elem->index)
1329 err = tplg_save_printf(dst, pfx, "\tindex %u\n",
1330 elem->index);
1331 if (err >= 0 && link->id)
1332 err = tplg_save_printf(dst, pfx, "\tid %u\n",
1333 link->id);
1334 if (err >= 0)
1335 err = tplg_save_printf(dst, pfx, "}\n");
1336 return err;
1337 }
1338
1339 struct audio_hw_format {
1340 unsigned int type;
1341 const char *name;
1342 };
1343
1344 static struct audio_hw_format audio_hw_formats[] = {
1345 {
1346 .type = SND_SOC_DAI_FORMAT_I2S,
1347 .name = "I2S",
1348 },
1349 {
1350 .type = SND_SOC_DAI_FORMAT_RIGHT_J,
1351 .name = "RIGHT_J",
1352 },
1353 {
1354 .type = SND_SOC_DAI_FORMAT_LEFT_J,
1355 .name = "LEFT_J",
1356 },
1357 {
1358 .type = SND_SOC_DAI_FORMAT_DSP_A,
1359 .name = "DSP_A",
1360 },
1361 {
1362 .type = SND_SOC_DAI_FORMAT_DSP_B,
1363 .name = "DSP_B",
1364 },
1365 {
1366 .type = SND_SOC_DAI_FORMAT_AC97,
1367 .name = "AC97",
1368 },
1369 {
1370 .type = SND_SOC_DAI_FORMAT_PDM,
1371 .name = "PDM",
1372 },
1373 };
1374
get_audio_hw_format(const char * val)1375 static int get_audio_hw_format(const char *val)
1376 {
1377 unsigned int i;
1378
1379 if (val[0] == '\0')
1380 return -EINVAL;
1381
1382 for (i = 0; i < ARRAY_SIZE(audio_hw_formats); i++)
1383 if (strcasecmp(audio_hw_formats[i].name, val) == 0)
1384 return audio_hw_formats[i].type;
1385
1386 SNDERR("invalid audio HW format %s", val);
1387 return -EINVAL;
1388 }
1389
get_audio_hw_format_name(unsigned int type)1390 static const char *get_audio_hw_format_name(unsigned int type)
1391 {
1392 unsigned int i;
1393
1394 for (i = 0; i < ARRAY_SIZE(audio_hw_formats); i++)
1395 if (audio_hw_formats[i].type == type)
1396 return audio_hw_formats[i].name;
1397 return NULL;
1398 }
1399
tplg_parse_hw_config(snd_tplg_t * tplg,snd_config_t * cfg,void * private ATTRIBUTE_UNUSED)1400 int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg,
1401 void *private ATTRIBUTE_UNUSED)
1402 {
1403
1404 struct snd_soc_tplg_hw_config *hw_cfg;
1405 struct tplg_elem *elem;
1406 snd_config_iterator_t i, next;
1407 snd_config_t *n;
1408 const char *id, *val = NULL;
1409 int ret, ival;
1410 bool provider_legacy;
1411
1412 elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_HW_CONFIG);
1413 if (!elem)
1414 return -ENOMEM;
1415
1416 hw_cfg = elem->hw_cfg;
1417 hw_cfg->size = elem->size;
1418
1419 tplg_dbg(" Link HW config: %s", elem->id);
1420
1421 snd_config_for_each(i, next, cfg) {
1422
1423 n = snd_config_iterator_entry(i);
1424 if (snd_config_get_id(n, &id) < 0)
1425 continue;
1426
1427 /* skip comments */
1428 if (strcmp(id, "comment") == 0)
1429 continue;
1430 if (id[0] == '#')
1431 continue;
1432
1433 if (strcmp(id, "id") == 0) {
1434 if (parse_unsigned(n, &hw_cfg->id))
1435 return -EINVAL;
1436 continue;
1437 }
1438
1439 if (strcmp(id, "format") == 0 ||
1440 strcmp(id, "fmt") == 0) {
1441 if (snd_config_get_string(n, &val) < 0)
1442 return -EINVAL;
1443
1444 ret = get_audio_hw_format(val);
1445 if (ret < 0)
1446 return ret;
1447 hw_cfg->fmt = ret;
1448 continue;
1449 }
1450
1451 provider_legacy = false;
1452 if (strcmp(id, "bclk_master") == 0) {
1453 SNDERR("deprecated option %s, please use 'bclk'\n", id);
1454 provider_legacy = true;
1455 }
1456
1457 if (provider_legacy ||
1458 strcmp(id, "bclk") == 0) {
1459
1460 if (snd_config_get_string(n, &val) < 0)
1461 return -EINVAL;
1462
1463 if (!strcmp(val, "master")) {
1464 /* For backwards capability,
1465 * "master" == "codec is slave"
1466 */
1467 SNDERR("deprecated bclk value '%s'", val);
1468
1469 hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CC;
1470 } else if (!strcmp(val, "codec_slave")) {
1471 SNDERR("deprecated bclk value '%s', use 'codec_consumer'", val);
1472
1473 hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CC;
1474 } else if (!strcmp(val, "codec_consumer")) {
1475 hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CC;
1476 } else if (!strcmp(val, "codec_master")) {
1477 SNDERR("deprecated bclk value '%s', use 'codec_provider", val);
1478
1479 hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CP;
1480 } else if (!strcmp(val, "codec_provider")) {
1481 hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CP;
1482 }
1483 continue;
1484 }
1485
1486 if (strcmp(id, "bclk_freq") == 0 ||
1487 strcmp(id, "bclk_rate") == 0) {
1488 if (parse_unsigned(n, &hw_cfg->bclk_rate))
1489 return -EINVAL;
1490 continue;
1491 }
1492
1493 if (strcmp(id, "bclk_invert") == 0 ||
1494 strcmp(id, "invert_bclk") == 0) {
1495 ival = snd_config_get_bool(n);
1496 if (ival < 0)
1497 return -EINVAL;
1498
1499 hw_cfg->invert_bclk = ival;
1500 continue;
1501 }
1502
1503 provider_legacy = false;
1504 if (strcmp(id, "fsync_master") == 0) {
1505 SNDERR("deprecated option %s, please use 'fsync'\n", id);
1506 provider_legacy = true;
1507 }
1508
1509 if (provider_legacy ||
1510 strcmp(id, "fsync") == 0) {
1511
1512 if (snd_config_get_string(n, &val) < 0)
1513 return -EINVAL;
1514
1515 if (!strcmp(val, "master")) {
1516 /* For backwards capability,
1517 * "master" == "codec is slave"
1518 */
1519 SNDERR("deprecated fsync value '%s'", val);
1520
1521 hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CC;
1522 } else if (!strcmp(val, "codec_slave")) {
1523 SNDERR("deprecated fsync value '%s', use 'codec_consumer'", val);
1524
1525 hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CC;
1526 } else if (!strcmp(val, "codec_consumer")) {
1527 hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CC;
1528 } else if (!strcmp(val, "codec_master")) {
1529 SNDERR("deprecated fsync value '%s', use 'codec_provider'", val);
1530
1531 hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CP;
1532 } else if (!strcmp(val, "codec_provider")) {
1533 hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CP;
1534 }
1535 continue;
1536 }
1537
1538 if (strcmp(id, "fsync_invert") == 0 ||
1539 strcmp(id, "invert_fsync") == 0) {
1540 ival = snd_config_get_bool(n);
1541 if (ival < 0)
1542 return -EINVAL;
1543
1544 hw_cfg->invert_fsync = ival;
1545 continue;
1546 }
1547
1548 if (strcmp(id, "fsync_freq") == 0 ||
1549 strcmp(id, "fsync_rate") == 0) {
1550 if (parse_unsigned(n, &hw_cfg->fsync_rate))
1551 return -EINVAL;
1552 continue;
1553 }
1554
1555 if (strcmp(id, "mclk_freq") == 0 ||
1556 strcmp(id, "mclk_rate") == 0) {
1557 if (parse_unsigned(n, &hw_cfg->mclk_rate))
1558 return -EINVAL;
1559 continue;
1560 }
1561
1562 if (strcmp(id, "mclk") == 0 ||
1563 strcmp(id, "mclk_direction") == 0) {
1564 if (snd_config_get_string(n, &val) < 0)
1565 return -EINVAL;
1566
1567 if (!strcmp(val, "master")) {
1568 /* For backwards capability,
1569 * "master" == "for codec, mclk is input"
1570 */
1571 SNDERR("deprecated mclk value '%s'", val);
1572
1573 hw_cfg->mclk_direction = SND_SOC_TPLG_MCLK_CI;
1574 } else if (!strcmp(val, "codec_mclk_in")) {
1575 hw_cfg->mclk_direction = SND_SOC_TPLG_MCLK_CI;
1576 } else if (!strcmp(val, "codec_mclk_out")) {
1577 hw_cfg->mclk_direction = SND_SOC_TPLG_MCLK_CO;
1578 }
1579 continue;
1580 }
1581
1582 if (strcmp(id, "pm_gate_clocks") == 0 ||
1583 strcmp(id, "clock_gated") == 0) {
1584 ival = snd_config_get_bool(n);
1585 if (ival < 0)
1586 return -EINVAL;
1587
1588 if (ival)
1589 hw_cfg->clock_gated =
1590 SND_SOC_TPLG_DAI_CLK_GATE_GATED;
1591 else
1592 hw_cfg->clock_gated =
1593 SND_SOC_TPLG_DAI_CLK_GATE_CONT;
1594 continue;
1595 }
1596
1597 if (strcmp(id, "tdm_slots") == 0) {
1598 if (parse_unsigned(n, &hw_cfg->tdm_slots))
1599 return -EINVAL;
1600 continue;
1601 }
1602
1603 if (strcmp(id, "tdm_slot_width") == 0) {
1604 if (parse_unsigned(n, &hw_cfg->tdm_slot_width))
1605 return -EINVAL;
1606 continue;
1607 }
1608
1609 if (strcmp(id, "tx_slots") == 0) {
1610 if (parse_unsigned(n, &hw_cfg->tx_slots))
1611 return -EINVAL;
1612 continue;
1613 }
1614
1615 if (strcmp(id, "rx_slots") == 0) {
1616 if (parse_unsigned(n, &hw_cfg->rx_slots))
1617 return -EINVAL;
1618 continue;
1619 }
1620
1621 if (strcmp(id, "tx_channels") == 0) {
1622 if (parse_unsigned(n, &hw_cfg->tx_channels))
1623 return -EINVAL;
1624 continue;
1625 }
1626
1627 if (strcmp(id, "rx_channels") == 0) {
1628 if (parse_unsigned(n, &hw_cfg->rx_channels))
1629 return -EINVAL;
1630 continue;
1631 }
1632
1633 }
1634
1635 return 0;
1636 }
1637
1638 /* save hw config */
tplg_save_hw_config(snd_tplg_t * tplg ATTRIBUTE_UNUSED,struct tplg_elem * elem,struct tplg_buf * dst,const char * pfx)1639 int tplg_save_hw_config(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
1640 struct tplg_elem *elem,
1641 struct tplg_buf *dst, const char *pfx)
1642 {
1643 struct snd_soc_tplg_hw_config *hc = elem->hw_cfg;
1644 int err;
1645
1646 err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
1647 if (err >= 0 && hc->id)
1648 err = tplg_save_printf(dst, pfx, "\tid %u\n",
1649 hc->id);
1650 if (err >= 0 && hc->fmt)
1651 err = tplg_save_printf(dst, pfx, "\tformat '%s'\n",
1652 get_audio_hw_format_name(hc->fmt));
1653 if (err >= 0 && hc->bclk_provider)
1654 err = tplg_save_printf(dst, pfx, "\tbclk '%s'\n",
1655 hc->bclk_provider == SND_SOC_TPLG_BCLK_CC ?
1656 "codec_consumer" : "codec_provider");
1657 if (err >= 0 && hc->bclk_rate)
1658 err = tplg_save_printf(dst, pfx, "\tbclk_freq %u\n",
1659 hc->bclk_rate);
1660 if (err >= 0 && hc->invert_bclk)
1661 err = tplg_save_printf(dst, pfx, "\tbclk_invert 1\n");
1662 if (err >= 0 && hc->fsync_provider)
1663 err = tplg_save_printf(dst, pfx, "\tfsync_provider '%s'\n",
1664 hc->fsync_provider == SND_SOC_TPLG_FSYNC_CC ?
1665 "codec_consumer" : "codec_provider");
1666 if (err >= 0 && hc->fsync_rate)
1667 err = tplg_save_printf(dst, pfx, "\tfsync_freq %u\n",
1668 hc->fsync_rate);
1669 if (err >= 0 && hc->invert_fsync)
1670 err = tplg_save_printf(dst, pfx, "\tfsync_invert 1\n");
1671 if (err >= 0 && hc->mclk_rate)
1672 err = tplg_save_printf(dst, pfx, "\tmclk_freq %u\n",
1673 hc->mclk_rate);
1674 if (err >= 0 && hc->mclk_direction)
1675 err = tplg_save_printf(dst, pfx, "\tmclk '%s'\n",
1676 hc->mclk_direction == SND_SOC_TPLG_MCLK_CI ?
1677 "codec_mclk_in" : "codec_mclk_out");
1678 if (err >= 0 && hc->clock_gated)
1679 err = tplg_save_printf(dst, pfx, "\tpm_gate_clocks 1\n");
1680 if (err >= 0 && hc->tdm_slots)
1681 err = tplg_save_printf(dst, pfx, "\ttdm_slots %u\n",
1682 hc->tdm_slots);
1683 if (err >= 0 && hc->tdm_slot_width)
1684 err = tplg_save_printf(dst, pfx, "\ttdm_slot_width %u\n",
1685 hc->tdm_slot_width);
1686 if (err >= 0 && hc->tx_slots)
1687 err = tplg_save_printf(dst, pfx, "\ttx_slots %u\n",
1688 hc->tx_slots);
1689 if (err >= 0 && hc->rx_slots)
1690 err = tplg_save_printf(dst, pfx, "\trx_slots %u\n",
1691 hc->rx_slots);
1692 if (err >= 0 && hc->tx_channels)
1693 err = tplg_save_printf(dst, pfx, "\ttx_channels %u\n",
1694 hc->tx_channels);
1695 if (err >= 0 && hc->rx_channels)
1696 err = tplg_save_printf(dst, pfx, "\trx_channels %u\n",
1697 hc->rx_channels);
1698 if (err >= 0)
1699 err = tplg_save_printf(dst, pfx, "}\n");
1700 return err;
1701 }
1702
1703 /* copy stream object */
tplg_add_stream_object(struct snd_soc_tplg_stream * strm,struct snd_tplg_stream_template * strm_tpl)1704 static void tplg_add_stream_object(struct snd_soc_tplg_stream *strm,
1705 struct snd_tplg_stream_template *strm_tpl)
1706 {
1707 snd_strlcpy(strm->name, strm_tpl->name,
1708 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1709 strm->format = strm_tpl->format;
1710 strm->rate = strm_tpl->rate;
1711 strm->period_bytes = strm_tpl->period_bytes;
1712 strm->buffer_bytes = strm_tpl->buffer_bytes;
1713 strm->channels = strm_tpl->channels;
1714 }
1715
tplg_add_stream_caps(snd_tplg_t * tplg,struct snd_tplg_stream_caps_template * caps_tpl)1716 static int tplg_add_stream_caps(snd_tplg_t *tplg,
1717 struct snd_tplg_stream_caps_template *caps_tpl)
1718 {
1719 struct snd_soc_tplg_stream_caps *caps;
1720 struct tplg_elem *elem;
1721
1722 elem = tplg_elem_new_common(tplg, NULL, caps_tpl->name,
1723 SND_TPLG_TYPE_STREAM_CAPS);
1724 if (!elem)
1725 return -ENOMEM;
1726
1727 caps = elem->stream_caps;
1728
1729 snd_strlcpy(caps->name, caps_tpl->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1730
1731 caps->formats = caps_tpl->formats;
1732 caps->rates = caps_tpl->rates;
1733 caps->rate_min = caps_tpl->rate_min;
1734 caps->rate_max = caps_tpl->rate_max;
1735 caps->channels_min = caps_tpl->channels_min;
1736 caps->channels_max = caps_tpl->channels_max;
1737 caps->periods_min = caps_tpl->periods_min;
1738 caps->periods_max = caps_tpl->periods_max;
1739 caps->period_size_min = caps_tpl->period_size_min;
1740 caps->period_size_max = caps_tpl->period_size_max;
1741 caps->buffer_size_min = caps_tpl->buffer_size_min;
1742 caps->buffer_size_max = caps_tpl->buffer_size_max;
1743 caps->sig_bits = caps_tpl->sig_bits;
1744 return 0;
1745 }
1746
1747 /* 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)1748 int tplg_add_pcm_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
1749 {
1750 struct snd_tplg_pcm_template *pcm_tpl = t->pcm;
1751 struct snd_soc_tplg_private *priv;
1752 struct snd_soc_tplg_pcm *pcm;
1753 struct tplg_elem *elem;
1754 int ret, i;
1755
1756 tplg_dbg("PCM: %s, DAI %s", pcm_tpl->pcm_name, pcm_tpl->dai_name);
1757
1758 if (pcm_tpl->num_streams > SND_SOC_TPLG_STREAM_CONFIG_MAX)
1759 return -EINVAL;
1760
1761 elem = tplg_elem_new_common(tplg, NULL, pcm_tpl->pcm_name,
1762 SND_TPLG_TYPE_PCM);
1763 if (!elem)
1764 return -ENOMEM;
1765
1766 pcm = elem->pcm;
1767 pcm->size = elem->size;
1768
1769 snd_strlcpy(pcm->pcm_name, pcm_tpl->pcm_name,
1770 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1771 snd_strlcpy(pcm->dai_name, pcm_tpl->dai_name,
1772 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1773 pcm->pcm_id = pcm_tpl->pcm_id;
1774 pcm->dai_id = pcm_tpl->dai_id;
1775 pcm->playback = pcm_tpl->playback;
1776 pcm->capture = pcm_tpl->capture;
1777 pcm->compress = pcm_tpl->compress;
1778
1779 for (i = 0; i < 2; i++) {
1780 if (!pcm_tpl->caps[i] || !pcm_tpl->caps[i]->name)
1781 continue;
1782 ret = tplg_add_stream_caps(tplg, pcm_tpl->caps[i]);
1783 if (ret < 0)
1784 return ret;
1785 snd_strlcpy(pcm->caps[i].name, pcm_tpl->caps[i]->name,
1786 sizeof(pcm->caps[i].name));
1787 }
1788
1789 pcm->flag_mask = pcm_tpl->flag_mask;
1790 pcm->flags = pcm_tpl->flags;
1791
1792 pcm->num_streams = pcm_tpl->num_streams;
1793 for (i = 0; i < pcm_tpl->num_streams; i++)
1794 tplg_add_stream_object(&pcm->stream[i], &pcm_tpl->stream[i]);
1795
1796 /* private data */
1797 priv = pcm_tpl->priv;
1798 if (priv && priv->size > 0) {
1799 ret = tplg_add_data(tplg, elem, priv,
1800 sizeof(*priv) + priv->size);
1801 if (ret < 0)
1802 return ret;
1803 }
1804
1805 return 0;
1806 }
1807
1808 /* 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)1809 static int set_link_hw_config(struct snd_soc_tplg_hw_config *cfg,
1810 struct snd_tplg_hw_config_template *tpl)
1811 {
1812 unsigned int i;
1813
1814 cfg->size = sizeof(*cfg);
1815 cfg->id = tpl->id;
1816
1817 cfg->fmt = tpl->fmt;
1818 cfg->clock_gated = tpl->clock_gated;
1819 cfg->invert_bclk = tpl->invert_bclk;
1820 cfg->invert_fsync = tpl->invert_fsync;
1821 cfg->bclk_provider = tpl->bclk_provider;
1822 cfg->fsync_provider = tpl->fsync_provider;
1823 cfg->mclk_direction = tpl->mclk_direction;
1824 cfg->reserved = tpl->reserved;
1825 cfg->mclk_rate = tpl->mclk_rate;
1826 cfg->bclk_rate = tpl->bclk_rate;
1827 cfg->fsync_rate = tpl->fsync_rate;
1828
1829 cfg->tdm_slots = tpl->tdm_slots;
1830 cfg->tdm_slot_width = tpl->tdm_slot_width;
1831 cfg->tx_slots = tpl->tx_slots;
1832 cfg->rx_slots = tpl->rx_slots;
1833
1834 if (cfg->tx_channels > SND_SOC_TPLG_MAX_CHAN
1835 || cfg->rx_channels > SND_SOC_TPLG_MAX_CHAN)
1836 return -EINVAL;
1837
1838 cfg->tx_channels = tpl->tx_channels;
1839 for (i = 0; i < cfg->tx_channels; i++)
1840 cfg->tx_chanmap[i] = tpl->tx_chanmap[i];
1841
1842 cfg->rx_channels = tpl->rx_channels;
1843 for (i = 0; i < cfg->rx_channels; i++)
1844 cfg->rx_chanmap[i] = tpl->rx_chanmap[i];
1845
1846 return 0;
1847 }
1848
1849 /* Add a physical DAI link element from C API */
tplg_add_link_object(snd_tplg_t * tplg,snd_tplg_obj_template_t * t)1850 int tplg_add_link_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
1851 {
1852 struct snd_tplg_link_template *link_tpl = t->link;
1853 struct snd_soc_tplg_link_config *link;
1854 struct snd_soc_tplg_private *priv;
1855 struct tplg_elem *elem;
1856 unsigned int i;
1857 int ret;
1858
1859 if (t->type != SND_TPLG_TYPE_LINK && t->type != SND_TPLG_TYPE_BE
1860 && t->type != SND_TPLG_TYPE_CC)
1861 return -EINVAL;
1862
1863 elem = tplg_elem_new_common(tplg, NULL, link_tpl->name, t->type);
1864 if (!elem)
1865 return -ENOMEM;
1866
1867 tplg_dbg("Link: %s", link_tpl->name);
1868
1869 link = elem->link;
1870 link->size = elem->size;
1871
1872 /* ID and names */
1873 link->id = link_tpl->id;
1874 snd_strlcpy(link->name, link_tpl->name,
1875 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1876 snd_strlcpy(link->stream_name, link_tpl->stream_name,
1877 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1878
1879 /* stream configs */
1880 if (link_tpl->num_streams > SND_SOC_TPLG_STREAM_CONFIG_MAX)
1881 return -EINVAL;
1882 link->num_streams = link_tpl->num_streams;
1883 for (i = 0; i < link->num_streams; i++)
1884 tplg_add_stream_object(&link->stream[i], &link_tpl->stream[i]);
1885
1886 /* HW configs */
1887 if (link_tpl->num_hw_configs > SND_SOC_TPLG_HW_CONFIG_MAX)
1888 return -EINVAL;
1889 link->num_hw_configs = link_tpl->num_hw_configs;
1890 link->default_hw_config_id = link_tpl->default_hw_config_id;
1891 for (i = 0; i < link->num_hw_configs; i++)
1892 set_link_hw_config(&link->hw_config[i], &link_tpl->hw_config[i]);
1893
1894 /* flags */
1895 link->flag_mask = link_tpl->flag_mask;
1896 link->flags = link_tpl->flags;
1897
1898 /* private data */
1899 priv = link_tpl->priv;
1900 if (priv && priv->size > 0) {
1901 ret = tplg_add_data(tplg, elem, priv,
1902 sizeof(*priv) + priv->size);
1903 if (ret < 0)
1904 return ret;
1905 }
1906
1907 return 0;
1908 }
1909
tplg_add_dai_object(snd_tplg_t * tplg,snd_tplg_obj_template_t * t)1910 int tplg_add_dai_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
1911 {
1912 struct snd_tplg_dai_template *dai_tpl = t->dai;
1913 struct snd_soc_tplg_dai *dai;
1914 struct snd_soc_tplg_private *priv;
1915 struct tplg_elem *elem;
1916 int ret, i;
1917
1918 tplg_dbg("DAI %s", dai_tpl->dai_name);
1919
1920 elem = tplg_elem_new_common(tplg, NULL, dai_tpl->dai_name,
1921 SND_TPLG_TYPE_DAI);
1922 if (!elem)
1923 return -ENOMEM;
1924
1925 dai = elem->dai;
1926 dai->size = elem->size;
1927
1928 snd_strlcpy(dai->dai_name, dai_tpl->dai_name,
1929 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1930 dai->dai_id = dai_tpl->dai_id;
1931
1932 /* stream caps */
1933 dai->playback = dai_tpl->playback;
1934 dai->capture = dai_tpl->capture;
1935
1936 for (i = 0; i < 2; i++) {
1937 if (!dai_tpl->caps[i] || !dai_tpl->caps[i]->name)
1938 continue;
1939 ret = tplg_add_stream_caps(tplg, dai_tpl->caps[i]);
1940 if (ret < 0)
1941 return ret;
1942 snd_strlcpy(dai->caps[i].name, dai_tpl->caps[i]->name,
1943 sizeof(dai->caps[i].name));
1944 }
1945
1946 /* flags */
1947 dai->flag_mask = dai_tpl->flag_mask;
1948 dai->flags = dai_tpl->flags;
1949
1950 /* private data */
1951 priv = dai_tpl->priv;
1952 if (priv && priv->size > 0) {
1953 ret = tplg_add_data(tplg, elem, priv,
1954 sizeof(*priv) + priv->size);
1955 if (ret < 0)
1956 return ret;
1957 }
1958
1959 return 0;
1960 }
1961
1962 /* 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)1963 int tplg_decode_pcm(snd_tplg_t *tplg,
1964 size_t pos,
1965 struct snd_soc_tplg_hdr *hdr,
1966 void *bin, size_t size)
1967 {
1968 struct snd_soc_tplg_pcm *pcm;
1969 snd_tplg_obj_template_t t;
1970 struct snd_tplg_pcm_template *pt;
1971 struct snd_tplg_stream_caps_template caps[2], *cap;
1972 struct snd_tplg_stream_template *stream;
1973 unsigned int i;
1974 size_t asize;
1975 int err;
1976
1977 err = tplg_decode_template(tplg, pos, hdr, &t);
1978 if (err < 0)
1979 return err;
1980
1981 asize = sizeof(*pt) + SND_SOC_TPLG_STREAM_CONFIG_MAX * sizeof(*stream);
1982 pt = alloca(asize);
1983
1984 next:
1985 memset(pt, 0, asize);
1986 pcm = bin;
1987
1988 if (size < sizeof(*pcm)) {
1989 SNDERR("pcm: small size %d", size);
1990 return -EINVAL;
1991 }
1992 if (sizeof(*pcm) != pcm->size) {
1993 SNDERR("pcm: unknown element size %d (expected %zd)",
1994 pcm->size, sizeof(*pcm));
1995 return -EINVAL;
1996 }
1997 if (pcm->num_streams > SND_SOC_TPLG_STREAM_CONFIG_MAX) {
1998 SNDERR("pcm: wrong number of streams %d", pcm->num_streams);
1999 return -EINVAL;
2000 }
2001 if (sizeof(*pcm) + pcm->priv.size > size) {
2002 SNDERR("pcm: wrong private data size %d", pcm->priv.size);
2003 return -EINVAL;
2004 }
2005
2006 tplg_log(tplg, 'D', pos, "pcm: size %d private size %d streams %d",
2007 pcm->size, pcm->priv.size, pcm->num_streams);
2008
2009 pt->pcm_name = pcm->pcm_name;
2010 tplg_log(tplg, 'D', pos, "pcm: pcm_name '%s'", pt->pcm_name);
2011 pt->dai_name = pcm->dai_name;
2012 tplg_log(tplg, 'D', pos, "pcm: dai_name '%s'", pt->dai_name);
2013 pt->pcm_id = pcm->pcm_id;
2014 pt->dai_id = pcm->dai_id;
2015 tplg_log(tplg, 'D', pos, "pcm: pcm_id %d dai_id %d", pt->pcm_id, pt->dai_id);
2016 pt->playback = pcm->playback;
2017 pt->capture = pcm->capture;
2018 pt->compress = pcm->compress;
2019 tplg_log(tplg, 'D', pos, "pcm: playback %d capture %d compress %d",
2020 pt->playback, pt->capture, pt->compress);
2021 pt->num_streams = pcm->num_streams;
2022 pt->flag_mask = pcm->flag_mask;
2023 pt->flags = pcm->flags;
2024 for (i = 0; i < pcm->num_streams; i++) {
2025 stream = &pt->stream[i];
2026 if (pcm->stream[i].size != sizeof(pcm->stream[0])) {
2027 SNDERR("pcm: unknown stream structure size %d",
2028 pcm->stream[i].size);
2029 return -EINVAL;
2030 }
2031 stream->name = pcm->stream[i].name;
2032 tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_pcm, stream[i]),
2033 "stream %d: '%s'", i, stream->name);
2034 stream->format = pcm->stream[i].format;
2035 stream->rate = pcm->stream[i].rate;
2036 stream->period_bytes = pcm->stream[i].period_bytes;
2037 stream->buffer_bytes = pcm->stream[i].buffer_bytes;
2038 stream->channels = pcm->stream[i].channels;
2039 }
2040 for (i = 0; i < 2; i++) {
2041 if (i == 0 && !pcm->playback)
2042 continue;
2043 if (i == 1 && !pcm->capture)
2044 continue;
2045 cap = &caps[i];
2046 pt->caps[i] = cap;
2047 if (pcm->caps[i].size != sizeof(pcm->caps[0])) {
2048 SNDERR("pcm: unknown caps structure size %d",
2049 pcm->caps[i].size);
2050 return -EINVAL;
2051 }
2052 cap->name = pcm->caps[i].name;
2053 tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_pcm, caps[i]),
2054 "caps %d: '%s'", i, cap->name);
2055 cap->formats = pcm->caps[i].formats;
2056 cap->rates = pcm->caps[i].rates;
2057 cap->rate_min = pcm->caps[i].rate_min;
2058 cap->rate_max = pcm->caps[i].rate_max;
2059 cap->channels_min = pcm->caps[i].channels_min;
2060 cap->channels_max = pcm->caps[i].channels_max;
2061 cap->periods_min = pcm->caps[i].periods_min;
2062 cap->periods_max = pcm->caps[i].periods_max;
2063 cap->period_size_min = pcm->caps[i].period_size_min;
2064 cap->period_size_max = pcm->caps[i].period_size_max;
2065 cap->buffer_size_min = pcm->caps[i].buffer_size_min;
2066 cap->buffer_size_max = pcm->caps[i].buffer_size_max;
2067 cap->sig_bits = pcm->caps[i].sig_bits;
2068 }
2069
2070 tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_pcm, priv),
2071 "pcm: private start");
2072 pt->priv = &pcm->priv;
2073
2074 bin += sizeof(*pcm) + pcm->priv.size;
2075 size -= sizeof(*pcm) + pcm->priv.size;
2076 pos += sizeof(*pcm) + pcm->priv.size;
2077
2078 t.pcm = pt;
2079 err = snd_tplg_add_object(tplg, &t);
2080 if (err < 0)
2081 return err;
2082
2083 if (size > 0)
2084 goto next;
2085
2086 return 0;
2087 }
2088
2089 /* 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)2090 int tplg_decode_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
2091 size_t pos ATTRIBUTE_UNUSED,
2092 struct snd_soc_tplg_hdr *hdr ATTRIBUTE_UNUSED,
2093 void *bin ATTRIBUTE_UNUSED,
2094 size_t size ATTRIBUTE_UNUSED)
2095 {
2096 SNDERR("not implemented");
2097 return -ENXIO;
2098 }
2099
2100 /* 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)2101 int tplg_decode_cc(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
2102 size_t pos ATTRIBUTE_UNUSED,
2103 struct snd_soc_tplg_hdr *hdr ATTRIBUTE_UNUSED,
2104 void *bin ATTRIBUTE_UNUSED,
2105 size_t size ATTRIBUTE_UNUSED)
2106 {
2107 SNDERR("not implemented");
2108 return -ENXIO;
2109 }
2110
2111 /* 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)2112 int tplg_decode_link(snd_tplg_t *tplg,
2113 size_t pos,
2114 struct snd_soc_tplg_hdr *hdr,
2115 void *bin, size_t size)
2116 {
2117 struct snd_soc_tplg_link_config *link;
2118 snd_tplg_obj_template_t t;
2119 struct snd_tplg_link_template lt;
2120 struct snd_tplg_stream_template streams[SND_SOC_TPLG_STREAM_CONFIG_MAX];
2121 struct snd_tplg_stream_template *stream;
2122 struct snd_tplg_hw_config_template hws[SND_SOC_TPLG_HW_CONFIG_MAX];
2123 struct snd_tplg_hw_config_template *hw;
2124 unsigned int i, j;
2125 int err;
2126
2127 err = tplg_decode_template(tplg, pos, hdr, &t);
2128 if (err < 0)
2129 return err;
2130
2131 next:
2132 memset(<, 0, sizeof(lt));
2133 memset(streams, 0, sizeof(streams));
2134 memset(hws, 0, sizeof(hws));
2135 link = bin;
2136
2137 if (size < sizeof(*link)) {
2138 SNDERR("link: small size %d", size);
2139 return -EINVAL;
2140 }
2141 if (sizeof(*link) != link->size) {
2142 SNDERR("link: unknown element size %d (expected %zd)",
2143 link->size, sizeof(*link));
2144 return -EINVAL;
2145 }
2146 if (link->num_streams > SND_SOC_TPLG_STREAM_CONFIG_MAX) {
2147 SNDERR("link: wrong number of streams %d", link->num_streams);
2148 return -EINVAL;
2149 }
2150 if (link->num_hw_configs > SND_SOC_TPLG_HW_CONFIG_MAX) {
2151 SNDERR("link: wrong number of streams %d", link->num_streams);
2152 return -EINVAL;
2153 }
2154 if (sizeof(*link) + link->priv.size > size) {
2155 SNDERR("link: wrong private data size %d", link->priv.size);
2156 return -EINVAL;
2157 }
2158
2159 tplg_log(tplg, 'D', pos, "link: size %d private size %d streams %d "
2160 "hw_configs %d",
2161 link->size, link->priv.size, link->num_streams,
2162 link->num_hw_configs);
2163
2164 lt.id = link->id;
2165 lt.name = link->name;
2166 tplg_log(tplg, 'D', pos, "link: name '%s'", lt.name);
2167 lt.stream_name = link->stream_name;
2168 tplg_log(tplg, 'D', pos, "link: stream_name '%s'", lt.stream_name);
2169 lt.num_streams = link->num_streams;
2170 lt.num_hw_configs = link->num_hw_configs;
2171 lt.default_hw_config_id = link->default_hw_config_id;
2172 lt.flag_mask = link->flag_mask;
2173 lt.flags = link->flags;
2174 for (i = 0; i < link->num_streams; i++) {
2175 stream = &streams[i];
2176 if (link->stream[i].size != sizeof(link->stream[0])) {
2177 SNDERR("link: unknown stream structure size %d",
2178 link->stream[i].size);
2179 return -EINVAL;
2180 }
2181 stream->name = link->stream[i].name;
2182 tplg_log(tplg, 'D',
2183 pos + offsetof(struct snd_soc_tplg_link_config, stream[i]),
2184 "stream %d: '%s'", i, stream->name);
2185 stream->format = link->stream[i].format;
2186 stream->rate = link->stream[i].rate;
2187 stream->period_bytes = link->stream[i].period_bytes;
2188 stream->buffer_bytes = link->stream[i].buffer_bytes;
2189 stream->channels = link->stream[i].channels;
2190 }
2191 lt.stream = streams;
2192 for (i = 0; i < link->num_hw_configs; i++) {
2193 hw = &hws[i];
2194 if (link->hw_config[i].size != sizeof(link->hw_config[0])) {
2195 SNDERR("link: unknown hw_config structure size %d",
2196 link->hw_config[i].size);
2197 return -EINVAL;
2198 }
2199 hw->id = link->hw_config[i].id;
2200 hw->fmt = link->hw_config[i].fmt;
2201 hw->clock_gated = link->hw_config[i].clock_gated;
2202 hw->invert_bclk = link->hw_config[i].invert_bclk;
2203 hw->invert_fsync = link->hw_config[i].invert_fsync;
2204 hw->bclk_provider = link->hw_config[i].bclk_provider;
2205 hw->fsync_provider = link->hw_config[i].fsync_provider;
2206 hw->mclk_direction = link->hw_config[i].mclk_direction;
2207 hw->mclk_rate = link->hw_config[i].mclk_rate;
2208 hw->bclk_rate = link->hw_config[i].bclk_rate;
2209 hw->fsync_rate = link->hw_config[i].fsync_rate;
2210 hw->tdm_slots = link->hw_config[i].tdm_slots;
2211 hw->tdm_slot_width = link->hw_config[i].tdm_slot_width;
2212 hw->tx_slots = link->hw_config[i].tx_slots;
2213 hw->rx_slots = link->hw_config[i].rx_slots;
2214 hw->tx_channels = link->hw_config[i].tx_channels;
2215 if (hw->tx_channels > SND_SOC_TPLG_MAX_CHAN) {
2216 SNDERR("link: wrong tx channels %d", hw->tx_channels);
2217 return -EINVAL;
2218 }
2219 for (j = 0; j < hw->tx_channels; j++)
2220 hw->tx_chanmap[j] = link->hw_config[i].tx_chanmap[j];
2221 hw->rx_channels = link->hw_config[i].rx_channels;
2222 if (hw->rx_channels > SND_SOC_TPLG_MAX_CHAN) {
2223 SNDERR("link: wrong rx channels %d", hw->tx_channels);
2224 return -EINVAL;
2225 }
2226 for (j = 0; j < hw->rx_channels; j++)
2227 hw->rx_chanmap[j] = link->hw_config[i].rx_chanmap[j];
2228 }
2229 lt.hw_config = hws;
2230
2231 tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_pcm, priv),
2232 "link: private start");
2233 lt.priv = &link->priv;
2234
2235 bin += sizeof(*link) + link->priv.size;
2236 size -= sizeof(*link) + link->priv.size;
2237 pos += sizeof(*link) + link->priv.size;
2238
2239 t.link = <
2240 err = snd_tplg_add_object(tplg, &t);
2241 if (err < 0)
2242 return err;
2243
2244 if (size > 0)
2245 goto next;
2246
2247 return 0;
2248 }
2249