OSDN Git Service

topology: add support for CTL access flags to config file
[android-x86/external-alsa-lib.git] / src / topology / ctl.c
1 /*
2   Copyright(c) 2014-2015 Intel Corporation
3   All rights reserved.
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of version 2 of the GNU General Public License as
7   published by the Free Software Foundation.
8
9   This program is distributed in the hope that it will be useful, but
10   WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   General Public License for more details.
13
14   Authors: Mengdong Lin <mengdong.lin@intel.com>
15            Yao Jin <yao.jin@intel.com>
16            Liam Girdwood <liam.r.girdwood@linux.intel.com>
17 */
18
19 #include "list.h"
20 #include "tplg_local.h"
21
22 #define ENUM_VAL_SIZE   (SNDRV_CTL_ELEM_ID_NAME_MAXLEN >> 2)
23
24 struct ctl_access_elem {
25         const char *name;
26         unsigned int value;
27 };
28
29 /* CTL access strings and codes */
30 static const struct ctl_access_elem ctl_access[] = {
31         {"read", SNDRV_CTL_ELEM_ACCESS_READ},
32         {"write", SNDRV_CTL_ELEM_ACCESS_WRITE},
33         {"read_write", SNDRV_CTL_ELEM_ACCESS_READWRITE},
34         {"volatile", SNDRV_CTL_ELEM_ACCESS_VOLATILE},
35         {"timestamp", SNDRV_CTL_ELEM_ACCESS_TIMESTAMP},
36         {"tlv_read", SNDRV_CTL_ELEM_ACCESS_TLV_READ},
37         {"tlv_write", SNDRV_CTL_ELEM_ACCESS_TLV_WRITE},
38         {"tlv_read_write", SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE},
39         {"tlv_command", SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND},
40         {"inactive", SNDRV_CTL_ELEM_ACCESS_INACTIVE},
41         {"lock", SNDRV_CTL_ELEM_ACCESS_LOCK},
42         {"owner", SNDRV_CTL_ELEM_ACCESS_OWNER},
43         {"tlv_callback", SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK},
44         {"user", SNDRV_CTL_ELEM_ACCESS_USER},
45 };
46
47 /* find CTL access strings and conver to values */
48 static int parse_access_values(snd_config_t *cfg,
49         struct snd_soc_tplg_ctl_hdr *hdr)
50 {
51         snd_config_iterator_t i, next;
52         snd_config_t *n;
53         const char *value = NULL;
54         unsigned int j;
55
56         tplg_dbg(" Access:\n");
57
58         snd_config_for_each(i, next, cfg) {
59                 n = snd_config_iterator_entry(i);
60
61                 /* get value */
62                 if (snd_config_get_string(n, &value) < 0)
63                         continue;
64
65                 /* match access value and set flags */
66                 for (j = 0; j < ARRAY_SIZE(ctl_access); j++) {
67                         if (strcmp(value, ctl_access[j].name) == 0) {
68                                 hdr->access |= ctl_access[j].value;
69                                 tplg_dbg("\t%s\n", value);
70                                 break;
71                         }
72                 }
73         }
74
75         return 0;
76 }
77
78 /* Parse Access */
79 int parse_access(snd_config_t *cfg,
80         struct snd_soc_tplg_ctl_hdr *hdr)
81 {
82         snd_config_iterator_t i, next;
83         snd_config_t *n;
84         const char *id;
85         int err = 0;
86
87         snd_config_for_each(i, next, cfg) {
88
89                 n = snd_config_iterator_entry(i);
90                 if (snd_config_get_id(n, &id) < 0)
91                         continue;
92
93                 if (strcmp(id, "access") == 0) {
94                         err = parse_access_values(n, hdr);
95                         if (err < 0) {
96                                 SNDERR("error: failed to parse access");
97                                 return err;
98                         }
99                         continue;
100                 }
101         }
102
103         return err;
104 }
105
106 /* copy referenced TLV to the mixer control */
107 static int copy_tlv(struct tplg_elem *elem, struct tplg_elem *ref)
108 {
109         struct snd_soc_tplg_mixer_control *mixer_ctrl =  elem->mixer_ctrl;
110         struct snd_soc_tplg_ctl_tlv *tlv = ref->tlv;
111
112         tplg_dbg("TLV '%s' used by '%s\n", ref->id, elem->id);
113
114         /* TLV has a fixed size */
115         mixer_ctrl->hdr.tlv = *tlv;
116         return 0;
117 }
118
119 /* check referenced TLV for a mixer control */
120 static int tplg_build_mixer_control(snd_tplg_t *tplg,
121                                 struct tplg_elem *elem)
122 {
123         struct tplg_ref *ref;
124         struct list_head *base, *pos;
125         int err = 0;
126
127         base = &elem->ref_list;
128
129         /* for each ref in this control elem */
130         list_for_each(pos, base) {
131
132                 ref = list_entry(pos, struct tplg_ref, list);
133                 if (ref->id == NULL || ref->elem)
134                         continue;
135
136                 if (ref->type == SND_TPLG_TYPE_TLV) {
137                         ref->elem = tplg_elem_lookup(&tplg->tlv_list,
138                                                 ref->id, SND_TPLG_TYPE_TLV);
139                         if (ref->elem)
140                                  err = copy_tlv(elem, ref->elem);
141
142                 } else if (ref->type == SND_TPLG_TYPE_DATA) {
143                         ref->elem = tplg_elem_lookup(&tplg->pdata_list,
144                                                 ref->id, SND_TPLG_TYPE_DATA);
145                          err = tplg_copy_data(elem, ref->elem);
146                 }
147
148                 if (!ref->elem) {
149                         SNDERR("error: cannot find '%s' referenced by"
150                                 " control '%s'\n", ref->id, elem->id);
151                         return -EINVAL;
152                 } else if (err < 0)
153                         return err;
154         }
155
156         return 0;
157 }
158
159 static void copy_enum_texts(struct tplg_elem *enum_elem,
160         struct tplg_elem *ref_elem)
161 {
162         struct snd_soc_tplg_enum_control *ec = enum_elem->enum_ctrl;
163
164         memcpy(ec->texts, ref_elem->texts,
165                 SND_SOC_TPLG_NUM_TEXTS * SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
166 }
167
168 /* check referenced text for a enum control */
169 static int tplg_build_enum_control(snd_tplg_t *tplg,
170                                 struct tplg_elem *elem)
171 {
172         struct tplg_ref *ref;
173         struct list_head *base, *pos;
174         int err = 0;
175
176         base = &elem->ref_list;
177
178         list_for_each(pos, base) {
179
180                 ref = list_entry(pos, struct tplg_ref, list);
181                 if (ref->id == NULL || ref->elem)
182                         continue;
183
184                 if (ref->type == SND_TPLG_TYPE_TEXT) {
185                         ref->elem = tplg_elem_lookup(&tplg->text_list,
186                                                 ref->id, SND_TPLG_TYPE_TEXT);
187                         if (ref->elem)
188                                 copy_enum_texts(elem, ref->elem);
189
190                 } else if (ref->type == SND_TPLG_TYPE_DATA) {
191                         ref->elem = tplg_elem_lookup(&tplg->pdata_list,
192                                                 ref->id, SND_TPLG_TYPE_DATA);
193                         err = tplg_copy_data(elem, ref->elem);
194                 }
195                 if (!ref->elem) {
196                         SNDERR("error: cannot find '%s' referenced by"
197                                 " control '%s'\n", ref->id, elem->id);
198                         return -EINVAL;
199                 } else if (err < 0)
200                         return err;
201         }
202
203         return 0;
204 }
205
206 /* check referenced private data for a byte control */
207 static int tplg_build_bytes_control(snd_tplg_t *tplg, struct tplg_elem *elem)
208 {
209         struct tplg_ref *ref;
210         struct list_head *base, *pos;
211
212         base = &elem->ref_list;
213
214         list_for_each(pos, base) {
215
216                 ref = list_entry(pos, struct tplg_ref, list);
217                 if (ref->id == NULL || ref->elem)
218                         continue;
219
220                 /* bytes control only reference one private data section */
221                 ref->elem = tplg_elem_lookup(&tplg->pdata_list,
222                         ref->id, SND_TPLG_TYPE_DATA);
223                 if (!ref->elem) {
224                         SNDERR("error: cannot find data '%s'"
225                                 " referenced by control '%s'\n",
226                                 ref->id, elem->id);
227                         return -EINVAL;
228                 }
229
230                 /* copy texts to enum elem */
231                 return tplg_copy_data(elem, ref->elem);
232         }
233
234         return 0;
235 }
236
237 int tplg_build_controls(snd_tplg_t *tplg)
238 {
239         struct list_head *base, *pos;
240         struct tplg_elem *elem;
241         int err = 0;
242
243         base = &tplg->mixer_list;
244         list_for_each(pos, base) {
245
246                 elem = list_entry(pos, struct tplg_elem, list);
247                 err = tplg_build_mixer_control(tplg, elem);
248                 if (err < 0)
249                         return err;
250
251                 /* add control to manifest */
252                 tplg->manifest.control_elems++;
253         }
254
255         base = &tplg->enum_list;
256         list_for_each(pos, base) {
257
258                 elem = list_entry(pos, struct tplg_elem, list);
259                 err = tplg_build_enum_control(tplg, elem);
260                 if (err < 0)
261                         return err;
262
263                 /* add control to manifest */
264                 tplg->manifest.control_elems++;
265         }
266
267         base = &tplg->bytes_ext_list;
268         list_for_each(pos, base) {
269
270                 elem = list_entry(pos, struct tplg_elem, list);
271                 err = tplg_build_bytes_control(tplg, elem);
272                 if (err < 0)
273                         return err;
274
275                 /* add control to manifest */
276                 tplg->manifest.control_elems++;
277         }
278
279         return 0;
280 }
281
282
283 /*
284  * Parse TLV of DBScale type.
285  *
286  * Parse DBScale describing min, step, mute in DB.
287  */
288 static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem)
289 {
290         snd_config_iterator_t i, next;
291         snd_config_t *n;
292         struct snd_soc_tplg_ctl_tlv *tplg_tlv;
293         struct snd_soc_tplg_tlv_dbscale *scale;
294         const char *id = NULL, *value = NULL;
295
296         tplg_dbg(" scale: %s\n", elem->id);
297
298         tplg_tlv = calloc(1, sizeof(*tplg_tlv));
299         if (!tplg_tlv)
300                 return -ENOMEM;
301
302         elem->tlv = tplg_tlv;
303         tplg_tlv->size = sizeof(struct snd_soc_tplg_ctl_tlv);
304         tplg_tlv->type = SNDRV_CTL_TLVT_DB_SCALE;
305         scale = &tplg_tlv->scale;
306
307         snd_config_for_each(i, next, cfg) {
308
309                 n = snd_config_iterator_entry(i);
310
311                 /* get ID */
312                 if (snd_config_get_id(n, &id) < 0) {
313                         SNDERR("error: cant get ID\n");
314                         return -EINVAL;
315                 }
316
317                 /* get value */
318                 if (snd_config_get_string(n, &value) < 0)
319                         continue;
320
321                 tplg_dbg("\t%s = %s\n", id, value);
322
323                 /* get TLV data */
324                 if (strcmp(id, "min") == 0)
325                         scale->min = atoi(value);
326                 else if (strcmp(id, "step") == 0)
327                         scale->step = atoi(value);
328                 else if (strcmp(id, "mute") == 0)
329                         scale->mute = atoi(value);
330                 else
331                         SNDERR("error: unknown key %s\n", id);
332         }
333
334         return 0;
335 }
336
337 /* Parse TLV */
338 int tplg_parse_tlv(snd_tplg_t *tplg, snd_config_t *cfg,
339         void *private ATTRIBUTE_UNUSED)
340 {
341         snd_config_iterator_t i, next;
342         snd_config_t *n;
343         const char *id;
344         int err = 0;
345         struct tplg_elem *elem;
346
347         elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_TLV);
348         if (!elem)
349                 return -ENOMEM;
350
351         snd_config_for_each(i, next, cfg) {
352
353                 n = snd_config_iterator_entry(i);
354                 if (snd_config_get_id(n, &id) < 0)
355                         continue;
356
357                 if (strcmp(id, "scale") == 0) {
358                         err = tplg_parse_tlv_dbscale(n, elem);
359                         if (err < 0) {
360                                 SNDERR("error: failed to DBScale");
361                                 return err;
362                         }
363                         continue;
364                 }
365         }
366
367         return err;
368 }
369
370 /* Parse Control Bytes */
371 int tplg_parse_control_bytes(snd_tplg_t *tplg,
372         snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
373 {
374         struct snd_soc_tplg_bytes_control *be;
375         struct tplg_elem *elem;
376         snd_config_iterator_t i, next;
377         snd_config_t *n;
378         const char *id, *val = NULL;
379         int err;
380         bool access_set = false, tlv_set = false;
381
382         elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_BYTES);
383         if (!elem)
384                 return -ENOMEM;
385
386         be = elem->bytes_ext;
387         be->size = elem->size;
388         elem_copy_text(be->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
389         be->hdr.type = SND_SOC_TPLG_TYPE_BYTES;
390
391         tplg_dbg(" Control Bytes: %s\n", elem->id);
392
393         snd_config_for_each(i, next, cfg) {
394                 n = snd_config_iterator_entry(i);
395                 if (snd_config_get_id(n, &id) < 0)
396                         continue;
397
398                 /* skip comments */
399                 if (strcmp(id, "comment") == 0)
400                         continue;
401                 if (id[0] == '#')
402                         continue;
403
404                 if (strcmp(id, "index") == 0) {
405                         if (snd_config_get_string(n, &val) < 0)
406                                 return -EINVAL;
407
408                         elem->index = atoi(val);
409                         tplg_dbg("\t%s: %d\n", id, elem->index);
410                         continue;
411                 }
412
413                 if (strcmp(id, "base") == 0) {
414                         if (snd_config_get_string(n, &val) < 0)
415                                 return -EINVAL;
416
417                         be->base = atoi(val);
418                         tplg_dbg("\t%s: %d\n", id, be->base);
419                         continue;
420                 }
421
422                 if (strcmp(id, "num_regs") == 0) {
423                         if (snd_config_get_string(n, &val) < 0)
424                                 return -EINVAL;
425
426                         be->num_regs = atoi(val);
427                         tplg_dbg("\t%s: %d\n", id, be->num_regs);
428                         continue;
429                 }
430
431                 if (strcmp(id, "max") == 0) {
432                         if (snd_config_get_string(n, &val) < 0)
433                                 return -EINVAL;
434
435                         be->max = atoi(val);
436                         tplg_dbg("\t%s: %d\n", id, be->max);
437                         continue;
438                 }
439
440                 if (strcmp(id, "mask") == 0) {
441                         if (snd_config_get_string(n, &val) < 0)
442                                 return -EINVAL;
443
444                         be->mask = strtol(val, NULL, 16);
445                         tplg_dbg("\t%s: %d\n", id, be->mask);
446                         continue;
447                 }
448
449                 if (strcmp(id, "data") == 0) {
450                         if (snd_config_get_string(n, &val) < 0)
451                                 return -EINVAL;
452
453                         tplg_ref_add(elem, SND_TPLG_TYPE_DATA, val);
454                         tplg_dbg("\t%s: %s\n", id, val);
455                         continue;
456                 }
457
458                 if (strcmp(id, "tlv") == 0) {
459                         if (snd_config_get_string(n, &val) < 0)
460                                 return -EINVAL;
461
462                         err = tplg_ref_add(elem, SND_TPLG_TYPE_TLV, val);
463                         if (err < 0)
464                                 return err;
465
466                         tlv_set = true;
467                         tplg_dbg("\t%s: %s\n", id, val);
468                         continue;
469                 }
470
471                 if (strcmp(id, "ops") == 0) {
472                         err = tplg_parse_compound(tplg, n, tplg_parse_ops,
473                                 &be->hdr);
474                         if (err < 0)
475                                 return err;
476                         continue;
477                 }
478
479                 if (strcmp(id, "extops") == 0) {
480                         err = tplg_parse_compound(tplg, n, tplg_parse_ext_ops,
481                                 be);
482                         if (err < 0)
483                                 return err;
484                         continue;
485                 }
486
487                 if (strcmp(id, "access") == 0) {
488                         err = parse_access(cfg, &be->hdr);
489                         if (err < 0)
490                                 return err;
491                         access_set = true;
492                         continue;
493                 }
494         }
495
496         /* set CTL access to default values if none are provided */
497         if (!access_set) {
498
499                 be->hdr.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
500                 if (tlv_set)
501                         be->hdr.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
502         }
503
504         return 0;
505 }
506
507 /* Parse Control Enums. */
508 int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg,
509         void *private ATTRIBUTE_UNUSED)
510 {
511         struct snd_soc_tplg_enum_control *ec;
512         struct tplg_elem *elem;
513         snd_config_iterator_t i, next;
514         snd_config_t *n;
515         const char *id, *val = NULL;
516         int err, j;
517         bool access_set = false;
518
519         elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_ENUM);
520         if (!elem)
521                 return -ENOMEM;
522
523         ec = elem->enum_ctrl;
524         elem_copy_text(ec->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
525         ec->hdr.type = SND_SOC_TPLG_TYPE_ENUM;
526         ec->size = elem->size;
527         tplg->channel_idx = 0;
528
529         /* set channel reg to default state */
530         for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++)
531                 ec->channel[j].reg = -1;
532
533         tplg_dbg(" Control Enum: %s\n", elem->id);
534
535         snd_config_for_each(i, next, cfg) {
536
537                 n = snd_config_iterator_entry(i);
538                 if (snd_config_get_id(n, &id) < 0)
539                         continue;
540
541                 /* skip comments */
542                 if (strcmp(id, "comment") == 0)
543                         continue;
544                 if (id[0] == '#')
545                         continue;
546
547                 if (strcmp(id, "index") == 0) {
548                         if (snd_config_get_string(n, &val) < 0)
549                                 return -EINVAL;
550
551                         elem->index = atoi(val);
552                         tplg_dbg("\t%s: %d\n", id, elem->index);
553                         continue;
554                 }
555
556                 if (strcmp(id, "texts") == 0) {
557                         if (snd_config_get_string(n, &val) < 0)
558                                 return -EINVAL;
559
560                         tplg_ref_add(elem, SND_TPLG_TYPE_TEXT, val);
561                         tplg_dbg("\t%s: %s\n", id, val);
562                         continue;
563                 }
564
565                 if (strcmp(id, "channel") == 0) {
566                         if (ec->num_channels >= SND_SOC_TPLG_MAX_CHAN) {
567                                 SNDERR("error: too many channels %s\n",
568                                         elem->id);
569                                 return -EINVAL;
570                         }
571
572                         err = tplg_parse_compound(tplg, n, tplg_parse_channel,
573                                 ec->channel);
574                         if (err < 0)
575                                 return err;
576
577                         ec->num_channels = tplg->channel_idx;
578                         continue;
579                 }
580
581                 if (strcmp(id, "ops") == 0) {
582                         err = tplg_parse_compound(tplg, n, tplg_parse_ops,
583                                 &ec->hdr);
584                         if (err < 0)
585                                 return err;
586                         continue;
587                 }
588
589                 if (strcmp(id, "data") == 0) {
590                         if (snd_config_get_string(n, &val) < 0)
591                                 return -EINVAL;
592
593                         tplg_ref_add(elem, SND_TPLG_TYPE_DATA, val);
594                         tplg_dbg("\t%s: %s\n", id, val);
595                         continue;
596                 }
597
598                 if (strcmp(id, "access") == 0) {
599                         err = parse_access(cfg, &ec->hdr);
600                         if (err < 0)
601                                 return err;
602                         access_set = true;
603                         continue;
604                 }
605         }
606
607         /* set CTL access to default values if none are provided */
608         if (!access_set) {
609                 ec->hdr.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
610         }
611
612         return 0;
613 }
614
615 /* Parse Controls.
616  *
617  * Mixer control. Supports multiple channels.
618  */
619 int tplg_parse_control_mixer(snd_tplg_t *tplg,
620         snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
621 {
622         struct snd_soc_tplg_mixer_control *mc;
623         struct tplg_elem *elem;
624         snd_config_iterator_t i, next;
625         snd_config_t *n;
626         const char *id, *val = NULL;
627         int err, j;
628         bool access_set = false, tlv_set = false;
629
630         elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_MIXER);
631         if (!elem)
632                 return -ENOMEM;
633
634         /* init new mixer */
635         mc = elem->mixer_ctrl;
636         elem_copy_text(mc->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
637         mc->hdr.type = SND_SOC_TPLG_TYPE_MIXER;
638         mc->size = elem->size;
639         tplg->channel_idx = 0;
640
641         /* set channel reg to default state */
642         for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++)
643                 mc->channel[j].reg = -1;
644
645         tplg_dbg(" Control Mixer: %s\n", elem->id);
646
647         /* giterate trough each mixer elment */
648         snd_config_for_each(i, next, cfg) {
649                 n = snd_config_iterator_entry(i);
650                 if (snd_config_get_id(n, &id) < 0)
651                         continue;
652
653                 /* skip comments */
654                 if (strcmp(id, "comment") == 0)
655                         continue;
656                 if (id[0] == '#')
657                         continue;
658
659                 if (strcmp(id, "index") == 0) {
660                         if (snd_config_get_string(n, &val) < 0)
661                                 return -EINVAL;
662
663                         elem->index = atoi(val);
664                         tplg_dbg("\t%s: %d\n", id, elem->index);
665                         continue;
666                 }
667
668                 if (strcmp(id, "channel") == 0) {
669                         if (mc->num_channels >= SND_SOC_TPLG_MAX_CHAN) {
670                                 SNDERR("error: too many channels %s\n",
671                                         elem->id);
672                                 return -EINVAL;
673                         }
674
675                         err = tplg_parse_compound(tplg, n, tplg_parse_channel,
676                                 mc->channel);
677                         if (err < 0)
678                                 return err;
679
680                         mc->num_channels = tplg->channel_idx;
681                         continue;
682                 }
683
684                 if (strcmp(id, "max") == 0) {
685                         if (snd_config_get_string(n, &val) < 0)
686                                 return -EINVAL;
687
688                         mc->max = atoi(val);
689                         tplg_dbg("\t%s: %d\n", id, mc->max);
690                         continue;
691                 }
692
693                 if (strcmp(id, "invert") == 0) {
694                         if (snd_config_get_string(n, &val) < 0)
695                                 return -EINVAL;
696
697                         if (strcmp(val, "true") == 0)
698                                 mc->invert = 1;
699                         else if (strcmp(val, "false") == 0)
700                                 mc->invert = 0;
701
702                         tplg_dbg("\t%s: %d\n", id, mc->invert);
703                         continue;
704                 }
705
706                 if (strcmp(id, "ops") == 0) {
707                         err = tplg_parse_compound(tplg, n, tplg_parse_ops,
708                                 &mc->hdr);
709                         if (err < 0)
710                                 return err;
711                         continue;
712                 }
713
714                 if (strcmp(id, "tlv") == 0) {
715                         if (snd_config_get_string(n, &val) < 0)
716                                 return -EINVAL;
717
718                         err = tplg_ref_add(elem, SND_TPLG_TYPE_TLV, val);
719                         if (err < 0)
720                                 return err;
721
722                         tlv_set = true;
723                         tplg_dbg("\t%s: %s\n", id, val);
724                         continue;
725                 }
726
727                 if (strcmp(id, "data") == 0) {
728                         if (snd_config_get_string(n, &val) < 0)
729                                 return -EINVAL;
730
731                         tplg_ref_add(elem, SND_TPLG_TYPE_DATA, val);
732                         tplg_dbg("\t%s: %s\n", id, val);
733                         continue;
734                 }
735
736                 if (strcmp(id, "access") == 0) {
737                         err = parse_access(cfg, &mc->hdr);
738                         if (err < 0)
739                                 return err;
740                         access_set = true;
741                         continue;
742                 }
743         }
744
745         /* set CTL access to default values if none are provided */
746         if (!access_set) {
747
748                 mc->hdr.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
749                 if (tlv_set)
750                         mc->hdr.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
751         }
752
753         return 0;
754 }
755
756 static int init_ctl_hdr(struct snd_soc_tplg_ctl_hdr *hdr,
757                 struct snd_tplg_ctl_template *t)
758 {
759         hdr->size = sizeof(struct snd_soc_tplg_ctl_hdr);
760         hdr->type = t->type;
761
762         elem_copy_text(hdr->name, t->name,
763                 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
764
765         /* clean up access flag */
766         if (t->access == 0)
767                 t->access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
768         t->access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE |
769                 SNDRV_CTL_ELEM_ACCESS_VOLATILE |
770                 SNDRV_CTL_ELEM_ACCESS_INACTIVE |
771                 SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE |
772                 SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND |
773                 SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK);
774
775         hdr->access = t->access;
776         hdr->ops.get = t->ops.get;
777         hdr->ops.put = t->ops.put;
778         hdr->ops.info = t->ops.info;
779
780         /* TLV */
781         if (hdr->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE
782                 && !(hdr->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)) {
783
784                 struct snd_tplg_tlv_template *tlvt = t->tlv;
785                 struct snd_soc_tplg_ctl_tlv *tlv = &hdr->tlv;
786                 struct snd_tplg_tlv_dbscale_template *scalet;
787                 struct snd_soc_tplg_tlv_dbscale *scale;
788
789                 if (!tlvt) {
790                         SNDERR("error: missing TLV data\n");
791                         return -EINVAL;
792                 }
793
794                 tlv->size = sizeof(struct snd_soc_tplg_ctl_tlv);
795                 tlv->type = tlvt->type;
796
797                 switch (tlvt->type) {
798                 case SNDRV_CTL_TLVT_DB_SCALE:
799                         scalet = container_of(tlvt,
800                                 struct snd_tplg_tlv_dbscale_template, hdr);
801                         scale = &tlv->scale;
802                         scale->min = scalet->min;
803                         scale->step = scalet->step;
804                         scale->mute = scalet->mute;
805                         break;
806
807                 /* TODO: add support for other TLV types */
808                 default:
809                         SNDERR("error: unsupported TLV type %d\n", tlv->type);
810                         break;
811                 }
812         }
813
814         return 0;
815 }
816
817 int tplg_add_mixer(snd_tplg_t *tplg, struct snd_tplg_mixer_template *mixer,
818         struct tplg_elem **e)
819 {
820         struct snd_soc_tplg_private *priv = mixer->priv;
821         struct snd_soc_tplg_mixer_control *mc;
822         struct tplg_elem *elem;
823         int ret, i, num_channels;
824
825         tplg_dbg(" Control Mixer: %s\n", mixer->hdr.name);
826
827         if (mixer->hdr.type != SND_SOC_TPLG_TYPE_MIXER) {
828                 SNDERR("error: invalid mixer type %d\n", mixer->hdr.type);
829                 return -EINVAL;
830         }
831
832         elem = tplg_elem_new_common(tplg, NULL, mixer->hdr.name,
833                 SND_TPLG_TYPE_MIXER);
834         if (!elem)
835                 return -ENOMEM;
836
837         /* init new mixer */
838         mc = elem->mixer_ctrl;
839         mc->size = elem->size;
840         ret =  init_ctl_hdr(&mc->hdr, &mixer->hdr);
841         if (ret < 0) {
842                 tplg_elem_free(elem);
843                 return ret;
844         }
845
846         mc->min = mixer->min;
847         mc->max = mixer->max;
848         mc->platform_max = mixer->platform_max;
849         mc->invert = mixer->invert;
850
851         /* set channel reg to default state */
852         for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++)
853                 mc->channel[i].reg = -1;
854
855         num_channels = mixer->map ? mixer->map->num_channels : 0;
856         mc->num_channels = num_channels;
857
858         for (i = 0; i < num_channels; i++) {
859                 struct snd_tplg_channel_elem *channel = &mixer->map->channel[i];
860
861                 mc->channel[i].size = channel->size;
862                 mc->channel[i].reg = channel->reg;
863                 mc->channel[i].shift = channel->shift;
864                 mc->channel[i].id = channel->id;
865         }
866
867         /* priv data */
868         if (priv) {
869                 mc = realloc(mc, elem->size + priv->size);
870                 if (!mc) {
871                         tplg_elem_free(elem);
872                         return -ENOMEM;
873                 }
874
875                 elem->mixer_ctrl = mc;
876                 elem->size += priv->size;
877                 mc->priv.size = priv->size;
878                 memcpy(mc->priv.data, priv->data,  priv->size);
879         }
880
881         if (e)
882                 *e = elem;
883         return 0;
884 }
885
886 int tplg_add_enum(snd_tplg_t *tplg, struct snd_tplg_enum_template *enum_ctl,
887         struct tplg_elem **e)
888 {
889         struct snd_soc_tplg_enum_control *ec;
890         struct tplg_elem *elem;
891         int ret, i, num_items;
892
893         tplg_dbg(" Control Enum: %s\n", enum_ctl->hdr.name);
894
895         if (enum_ctl->hdr.type != SND_SOC_TPLG_TYPE_ENUM) {
896                 SNDERR("error: invalid enum type %d\n", enum_ctl->hdr.type);
897                 return -EINVAL;
898         }
899
900         elem = tplg_elem_new_common(tplg, NULL, enum_ctl->hdr.name,
901                 SND_TPLG_TYPE_ENUM);
902         if (!elem)
903                 return -ENOMEM;
904
905         ec = elem->enum_ctrl;
906         ec->size = elem->size;
907         ret = init_ctl_hdr(&ec->hdr, &enum_ctl->hdr);
908         if (ret < 0) {
909                 tplg_elem_free(elem);
910                 return ret;
911         }
912
913         num_items =  enum_ctl->items < SND_SOC_TPLG_NUM_TEXTS ?
914                 enum_ctl->items : SND_SOC_TPLG_NUM_TEXTS;
915         ec->items = num_items;
916         ec->mask = enum_ctl->mask;
917         ec->count = enum_ctl->items;
918
919         if (enum_ctl->texts != NULL) {
920                 for (i = 0; i < num_items; i++) {
921                         if (enum_ctl->texts[i] != NULL)
922                                 strncpy(ec->texts[i], enum_ctl->texts[i],
923                                         SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
924                 }
925         }
926
927         if (enum_ctl->values != NULL) {
928                 for (i = 0; i < num_items; i++) {
929                         if (enum_ctl->values[i])
930                                 continue;
931
932                         memcpy(&ec->values[i * sizeof(int) * ENUM_VAL_SIZE],
933                                 enum_ctl->values[i],
934                                 sizeof(int) * ENUM_VAL_SIZE);
935                 }
936         }
937
938         if (enum_ctl->priv != NULL) {
939                 ec = realloc(ec,
940                         elem->size + enum_ctl->priv->size);
941                 if (!ec) {
942                         tplg_elem_free(elem);
943                         return -ENOMEM;
944                 }
945
946                 elem->enum_ctrl = ec;
947                 elem->size += enum_ctl->priv->size;
948
949                 memcpy(ec->priv.data, enum_ctl->priv->data,
950                         enum_ctl->priv->size);
951
952                 ec->priv.size = enum_ctl->priv->size;
953         }
954
955         if (e)
956                 *e = elem;
957         return 0;
958 }
959
960 int tplg_add_bytes(snd_tplg_t *tplg, struct snd_tplg_bytes_template *bytes_ctl,
961         struct tplg_elem **e)
962 {
963         struct snd_soc_tplg_bytes_control *be;
964         struct tplg_elem *elem;
965         int ret;
966
967         tplg_dbg(" Control Bytes: %s\n", bytes_ctl->hdr.name);
968
969         if (bytes_ctl->hdr.type != SND_SOC_TPLG_TYPE_BYTES) {
970                 SNDERR("error: invalid bytes type %d\n", bytes_ctl->hdr.type);
971                 return -EINVAL;
972         }
973
974         elem = tplg_elem_new_common(tplg, NULL, bytes_ctl->hdr.name,
975                 SND_TPLG_TYPE_BYTES);
976         if (!elem)
977                 return -ENOMEM;
978
979         be = elem->bytes_ext;
980         be->size = elem->size;
981         ret = init_ctl_hdr(&be->hdr, &bytes_ctl->hdr);
982         if (ret < 0) {
983                 tplg_elem_free(elem);
984                 return ret;
985         }
986
987         be->max = bytes_ctl->max;
988         be->mask = bytes_ctl->mask;
989         be->base = bytes_ctl->base;
990         be->num_regs = bytes_ctl->num_regs;
991         be->ext_ops.put = bytes_ctl->ext_ops.put;
992         be->ext_ops.get = bytes_ctl->ext_ops.get;
993
994         if (bytes_ctl->priv != NULL) {
995                 be = realloc(be,
996                         elem->size + bytes_ctl->priv->size);
997                 if (!be) {
998                         tplg_elem_free(elem);
999                         return -ENOMEM;
1000                 }
1001                 elem->bytes_ext = be;
1002                 elem->size += bytes_ctl->priv->size;
1003
1004                 memcpy(be->priv.data, bytes_ctl->priv->data,
1005                         bytes_ctl->priv->size);
1006
1007                 be->priv.size = bytes_ctl->priv->size;
1008         }
1009
1010         /* check on TLV bytes control */
1011         if (be->hdr.access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
1012                 if ((be->hdr.access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)
1013                         != SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
1014                         SNDERR("error: Invalid TLV bytes control access 0x%x\n",
1015                                 be->hdr.access);
1016                         tplg_elem_free(elem);
1017                         return -EINVAL;
1018                 }
1019
1020                 if (!be->max) {
1021                         tplg_elem_free(elem);
1022                         return -EINVAL;
1023                 }
1024         }
1025
1026         if (e)
1027                 *e = elem;
1028         return 0;
1029 }
1030
1031 int tplg_add_mixer_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
1032 {
1033         return tplg_add_mixer(tplg, t->mixer, NULL);
1034 }
1035
1036 int tplg_add_enum_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
1037 {
1038         return tplg_add_enum(tplg, t->enum_ctl, NULL);
1039 }
1040
1041 int tplg_add_bytes_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
1042 {
1043         return tplg_add_bytes(tplg, t->bytes_ctl, NULL);
1044 }