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(&lt, 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 = &lt;
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