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