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