1d5ac70f0Sopenharmony_ci/*
2d5ac70f0Sopenharmony_ci  Copyright(c) 2014-2015 Intel Corporation
3d5ac70f0Sopenharmony_ci  All rights reserved.
4d5ac70f0Sopenharmony_ci
5d5ac70f0Sopenharmony_ci  This library is free software; you can redistribute it and/or modify
6d5ac70f0Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as
7d5ac70f0Sopenharmony_ci  published by the Free Software Foundation; either version 2.1 of
8d5ac70f0Sopenharmony_ci  the License, or (at your option) any later version.
9d5ac70f0Sopenharmony_ci
10d5ac70f0Sopenharmony_ci  This program is distributed in the hope that it will be useful,
11d5ac70f0Sopenharmony_ci  but WITHOUT ANY WARRANTY; without even the implied warranty of
12d5ac70f0Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13d5ac70f0Sopenharmony_ci  GNU Lesser General Public License for more details.
14d5ac70f0Sopenharmony_ci
15d5ac70f0Sopenharmony_ci  Authors: Mengdong Lin <mengdong.lin@intel.com>
16d5ac70f0Sopenharmony_ci           Yao Jin <yao.jin@intel.com>
17d5ac70f0Sopenharmony_ci           Liam Girdwood <liam.r.girdwood@linux.intel.com>
18d5ac70f0Sopenharmony_ci*/
19d5ac70f0Sopenharmony_ci
20d5ac70f0Sopenharmony_ci#include "tplg_local.h"
21d5ac70f0Sopenharmony_ci
22d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN
23d5ac70f0Sopenharmony_ci#define ENUM_VAL_SIZE 	(SNDRV_CTL_ELEM_ID_NAME_MAXLEN >> 2)
24d5ac70f0Sopenharmony_ci
25d5ac70f0Sopenharmony_cistruct ctl_access_elem {
26d5ac70f0Sopenharmony_ci	const char *name;
27d5ac70f0Sopenharmony_ci	unsigned int value;
28d5ac70f0Sopenharmony_ci};
29d5ac70f0Sopenharmony_ci#endif /* DOC_HIDDEN */
30d5ac70f0Sopenharmony_ci
31d5ac70f0Sopenharmony_ci/* CTL access strings and codes */
32d5ac70f0Sopenharmony_ci/* place the multi-bit values on top - like read_write - for save */
33d5ac70f0Sopenharmony_cistatic const struct ctl_access_elem ctl_access[] = {
34d5ac70f0Sopenharmony_ci	{"read_write", SNDRV_CTL_ELEM_ACCESS_READWRITE},
35d5ac70f0Sopenharmony_ci	{"tlv_read_write", SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE},
36d5ac70f0Sopenharmony_ci	{"read", SNDRV_CTL_ELEM_ACCESS_READ},
37d5ac70f0Sopenharmony_ci	{"write", SNDRV_CTL_ELEM_ACCESS_WRITE},
38d5ac70f0Sopenharmony_ci	{"volatile", SNDRV_CTL_ELEM_ACCESS_VOLATILE},
39d5ac70f0Sopenharmony_ci	{"tlv_read", SNDRV_CTL_ELEM_ACCESS_TLV_READ},
40d5ac70f0Sopenharmony_ci	{"tlv_write", SNDRV_CTL_ELEM_ACCESS_TLV_WRITE},
41d5ac70f0Sopenharmony_ci	{"tlv_command", SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND},
42d5ac70f0Sopenharmony_ci	{"inactive", SNDRV_CTL_ELEM_ACCESS_INACTIVE},
43d5ac70f0Sopenharmony_ci	{"lock", SNDRV_CTL_ELEM_ACCESS_LOCK},
44d5ac70f0Sopenharmony_ci	{"owner", SNDRV_CTL_ELEM_ACCESS_OWNER},
45d5ac70f0Sopenharmony_ci	{"tlv_callback", SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK},
46d5ac70f0Sopenharmony_ci};
47d5ac70f0Sopenharmony_ci
48d5ac70f0Sopenharmony_ci/* find CTL access strings and conver to values */
49d5ac70f0Sopenharmony_cistatic int parse_access_values(snd_config_t *cfg,
50d5ac70f0Sopenharmony_ci			       struct snd_soc_tplg_ctl_hdr *hdr)
51d5ac70f0Sopenharmony_ci{
52d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
53d5ac70f0Sopenharmony_ci	snd_config_t *n;
54d5ac70f0Sopenharmony_ci	const char *value = NULL;
55d5ac70f0Sopenharmony_ci	unsigned int j;
56d5ac70f0Sopenharmony_ci
57d5ac70f0Sopenharmony_ci	tplg_dbg(" Access:");
58d5ac70f0Sopenharmony_ci
59d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, cfg) {
60d5ac70f0Sopenharmony_ci		n = snd_config_iterator_entry(i);
61d5ac70f0Sopenharmony_ci
62d5ac70f0Sopenharmony_ci		/* get value */
63d5ac70f0Sopenharmony_ci		if (snd_config_get_string(n, &value) < 0)
64d5ac70f0Sopenharmony_ci			continue;
65d5ac70f0Sopenharmony_ci
66d5ac70f0Sopenharmony_ci		/* match access value and set flags */
67d5ac70f0Sopenharmony_ci		for (j = 0; j < ARRAY_SIZE(ctl_access); j++) {
68d5ac70f0Sopenharmony_ci			if (strcmp(value, ctl_access[j].name) == 0) {
69d5ac70f0Sopenharmony_ci				hdr->access |= ctl_access[j].value;
70d5ac70f0Sopenharmony_ci				tplg_dbg("\t%s", value);
71d5ac70f0Sopenharmony_ci				break;
72d5ac70f0Sopenharmony_ci			}
73d5ac70f0Sopenharmony_ci		}
74d5ac70f0Sopenharmony_ci	}
75d5ac70f0Sopenharmony_ci
76d5ac70f0Sopenharmony_ci	return 0;
77d5ac70f0Sopenharmony_ci}
78d5ac70f0Sopenharmony_ci
79d5ac70f0Sopenharmony_ci/* Parse Access */
80d5ac70f0Sopenharmony_ciint parse_access(snd_config_t *cfg,
81d5ac70f0Sopenharmony_ci		 struct snd_soc_tplg_ctl_hdr *hdr)
82d5ac70f0Sopenharmony_ci{
83d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
84d5ac70f0Sopenharmony_ci	snd_config_t *n;
85d5ac70f0Sopenharmony_ci	const char *id;
86d5ac70f0Sopenharmony_ci	int err = 0;
87d5ac70f0Sopenharmony_ci
88d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, cfg) {
89d5ac70f0Sopenharmony_ci
90d5ac70f0Sopenharmony_ci		n = snd_config_iterator_entry(i);
91d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &id) < 0)
92d5ac70f0Sopenharmony_ci			continue;
93d5ac70f0Sopenharmony_ci
94d5ac70f0Sopenharmony_ci		if (strcmp(id, "access") == 0) {
95d5ac70f0Sopenharmony_ci			err = parse_access_values(n, hdr);
96d5ac70f0Sopenharmony_ci			if (err < 0) {
97d5ac70f0Sopenharmony_ci				SNDERR("failed to parse access");
98d5ac70f0Sopenharmony_ci				return err;
99d5ac70f0Sopenharmony_ci			}
100d5ac70f0Sopenharmony_ci			continue;
101d5ac70f0Sopenharmony_ci		}
102d5ac70f0Sopenharmony_ci	}
103d5ac70f0Sopenharmony_ci
104d5ac70f0Sopenharmony_ci	return err;
105d5ac70f0Sopenharmony_ci}
106d5ac70f0Sopenharmony_ci
107d5ac70f0Sopenharmony_ci/* Save Access */
108d5ac70f0Sopenharmony_cistatic int tplg_save_access(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
109d5ac70f0Sopenharmony_ci			    struct snd_soc_tplg_ctl_hdr *hdr,
110d5ac70f0Sopenharmony_ci			    struct tplg_buf *dst, const char *pfx)
111d5ac70f0Sopenharmony_ci{
112d5ac70f0Sopenharmony_ci	const char *last;
113d5ac70f0Sopenharmony_ci	unsigned int j, count, access, cval;
114d5ac70f0Sopenharmony_ci	int err;
115d5ac70f0Sopenharmony_ci
116d5ac70f0Sopenharmony_ci	if (hdr->access == 0)
117d5ac70f0Sopenharmony_ci		return 0;
118d5ac70f0Sopenharmony_ci
119d5ac70f0Sopenharmony_ci	access = hdr->access;
120d5ac70f0Sopenharmony_ci	for (j = 0, count = 0, last = NULL; j < ARRAY_SIZE(ctl_access); j++) {
121d5ac70f0Sopenharmony_ci		cval = ctl_access[j].value;
122d5ac70f0Sopenharmony_ci		if ((access & cval) == cval) {
123d5ac70f0Sopenharmony_ci			access &= ~cval;
124d5ac70f0Sopenharmony_ci			last = ctl_access[j].name;
125d5ac70f0Sopenharmony_ci			count++;
126d5ac70f0Sopenharmony_ci		}
127d5ac70f0Sopenharmony_ci	}
128d5ac70f0Sopenharmony_ci	if (count == 1)
129d5ac70f0Sopenharmony_ci		return tplg_save_printf(dst, pfx, "access.0 %s\n", last);
130d5ac70f0Sopenharmony_ci	err = tplg_save_printf(dst, pfx, "access [\n");
131d5ac70f0Sopenharmony_ci	if (err < 0)
132d5ac70f0Sopenharmony_ci		return err;
133d5ac70f0Sopenharmony_ci	access = hdr->access;
134d5ac70f0Sopenharmony_ci	for (j = 0; j < ARRAY_SIZE(ctl_access); j++) {
135d5ac70f0Sopenharmony_ci		cval = ctl_access[j].value;
136d5ac70f0Sopenharmony_ci		if ((access & cval) == cval) {
137d5ac70f0Sopenharmony_ci			err = tplg_save_printf(dst, pfx, "\t%s\n",
138d5ac70f0Sopenharmony_ci					       ctl_access[j].name);
139d5ac70f0Sopenharmony_ci			if (err < 0)
140d5ac70f0Sopenharmony_ci				return err;
141d5ac70f0Sopenharmony_ci			access &= ~cval;
142d5ac70f0Sopenharmony_ci		}
143d5ac70f0Sopenharmony_ci	}
144d5ac70f0Sopenharmony_ci	return tplg_save_printf(dst, pfx, "]\n");
145d5ac70f0Sopenharmony_ci}
146d5ac70f0Sopenharmony_ci
147d5ac70f0Sopenharmony_ci/* copy referenced TLV to the mixer control */
148d5ac70f0Sopenharmony_cistatic int copy_tlv(struct tplg_elem *elem, struct tplg_elem *ref)
149d5ac70f0Sopenharmony_ci{
150d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_mixer_control *mixer_ctrl =  elem->mixer_ctrl;
151d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_ctl_tlv *tlv = ref->tlv;
152d5ac70f0Sopenharmony_ci
153d5ac70f0Sopenharmony_ci	tplg_dbg("TLV '%s' used by '%s", ref->id, elem->id);
154d5ac70f0Sopenharmony_ci
155d5ac70f0Sopenharmony_ci	/* TLV has a fixed size */
156d5ac70f0Sopenharmony_ci	mixer_ctrl->hdr.tlv = *tlv;
157d5ac70f0Sopenharmony_ci	return 0;
158d5ac70f0Sopenharmony_ci}
159d5ac70f0Sopenharmony_ci
160d5ac70f0Sopenharmony_ci/* check referenced TLV for a mixer control */
161d5ac70f0Sopenharmony_cistatic int tplg_build_mixer_control(snd_tplg_t *tplg,
162d5ac70f0Sopenharmony_ci				    struct tplg_elem *elem)
163d5ac70f0Sopenharmony_ci{
164d5ac70f0Sopenharmony_ci	struct tplg_ref *ref;
165d5ac70f0Sopenharmony_ci	struct list_head *base, *pos;
166d5ac70f0Sopenharmony_ci	int err = 0;
167d5ac70f0Sopenharmony_ci
168d5ac70f0Sopenharmony_ci	base = &elem->ref_list;
169d5ac70f0Sopenharmony_ci
170d5ac70f0Sopenharmony_ci	/* for each ref in this control elem */
171d5ac70f0Sopenharmony_ci	list_for_each(pos, base) {
172d5ac70f0Sopenharmony_ci
173d5ac70f0Sopenharmony_ci		ref = list_entry(pos, struct tplg_ref, list);
174d5ac70f0Sopenharmony_ci		if (ref->elem)
175d5ac70f0Sopenharmony_ci			continue;
176d5ac70f0Sopenharmony_ci
177d5ac70f0Sopenharmony_ci		if (ref->type == SND_TPLG_TYPE_TLV) {
178d5ac70f0Sopenharmony_ci			ref->elem = tplg_elem_lookup(&tplg->tlv_list,
179d5ac70f0Sopenharmony_ci				ref->id, SND_TPLG_TYPE_TLV, elem->index);
180d5ac70f0Sopenharmony_ci			if (ref->elem)
181d5ac70f0Sopenharmony_ci				 err = copy_tlv(elem, ref->elem);
182d5ac70f0Sopenharmony_ci
183d5ac70f0Sopenharmony_ci		} else if (ref->type == SND_TPLG_TYPE_DATA) {
184d5ac70f0Sopenharmony_ci			err = tplg_copy_data(tplg, elem, ref);
185d5ac70f0Sopenharmony_ci			if (err < 0)
186d5ac70f0Sopenharmony_ci				return err;
187d5ac70f0Sopenharmony_ci		}
188d5ac70f0Sopenharmony_ci
189d5ac70f0Sopenharmony_ci		if (!ref->elem) {
190d5ac70f0Sopenharmony_ci			SNDERR("cannot find '%s' referenced by"
191d5ac70f0Sopenharmony_ci				" control '%s'", ref->id, elem->id);
192d5ac70f0Sopenharmony_ci			return -EINVAL;
193d5ac70f0Sopenharmony_ci		} else if (err < 0)
194d5ac70f0Sopenharmony_ci			return err;
195d5ac70f0Sopenharmony_ci	}
196d5ac70f0Sopenharmony_ci
197d5ac70f0Sopenharmony_ci	return 0;
198d5ac70f0Sopenharmony_ci}
199d5ac70f0Sopenharmony_ci
200d5ac70f0Sopenharmony_cistatic void copy_enum_texts(struct tplg_elem *enum_elem,
201d5ac70f0Sopenharmony_ci			    struct tplg_elem *ref_elem)
202d5ac70f0Sopenharmony_ci{
203d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_enum_control *ec = enum_elem->enum_ctrl;
204d5ac70f0Sopenharmony_ci	struct tplg_texts *texts = ref_elem->texts;
205d5ac70f0Sopenharmony_ci
206d5ac70f0Sopenharmony_ci	memcpy(ec->texts, texts->items,
207d5ac70f0Sopenharmony_ci		SND_SOC_TPLG_NUM_TEXTS * SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
208d5ac70f0Sopenharmony_ci	ec->items += texts->num_items;
209d5ac70f0Sopenharmony_ci}
210d5ac70f0Sopenharmony_ci
211d5ac70f0Sopenharmony_ci/* check referenced text for a enum control */
212d5ac70f0Sopenharmony_cistatic int tplg_build_enum_control(snd_tplg_t *tplg,
213d5ac70f0Sopenharmony_ci				   struct tplg_elem *elem)
214d5ac70f0Sopenharmony_ci{
215d5ac70f0Sopenharmony_ci	struct tplg_ref *ref;
216d5ac70f0Sopenharmony_ci	struct list_head *base, *pos;
217d5ac70f0Sopenharmony_ci	int err;
218d5ac70f0Sopenharmony_ci
219d5ac70f0Sopenharmony_ci	base = &elem->ref_list;
220d5ac70f0Sopenharmony_ci
221d5ac70f0Sopenharmony_ci	list_for_each(pos, base) {
222d5ac70f0Sopenharmony_ci
223d5ac70f0Sopenharmony_ci		ref = list_entry(pos, struct tplg_ref, list);
224d5ac70f0Sopenharmony_ci		if (ref->elem)
225d5ac70f0Sopenharmony_ci			continue;
226d5ac70f0Sopenharmony_ci
227d5ac70f0Sopenharmony_ci		if (ref->type == SND_TPLG_TYPE_TEXT) {
228d5ac70f0Sopenharmony_ci			ref->elem = tplg_elem_lookup(&tplg->text_list,
229d5ac70f0Sopenharmony_ci				ref->id, SND_TPLG_TYPE_TEXT, elem->index);
230d5ac70f0Sopenharmony_ci			if (ref->elem)
231d5ac70f0Sopenharmony_ci				copy_enum_texts(elem, ref->elem);
232d5ac70f0Sopenharmony_ci
233d5ac70f0Sopenharmony_ci		} else if (ref->type == SND_TPLG_TYPE_DATA) {
234d5ac70f0Sopenharmony_ci			err = tplg_copy_data(tplg, elem, ref);
235d5ac70f0Sopenharmony_ci			if (err < 0)
236d5ac70f0Sopenharmony_ci				return err;
237d5ac70f0Sopenharmony_ci		}
238d5ac70f0Sopenharmony_ci		if (!ref->elem) {
239d5ac70f0Sopenharmony_ci			SNDERR("cannot find '%s' referenced by"
240d5ac70f0Sopenharmony_ci				" control '%s'", ref->id, elem->id);
241d5ac70f0Sopenharmony_ci			return -EINVAL;
242d5ac70f0Sopenharmony_ci		}
243d5ac70f0Sopenharmony_ci	}
244d5ac70f0Sopenharmony_ci
245d5ac70f0Sopenharmony_ci	return 0;
246d5ac70f0Sopenharmony_ci}
247d5ac70f0Sopenharmony_ci
248d5ac70f0Sopenharmony_ci/* check referenced private data for a byte control */
249d5ac70f0Sopenharmony_cistatic int tplg_build_bytes_control(snd_tplg_t *tplg, struct tplg_elem *elem)
250d5ac70f0Sopenharmony_ci{
251d5ac70f0Sopenharmony_ci	struct tplg_ref *ref;
252d5ac70f0Sopenharmony_ci	struct list_head *base, *pos;
253d5ac70f0Sopenharmony_ci	int err;
254d5ac70f0Sopenharmony_ci
255d5ac70f0Sopenharmony_ci	base = &elem->ref_list;
256d5ac70f0Sopenharmony_ci
257d5ac70f0Sopenharmony_ci	list_for_each(pos, base) {
258d5ac70f0Sopenharmony_ci
259d5ac70f0Sopenharmony_ci		ref = list_entry(pos, struct tplg_ref, list);
260d5ac70f0Sopenharmony_ci		if (ref->elem)
261d5ac70f0Sopenharmony_ci			continue;
262d5ac70f0Sopenharmony_ci
263d5ac70f0Sopenharmony_ci		 if (ref->type == SND_TPLG_TYPE_DATA) {
264d5ac70f0Sopenharmony_ci			err = tplg_copy_data(tplg, elem, ref);
265d5ac70f0Sopenharmony_ci			if (err < 0)
266d5ac70f0Sopenharmony_ci				return err;
267d5ac70f0Sopenharmony_ci		}
268d5ac70f0Sopenharmony_ci	}
269d5ac70f0Sopenharmony_ci
270d5ac70f0Sopenharmony_ci	return 0;
271d5ac70f0Sopenharmony_ci}
272d5ac70f0Sopenharmony_ci
273d5ac70f0Sopenharmony_ciint tplg_build_controls(snd_tplg_t *tplg)
274d5ac70f0Sopenharmony_ci{
275d5ac70f0Sopenharmony_ci	struct list_head *base, *pos;
276d5ac70f0Sopenharmony_ci	struct tplg_elem *elem;
277d5ac70f0Sopenharmony_ci	int err = 0;
278d5ac70f0Sopenharmony_ci
279d5ac70f0Sopenharmony_ci	base = &tplg->mixer_list;
280d5ac70f0Sopenharmony_ci	list_for_each(pos, base) {
281d5ac70f0Sopenharmony_ci
282d5ac70f0Sopenharmony_ci		elem = list_entry(pos, struct tplg_elem, list);
283d5ac70f0Sopenharmony_ci		err = tplg_build_mixer_control(tplg, elem);
284d5ac70f0Sopenharmony_ci		if (err < 0)
285d5ac70f0Sopenharmony_ci			return err;
286d5ac70f0Sopenharmony_ci
287d5ac70f0Sopenharmony_ci		/* add control to manifest */
288d5ac70f0Sopenharmony_ci		tplg->manifest.control_elems++;
289d5ac70f0Sopenharmony_ci	}
290d5ac70f0Sopenharmony_ci
291d5ac70f0Sopenharmony_ci	base = &tplg->enum_list;
292d5ac70f0Sopenharmony_ci	list_for_each(pos, base) {
293d5ac70f0Sopenharmony_ci
294d5ac70f0Sopenharmony_ci		elem = list_entry(pos, struct tplg_elem, list);
295d5ac70f0Sopenharmony_ci		err = tplg_build_enum_control(tplg, elem);
296d5ac70f0Sopenharmony_ci		if (err < 0)
297d5ac70f0Sopenharmony_ci			return err;
298d5ac70f0Sopenharmony_ci
299d5ac70f0Sopenharmony_ci		/* add control to manifest */
300d5ac70f0Sopenharmony_ci		tplg->manifest.control_elems++;
301d5ac70f0Sopenharmony_ci	}
302d5ac70f0Sopenharmony_ci
303d5ac70f0Sopenharmony_ci	base = &tplg->bytes_ext_list;
304d5ac70f0Sopenharmony_ci	list_for_each(pos, base) {
305d5ac70f0Sopenharmony_ci
306d5ac70f0Sopenharmony_ci		elem = list_entry(pos, struct tplg_elem, list);
307d5ac70f0Sopenharmony_ci		err = tplg_build_bytes_control(tplg, elem);
308d5ac70f0Sopenharmony_ci		if (err < 0)
309d5ac70f0Sopenharmony_ci			return err;
310d5ac70f0Sopenharmony_ci
311d5ac70f0Sopenharmony_ci		/* add control to manifest */
312d5ac70f0Sopenharmony_ci		tplg->manifest.control_elems++;
313d5ac70f0Sopenharmony_ci	}
314d5ac70f0Sopenharmony_ci
315d5ac70f0Sopenharmony_ci	return 0;
316d5ac70f0Sopenharmony_ci}
317d5ac70f0Sopenharmony_ci
318d5ac70f0Sopenharmony_ci
319d5ac70f0Sopenharmony_ci/*
320d5ac70f0Sopenharmony_ci * Parse TLV of DBScale type.
321d5ac70f0Sopenharmony_ci *
322d5ac70f0Sopenharmony_ci * Parse DBScale describing min, step, mute in DB.
323d5ac70f0Sopenharmony_ci */
324d5ac70f0Sopenharmony_cistatic int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem)
325d5ac70f0Sopenharmony_ci{
326d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
327d5ac70f0Sopenharmony_ci	snd_config_t *n;
328d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_ctl_tlv *tplg_tlv = elem->tlv;
329d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_tlv_dbscale *scale;
330d5ac70f0Sopenharmony_ci	const char *id = NULL;
331d5ac70f0Sopenharmony_ci	int val;
332d5ac70f0Sopenharmony_ci
333d5ac70f0Sopenharmony_ci	tplg_dbg(" scale: %s", elem->id);
334d5ac70f0Sopenharmony_ci
335d5ac70f0Sopenharmony_ci	tplg_tlv->size = sizeof(struct snd_soc_tplg_ctl_tlv);
336d5ac70f0Sopenharmony_ci	tplg_tlv->type = SNDRV_CTL_TLVT_DB_SCALE;
337d5ac70f0Sopenharmony_ci	scale = &tplg_tlv->scale;
338d5ac70f0Sopenharmony_ci
339d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, cfg) {
340d5ac70f0Sopenharmony_ci
341d5ac70f0Sopenharmony_ci		n = snd_config_iterator_entry(i);
342d5ac70f0Sopenharmony_ci
343d5ac70f0Sopenharmony_ci		/* get ID */
344d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &id) < 0)
345d5ac70f0Sopenharmony_ci			return -EINVAL;
346d5ac70f0Sopenharmony_ci
347d5ac70f0Sopenharmony_ci		/* get value */
348d5ac70f0Sopenharmony_ci		if (tplg_get_integer(n, &val, 0))
349d5ac70f0Sopenharmony_ci			continue;
350d5ac70f0Sopenharmony_ci
351d5ac70f0Sopenharmony_ci		tplg_dbg("\t%s = %i", id, val);
352d5ac70f0Sopenharmony_ci
353d5ac70f0Sopenharmony_ci		/* get TLV data */
354d5ac70f0Sopenharmony_ci		if (strcmp(id, "min") == 0)
355d5ac70f0Sopenharmony_ci			scale->min = val;
356d5ac70f0Sopenharmony_ci		else if (strcmp(id, "step") == 0)
357d5ac70f0Sopenharmony_ci			scale->step = val;
358d5ac70f0Sopenharmony_ci		else if (strcmp(id, "mute") == 0)
359d5ac70f0Sopenharmony_ci			scale->mute = val;
360d5ac70f0Sopenharmony_ci		else
361d5ac70f0Sopenharmony_ci			SNDERR("unknown id '%s'", id);
362d5ac70f0Sopenharmony_ci	}
363d5ac70f0Sopenharmony_ci
364d5ac70f0Sopenharmony_ci	return 0;
365d5ac70f0Sopenharmony_ci}
366d5ac70f0Sopenharmony_ci
367d5ac70f0Sopenharmony_ci/* Parse TLV */
368d5ac70f0Sopenharmony_ciint tplg_parse_tlv(snd_tplg_t *tplg, snd_config_t *cfg,
369d5ac70f0Sopenharmony_ci		   void *private ATTRIBUTE_UNUSED)
370d5ac70f0Sopenharmony_ci{
371d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
372d5ac70f0Sopenharmony_ci	snd_config_t *n;
373d5ac70f0Sopenharmony_ci	const char *id;
374d5ac70f0Sopenharmony_ci	int err = 0;
375d5ac70f0Sopenharmony_ci	struct tplg_elem *elem;
376d5ac70f0Sopenharmony_ci
377d5ac70f0Sopenharmony_ci	elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_TLV);
378d5ac70f0Sopenharmony_ci	if (!elem)
379d5ac70f0Sopenharmony_ci		return -ENOMEM;
380d5ac70f0Sopenharmony_ci
381d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, cfg) {
382d5ac70f0Sopenharmony_ci
383d5ac70f0Sopenharmony_ci		n = snd_config_iterator_entry(i);
384d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &id) < 0)
385d5ac70f0Sopenharmony_ci			continue;
386d5ac70f0Sopenharmony_ci
387d5ac70f0Sopenharmony_ci		if (strcmp(id, "scale") == 0) {
388d5ac70f0Sopenharmony_ci			err = tplg_parse_tlv_dbscale(n, elem);
389d5ac70f0Sopenharmony_ci			if (err < 0) {
390d5ac70f0Sopenharmony_ci				SNDERR("failed to DBScale");
391d5ac70f0Sopenharmony_ci				return err;
392d5ac70f0Sopenharmony_ci			}
393d5ac70f0Sopenharmony_ci			continue;
394d5ac70f0Sopenharmony_ci		}
395d5ac70f0Sopenharmony_ci	}
396d5ac70f0Sopenharmony_ci
397d5ac70f0Sopenharmony_ci	return err;
398d5ac70f0Sopenharmony_ci}
399d5ac70f0Sopenharmony_ci
400d5ac70f0Sopenharmony_ci/* save TLV data */
401d5ac70f0Sopenharmony_ciint tplg_save_tlv(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
402d5ac70f0Sopenharmony_ci		  struct tplg_elem *elem,
403d5ac70f0Sopenharmony_ci		  struct tplg_buf *dst, const char *pfx)
404d5ac70f0Sopenharmony_ci{
405d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_ctl_tlv *tlv = elem->tlv;
406d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_tlv_dbscale *scale;
407d5ac70f0Sopenharmony_ci	int err;
408d5ac70f0Sopenharmony_ci
409d5ac70f0Sopenharmony_ci	if (tlv->type != SNDRV_CTL_TLVT_DB_SCALE) {
410d5ac70f0Sopenharmony_ci		SNDERR("unknown TLV type");
411d5ac70f0Sopenharmony_ci		return -EINVAL;
412d5ac70f0Sopenharmony_ci	}
413d5ac70f0Sopenharmony_ci
414d5ac70f0Sopenharmony_ci	scale = &tlv->scale;
415d5ac70f0Sopenharmony_ci	err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
416d5ac70f0Sopenharmony_ci	if (err >= 0)
417d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "\tscale {\n");
418d5ac70f0Sopenharmony_ci	if (err >= 0 && scale->min)
419d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "\t\tmin %i\n", scale->min);
420d5ac70f0Sopenharmony_ci	if (err >= 0 && scale->step > 0)
421d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "\t\tstep %i\n", scale->step);
422d5ac70f0Sopenharmony_ci	if (err >= 0 && scale->mute > 0)
423d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "\t\tmute %i\n", scale->mute);
424d5ac70f0Sopenharmony_ci	if (err >= 0)
425d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "\t}\n");
426d5ac70f0Sopenharmony_ci	if (err >= 0)
427d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "}\n");
428d5ac70f0Sopenharmony_ci	return err;
429d5ac70f0Sopenharmony_ci}
430d5ac70f0Sopenharmony_ci
431d5ac70f0Sopenharmony_ci/* Parse Control Bytes */
432d5ac70f0Sopenharmony_ciint tplg_parse_control_bytes(snd_tplg_t *tplg,
433d5ac70f0Sopenharmony_ci			     snd_config_t *cfg,
434d5ac70f0Sopenharmony_ci			     void *private ATTRIBUTE_UNUSED)
435d5ac70f0Sopenharmony_ci{
436d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_bytes_control *be;
437d5ac70f0Sopenharmony_ci	struct tplg_elem *elem;
438d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
439d5ac70f0Sopenharmony_ci	snd_config_t *n;
440d5ac70f0Sopenharmony_ci	const char *id, *val = NULL;
441d5ac70f0Sopenharmony_ci	int err, ival;
442d5ac70f0Sopenharmony_ci	bool access_set = false, tlv_set = false;
443d5ac70f0Sopenharmony_ci
444d5ac70f0Sopenharmony_ci	elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_BYTES);
445d5ac70f0Sopenharmony_ci	if (!elem)
446d5ac70f0Sopenharmony_ci		return -ENOMEM;
447d5ac70f0Sopenharmony_ci
448d5ac70f0Sopenharmony_ci	be = elem->bytes_ext;
449d5ac70f0Sopenharmony_ci	be->size = elem->size;
450d5ac70f0Sopenharmony_ci	snd_strlcpy(be->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
451d5ac70f0Sopenharmony_ci	be->hdr.type = SND_SOC_TPLG_TYPE_BYTES;
452d5ac70f0Sopenharmony_ci
453d5ac70f0Sopenharmony_ci	tplg_dbg(" Control Bytes: %s", elem->id);
454d5ac70f0Sopenharmony_ci
455d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, cfg) {
456d5ac70f0Sopenharmony_ci		n = snd_config_iterator_entry(i);
457d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &id) < 0)
458d5ac70f0Sopenharmony_ci			continue;
459d5ac70f0Sopenharmony_ci
460d5ac70f0Sopenharmony_ci		/* skip comments */
461d5ac70f0Sopenharmony_ci		if (strcmp(id, "comment") == 0)
462d5ac70f0Sopenharmony_ci			continue;
463d5ac70f0Sopenharmony_ci		if (id[0] == '#')
464d5ac70f0Sopenharmony_ci			continue;
465d5ac70f0Sopenharmony_ci
466d5ac70f0Sopenharmony_ci		if (strcmp(id, "base") == 0) {
467d5ac70f0Sopenharmony_ci			if (tplg_get_integer(n, &ival, 0))
468d5ac70f0Sopenharmony_ci				return -EINVAL;
469d5ac70f0Sopenharmony_ci
470d5ac70f0Sopenharmony_ci			be->base = ival;
471d5ac70f0Sopenharmony_ci			tplg_dbg("\t%s: %d", id, be->base);
472d5ac70f0Sopenharmony_ci			continue;
473d5ac70f0Sopenharmony_ci		}
474d5ac70f0Sopenharmony_ci
475d5ac70f0Sopenharmony_ci		if (strcmp(id, "num_regs") == 0) {
476d5ac70f0Sopenharmony_ci			if (tplg_get_integer(n, &ival, 0))
477d5ac70f0Sopenharmony_ci				return -EINVAL;
478d5ac70f0Sopenharmony_ci
479d5ac70f0Sopenharmony_ci			be->num_regs = ival;
480d5ac70f0Sopenharmony_ci			tplg_dbg("\t%s: %d", id, be->num_regs);
481d5ac70f0Sopenharmony_ci			continue;
482d5ac70f0Sopenharmony_ci		}
483d5ac70f0Sopenharmony_ci
484d5ac70f0Sopenharmony_ci		if (strcmp(id, "max") == 0) {
485d5ac70f0Sopenharmony_ci			if (tplg_get_integer(n, &ival, 0))
486d5ac70f0Sopenharmony_ci				return -EINVAL;
487d5ac70f0Sopenharmony_ci
488d5ac70f0Sopenharmony_ci			be->max = ival;
489d5ac70f0Sopenharmony_ci			tplg_dbg("\t%s: %d", id, be->max);
490d5ac70f0Sopenharmony_ci			continue;
491d5ac70f0Sopenharmony_ci		}
492d5ac70f0Sopenharmony_ci
493d5ac70f0Sopenharmony_ci		if (strcmp(id, "mask") == 0) {
494d5ac70f0Sopenharmony_ci			if (tplg_get_integer(n, &ival, 16))
495d5ac70f0Sopenharmony_ci				return -EINVAL;
496d5ac70f0Sopenharmony_ci
497d5ac70f0Sopenharmony_ci			be->mask = ival;
498d5ac70f0Sopenharmony_ci			tplg_dbg("\t%s: %d", id, be->mask);
499d5ac70f0Sopenharmony_ci			continue;
500d5ac70f0Sopenharmony_ci		}
501d5ac70f0Sopenharmony_ci
502d5ac70f0Sopenharmony_ci		if (strcmp(id, "data") == 0) {
503d5ac70f0Sopenharmony_ci			err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
504d5ac70f0Sopenharmony_ci			if (err < 0)
505d5ac70f0Sopenharmony_ci				return err;
506d5ac70f0Sopenharmony_ci			continue;
507d5ac70f0Sopenharmony_ci		}
508d5ac70f0Sopenharmony_ci
509d5ac70f0Sopenharmony_ci		if (strcmp(id, "tlv") == 0) {
510d5ac70f0Sopenharmony_ci			if (snd_config_get_string(n, &val) < 0)
511d5ac70f0Sopenharmony_ci				return -EINVAL;
512d5ac70f0Sopenharmony_ci
513d5ac70f0Sopenharmony_ci			err = tplg_ref_add(elem, SND_TPLG_TYPE_TLV, val);
514d5ac70f0Sopenharmony_ci			if (err < 0)
515d5ac70f0Sopenharmony_ci				return err;
516d5ac70f0Sopenharmony_ci
517d5ac70f0Sopenharmony_ci			tlv_set = true;
518d5ac70f0Sopenharmony_ci			tplg_dbg("\t%s: %s", id, val);
519d5ac70f0Sopenharmony_ci			continue;
520d5ac70f0Sopenharmony_ci		}
521d5ac70f0Sopenharmony_ci
522d5ac70f0Sopenharmony_ci		if (strcmp(id, "ops") == 0) {
523d5ac70f0Sopenharmony_ci			err = tplg_parse_compound(tplg, n, tplg_parse_ops,
524d5ac70f0Sopenharmony_ci				&be->hdr);
525d5ac70f0Sopenharmony_ci			if (err < 0)
526d5ac70f0Sopenharmony_ci				return err;
527d5ac70f0Sopenharmony_ci			continue;
528d5ac70f0Sopenharmony_ci		}
529d5ac70f0Sopenharmony_ci
530d5ac70f0Sopenharmony_ci		if (strcmp(id, "extops") == 0) {
531d5ac70f0Sopenharmony_ci			err = tplg_parse_compound(tplg, n, tplg_parse_ext_ops,
532d5ac70f0Sopenharmony_ci				be);
533d5ac70f0Sopenharmony_ci			if (err < 0)
534d5ac70f0Sopenharmony_ci				return err;
535d5ac70f0Sopenharmony_ci			continue;
536d5ac70f0Sopenharmony_ci		}
537d5ac70f0Sopenharmony_ci
538d5ac70f0Sopenharmony_ci		if (strcmp(id, "access") == 0) {
539d5ac70f0Sopenharmony_ci			err = parse_access(cfg, &be->hdr);
540d5ac70f0Sopenharmony_ci			if (err < 0)
541d5ac70f0Sopenharmony_ci				return err;
542d5ac70f0Sopenharmony_ci			access_set = true;
543d5ac70f0Sopenharmony_ci			continue;
544d5ac70f0Sopenharmony_ci		}
545d5ac70f0Sopenharmony_ci	}
546d5ac70f0Sopenharmony_ci
547d5ac70f0Sopenharmony_ci	/* set CTL access to default values if none are provided */
548d5ac70f0Sopenharmony_ci	if (!access_set) {
549d5ac70f0Sopenharmony_ci
550d5ac70f0Sopenharmony_ci		be->hdr.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
551d5ac70f0Sopenharmony_ci		if (tlv_set)
552d5ac70f0Sopenharmony_ci			be->hdr.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
553d5ac70f0Sopenharmony_ci	}
554d5ac70f0Sopenharmony_ci
555d5ac70f0Sopenharmony_ci	return 0;
556d5ac70f0Sopenharmony_ci}
557d5ac70f0Sopenharmony_ci
558d5ac70f0Sopenharmony_ci/* save control bytes */
559d5ac70f0Sopenharmony_ciint tplg_save_control_bytes(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
560d5ac70f0Sopenharmony_ci			    struct tplg_elem *elem,
561d5ac70f0Sopenharmony_ci			    struct tplg_buf *dst, const char *pfx)
562d5ac70f0Sopenharmony_ci{
563d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_bytes_control *be = elem->bytes_ext;
564d5ac70f0Sopenharmony_ci	char pfx2[16];
565d5ac70f0Sopenharmony_ci	int err;
566d5ac70f0Sopenharmony_ci
567d5ac70f0Sopenharmony_ci	if (!be)
568d5ac70f0Sopenharmony_ci		return 0;
569d5ac70f0Sopenharmony_ci
570d5ac70f0Sopenharmony_ci	snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
571d5ac70f0Sopenharmony_ci	err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
572d5ac70f0Sopenharmony_ci	if (err < 0)
573d5ac70f0Sopenharmony_ci		return err;
574d5ac70f0Sopenharmony_ci	if (err >= 0 && elem->index > 0)
575d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "\tindex %u\n", elem->index);
576d5ac70f0Sopenharmony_ci	if (err >= 0 && be->base > 0)
577d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "\tbase %u\n", be->base);
578d5ac70f0Sopenharmony_ci	if (err >= 0 && be->num_regs > 0)
579d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "\tnum_regs %u\n", be->num_regs);
580d5ac70f0Sopenharmony_ci	if (err >= 0 && be->max > 0)
581d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "\tmax %u\n", be->max);
582d5ac70f0Sopenharmony_ci	if (err >= 0 && be->mask > 0)
583d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "\tmask %u\n", be->mask);
584d5ac70f0Sopenharmony_ci	if (err >= 0)
585d5ac70f0Sopenharmony_ci		err = tplg_save_ops(tplg, &be->hdr, dst, pfx2);
586d5ac70f0Sopenharmony_ci	if (err >= 0)
587d5ac70f0Sopenharmony_ci		err = tplg_save_ext_ops(tplg, be, dst, pfx2);
588d5ac70f0Sopenharmony_ci	if (err >= 0)
589d5ac70f0Sopenharmony_ci		err = tplg_save_access(tplg, &be->hdr, dst, pfx2);
590d5ac70f0Sopenharmony_ci	if (err >= 0)
591d5ac70f0Sopenharmony_ci		err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_TLV,
592d5ac70f0Sopenharmony_ci				     "tlv", dst, pfx2);
593d5ac70f0Sopenharmony_ci	if (err >= 0)
594d5ac70f0Sopenharmony_ci		err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA,
595d5ac70f0Sopenharmony_ci				     "data", dst, pfx2);
596d5ac70f0Sopenharmony_ci	if (err >= 0)
597d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "}\n");
598d5ac70f0Sopenharmony_ci	return err;
599d5ac70f0Sopenharmony_ci}
600d5ac70f0Sopenharmony_ci
601d5ac70f0Sopenharmony_ci/* Parse Control Enums. */
602d5ac70f0Sopenharmony_ciint tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg,
603d5ac70f0Sopenharmony_ci			    void *private ATTRIBUTE_UNUSED)
604d5ac70f0Sopenharmony_ci{
605d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_enum_control *ec;
606d5ac70f0Sopenharmony_ci	struct tplg_elem *elem;
607d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
608d5ac70f0Sopenharmony_ci	snd_config_t *n;
609d5ac70f0Sopenharmony_ci	const char *id, *val = NULL;
610d5ac70f0Sopenharmony_ci	int err, j;
611d5ac70f0Sopenharmony_ci	bool access_set = false;
612d5ac70f0Sopenharmony_ci
613d5ac70f0Sopenharmony_ci	elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_ENUM);
614d5ac70f0Sopenharmony_ci	if (!elem)
615d5ac70f0Sopenharmony_ci		return -ENOMEM;
616d5ac70f0Sopenharmony_ci
617d5ac70f0Sopenharmony_ci	ec = elem->enum_ctrl;
618d5ac70f0Sopenharmony_ci	snd_strlcpy(ec->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
619d5ac70f0Sopenharmony_ci	ec->hdr.type = SND_SOC_TPLG_TYPE_ENUM;
620d5ac70f0Sopenharmony_ci	ec->size = elem->size;
621d5ac70f0Sopenharmony_ci	tplg->channel_idx = 0;
622d5ac70f0Sopenharmony_ci
623d5ac70f0Sopenharmony_ci	/* set channel reg to default state */
624d5ac70f0Sopenharmony_ci	for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++) {
625d5ac70f0Sopenharmony_ci		ec->channel[j].reg = -1;
626d5ac70f0Sopenharmony_ci	}
627d5ac70f0Sopenharmony_ci
628d5ac70f0Sopenharmony_ci	tplg_dbg(" Control Enum: %s", elem->id);
629d5ac70f0Sopenharmony_ci
630d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, cfg) {
631d5ac70f0Sopenharmony_ci
632d5ac70f0Sopenharmony_ci		n = snd_config_iterator_entry(i);
633d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &id) < 0)
634d5ac70f0Sopenharmony_ci			continue;
635d5ac70f0Sopenharmony_ci
636d5ac70f0Sopenharmony_ci		/* skip comments */
637d5ac70f0Sopenharmony_ci		if (strcmp(id, "comment") == 0)
638d5ac70f0Sopenharmony_ci			continue;
639d5ac70f0Sopenharmony_ci		if (id[0] == '#')
640d5ac70f0Sopenharmony_ci			continue;
641d5ac70f0Sopenharmony_ci
642d5ac70f0Sopenharmony_ci		if (strcmp(id, "texts") == 0) {
643d5ac70f0Sopenharmony_ci			if (snd_config_get_string(n, &val) < 0)
644d5ac70f0Sopenharmony_ci				return -EINVAL;
645d5ac70f0Sopenharmony_ci
646d5ac70f0Sopenharmony_ci			tplg_ref_add(elem, SND_TPLG_TYPE_TEXT, val);
647d5ac70f0Sopenharmony_ci			tplg_dbg("\t%s: %s", id, val);
648d5ac70f0Sopenharmony_ci			continue;
649d5ac70f0Sopenharmony_ci		}
650d5ac70f0Sopenharmony_ci
651d5ac70f0Sopenharmony_ci		if (strcmp(id, "channel") == 0) {
652d5ac70f0Sopenharmony_ci			if (ec->num_channels >= SND_SOC_TPLG_MAX_CHAN) {
653d5ac70f0Sopenharmony_ci				SNDERR("too many channels %s", elem->id);
654d5ac70f0Sopenharmony_ci				return -EINVAL;
655d5ac70f0Sopenharmony_ci			}
656d5ac70f0Sopenharmony_ci
657d5ac70f0Sopenharmony_ci			err = tplg_parse_compound(tplg, n, tplg_parse_channel,
658d5ac70f0Sopenharmony_ci				ec->channel);
659d5ac70f0Sopenharmony_ci			if (err < 0)
660d5ac70f0Sopenharmony_ci				return err;
661d5ac70f0Sopenharmony_ci
662d5ac70f0Sopenharmony_ci			ec->num_channels = tplg->channel_idx;
663d5ac70f0Sopenharmony_ci			continue;
664d5ac70f0Sopenharmony_ci		}
665d5ac70f0Sopenharmony_ci
666d5ac70f0Sopenharmony_ci		if (strcmp(id, "ops") == 0) {
667d5ac70f0Sopenharmony_ci			err = tplg_parse_compound(tplg, n, tplg_parse_ops,
668d5ac70f0Sopenharmony_ci				&ec->hdr);
669d5ac70f0Sopenharmony_ci			if (err < 0)
670d5ac70f0Sopenharmony_ci				return err;
671d5ac70f0Sopenharmony_ci			continue;
672d5ac70f0Sopenharmony_ci		}
673d5ac70f0Sopenharmony_ci
674d5ac70f0Sopenharmony_ci		if (strcmp(id, "data") == 0) {
675d5ac70f0Sopenharmony_ci			err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
676d5ac70f0Sopenharmony_ci			if (err < 0)
677d5ac70f0Sopenharmony_ci				return err;
678d5ac70f0Sopenharmony_ci			continue;
679d5ac70f0Sopenharmony_ci		}
680d5ac70f0Sopenharmony_ci
681d5ac70f0Sopenharmony_ci		if (strcmp(id, "access") == 0) {
682d5ac70f0Sopenharmony_ci			err = parse_access(cfg, &ec->hdr);
683d5ac70f0Sopenharmony_ci			if (err < 0)
684d5ac70f0Sopenharmony_ci				return err;
685d5ac70f0Sopenharmony_ci			access_set = true;
686d5ac70f0Sopenharmony_ci			continue;
687d5ac70f0Sopenharmony_ci		}
688d5ac70f0Sopenharmony_ci	}
689d5ac70f0Sopenharmony_ci
690d5ac70f0Sopenharmony_ci	/* set CTL access to default values if none are provided */
691d5ac70f0Sopenharmony_ci	if (!access_set) {
692d5ac70f0Sopenharmony_ci		ec->hdr.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
693d5ac70f0Sopenharmony_ci	}
694d5ac70f0Sopenharmony_ci
695d5ac70f0Sopenharmony_ci	return 0;
696d5ac70f0Sopenharmony_ci}
697d5ac70f0Sopenharmony_ci
698d5ac70f0Sopenharmony_ci/* save control eunm */
699d5ac70f0Sopenharmony_ciint tplg_save_control_enum(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
700d5ac70f0Sopenharmony_ci			   struct tplg_elem *elem,
701d5ac70f0Sopenharmony_ci			   struct tplg_buf *dst, const char *pfx)
702d5ac70f0Sopenharmony_ci{
703d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_enum_control *ec = elem->enum_ctrl;
704d5ac70f0Sopenharmony_ci	char pfx2[16];
705d5ac70f0Sopenharmony_ci	int err;
706d5ac70f0Sopenharmony_ci
707d5ac70f0Sopenharmony_ci	if (!ec)
708d5ac70f0Sopenharmony_ci		return 0;
709d5ac70f0Sopenharmony_ci
710d5ac70f0Sopenharmony_ci	snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
711d5ac70f0Sopenharmony_ci	err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
712d5ac70f0Sopenharmony_ci	if (err < 0)
713d5ac70f0Sopenharmony_ci		return err;
714d5ac70f0Sopenharmony_ci	if (err >= 0 && elem->index > 0)
715d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "\tindex %u\n", elem->index);
716d5ac70f0Sopenharmony_ci	if (err >= 0)
717d5ac70f0Sopenharmony_ci		err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_TEXT,
718d5ac70f0Sopenharmony_ci				     "texts", dst, pfx2);
719d5ac70f0Sopenharmony_ci	if (err >= 0)
720d5ac70f0Sopenharmony_ci		err = tplg_save_channels(tplg, ec->channel, ec->num_channels,
721d5ac70f0Sopenharmony_ci					 dst, pfx2);
722d5ac70f0Sopenharmony_ci	if (err >= 0)
723d5ac70f0Sopenharmony_ci		err = tplg_save_ops(tplg, &ec->hdr, dst, pfx2);
724d5ac70f0Sopenharmony_ci	if (err >= 0)
725d5ac70f0Sopenharmony_ci		err = tplg_save_access(tplg, &ec->hdr, dst, pfx2);
726d5ac70f0Sopenharmony_ci	if (err >= 0)
727d5ac70f0Sopenharmony_ci		err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA,
728d5ac70f0Sopenharmony_ci				     "data", dst, pfx2);
729d5ac70f0Sopenharmony_ci	if (err >= 0)
730d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "}\n");
731d5ac70f0Sopenharmony_ci	return err;
732d5ac70f0Sopenharmony_ci}
733d5ac70f0Sopenharmony_ci
734d5ac70f0Sopenharmony_ci/* Parse Controls.
735d5ac70f0Sopenharmony_ci *
736d5ac70f0Sopenharmony_ci * Mixer control. Supports multiple channels.
737d5ac70f0Sopenharmony_ci */
738d5ac70f0Sopenharmony_ciint tplg_parse_control_mixer(snd_tplg_t *tplg,
739d5ac70f0Sopenharmony_ci			     snd_config_t *cfg,
740d5ac70f0Sopenharmony_ci			     void *private ATTRIBUTE_UNUSED)
741d5ac70f0Sopenharmony_ci{
742d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_mixer_control *mc;
743d5ac70f0Sopenharmony_ci	struct tplg_elem *elem;
744d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
745d5ac70f0Sopenharmony_ci	snd_config_t *n;
746d5ac70f0Sopenharmony_ci	const char *id, *val = NULL;
747d5ac70f0Sopenharmony_ci	int err, j, ival;
748d5ac70f0Sopenharmony_ci	bool access_set = false, tlv_set = false;
749d5ac70f0Sopenharmony_ci
750d5ac70f0Sopenharmony_ci	elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_MIXER);
751d5ac70f0Sopenharmony_ci	if (!elem)
752d5ac70f0Sopenharmony_ci		return -ENOMEM;
753d5ac70f0Sopenharmony_ci
754d5ac70f0Sopenharmony_ci	/* init new mixer */
755d5ac70f0Sopenharmony_ci	mc = elem->mixer_ctrl;
756d5ac70f0Sopenharmony_ci	snd_strlcpy(mc->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
757d5ac70f0Sopenharmony_ci	mc->hdr.type = SND_SOC_TPLG_TYPE_MIXER;
758d5ac70f0Sopenharmony_ci	mc->size = elem->size;
759d5ac70f0Sopenharmony_ci	tplg->channel_idx = 0;
760d5ac70f0Sopenharmony_ci
761d5ac70f0Sopenharmony_ci	/* set channel reg to default state */
762d5ac70f0Sopenharmony_ci	for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++)
763d5ac70f0Sopenharmony_ci		mc->channel[j].reg = -1;
764d5ac70f0Sopenharmony_ci
765d5ac70f0Sopenharmony_ci	tplg_dbg(" Control Mixer: %s", elem->id);
766d5ac70f0Sopenharmony_ci
767d5ac70f0Sopenharmony_ci	/* giterate trough each mixer elment */
768d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, cfg) {
769d5ac70f0Sopenharmony_ci		n = snd_config_iterator_entry(i);
770d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &id) < 0)
771d5ac70f0Sopenharmony_ci			continue;
772d5ac70f0Sopenharmony_ci
773d5ac70f0Sopenharmony_ci		/* skip comments */
774d5ac70f0Sopenharmony_ci		if (strcmp(id, "comment") == 0)
775d5ac70f0Sopenharmony_ci			continue;
776d5ac70f0Sopenharmony_ci		if (id[0] == '#')
777d5ac70f0Sopenharmony_ci			continue;
778d5ac70f0Sopenharmony_ci
779d5ac70f0Sopenharmony_ci		if (strcmp(id, "channel") == 0) {
780d5ac70f0Sopenharmony_ci			if (mc->num_channels >= SND_SOC_TPLG_MAX_CHAN) {
781d5ac70f0Sopenharmony_ci				SNDERR("too many channels %s", elem->id);
782d5ac70f0Sopenharmony_ci				return -EINVAL;
783d5ac70f0Sopenharmony_ci			}
784d5ac70f0Sopenharmony_ci
785d5ac70f0Sopenharmony_ci			err = tplg_parse_compound(tplg, n, tplg_parse_channel,
786d5ac70f0Sopenharmony_ci				mc->channel);
787d5ac70f0Sopenharmony_ci			if (err < 0)
788d5ac70f0Sopenharmony_ci				return err;
789d5ac70f0Sopenharmony_ci
790d5ac70f0Sopenharmony_ci			mc->num_channels = tplg->channel_idx;
791d5ac70f0Sopenharmony_ci			continue;
792d5ac70f0Sopenharmony_ci		}
793d5ac70f0Sopenharmony_ci
794d5ac70f0Sopenharmony_ci		if (strcmp(id, "max") == 0) {
795d5ac70f0Sopenharmony_ci			if (tplg_get_integer(n, &ival, 0))
796d5ac70f0Sopenharmony_ci				return -EINVAL;
797d5ac70f0Sopenharmony_ci
798d5ac70f0Sopenharmony_ci			mc->max = ival;
799d5ac70f0Sopenharmony_ci			tplg_dbg("\t%s: %d", id, mc->max);
800d5ac70f0Sopenharmony_ci			continue;
801d5ac70f0Sopenharmony_ci		}
802d5ac70f0Sopenharmony_ci
803d5ac70f0Sopenharmony_ci		if (strcmp(id, "invert") == 0) {
804d5ac70f0Sopenharmony_ci			ival = snd_config_get_bool(n);
805d5ac70f0Sopenharmony_ci			if (ival < 0)
806d5ac70f0Sopenharmony_ci				return -EINVAL;
807d5ac70f0Sopenharmony_ci			mc->invert = ival;
808d5ac70f0Sopenharmony_ci
809d5ac70f0Sopenharmony_ci			tplg_dbg("\t%s: %d", id, mc->invert);
810d5ac70f0Sopenharmony_ci			continue;
811d5ac70f0Sopenharmony_ci		}
812d5ac70f0Sopenharmony_ci
813d5ac70f0Sopenharmony_ci		if (strcmp(id, "ops") == 0) {
814d5ac70f0Sopenharmony_ci			err = tplg_parse_compound(tplg, n, tplg_parse_ops,
815d5ac70f0Sopenharmony_ci				&mc->hdr);
816d5ac70f0Sopenharmony_ci			if (err < 0)
817d5ac70f0Sopenharmony_ci				return err;
818d5ac70f0Sopenharmony_ci			continue;
819d5ac70f0Sopenharmony_ci		}
820d5ac70f0Sopenharmony_ci
821d5ac70f0Sopenharmony_ci		if (strcmp(id, "tlv") == 0) {
822d5ac70f0Sopenharmony_ci			if (snd_config_get_string(n, &val) < 0)
823d5ac70f0Sopenharmony_ci				return -EINVAL;
824d5ac70f0Sopenharmony_ci
825d5ac70f0Sopenharmony_ci			err = tplg_ref_add(elem, SND_TPLG_TYPE_TLV, val);
826d5ac70f0Sopenharmony_ci			if (err < 0)
827d5ac70f0Sopenharmony_ci				return err;
828d5ac70f0Sopenharmony_ci
829d5ac70f0Sopenharmony_ci			tlv_set = true;
830d5ac70f0Sopenharmony_ci			tplg_dbg("\t%s: %s", id, val);
831d5ac70f0Sopenharmony_ci			continue;
832d5ac70f0Sopenharmony_ci		}
833d5ac70f0Sopenharmony_ci
834d5ac70f0Sopenharmony_ci		if (strcmp(id, "data") == 0) {
835d5ac70f0Sopenharmony_ci			err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
836d5ac70f0Sopenharmony_ci			if (err < 0)
837d5ac70f0Sopenharmony_ci				return err;
838d5ac70f0Sopenharmony_ci			continue;
839d5ac70f0Sopenharmony_ci		}
840d5ac70f0Sopenharmony_ci
841d5ac70f0Sopenharmony_ci		if (strcmp(id, "access") == 0) {
842d5ac70f0Sopenharmony_ci			err = parse_access(cfg, &mc->hdr);
843d5ac70f0Sopenharmony_ci			if (err < 0)
844d5ac70f0Sopenharmony_ci				return err;
845d5ac70f0Sopenharmony_ci			access_set = true;
846d5ac70f0Sopenharmony_ci			continue;
847d5ac70f0Sopenharmony_ci		}
848d5ac70f0Sopenharmony_ci	}
849d5ac70f0Sopenharmony_ci
850d5ac70f0Sopenharmony_ci	/* set CTL access to default values if none are provided */
851d5ac70f0Sopenharmony_ci	if (!access_set) {
852d5ac70f0Sopenharmony_ci
853d5ac70f0Sopenharmony_ci		mc->hdr.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
854d5ac70f0Sopenharmony_ci		if (tlv_set)
855d5ac70f0Sopenharmony_ci			mc->hdr.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
856d5ac70f0Sopenharmony_ci	}
857d5ac70f0Sopenharmony_ci
858d5ac70f0Sopenharmony_ci	return 0;
859d5ac70f0Sopenharmony_ci}
860d5ac70f0Sopenharmony_ci
861d5ac70f0Sopenharmony_ciint tplg_save_control_mixer(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
862d5ac70f0Sopenharmony_ci			    struct tplg_elem *elem,
863d5ac70f0Sopenharmony_ci			    struct tplg_buf *dst, const char *pfx)
864d5ac70f0Sopenharmony_ci{
865d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_mixer_control *mc = elem->mixer_ctrl;
866d5ac70f0Sopenharmony_ci	char pfx2[16];
867d5ac70f0Sopenharmony_ci	int err;
868d5ac70f0Sopenharmony_ci
869d5ac70f0Sopenharmony_ci	if (!mc)
870d5ac70f0Sopenharmony_ci		return 0;
871d5ac70f0Sopenharmony_ci	err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
872d5ac70f0Sopenharmony_ci	if (err < 0)
873d5ac70f0Sopenharmony_ci		return err;
874d5ac70f0Sopenharmony_ci	snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
875d5ac70f0Sopenharmony_ci	if (err >= 0 && elem->index > 0)
876d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "\tindex %u\n", elem->index);
877d5ac70f0Sopenharmony_ci	if (err >= 0)
878d5ac70f0Sopenharmony_ci		err = tplg_save_channels(tplg, mc->channel, mc->num_channels,
879d5ac70f0Sopenharmony_ci					 dst, pfx2);
880d5ac70f0Sopenharmony_ci	if (err >= 0 && mc->max > 0)
881d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "\tmax %u\n", mc->max);
882d5ac70f0Sopenharmony_ci	if (err >= 0 && mc->invert > 0)
883d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "\tinvert 1\n");
884d5ac70f0Sopenharmony_ci	if (err >= 0 && mc->invert > 0)
885d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "\tinvert 1\n");
886d5ac70f0Sopenharmony_ci	if (err >= 0)
887d5ac70f0Sopenharmony_ci		err = tplg_save_ops(tplg, &mc->hdr, dst, pfx2);
888d5ac70f0Sopenharmony_ci	if (err >= 0)
889d5ac70f0Sopenharmony_ci		err = tplg_save_access(tplg, &mc->hdr, dst, pfx2);
890d5ac70f0Sopenharmony_ci	if (err >= 0)
891d5ac70f0Sopenharmony_ci		err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_TLV,
892d5ac70f0Sopenharmony_ci				     "tlv", dst, pfx2);
893d5ac70f0Sopenharmony_ci	if (err >= 0)
894d5ac70f0Sopenharmony_ci		err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA,
895d5ac70f0Sopenharmony_ci				     "data", dst, pfx2);
896d5ac70f0Sopenharmony_ci	if (err >= 0)
897d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "}\n");
898d5ac70f0Sopenharmony_ci	return err;
899d5ac70f0Sopenharmony_ci}
900d5ac70f0Sopenharmony_ci
901d5ac70f0Sopenharmony_cistatic int init_ctl_hdr(snd_tplg_t *tplg,
902d5ac70f0Sopenharmony_ci			struct tplg_elem *parent,
903d5ac70f0Sopenharmony_ci			struct snd_soc_tplg_ctl_hdr *hdr,
904d5ac70f0Sopenharmony_ci			struct snd_tplg_ctl_template *t)
905d5ac70f0Sopenharmony_ci{
906d5ac70f0Sopenharmony_ci	struct tplg_elem *elem;
907d5ac70f0Sopenharmony_ci	int err;
908d5ac70f0Sopenharmony_ci
909d5ac70f0Sopenharmony_ci	hdr->size = sizeof(struct snd_soc_tplg_ctl_hdr);
910d5ac70f0Sopenharmony_ci	hdr->type = t->type;
911d5ac70f0Sopenharmony_ci
912d5ac70f0Sopenharmony_ci	snd_strlcpy(hdr->name, t->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
913d5ac70f0Sopenharmony_ci
914d5ac70f0Sopenharmony_ci	/* clean up access flag */
915d5ac70f0Sopenharmony_ci	if (t->access == 0)
916d5ac70f0Sopenharmony_ci		t->access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
917d5ac70f0Sopenharmony_ci	t->access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE |
918d5ac70f0Sopenharmony_ci		SNDRV_CTL_ELEM_ACCESS_VOLATILE |
919d5ac70f0Sopenharmony_ci		SNDRV_CTL_ELEM_ACCESS_INACTIVE |
920d5ac70f0Sopenharmony_ci		SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE |
921d5ac70f0Sopenharmony_ci		SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND |
922d5ac70f0Sopenharmony_ci		SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK);
923d5ac70f0Sopenharmony_ci
924d5ac70f0Sopenharmony_ci	hdr->access = t->access;
925d5ac70f0Sopenharmony_ci	hdr->ops.get = t->ops.get;
926d5ac70f0Sopenharmony_ci	hdr->ops.put = t->ops.put;
927d5ac70f0Sopenharmony_ci	hdr->ops.info = t->ops.info;
928d5ac70f0Sopenharmony_ci
929d5ac70f0Sopenharmony_ci	/* TLV */
930d5ac70f0Sopenharmony_ci	if (hdr->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE
931d5ac70f0Sopenharmony_ci		&& !(hdr->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)) {
932d5ac70f0Sopenharmony_ci
933d5ac70f0Sopenharmony_ci		struct snd_tplg_tlv_template *tlvt = t->tlv;
934d5ac70f0Sopenharmony_ci		struct snd_soc_tplg_ctl_tlv *tlv;
935d5ac70f0Sopenharmony_ci		struct snd_tplg_tlv_dbscale_template *scalet;
936d5ac70f0Sopenharmony_ci		struct snd_soc_tplg_tlv_dbscale *scale;
937d5ac70f0Sopenharmony_ci
938d5ac70f0Sopenharmony_ci		if (!tlvt) {
939d5ac70f0Sopenharmony_ci			SNDERR("missing TLV data");
940d5ac70f0Sopenharmony_ci			return -EINVAL;
941d5ac70f0Sopenharmony_ci		}
942d5ac70f0Sopenharmony_ci
943d5ac70f0Sopenharmony_ci		elem = tplg_elem_new_common(tplg, NULL, parent->id,
944d5ac70f0Sopenharmony_ci					    SND_TPLG_TYPE_TLV);
945d5ac70f0Sopenharmony_ci		if (!elem)
946d5ac70f0Sopenharmony_ci			return -ENOMEM;
947d5ac70f0Sopenharmony_ci
948d5ac70f0Sopenharmony_ci		tlv = elem->tlv;
949d5ac70f0Sopenharmony_ci
950d5ac70f0Sopenharmony_ci		err = tplg_ref_add(parent, SND_TPLG_TYPE_TLV, parent->id);
951d5ac70f0Sopenharmony_ci		if (err < 0)
952d5ac70f0Sopenharmony_ci			return err;
953d5ac70f0Sopenharmony_ci
954d5ac70f0Sopenharmony_ci		tlv->size = sizeof(struct snd_soc_tplg_ctl_tlv);
955d5ac70f0Sopenharmony_ci		tlv->type = tlvt->type;
956d5ac70f0Sopenharmony_ci
957d5ac70f0Sopenharmony_ci		switch (tlvt->type) {
958d5ac70f0Sopenharmony_ci		case SNDRV_CTL_TLVT_DB_SCALE:
959d5ac70f0Sopenharmony_ci			scalet = container_of(tlvt,
960d5ac70f0Sopenharmony_ci				struct snd_tplg_tlv_dbscale_template, hdr);
961d5ac70f0Sopenharmony_ci			scale = &tlv->scale;
962d5ac70f0Sopenharmony_ci			scale->min = scalet->min;
963d5ac70f0Sopenharmony_ci			scale->step = scalet->step;
964d5ac70f0Sopenharmony_ci			scale->mute = scalet->mute;
965d5ac70f0Sopenharmony_ci			break;
966d5ac70f0Sopenharmony_ci
967d5ac70f0Sopenharmony_ci		/* TODO: add support for other TLV types */
968d5ac70f0Sopenharmony_ci		default:
969d5ac70f0Sopenharmony_ci			SNDERR("unsupported TLV type %d", tlv->type);
970d5ac70f0Sopenharmony_ci			break;
971d5ac70f0Sopenharmony_ci		}
972d5ac70f0Sopenharmony_ci	}
973d5ac70f0Sopenharmony_ci
974d5ac70f0Sopenharmony_ci	return 0;
975d5ac70f0Sopenharmony_ci}
976d5ac70f0Sopenharmony_ci
977d5ac70f0Sopenharmony_ciint tplg_add_mixer(snd_tplg_t *tplg, struct snd_tplg_mixer_template *mixer,
978d5ac70f0Sopenharmony_ci		   struct tplg_elem **e)
979d5ac70f0Sopenharmony_ci{
980d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_mixer_control *mc;
981d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_private *priv;
982d5ac70f0Sopenharmony_ci	struct tplg_elem *elem;
983d5ac70f0Sopenharmony_ci	int ret, i, num_channels;
984d5ac70f0Sopenharmony_ci
985d5ac70f0Sopenharmony_ci	tplg_dbg(" Control Mixer: %s", mixer->hdr.name);
986d5ac70f0Sopenharmony_ci
987d5ac70f0Sopenharmony_ci	if (mixer->hdr.type != SND_SOC_TPLG_TYPE_MIXER) {
988d5ac70f0Sopenharmony_ci		SNDERR("invalid mixer type %d", mixer->hdr.type);
989d5ac70f0Sopenharmony_ci		return -EINVAL;
990d5ac70f0Sopenharmony_ci	}
991d5ac70f0Sopenharmony_ci
992d5ac70f0Sopenharmony_ci	elem = tplg_elem_new_common(tplg, NULL, mixer->hdr.name,
993d5ac70f0Sopenharmony_ci		SND_TPLG_TYPE_MIXER);
994d5ac70f0Sopenharmony_ci	if (!elem)
995d5ac70f0Sopenharmony_ci		return -ENOMEM;
996d5ac70f0Sopenharmony_ci
997d5ac70f0Sopenharmony_ci	/* init new mixer */
998d5ac70f0Sopenharmony_ci	mc = elem->mixer_ctrl;
999d5ac70f0Sopenharmony_ci	mc->size = elem->size;
1000d5ac70f0Sopenharmony_ci	ret = init_ctl_hdr(tplg, elem, &mc->hdr, &mixer->hdr);
1001d5ac70f0Sopenharmony_ci	if (ret < 0) {
1002d5ac70f0Sopenharmony_ci		tplg_elem_free(elem);
1003d5ac70f0Sopenharmony_ci		return ret;
1004d5ac70f0Sopenharmony_ci	}
1005d5ac70f0Sopenharmony_ci
1006d5ac70f0Sopenharmony_ci	mc->min = mixer->min;
1007d5ac70f0Sopenharmony_ci	mc->max = mixer->max;
1008d5ac70f0Sopenharmony_ci	mc->platform_max = mixer->platform_max;
1009d5ac70f0Sopenharmony_ci	mc->invert = mixer->invert;
1010d5ac70f0Sopenharmony_ci
1011d5ac70f0Sopenharmony_ci	/* set channel reg to default state */
1012d5ac70f0Sopenharmony_ci	for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++)
1013d5ac70f0Sopenharmony_ci		mc->channel[i].reg = -1;
1014d5ac70f0Sopenharmony_ci
1015d5ac70f0Sopenharmony_ci	num_channels = mixer->map ? mixer->map->num_channels : 0;
1016d5ac70f0Sopenharmony_ci	mc->num_channels = num_channels;
1017d5ac70f0Sopenharmony_ci
1018d5ac70f0Sopenharmony_ci	for (i = 0; i < num_channels; i++) {
1019d5ac70f0Sopenharmony_ci		struct snd_tplg_channel_elem *channel = &mixer->map->channel[i];
1020d5ac70f0Sopenharmony_ci
1021d5ac70f0Sopenharmony_ci		mc->channel[i].size = sizeof(mc->channel[0]);
1022d5ac70f0Sopenharmony_ci		mc->channel[i].reg = channel->reg;
1023d5ac70f0Sopenharmony_ci		mc->channel[i].shift = channel->shift;
1024d5ac70f0Sopenharmony_ci		mc->channel[i].id = channel->id;
1025d5ac70f0Sopenharmony_ci	}
1026d5ac70f0Sopenharmony_ci
1027d5ac70f0Sopenharmony_ci	/* priv data */
1028d5ac70f0Sopenharmony_ci	priv = mixer->priv;
1029d5ac70f0Sopenharmony_ci	if (priv && priv->size > 0) {
1030d5ac70f0Sopenharmony_ci		ret = tplg_add_data(tplg, elem, priv,
1031d5ac70f0Sopenharmony_ci				    sizeof(*priv) + priv->size);
1032d5ac70f0Sopenharmony_ci		if (ret < 0)
1033d5ac70f0Sopenharmony_ci			return ret;
1034d5ac70f0Sopenharmony_ci	}
1035d5ac70f0Sopenharmony_ci
1036d5ac70f0Sopenharmony_ci	if (e)
1037d5ac70f0Sopenharmony_ci		*e = elem;
1038d5ac70f0Sopenharmony_ci	return 0;
1039d5ac70f0Sopenharmony_ci}
1040d5ac70f0Sopenharmony_ci
1041d5ac70f0Sopenharmony_ciint tplg_add_enum(snd_tplg_t *tplg, struct snd_tplg_enum_template *enum_ctl,
1042d5ac70f0Sopenharmony_ci		  struct tplg_elem **e)
1043d5ac70f0Sopenharmony_ci{
1044d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_enum_control *ec;
1045d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_private *priv;
1046d5ac70f0Sopenharmony_ci	struct tplg_elem *elem;
1047d5ac70f0Sopenharmony_ci	int ret, i, num_items, num_channels;
1048d5ac70f0Sopenharmony_ci
1049d5ac70f0Sopenharmony_ci	tplg_dbg(" Control Enum: %s", enum_ctl->hdr.name);
1050d5ac70f0Sopenharmony_ci
1051d5ac70f0Sopenharmony_ci	if (enum_ctl->hdr.type != SND_SOC_TPLG_TYPE_ENUM) {
1052d5ac70f0Sopenharmony_ci		SNDERR("invalid enum type %d", enum_ctl->hdr.type);
1053d5ac70f0Sopenharmony_ci		return -EINVAL;
1054d5ac70f0Sopenharmony_ci	}
1055d5ac70f0Sopenharmony_ci
1056d5ac70f0Sopenharmony_ci	elem = tplg_elem_new_common(tplg, NULL, enum_ctl->hdr.name,
1057d5ac70f0Sopenharmony_ci		SND_TPLG_TYPE_ENUM);
1058d5ac70f0Sopenharmony_ci	if (!elem)
1059d5ac70f0Sopenharmony_ci		return -ENOMEM;
1060d5ac70f0Sopenharmony_ci
1061d5ac70f0Sopenharmony_ci	ec = elem->enum_ctrl;
1062d5ac70f0Sopenharmony_ci	ec->size = elem->size;
1063d5ac70f0Sopenharmony_ci	ret = init_ctl_hdr(tplg, elem, &ec->hdr, &enum_ctl->hdr);
1064d5ac70f0Sopenharmony_ci	if (ret < 0) {
1065d5ac70f0Sopenharmony_ci		tplg_elem_free(elem);
1066d5ac70f0Sopenharmony_ci		return ret;
1067d5ac70f0Sopenharmony_ci	}
1068d5ac70f0Sopenharmony_ci
1069d5ac70f0Sopenharmony_ci	num_items =  enum_ctl->items < SND_SOC_TPLG_NUM_TEXTS ?
1070d5ac70f0Sopenharmony_ci		enum_ctl->items : SND_SOC_TPLG_NUM_TEXTS;
1071d5ac70f0Sopenharmony_ci	ec->items = num_items;
1072d5ac70f0Sopenharmony_ci	ec->mask = enum_ctl->mask;
1073d5ac70f0Sopenharmony_ci	ec->count = enum_ctl->items;
1074d5ac70f0Sopenharmony_ci
1075d5ac70f0Sopenharmony_ci	/* set channel reg to default state */
1076d5ac70f0Sopenharmony_ci	for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++)
1077d5ac70f0Sopenharmony_ci		ec->channel[i].reg = -1;
1078d5ac70f0Sopenharmony_ci
1079d5ac70f0Sopenharmony_ci	num_channels = enum_ctl->map ? enum_ctl->map->num_channels : 0;
1080d5ac70f0Sopenharmony_ci	ec->num_channels = num_channels;
1081d5ac70f0Sopenharmony_ci
1082d5ac70f0Sopenharmony_ci	for (i = 0; i < num_channels; i++) {
1083d5ac70f0Sopenharmony_ci		struct snd_tplg_channel_elem *channel = &enum_ctl->map->channel[i];
1084d5ac70f0Sopenharmony_ci
1085d5ac70f0Sopenharmony_ci		ec->channel[i].size = sizeof(ec->channel[0]);
1086d5ac70f0Sopenharmony_ci		ec->channel[i].reg = channel->reg;
1087d5ac70f0Sopenharmony_ci		ec->channel[i].shift = channel->shift;
1088d5ac70f0Sopenharmony_ci		ec->channel[i].id = channel->id;
1089d5ac70f0Sopenharmony_ci	}
1090d5ac70f0Sopenharmony_ci
1091d5ac70f0Sopenharmony_ci	if (enum_ctl->texts != NULL) {
1092d5ac70f0Sopenharmony_ci		struct tplg_elem *texts = tplg_elem_new_common(tplg, NULL,
1093d5ac70f0Sopenharmony_ci						enum_ctl->hdr.name, SND_TPLG_TYPE_TEXT);
1094d5ac70f0Sopenharmony_ci
1095d5ac70f0Sopenharmony_ci		texts->texts->num_items = num_items;
1096d5ac70f0Sopenharmony_ci		for (i = 0; i < num_items; i++) {
1097d5ac70f0Sopenharmony_ci			if (!enum_ctl->texts[i])
1098d5ac70f0Sopenharmony_ci				continue;
1099d5ac70f0Sopenharmony_ci			snd_strlcpy(ec->texts[i], enum_ctl->texts[i],
1100d5ac70f0Sopenharmony_ci				    SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1101d5ac70f0Sopenharmony_ci			snd_strlcpy(texts->texts->items[i], enum_ctl->texts[i],
1102d5ac70f0Sopenharmony_ci				    SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
1103d5ac70f0Sopenharmony_ci		}
1104d5ac70f0Sopenharmony_ci		tplg_ref_add(elem, SND_TPLG_TYPE_TEXT, enum_ctl->hdr.name);
1105d5ac70f0Sopenharmony_ci	}
1106d5ac70f0Sopenharmony_ci
1107d5ac70f0Sopenharmony_ci	if (enum_ctl->values != NULL) {
1108d5ac70f0Sopenharmony_ci		for (i = 0; i < num_items; i++) {
1109d5ac70f0Sopenharmony_ci			if (enum_ctl->values[i] == NULL)
1110d5ac70f0Sopenharmony_ci				continue;
1111d5ac70f0Sopenharmony_ci
1112d5ac70f0Sopenharmony_ci			memcpy(&ec->values[i * sizeof(int) * ENUM_VAL_SIZE],
1113d5ac70f0Sopenharmony_ci				enum_ctl->values[i],
1114d5ac70f0Sopenharmony_ci				sizeof(int) * ENUM_VAL_SIZE);
1115d5ac70f0Sopenharmony_ci		}
1116d5ac70f0Sopenharmony_ci	}
1117d5ac70f0Sopenharmony_ci
1118d5ac70f0Sopenharmony_ci	/* priv data */
1119d5ac70f0Sopenharmony_ci	priv = enum_ctl->priv;
1120d5ac70f0Sopenharmony_ci	if (priv && priv->size > 0) {
1121d5ac70f0Sopenharmony_ci		ret = tplg_add_data(tplg, elem, priv,
1122d5ac70f0Sopenharmony_ci				    sizeof(*priv) + priv->size);
1123d5ac70f0Sopenharmony_ci		if (ret < 0)
1124d5ac70f0Sopenharmony_ci			return ret;
1125d5ac70f0Sopenharmony_ci	}
1126d5ac70f0Sopenharmony_ci
1127d5ac70f0Sopenharmony_ci	if (e)
1128d5ac70f0Sopenharmony_ci		*e = elem;
1129d5ac70f0Sopenharmony_ci	return 0;
1130d5ac70f0Sopenharmony_ci}
1131d5ac70f0Sopenharmony_ci
1132d5ac70f0Sopenharmony_ciint tplg_add_bytes(snd_tplg_t *tplg, struct snd_tplg_bytes_template *bytes_ctl,
1133d5ac70f0Sopenharmony_ci		   struct tplg_elem **e)
1134d5ac70f0Sopenharmony_ci{
1135d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_bytes_control *be;
1136d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_private *priv;
1137d5ac70f0Sopenharmony_ci	struct tplg_elem *elem;
1138d5ac70f0Sopenharmony_ci	int ret;
1139d5ac70f0Sopenharmony_ci
1140d5ac70f0Sopenharmony_ci	tplg_dbg(" Control Bytes: %s", bytes_ctl->hdr.name);
1141d5ac70f0Sopenharmony_ci
1142d5ac70f0Sopenharmony_ci	if (bytes_ctl->hdr.type != SND_SOC_TPLG_TYPE_BYTES) {
1143d5ac70f0Sopenharmony_ci		SNDERR("invalid bytes type %d", bytes_ctl->hdr.type);
1144d5ac70f0Sopenharmony_ci		return -EINVAL;
1145d5ac70f0Sopenharmony_ci	}
1146d5ac70f0Sopenharmony_ci
1147d5ac70f0Sopenharmony_ci	elem = tplg_elem_new_common(tplg, NULL, bytes_ctl->hdr.name,
1148d5ac70f0Sopenharmony_ci		SND_TPLG_TYPE_BYTES);
1149d5ac70f0Sopenharmony_ci	if (!elem)
1150d5ac70f0Sopenharmony_ci		return -ENOMEM;
1151d5ac70f0Sopenharmony_ci
1152d5ac70f0Sopenharmony_ci	be = elem->bytes_ext;
1153d5ac70f0Sopenharmony_ci	be->size = elem->size;
1154d5ac70f0Sopenharmony_ci	ret = init_ctl_hdr(tplg, elem, &be->hdr, &bytes_ctl->hdr);
1155d5ac70f0Sopenharmony_ci	if (ret < 0) {
1156d5ac70f0Sopenharmony_ci		tplg_elem_free(elem);
1157d5ac70f0Sopenharmony_ci		return ret;
1158d5ac70f0Sopenharmony_ci	}
1159d5ac70f0Sopenharmony_ci
1160d5ac70f0Sopenharmony_ci	be->max = bytes_ctl->max;
1161d5ac70f0Sopenharmony_ci	be->mask = bytes_ctl->mask;
1162d5ac70f0Sopenharmony_ci	be->base = bytes_ctl->base;
1163d5ac70f0Sopenharmony_ci	be->num_regs = bytes_ctl->num_regs;
1164d5ac70f0Sopenharmony_ci	be->ext_ops.put = bytes_ctl->ext_ops.put;
1165d5ac70f0Sopenharmony_ci	be->ext_ops.get = bytes_ctl->ext_ops.get;
1166d5ac70f0Sopenharmony_ci
1167d5ac70f0Sopenharmony_ci	/* priv data */
1168d5ac70f0Sopenharmony_ci	priv = bytes_ctl->priv;
1169d5ac70f0Sopenharmony_ci	if (priv && priv->size > 0) {
1170d5ac70f0Sopenharmony_ci		ret = tplg_add_data(tplg, elem, priv,
1171d5ac70f0Sopenharmony_ci				    sizeof(*priv) + priv->size);
1172d5ac70f0Sopenharmony_ci		if (ret < 0)
1173d5ac70f0Sopenharmony_ci			return ret;
1174d5ac70f0Sopenharmony_ci	}
1175d5ac70f0Sopenharmony_ci
1176d5ac70f0Sopenharmony_ci	/* check on TLV bytes control */
1177d5ac70f0Sopenharmony_ci	if (be->hdr.access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
1178d5ac70f0Sopenharmony_ci		if ((be->hdr.access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)
1179d5ac70f0Sopenharmony_ci			!= SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
1180d5ac70f0Sopenharmony_ci			SNDERR("Invalid TLV bytes control access 0x%x",
1181d5ac70f0Sopenharmony_ci				be->hdr.access);
1182d5ac70f0Sopenharmony_ci			tplg_elem_free(elem);
1183d5ac70f0Sopenharmony_ci			return -EINVAL;
1184d5ac70f0Sopenharmony_ci		}
1185d5ac70f0Sopenharmony_ci
1186d5ac70f0Sopenharmony_ci		if (!be->max) {
1187d5ac70f0Sopenharmony_ci			tplg_elem_free(elem);
1188d5ac70f0Sopenharmony_ci			return -EINVAL;
1189d5ac70f0Sopenharmony_ci		}
1190d5ac70f0Sopenharmony_ci	}
1191d5ac70f0Sopenharmony_ci
1192d5ac70f0Sopenharmony_ci	if (e)
1193d5ac70f0Sopenharmony_ci		*e = elem;
1194d5ac70f0Sopenharmony_ci	return 0;
1195d5ac70f0Sopenharmony_ci}
1196d5ac70f0Sopenharmony_ci
1197d5ac70f0Sopenharmony_ciint tplg_add_mixer_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
1198d5ac70f0Sopenharmony_ci{
1199d5ac70f0Sopenharmony_ci	return tplg_add_mixer(tplg, t->mixer, NULL);
1200d5ac70f0Sopenharmony_ci}
1201d5ac70f0Sopenharmony_ci
1202d5ac70f0Sopenharmony_ciint tplg_add_enum_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
1203d5ac70f0Sopenharmony_ci{
1204d5ac70f0Sopenharmony_ci	return tplg_add_enum(tplg, t->enum_ctl, NULL);
1205d5ac70f0Sopenharmony_ci}
1206d5ac70f0Sopenharmony_ci
1207d5ac70f0Sopenharmony_ciint tplg_add_bytes_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
1208d5ac70f0Sopenharmony_ci{
1209d5ac70f0Sopenharmony_ci	return tplg_add_bytes(tplg, t->bytes_ctl, NULL);
1210d5ac70f0Sopenharmony_ci}
1211d5ac70f0Sopenharmony_ci
1212d5ac70f0Sopenharmony_ciint tplg_decode_control_mixer1(snd_tplg_t *tplg,
1213d5ac70f0Sopenharmony_ci			       struct list_head *heap,
1214d5ac70f0Sopenharmony_ci			       struct snd_tplg_mixer_template *mt,
1215d5ac70f0Sopenharmony_ci			       size_t pos,
1216d5ac70f0Sopenharmony_ci			       void *bin, size_t size)
1217d5ac70f0Sopenharmony_ci{
1218d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_mixer_control *mc = bin;
1219d5ac70f0Sopenharmony_ci	struct snd_tplg_channel_map_template *map;
1220d5ac70f0Sopenharmony_ci	struct snd_tplg_tlv_dbscale_template *db;
1221d5ac70f0Sopenharmony_ci	int i;
1222d5ac70f0Sopenharmony_ci
1223d5ac70f0Sopenharmony_ci	if (size < sizeof(*mc)) {
1224d5ac70f0Sopenharmony_ci		SNDERR("mixer: small size %d", size);
1225d5ac70f0Sopenharmony_ci		return -EINVAL;
1226d5ac70f0Sopenharmony_ci	}
1227d5ac70f0Sopenharmony_ci
1228d5ac70f0Sopenharmony_ci	tplg_log(tplg, 'D', pos, "mixer: size %d TLV size %d private size %d",
1229d5ac70f0Sopenharmony_ci		 mc->size, mc->hdr.tlv.size, mc->priv.size);
1230d5ac70f0Sopenharmony_ci	if (size != mc->size + mc->priv.size) {
1231d5ac70f0Sopenharmony_ci		SNDERR("mixer: unexpected element size %d", size);
1232d5ac70f0Sopenharmony_ci		return -EINVAL;
1233d5ac70f0Sopenharmony_ci	}
1234d5ac70f0Sopenharmony_ci
1235d5ac70f0Sopenharmony_ci	memset(mt, 0, sizeof(*mt));
1236d5ac70f0Sopenharmony_ci	mt->hdr.type = mc->hdr.type;
1237d5ac70f0Sopenharmony_ci	mt->hdr.name = mc->hdr.name;
1238d5ac70f0Sopenharmony_ci	mt->hdr.access = mc->hdr.access;
1239d5ac70f0Sopenharmony_ci	mt->hdr.ops.get = mc->hdr.ops.get;
1240d5ac70f0Sopenharmony_ci	mt->hdr.ops.put = mc->hdr.ops.put;
1241d5ac70f0Sopenharmony_ci	mt->hdr.ops.info = mc->hdr.ops.info;
1242d5ac70f0Sopenharmony_ci	mt->min = mc->min;
1243d5ac70f0Sopenharmony_ci	mt->max = mc->max;
1244d5ac70f0Sopenharmony_ci	mt->platform_max = mc->platform_max;
1245d5ac70f0Sopenharmony_ci	tplg_log(tplg, 'D', pos, "mixer: name '%s' access 0x%x",
1246d5ac70f0Sopenharmony_ci		mt->hdr.name, mt->hdr.access);
1247d5ac70f0Sopenharmony_ci	if (mc->num_channels > 0) {
1248d5ac70f0Sopenharmony_ci		map = tplg_calloc(heap, sizeof(*map));
1249d5ac70f0Sopenharmony_ci		map->num_channels = mc->num_channels;
1250d5ac70f0Sopenharmony_ci		for (i = 0; i < map->num_channels; i++) {
1251d5ac70f0Sopenharmony_ci			map->channel[i].reg = mc->channel[i].reg;
1252d5ac70f0Sopenharmony_ci			map->channel[i].shift = mc->channel[i].shift;
1253d5ac70f0Sopenharmony_ci			map->channel[i].id = mc->channel[i].id;
1254d5ac70f0Sopenharmony_ci		}
1255d5ac70f0Sopenharmony_ci		mt->map = map;
1256d5ac70f0Sopenharmony_ci	}
1257d5ac70f0Sopenharmony_ci	if (mc->hdr.tlv.size == 0) {
1258d5ac70f0Sopenharmony_ci		/* nothing */
1259d5ac70f0Sopenharmony_ci	} else if (mc->hdr.tlv.size == sizeof(struct snd_soc_tplg_ctl_tlv)) {
1260d5ac70f0Sopenharmony_ci		if (mc->hdr.tlv.type != SNDRV_CTL_TLVT_DB_SCALE) {
1261d5ac70f0Sopenharmony_ci			SNDERR("mixer: unknown TLV type %d",
1262d5ac70f0Sopenharmony_ci			       mc->hdr.tlv.type);
1263d5ac70f0Sopenharmony_ci			return -EINVAL;
1264d5ac70f0Sopenharmony_ci		}
1265d5ac70f0Sopenharmony_ci		db = tplg_calloc(heap, sizeof(*db));
1266d5ac70f0Sopenharmony_ci		if (db == NULL)
1267d5ac70f0Sopenharmony_ci			return -ENOMEM;
1268d5ac70f0Sopenharmony_ci		mt->hdr.tlv_scale = db;
1269d5ac70f0Sopenharmony_ci		db->hdr.type = mc->hdr.tlv.type;
1270d5ac70f0Sopenharmony_ci		db->min = mc->hdr.tlv.scale.min;
1271d5ac70f0Sopenharmony_ci		db->step = mc->hdr.tlv.scale.step;
1272d5ac70f0Sopenharmony_ci		db->mute = mc->hdr.tlv.scale.mute;
1273d5ac70f0Sopenharmony_ci		tplg_log(tplg, 'D', pos, "mixer: dB scale TLV: min %d step %d mute %d",
1274d5ac70f0Sopenharmony_ci			 db->min, db->step, db->mute);
1275d5ac70f0Sopenharmony_ci	} else {
1276d5ac70f0Sopenharmony_ci		SNDERR("mixer: wrong TLV size %d", mc->hdr.tlv.size);
1277d5ac70f0Sopenharmony_ci		return -EINVAL;
1278d5ac70f0Sopenharmony_ci	}
1279d5ac70f0Sopenharmony_ci
1280d5ac70f0Sopenharmony_ci	mt->priv = &mc->priv;
1281d5ac70f0Sopenharmony_ci	tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_mixer_control, priv),
1282d5ac70f0Sopenharmony_ci		 "mixer: private start");
1283d5ac70f0Sopenharmony_ci	return 0;
1284d5ac70f0Sopenharmony_ci}
1285d5ac70f0Sopenharmony_ci
1286d5ac70f0Sopenharmony_ciint tplg_decode_control_mixer(snd_tplg_t *tplg,
1287d5ac70f0Sopenharmony_ci			      size_t pos,
1288d5ac70f0Sopenharmony_ci			      struct snd_soc_tplg_hdr *hdr,
1289d5ac70f0Sopenharmony_ci			      void *bin, size_t size)
1290d5ac70f0Sopenharmony_ci{
1291d5ac70f0Sopenharmony_ci	struct list_head heap;
1292d5ac70f0Sopenharmony_ci	snd_tplg_obj_template_t t;
1293d5ac70f0Sopenharmony_ci	struct snd_tplg_mixer_template mt;
1294d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_mixer_control *mc;
1295d5ac70f0Sopenharmony_ci	size_t size2;
1296d5ac70f0Sopenharmony_ci	int err;
1297d5ac70f0Sopenharmony_ci
1298d5ac70f0Sopenharmony_ci	err = tplg_decode_template(tplg, pos, hdr, &t);
1299d5ac70f0Sopenharmony_ci	if (err < 0)
1300d5ac70f0Sopenharmony_ci		return err;
1301d5ac70f0Sopenharmony_ci
1302d5ac70f0Sopenharmony_cinext:
1303d5ac70f0Sopenharmony_ci	if (size < sizeof(*mc)) {
1304d5ac70f0Sopenharmony_ci		SNDERR("mixer: small size %d", size);
1305d5ac70f0Sopenharmony_ci		return -EINVAL;
1306d5ac70f0Sopenharmony_ci	}
1307d5ac70f0Sopenharmony_ci	INIT_LIST_HEAD(&heap);
1308d5ac70f0Sopenharmony_ci	mc = bin;
1309d5ac70f0Sopenharmony_ci	size2 = mc->size + mc->priv.size;
1310d5ac70f0Sopenharmony_ci	if (size2 > size) {
1311d5ac70f0Sopenharmony_ci		SNDERR("mixer: wrong element size (%d, priv %d)",
1312d5ac70f0Sopenharmony_ci		       mc->size, mc->priv.size);
1313d5ac70f0Sopenharmony_ci		return -EINVAL;
1314d5ac70f0Sopenharmony_ci	}
1315d5ac70f0Sopenharmony_ci
1316d5ac70f0Sopenharmony_ci	err = tplg_decode_control_mixer1(tplg, &heap, &mt, pos, bin, size2);
1317d5ac70f0Sopenharmony_ci	if (err >= 0) {
1318d5ac70f0Sopenharmony_ci		t.mixer = &mt;
1319d5ac70f0Sopenharmony_ci		err = snd_tplg_add_object(tplg, &t);
1320d5ac70f0Sopenharmony_ci	}
1321d5ac70f0Sopenharmony_ci	tplg_free(&heap);
1322d5ac70f0Sopenharmony_ci	if (err < 0)
1323d5ac70f0Sopenharmony_ci		return err;
1324d5ac70f0Sopenharmony_ci
1325d5ac70f0Sopenharmony_ci	bin += size2;
1326d5ac70f0Sopenharmony_ci	size -= size2;
1327d5ac70f0Sopenharmony_ci	pos += size2;
1328d5ac70f0Sopenharmony_ci
1329d5ac70f0Sopenharmony_ci	if (size > 0)
1330d5ac70f0Sopenharmony_ci		goto next;
1331d5ac70f0Sopenharmony_ci
1332d5ac70f0Sopenharmony_ci	return 0;
1333d5ac70f0Sopenharmony_ci}
1334d5ac70f0Sopenharmony_ci
1335d5ac70f0Sopenharmony_ciint tplg_decode_control_enum1(snd_tplg_t *tplg,
1336d5ac70f0Sopenharmony_ci			      struct list_head *heap,
1337d5ac70f0Sopenharmony_ci			      struct snd_tplg_enum_template *et,
1338d5ac70f0Sopenharmony_ci			      size_t pos,
1339d5ac70f0Sopenharmony_ci			      struct snd_soc_tplg_enum_control *ec)
1340d5ac70f0Sopenharmony_ci{
1341d5ac70f0Sopenharmony_ci	int i;
1342d5ac70f0Sopenharmony_ci
1343d5ac70f0Sopenharmony_ci	if (ec->num_channels > SND_TPLG_MAX_CHAN ||
1344d5ac70f0Sopenharmony_ci	    ec->num_channels > SND_SOC_TPLG_MAX_CHAN) {
1345d5ac70f0Sopenharmony_ci		SNDERR("enum: unexpected channel count %d", ec->num_channels);
1346d5ac70f0Sopenharmony_ci		return -EINVAL;
1347d5ac70f0Sopenharmony_ci	}
1348d5ac70f0Sopenharmony_ci	if (ec->items > SND_SOC_TPLG_NUM_TEXTS) {
1349d5ac70f0Sopenharmony_ci		SNDERR("enum: unexpected texts count %d", ec->items);
1350d5ac70f0Sopenharmony_ci		return -EINVAL;
1351d5ac70f0Sopenharmony_ci	}
1352d5ac70f0Sopenharmony_ci
1353d5ac70f0Sopenharmony_ci	memset(et, 0, sizeof(*et));
1354d5ac70f0Sopenharmony_ci	et->hdr.type = ec->hdr.type;
1355d5ac70f0Sopenharmony_ci	et->hdr.name = ec->hdr.name;
1356d5ac70f0Sopenharmony_ci	et->hdr.access = ec->hdr.access;
1357d5ac70f0Sopenharmony_ci	et->hdr.ops.get = ec->hdr.ops.get;
1358d5ac70f0Sopenharmony_ci	et->hdr.ops.put = ec->hdr.ops.put;
1359d5ac70f0Sopenharmony_ci	et->hdr.ops.info = ec->hdr.ops.info;
1360d5ac70f0Sopenharmony_ci	et->mask = ec->mask;
1361d5ac70f0Sopenharmony_ci
1362d5ac70f0Sopenharmony_ci	if (ec->items > 0) {
1363d5ac70f0Sopenharmony_ci		et->items = ec->items;
1364d5ac70f0Sopenharmony_ci		et->texts = tplg_calloc(heap, sizeof(char *) * ec->items);
1365d5ac70f0Sopenharmony_ci		if (!et->texts)
1366d5ac70f0Sopenharmony_ci			return -ENOMEM;
1367d5ac70f0Sopenharmony_ci		for (i = 0; (unsigned int)i < ec->items; i++)
1368d5ac70f0Sopenharmony_ci			et->texts[i] = ec->texts[i];
1369d5ac70f0Sopenharmony_ci	}
1370d5ac70f0Sopenharmony_ci
1371d5ac70f0Sopenharmony_ci	et->map = tplg_calloc(heap, sizeof(struct snd_tplg_channel_map_template));
1372d5ac70f0Sopenharmony_ci	if (!et->map)
1373d5ac70f0Sopenharmony_ci		return -ENOMEM;
1374d5ac70f0Sopenharmony_ci	et->map->num_channels = ec->num_channels;
1375d5ac70f0Sopenharmony_ci	for (i = 0; i < et->map->num_channels; i++) {
1376d5ac70f0Sopenharmony_ci		struct snd_tplg_channel_elem *channel = &et->map->channel[i];
1377d5ac70f0Sopenharmony_ci
1378d5ac70f0Sopenharmony_ci		tplg_log(tplg, 'D', pos + ((void *)&ec->channel[i] - (void *)ec),
1379d5ac70f0Sopenharmony_ci			 "enum: channel size %d", ec->channel[i].size);
1380d5ac70f0Sopenharmony_ci		channel->reg = ec->channel[i].reg;
1381d5ac70f0Sopenharmony_ci		channel->shift = ec->channel[i].shift;
1382d5ac70f0Sopenharmony_ci		channel->id = ec->channel[i].id;
1383d5ac70f0Sopenharmony_ci	}
1384d5ac70f0Sopenharmony_ci
1385d5ac70f0Sopenharmony_ci	et->priv = &ec->priv;
1386d5ac70f0Sopenharmony_ci	return 0;
1387d5ac70f0Sopenharmony_ci}
1388d5ac70f0Sopenharmony_ci
1389d5ac70f0Sopenharmony_ciint tplg_decode_control_enum(snd_tplg_t *tplg,
1390d5ac70f0Sopenharmony_ci			     size_t pos,
1391d5ac70f0Sopenharmony_ci			     struct snd_soc_tplg_hdr *hdr,
1392d5ac70f0Sopenharmony_ci			     void *bin, size_t size)
1393d5ac70f0Sopenharmony_ci{
1394d5ac70f0Sopenharmony_ci	struct list_head heap;
1395d5ac70f0Sopenharmony_ci	snd_tplg_obj_template_t t;
1396d5ac70f0Sopenharmony_ci	struct snd_tplg_enum_template et;
1397d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_enum_control *ec;
1398d5ac70f0Sopenharmony_ci	size_t size2;
1399d5ac70f0Sopenharmony_ci	int err;
1400d5ac70f0Sopenharmony_ci
1401d5ac70f0Sopenharmony_ci	err = tplg_decode_template(tplg, pos, hdr, &t);
1402d5ac70f0Sopenharmony_ci	if (err < 0)
1403d5ac70f0Sopenharmony_ci		return err;
1404d5ac70f0Sopenharmony_ci
1405d5ac70f0Sopenharmony_cinext:
1406d5ac70f0Sopenharmony_ci	if (size < sizeof(*ec)) {
1407d5ac70f0Sopenharmony_ci		SNDERR("enum: small size %d", size);
1408d5ac70f0Sopenharmony_ci		return -EINVAL;
1409d5ac70f0Sopenharmony_ci	}
1410d5ac70f0Sopenharmony_ci	INIT_LIST_HEAD(&heap);
1411d5ac70f0Sopenharmony_ci	ec = bin;
1412d5ac70f0Sopenharmony_ci	size2 = ec->size + ec->priv.size;
1413d5ac70f0Sopenharmony_ci	if (size2 > size) {
1414d5ac70f0Sopenharmony_ci		SNDERR("enum: wrong element size (%d, priv %d)",
1415d5ac70f0Sopenharmony_ci		       ec->size, ec->priv.size);
1416d5ac70f0Sopenharmony_ci		return -EINVAL;
1417d5ac70f0Sopenharmony_ci	}
1418d5ac70f0Sopenharmony_ci
1419d5ac70f0Sopenharmony_ci	tplg_log(tplg, 'D', pos, "enum: size %d private size %d",
1420d5ac70f0Sopenharmony_ci		 ec->size, ec->priv.size);
1421d5ac70f0Sopenharmony_ci
1422d5ac70f0Sopenharmony_ci	err = tplg_decode_control_enum1(tplg, &heap, &et, pos, ec);
1423d5ac70f0Sopenharmony_ci	if (err >= 0) {
1424d5ac70f0Sopenharmony_ci		t.enum_ctl = &et;
1425d5ac70f0Sopenharmony_ci		err = snd_tplg_add_object(tplg, &t);
1426d5ac70f0Sopenharmony_ci	}
1427d5ac70f0Sopenharmony_ci	tplg_free(&heap);
1428d5ac70f0Sopenharmony_ci	if (err < 0)
1429d5ac70f0Sopenharmony_ci		return err;
1430d5ac70f0Sopenharmony_ci
1431d5ac70f0Sopenharmony_ci	bin += size2;
1432d5ac70f0Sopenharmony_ci	size -= size2;
1433d5ac70f0Sopenharmony_ci	pos += size2;
1434d5ac70f0Sopenharmony_ci
1435d5ac70f0Sopenharmony_ci	if (size > 0)
1436d5ac70f0Sopenharmony_ci		goto next;
1437d5ac70f0Sopenharmony_ci
1438d5ac70f0Sopenharmony_ci	return 0;
1439d5ac70f0Sopenharmony_ci}
1440d5ac70f0Sopenharmony_ci
1441d5ac70f0Sopenharmony_ciint tplg_decode_control_bytes1(snd_tplg_t *tplg,
1442d5ac70f0Sopenharmony_ci			       struct snd_tplg_bytes_template *bt,
1443d5ac70f0Sopenharmony_ci			       size_t pos,
1444d5ac70f0Sopenharmony_ci			       void *bin, size_t size)
1445d5ac70f0Sopenharmony_ci{
1446d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_bytes_control *bc = bin;
1447d5ac70f0Sopenharmony_ci
1448d5ac70f0Sopenharmony_ci	if (size < sizeof(*bc)) {
1449d5ac70f0Sopenharmony_ci		SNDERR("bytes: small size %d", size);
1450d5ac70f0Sopenharmony_ci		return -EINVAL;
1451d5ac70f0Sopenharmony_ci	}
1452d5ac70f0Sopenharmony_ci
1453d5ac70f0Sopenharmony_ci	tplg_log(tplg, 'D', pos, "control bytes: size %d private size %d",
1454d5ac70f0Sopenharmony_ci		 bc->size, bc->priv.size);
1455d5ac70f0Sopenharmony_ci	if (size != bc->size + bc->priv.size) {
1456d5ac70f0Sopenharmony_ci		SNDERR("bytes: unexpected element size %d", size);
1457d5ac70f0Sopenharmony_ci		return -EINVAL;
1458d5ac70f0Sopenharmony_ci	}
1459d5ac70f0Sopenharmony_ci
1460d5ac70f0Sopenharmony_ci	memset(bt, 0, sizeof(*bt));
1461d5ac70f0Sopenharmony_ci	bt->hdr.type = bc->hdr.type;
1462d5ac70f0Sopenharmony_ci	bt->hdr.name = bc->hdr.name;
1463d5ac70f0Sopenharmony_ci	bt->hdr.access = bc->hdr.access;
1464d5ac70f0Sopenharmony_ci	bt->hdr.ops.get = bc->hdr.ops.get;
1465d5ac70f0Sopenharmony_ci	bt->hdr.ops.put = bc->hdr.ops.put;
1466d5ac70f0Sopenharmony_ci	bt->hdr.ops.info = bc->hdr.ops.info;
1467d5ac70f0Sopenharmony_ci	bt->max = bc->max;
1468d5ac70f0Sopenharmony_ci	bt->mask = bc->mask;
1469d5ac70f0Sopenharmony_ci	bt->base = bc->base;
1470d5ac70f0Sopenharmony_ci	bt->num_regs = bc->num_regs;
1471d5ac70f0Sopenharmony_ci	bt->ext_ops.get = bc->ext_ops.get;
1472d5ac70f0Sopenharmony_ci	bt->ext_ops.put = bc->ext_ops.put;
1473d5ac70f0Sopenharmony_ci	bt->ext_ops.info = bc->ext_ops.info;
1474d5ac70f0Sopenharmony_ci	tplg_log(tplg, 'D', pos, "control bytes: name '%s' access 0x%x",
1475d5ac70f0Sopenharmony_ci		 bt->hdr.name, bt->hdr.access);
1476d5ac70f0Sopenharmony_ci
1477d5ac70f0Sopenharmony_ci	bt->priv = &bc->priv;
1478d5ac70f0Sopenharmony_ci	return 0;
1479d5ac70f0Sopenharmony_ci}
1480d5ac70f0Sopenharmony_ci
1481d5ac70f0Sopenharmony_ciint tplg_decode_control_bytes(snd_tplg_t *tplg,
1482d5ac70f0Sopenharmony_ci			      size_t pos,
1483d5ac70f0Sopenharmony_ci			      struct snd_soc_tplg_hdr *hdr,
1484d5ac70f0Sopenharmony_ci			      void *bin, size_t size)
1485d5ac70f0Sopenharmony_ci{
1486d5ac70f0Sopenharmony_ci	snd_tplg_obj_template_t t;
1487d5ac70f0Sopenharmony_ci	struct snd_tplg_bytes_template bt;
1488d5ac70f0Sopenharmony_ci	struct snd_soc_tplg_bytes_control *bc;
1489d5ac70f0Sopenharmony_ci	size_t size2;
1490d5ac70f0Sopenharmony_ci	int err;
1491d5ac70f0Sopenharmony_ci
1492d5ac70f0Sopenharmony_ci	err = tplg_decode_template(tplg, pos, hdr, &t);
1493d5ac70f0Sopenharmony_ci	if (err < 0)
1494d5ac70f0Sopenharmony_ci		return err;
1495d5ac70f0Sopenharmony_ci
1496d5ac70f0Sopenharmony_cinext:
1497d5ac70f0Sopenharmony_ci	if (size < sizeof(*bc)) {
1498d5ac70f0Sopenharmony_ci		SNDERR("bytes: small size %d", size);
1499d5ac70f0Sopenharmony_ci		return -EINVAL;
1500d5ac70f0Sopenharmony_ci	}
1501d5ac70f0Sopenharmony_ci	bc = bin;
1502d5ac70f0Sopenharmony_ci	size2 = bc->size + bc->priv.size;
1503d5ac70f0Sopenharmony_ci	if (size2 > size) {
1504d5ac70f0Sopenharmony_ci		SNDERR("bytes: wrong element size (%d, priv %d)",
1505d5ac70f0Sopenharmony_ci		       bc->size, bc->priv.size);
1506d5ac70f0Sopenharmony_ci		return -EINVAL;
1507d5ac70f0Sopenharmony_ci	}
1508d5ac70f0Sopenharmony_ci
1509d5ac70f0Sopenharmony_ci	err = tplg_decode_control_bytes1(tplg, &bt, pos, bin, size);
1510d5ac70f0Sopenharmony_ci	if (err < 0)
1511d5ac70f0Sopenharmony_ci		return err;
1512d5ac70f0Sopenharmony_ci
1513d5ac70f0Sopenharmony_ci	t.bytes_ctl = &bt;
1514d5ac70f0Sopenharmony_ci	err = snd_tplg_add_object(tplg, &t);
1515d5ac70f0Sopenharmony_ci	if (err < 0)
1516d5ac70f0Sopenharmony_ci		return err;
1517d5ac70f0Sopenharmony_ci
1518d5ac70f0Sopenharmony_ci	bin += size2;
1519d5ac70f0Sopenharmony_ci	size -= size2;
1520d5ac70f0Sopenharmony_ci	pos += size2;
1521d5ac70f0Sopenharmony_ci
1522d5ac70f0Sopenharmony_ci	if (size > 0)
1523d5ac70f0Sopenharmony_ci		goto next;
1524d5ac70f0Sopenharmony_ci
1525d5ac70f0Sopenharmony_ci	return 0;
1526d5ac70f0Sopenharmony_ci}
1527