• 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 struct tplg_table tplg_table[] = {
23 	{
24 		.name  = "manifest",
25 		.id    = "SectionManifest",
26 		.loff  = offsetof(snd_tplg_t, manifest_list),
27 		.type  = SND_TPLG_TYPE_MANIFEST,
28 		.tsoc  = SND_SOC_TPLG_TYPE_MANIFEST,
29 		.size  = sizeof(struct snd_soc_tplg_manifest),
30 		.enew  = 1,
31 		.parse = tplg_parse_manifest_data,
32 		.save  = tplg_save_manifest_data,
33 		.decod = tplg_decode_manifest_data,
34 	},
35 	{
36 		.name  = "control mixer",
37 		.id    = "SectionControlMixer",
38 		.loff  = offsetof(snd_tplg_t, mixer_list),
39 		.type  = SND_TPLG_TYPE_MIXER,
40 		.tsoc  = SND_SOC_TPLG_TYPE_MIXER,
41 		.size  = sizeof(struct snd_soc_tplg_mixer_control),
42 		.build = 1,
43 		.enew  = 1,
44 		.parse = tplg_parse_control_mixer,
45 		.save  = tplg_save_control_mixer,
46 		.decod = tplg_decode_control_mixer,
47 	},
48 	{
49 		.name  = "control enum",
50 		.id    = "SectionControlEnum",
51 		.loff  = offsetof(snd_tplg_t, enum_list),
52 		.type  = SND_TPLG_TYPE_ENUM,
53 		.tsoc  = SND_SOC_TPLG_TYPE_ENUM,
54 		.size  = sizeof(struct snd_soc_tplg_enum_control),
55 		.build = 1,
56 		.enew  = 1,
57 		.parse = tplg_parse_control_enum,
58 		.save  = tplg_save_control_enum,
59 		.decod = tplg_decode_control_enum,
60 	},
61 	{
62 		.name  = "control extended (bytes)",
63 		.id    = "SectionControlBytes",
64 		.loff  = offsetof(snd_tplg_t, bytes_ext_list),
65 		.type  = SND_TPLG_TYPE_BYTES,
66 		.tsoc  = SND_SOC_TPLG_TYPE_BYTES,
67 		.size  = sizeof(struct snd_soc_tplg_bytes_control),
68 		.build = 1,
69 		.enew  = 1,
70 		.parse = tplg_parse_control_bytes,
71 		.save  = tplg_save_control_bytes,
72 		.decod = tplg_decode_control_bytes,
73 	},
74 	{
75 		.name  = "dapm widget",
76 		.id    = "SectionWidget",
77 		.loff  = offsetof(snd_tplg_t, widget_list),
78 		.type  = SND_TPLG_TYPE_DAPM_WIDGET,
79 		.tsoc  = SND_SOC_TPLG_TYPE_DAPM_WIDGET,
80 		.size  = sizeof(struct snd_soc_tplg_dapm_widget),
81 		.build = 1,
82 		.enew  = 1,
83 		.parse = tplg_parse_dapm_widget,
84 		.save  = tplg_save_dapm_widget,
85 		.decod = tplg_decode_dapm_widget,
86 	},
87 	{
88 		.name  = "pcm",
89 		.id    = "SectionPCM",
90 		.loff  = offsetof(snd_tplg_t, pcm_list),
91 		.type  = SND_TPLG_TYPE_PCM,
92 		.tsoc  = SND_SOC_TPLG_TYPE_PCM,
93 		.size  = sizeof(struct snd_soc_tplg_pcm),
94 		.build = 1,
95 		.enew  = 1,
96 		.parse = tplg_parse_pcm,
97 		.save  = tplg_save_pcm,
98 		.decod = tplg_decode_pcm,
99 	},
100 	{
101 		.name  = "physical dai",
102 		.id    = "SectionDAI",
103 		.loff  = offsetof(snd_tplg_t, dai_list),
104 		.type  = SND_TPLG_TYPE_DAI,
105 		.tsoc  = SND_SOC_TPLG_TYPE_DAI,
106 		.size  = sizeof(struct snd_soc_tplg_dai),
107 		.build = 1,
108 		.enew  = 1,
109 		.parse = tplg_parse_dai,
110 		.save  = tplg_save_dai,
111 		.decod = tplg_decode_dai,
112 	},
113 	{
114 		.name  = "be",
115 		.id    = "SectionBE",
116 		.id2   = "SectionLink",
117 		.loff  = offsetof(snd_tplg_t, be_list),
118 		.type  = SND_TPLG_TYPE_BE,
119 		.tsoc  = SND_SOC_TPLG_TYPE_BACKEND_LINK,
120 		.size  = sizeof(struct snd_soc_tplg_link_config),
121 		.build = 1,
122 		.enew  = 1,
123 		.parse = tplg_parse_link,
124 		.save  = tplg_save_link,
125 		.decod = tplg_decode_link,
126 	},
127 	{
128 		.name  = "cc",
129 		.id    = "SectionCC",
130 		.loff  = offsetof(snd_tplg_t, cc_list),
131 		.type  = SND_TPLG_TYPE_CC,
132 		.tsoc  = SND_SOC_TPLG_TYPE_CODEC_LINK,
133 		.size  = sizeof(struct snd_soc_tplg_link_config),
134 		.build = 1,
135 		.enew  = 1,
136 		.parse = tplg_parse_cc,
137 		.save  = tplg_save_cc,
138 		.decod = tplg_decode_cc,
139 	},
140 	{
141 		.name  = "route (dapm graph)",
142 		.id = "SectionGraph",
143 		.loff  = offsetof(snd_tplg_t, route_list),
144 		.type  = SND_TPLG_TYPE_DAPM_GRAPH,
145 		.tsoc  = SND_SOC_TPLG_TYPE_DAPM_GRAPH,
146 		.build = 1,
147 		.parse = tplg_parse_dapm_graph,
148 		.gsave = tplg_save_dapm_graph,
149 		.decod = tplg_decode_dapm_graph,
150 	},
151 	{
152 		.name  = "private data",
153 		.id    = "SectionData",
154 		.loff  = offsetof(snd_tplg_t, pdata_list),
155 		.type  = SND_TPLG_TYPE_DATA,
156 		.tsoc  = SND_SOC_TPLG_TYPE_PDATA,
157 		.build = 1,
158 		.enew  = 1,
159 		.parse = tplg_parse_data,
160 		.save  = tplg_save_data,
161 		.decod = tplg_decode_data,
162 	},
163 	{
164 		.name  = "text",
165 		.id    = "SectionText",
166 		.loff  = offsetof(snd_tplg_t, text_list),
167 		.type  = SND_TPLG_TYPE_TEXT,
168 		.size  = sizeof(struct tplg_texts),
169 		.enew  = 1,
170 		.parse = tplg_parse_text,
171 		.save  = tplg_save_text,
172 	},
173 	{
174 		.name  = "tlv",
175 		.id    = "SectionTLV",
176 		.loff  = offsetof(snd_tplg_t, tlv_list),
177 		.type  = SND_TPLG_TYPE_TLV,
178 		.size  = sizeof(struct snd_soc_tplg_ctl_tlv),
179 		.enew  = 1,
180 		.parse = tplg_parse_tlv,
181 		.save  = tplg_save_tlv,
182 	},
183 	{
184 		.name  = "stream config",
185 		.loff  = offsetof(snd_tplg_t, pcm_config_list),
186 		.type  = SND_TPLG_TYPE_STREAM_CONFIG,
187 		.size  = sizeof(struct snd_soc_tplg_stream),
188 		.enew  = 1,
189 	},
190 	{
191 		.name  = "stream capabilities",
192 		.id    = "SectionPCMCapabilities",
193 		.loff  = offsetof(snd_tplg_t, pcm_caps_list),
194 		.type  = SND_TPLG_TYPE_STREAM_CAPS,
195 		.size  = sizeof(struct snd_soc_tplg_stream_caps),
196 		.enew  = 1,
197 		.parse = tplg_parse_stream_caps,
198 		.save  = tplg_save_stream_caps,
199 	},
200 	{
201 		.name  = "token",
202 		.id    = "SectionVendorTokens",
203 		.loff  = offsetof(snd_tplg_t, token_list),
204 		.type  = SND_TPLG_TYPE_TOKEN,
205 		.enew  = 1,
206 		.parse = tplg_parse_tokens,
207 		.save  = tplg_save_tokens,
208 	},
209 	{
210 		.name  = "tuple",
211 		.id    = "SectionVendorTuples",
212 		.loff  = offsetof(snd_tplg_t, tuple_list),
213 		.type  = SND_TPLG_TYPE_TUPLE,
214 		.free  = tplg_free_tuples,
215 		.enew  = 1,
216 		.parse = tplg_parse_tuples,
217 		.save  = tplg_save_tuples,
218 	},
219 	{
220 		.name  = "hw config",
221 		.id    = "SectionHWConfig",
222 		.loff  = offsetof(snd_tplg_t, hw_cfg_list),
223 		.type  = SND_TPLG_TYPE_HW_CONFIG,
224 		.size  = sizeof(struct snd_soc_tplg_hw_config),
225 		.enew  = 1,
226 		.parse = tplg_parse_hw_config,
227 		.save  = tplg_save_hw_config,
228 	}
229 };
230 
231 unsigned int tplg_table_items = ARRAY_SIZE(tplg_table);
232 
tplg_get_type(int asoc_type)233 int tplg_get_type(int asoc_type)
234 {
235 	unsigned int index;
236 
237 	for (index = 0; index < tplg_table_items; index++)
238 		if (tplg_table[index].tsoc == asoc_type)
239 			return tplg_table[index].type;
240 	SNDERR("uknown asoc type %d", asoc_type);
241 	return -EINVAL;
242 }
243 
tplg_ref_add(struct tplg_elem * elem,int type,const char * id)244 int tplg_ref_add(struct tplg_elem *elem, int type, const char* id)
245 {
246 	struct tplg_ref *ref;
247 
248 	ref = calloc(1, sizeof(*ref));
249 	if (!ref)
250 		return -ENOMEM;
251 
252 	strncpy(ref->id, id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
253 	ref->id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
254 	ref->type = type;
255 
256 	list_add_tail(&ref->list, &elem->ref_list);
257 	return 0;
258 }
259 
260 /* directly add a reference elem */
tplg_ref_add_elem(struct tplg_elem * elem,struct tplg_elem * elem_ref)261 int tplg_ref_add_elem(struct tplg_elem *elem, struct tplg_elem *elem_ref)
262 {
263 	struct tplg_ref *ref;
264 
265 	ref = calloc(1, sizeof(*ref));
266 	if (!ref)
267 		return -ENOMEM;
268 
269 	ref->type = elem_ref->type;
270 	ref->elem = elem_ref;
271 	snd_strlcpy(ref->id, elem_ref->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
272 
273 	list_add_tail(&ref->list, &elem->ref_list);
274 	return 0;
275 }
276 
tplg_ref_free_list(struct list_head * base)277 void tplg_ref_free_list(struct list_head *base)
278 {
279 	struct list_head *pos, *npos;
280 	struct tplg_ref *ref;
281 
282 	list_for_each_safe(pos, npos, base) {
283 		ref = list_entry(pos, struct tplg_ref, list);
284 		list_del(&ref->list);
285 		free(ref);
286 	}
287 }
288 
tplg_elem_new(void)289 struct tplg_elem *tplg_elem_new(void)
290 {
291 	struct tplg_elem *elem;
292 
293 	elem = calloc(1, sizeof(*elem));
294 	if (!elem)
295 		return NULL;
296 
297 	INIT_LIST_HEAD(&elem->ref_list);
298 	return elem;
299 }
300 
tplg_elem_free(struct tplg_elem * elem)301 void tplg_elem_free(struct tplg_elem *elem)
302 {
303 	list_del(&elem->list);
304 
305 	tplg_ref_free_list(&elem->ref_list);
306 
307 	/* free struct snd_tplg_ object,
308 	 * the union pointers share the same address
309 	 */
310 	if (elem->obj) {
311 		if (elem->free)
312 			elem->free(elem->obj);
313 
314 		free(elem->obj);
315 	}
316 
317 	free(elem);
318 }
319 
tplg_elem_free_list(struct list_head * base)320 void tplg_elem_free_list(struct list_head *base)
321 {
322 	struct list_head *pos, *npos;
323 	struct tplg_elem *elem;
324 
325 	list_for_each_safe(pos, npos, base) {
326 		elem = list_entry(pos, struct tplg_elem, list);
327 		tplg_elem_free(elem);
328 	}
329 }
330 
tplg_elem_lookup(struct list_head * base,const char * id,unsigned int type,int index)331 struct tplg_elem *tplg_elem_lookup(struct list_head *base, const char* id,
332 				   unsigned int type, int index)
333 {
334 	struct list_head *pos;
335 	struct tplg_elem *elem;
336 
337 	if (!base || !id)
338 		return NULL;
339 
340 	list_for_each(pos, base) {
341 
342 		elem = list_entry(pos, struct tplg_elem, list);
343 
344 		if (!strcmp(elem->id, id) && elem->type == type)
345 			return elem;
346 		/* SND_TPLG_INDEX_ALL is the default value "0" and applicable
347 		   for all use cases */
348 		if ((index != SND_TPLG_INDEX_ALL)
349 			&& (elem->index > index))
350 			break;
351 	}
352 
353 	return NULL;
354 }
355 
356 /* find an element by type */
tplg_elem_type_lookup(snd_tplg_t * tplg,enum snd_tplg_type type)357 struct tplg_elem *tplg_elem_type_lookup(snd_tplg_t *tplg,
358 					enum snd_tplg_type type)
359 {
360 	struct tplg_table *tptr;
361 	struct list_head *pos, *list;
362 	struct tplg_elem *elem;
363 	unsigned int index;
364 
365 	for (index = 0; index < tplg_table_items; index++) {
366 		tptr = &tplg_table[index];
367 		if (!tptr->enew)
368 			continue;
369 		if ((int)type != tptr->type)
370 			continue;
371 		break;
372 	}
373 	if (index >= tplg_table_items)
374 		return NULL;
375 
376 	list = (struct list_head *)((void *)tplg + tptr->loff);
377 
378 	/* return only first element */
379 	list_for_each(pos, list) {
380 		elem = list_entry(pos, struct tplg_elem, list);
381 		return elem;
382 	}
383 	return NULL;
384 }
385 
386 /* insert a new element into list in the ascending order of index value */
tplg_elem_insert(struct tplg_elem * elem_p,struct list_head * list)387 void tplg_elem_insert(struct tplg_elem *elem_p, struct list_head *list)
388 {
389 	struct list_head *pos, *p = &(elem_p->list);
390 	struct tplg_elem *elem;
391 
392 	list_for_each(pos, list) {
393 		elem = list_entry(pos, struct tplg_elem, list);
394 		if (elem_p->index < elem->index)
395 			break;
396 	}
397 	/* insert item before pos */
398 	list_insert(p, pos->prev, pos);
399 }
400 
401 /* create a new common element and object */
tplg_elem_new_common(snd_tplg_t * tplg,snd_config_t * cfg,const char * name,enum snd_tplg_type type)402 struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg,
403 				       snd_config_t *cfg,
404 				       const char *name,
405 				       enum snd_tplg_type type)
406 {
407 	struct tplg_table *tptr;
408 	struct tplg_elem *elem;
409 	struct list_head *list;
410 	const char *id;
411 	int obj_size = 0;
412 	unsigned index;
413 	void *obj;
414 	snd_config_iterator_t i, next;
415 	snd_config_t *n;
416 
417 	if (!cfg && !name)
418 		return NULL;
419 
420 	elem = tplg_elem_new();
421 	if (!elem)
422 		return NULL;
423 
424 	/* do we get name from cfg */
425 	if (cfg) {
426 		snd_config_get_id(cfg, &id);
427 		snd_strlcpy(elem->id, id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
428 		elem->id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
429 		/* as we insert new elem based on the index value, move index
430 		   parsing here */
431 		snd_config_for_each(i, next, cfg) {
432 			n = snd_config_iterator_entry(i);
433 			if (snd_config_get_id(n, &id))
434 				continue;
435 			if (strcmp(id, "index") == 0) {
436 				if (tplg_get_integer(n, &elem->index, 0)) {
437 					free(elem);
438 					return NULL;
439 				}
440 				if (elem->index < 0) {
441 					free(elem);
442 					return NULL;
443 				}
444 			}
445 		}
446 	} else if (name != NULL)
447 		snd_strlcpy(elem->id, name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
448 
449 	for (index = 0; index < tplg_table_items; index++) {
450 		tptr = &tplg_table[index];
451 		if (!tptr->enew)
452 			continue;
453 		if ((int)type != tptr->type)
454 			continue;
455 		break;
456 	}
457 	if (index >= tplg_table_items) {
458 		free(elem);
459 		return NULL;
460 	}
461 
462 	list = (struct list_head *)((void *)tplg + tptr->loff);
463 	tplg_elem_insert(elem, list);
464 	obj_size = tptr->size;
465 	elem->free = tptr->free;
466 	elem->table = tptr;
467 
468 	/* create new object too if required */
469 	if (obj_size > 0) {
470 		obj = calloc(1, obj_size);
471 		if (obj == NULL) {
472 			free(elem);
473 			return NULL;
474 		}
475 
476 		elem->obj = obj;
477 		elem->size = obj_size;
478 	}
479 
480 	elem->type = type;
481 	return elem;
482 }
483 
484 #ifndef DOC_HIDDEN
485 struct tplg_alloc {
486 	struct list_head list;
487 	void *data[0];
488 };
489 #endif /* DOC_HIDDEN */
490 
tplg_calloc(struct list_head * heap,size_t size)491 void *tplg_calloc(struct list_head *heap, size_t size)
492 {
493 	struct tplg_alloc *a;
494 
495 	a = calloc(1, sizeof(*a) + size);
496 	if (a == NULL)
497 		return NULL;
498 	list_add_tail(&a->list, heap);
499 	return a->data;
500 }
501 
tplg_free(struct list_head * heap)502 void tplg_free(struct list_head *heap)
503 {
504 	struct list_head *pos, *npos;
505 	struct tplg_alloc *a;
506 
507 	list_for_each_safe(pos, npos, heap) {
508 		a = list_entry(pos, struct tplg_alloc, list);
509 		list_del(&a->list);
510 		free(a);
511 	}
512 }
513