|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
Copyright(c) 2014-2015 Intel Corporation
|
|
Packit |
4a16fb |
All rights reserved.
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
This library is free software; you can redistribute it and/or modify
|
|
Packit |
4a16fb |
it under the terms of the GNU Lesser General Public License as
|
|
Packit |
4a16fb |
published by the Free Software Foundation; either version 2.1 of
|
|
Packit |
4a16fb |
the License, or (at your option) any later version.
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
This program is distributed in the hope that it will be useful,
|
|
Packit |
4a16fb |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
4a16fb |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
4a16fb |
GNU Lesser General Public License for more details.
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
Authors: Mengdong Lin <mengdong.lin@intel.com>
|
|
Packit |
4a16fb |
Yao Jin <yao.jin@intel.com>
|
|
Packit |
4a16fb |
Liam Girdwood <liam.r.girdwood@linux.intel.com>
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
#include "list.h"
|
|
Packit |
4a16fb |
#include "tplg_local.h"
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
#define ENUM_VAL_SIZE (SNDRV_CTL_ELEM_ID_NAME_MAXLEN >> 2)
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
struct ctl_access_elem {
|
|
Packit |
4a16fb |
const char *name;
|
|
Packit |
4a16fb |
unsigned int value;
|
|
Packit |
4a16fb |
};
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* CTL access strings and codes */
|
|
Packit Service |
f36a15 |
/* place the multi-bit values on top - like read_write - for save */
|
|
Packit |
4a16fb |
static const struct ctl_access_elem ctl_access[] = {
|
|
Packit Service |
f36a15 |
{"read_write", SNDRV_CTL_ELEM_ACCESS_READWRITE},
|
|
Packit Service |
f36a15 |
{"tlv_read_write", SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE},
|
|
Packit |
4a16fb |
{"read", SNDRV_CTL_ELEM_ACCESS_READ},
|
|
Packit |
4a16fb |
{"write", SNDRV_CTL_ELEM_ACCESS_WRITE},
|
|
Packit |
4a16fb |
{"volatile", SNDRV_CTL_ELEM_ACCESS_VOLATILE},
|
|
Packit |
4a16fb |
{"tlv_read", SNDRV_CTL_ELEM_ACCESS_TLV_READ},
|
|
Packit |
4a16fb |
{"tlv_write", SNDRV_CTL_ELEM_ACCESS_TLV_WRITE},
|
|
Packit |
4a16fb |
{"tlv_command", SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND},
|
|
Packit |
4a16fb |
{"inactive", SNDRV_CTL_ELEM_ACCESS_INACTIVE},
|
|
Packit |
4a16fb |
{"lock", SNDRV_CTL_ELEM_ACCESS_LOCK},
|
|
Packit |
4a16fb |
{"owner", SNDRV_CTL_ELEM_ACCESS_OWNER},
|
|
Packit |
4a16fb |
{"tlv_callback", SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK},
|
|
Packit |
4a16fb |
};
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* find CTL access strings and conver to values */
|
|
Packit |
4a16fb |
static int parse_access_values(snd_config_t *cfg,
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_ctl_hdr *hdr)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_config_iterator_t i, next;
|
|
Packit |
4a16fb |
snd_config_t *n;
|
|
Packit |
4a16fb |
const char *value = NULL;
|
|
Packit |
4a16fb |
unsigned int j;
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
tplg_dbg(" Access:");
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_config_for_each(i, next, cfg) {
|
|
Packit |
4a16fb |
n = snd_config_iterator_entry(i);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* get value */
|
|
Packit |
4a16fb |
if (snd_config_get_string(n, &value) < 0)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* match access value and set flags */
|
|
Packit |
4a16fb |
for (j = 0; j < ARRAY_SIZE(ctl_access); j++) {
|
|
Packit |
4a16fb |
if (strcmp(value, ctl_access[j].name) == 0) {
|
|
Packit |
4a16fb |
hdr->access |= ctl_access[j].value;
|
|
Packit Service |
f36a15 |
tplg_dbg("\t%s", value);
|
|
Packit |
4a16fb |
break;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* Parse Access */
|
|
Packit |
4a16fb |
int parse_access(snd_config_t *cfg,
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_ctl_hdr *hdr)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_config_iterator_t i, next;
|
|
Packit |
4a16fb |
snd_config_t *n;
|
|
Packit |
4a16fb |
const char *id;
|
|
Packit |
4a16fb |
int err = 0;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_config_for_each(i, next, cfg) {
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
n = snd_config_iterator_entry(i);
|
|
Packit |
4a16fb |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "access") == 0) {
|
|
Packit |
4a16fb |
err = parse_access_values(n, hdr);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit Service |
f36a15 |
SNDERR("failed to parse access");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
/* Save Access */
|
|
Packit Service |
f36a15 |
static int tplg_save_access(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_ctl_hdr *hdr, char **dst,
|
|
Packit Service |
f36a15 |
const char *pfx)
|
|
Packit Service |
f36a15 |
{
|
|
Packit Service |
f36a15 |
const char *last;
|
|
Packit Service |
f36a15 |
unsigned int j, count, access, cval;
|
|
Packit Service |
f36a15 |
int err;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
if (hdr->access == 0)
|
|
Packit Service |
f36a15 |
return 0;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
access = hdr->access;
|
|
Packit Service |
f36a15 |
for (j = 0, count = 0, last = NULL; j < ARRAY_SIZE(ctl_access); j++) {
|
|
Packit Service |
f36a15 |
cval = ctl_access[j].value;
|
|
Packit Service |
f36a15 |
if ((access & cval) == cval) {
|
|
Packit Service |
f36a15 |
access &= ~cval;
|
|
Packit Service |
f36a15 |
last = ctl_access[j].name;
|
|
Packit Service |
f36a15 |
count++;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
if (count == 1)
|
|
Packit Service |
f36a15 |
return tplg_save_printf(dst, pfx, "access.0 %s\n", last);
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "access [\n");
|
|
Packit Service |
f36a15 |
if (err < 0)
|
|
Packit Service |
f36a15 |
return err;
|
|
Packit Service |
f36a15 |
access = hdr->access;
|
|
Packit Service |
f36a15 |
for (j = 0; j < ARRAY_SIZE(ctl_access); j++) {
|
|
Packit Service |
f36a15 |
cval = ctl_access[j].value;
|
|
Packit Service |
f36a15 |
if ((access & cval) == cval) {
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "\t%s\n",
|
|
Packit Service |
f36a15 |
ctl_access[j].name);
|
|
Packit Service |
f36a15 |
if (err < 0)
|
|
Packit Service |
f36a15 |
return err;
|
|
Packit Service |
f36a15 |
access &= ~cval;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
return tplg_save_printf(dst, pfx, "]\n");
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit |
4a16fb |
/* copy referenced TLV to the mixer control */
|
|
Packit |
4a16fb |
static int copy_tlv(struct tplg_elem *elem, struct tplg_elem *ref)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
struct snd_soc_tplg_mixer_control *mixer_ctrl = elem->mixer_ctrl;
|
|
Packit |
4a16fb |
struct snd_soc_tplg_ctl_tlv *tlv = ref->tlv;
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
tplg_dbg("TLV '%s' used by '%s", ref->id, elem->id);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* TLV has a fixed size */
|
|
Packit |
4a16fb |
mixer_ctrl->hdr.tlv = *tlv;
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* check referenced TLV for a mixer control */
|
|
Packit |
4a16fb |
static int tplg_build_mixer_control(snd_tplg_t *tplg,
|
|
Packit Service |
f36a15 |
struct tplg_elem *elem)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
struct tplg_ref *ref;
|
|
Packit |
4a16fb |
struct list_head *base, *pos;
|
|
Packit |
4a16fb |
int err = 0;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
base = &elem->ref_list;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* for each ref in this control elem */
|
|
Packit |
4a16fb |
list_for_each(pos, base) {
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
ref = list_entry(pos, struct tplg_ref, list);
|
|
Packit |
4a16fb |
if (ref->elem)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (ref->type == SND_TPLG_TYPE_TLV) {
|
|
Packit |
4a16fb |
ref->elem = tplg_elem_lookup(&tplg->tlv_list,
|
|
Packit |
4a16fb |
ref->id, SND_TPLG_TYPE_TLV, elem->index);
|
|
Packit |
4a16fb |
if (ref->elem)
|
|
Packit |
4a16fb |
err = copy_tlv(elem, ref->elem);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
} else if (ref->type == SND_TPLG_TYPE_DATA) {
|
|
Packit |
4a16fb |
err = tplg_copy_data(tplg, elem, ref);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (!ref->elem) {
|
|
Packit Service |
f36a15 |
SNDERR("cannot find '%s' referenced by"
|
|
Packit Service |
f36a15 |
" control '%s'", ref->id, elem->id);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
} else if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static void copy_enum_texts(struct tplg_elem *enum_elem,
|
|
Packit Service |
f36a15 |
struct tplg_elem *ref_elem)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
struct snd_soc_tplg_enum_control *ec = enum_elem->enum_ctrl;
|
|
Packit |
4a16fb |
struct tplg_texts *texts = ref_elem->texts;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
memcpy(ec->texts, texts->items,
|
|
Packit |
4a16fb |
SND_SOC_TPLG_NUM_TEXTS * SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
|
|
Packit |
4a16fb |
ec->items += texts->num_items;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* check referenced text for a enum control */
|
|
Packit |
4a16fb |
static int tplg_build_enum_control(snd_tplg_t *tplg,
|
|
Packit Service |
f36a15 |
struct tplg_elem *elem)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
struct tplg_ref *ref;
|
|
Packit |
4a16fb |
struct list_head *base, *pos;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
base = &elem->ref_list;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
list_for_each(pos, base) {
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
ref = list_entry(pos, struct tplg_ref, list);
|
|
Packit |
4a16fb |
if (ref->elem)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (ref->type == SND_TPLG_TYPE_TEXT) {
|
|
Packit |
4a16fb |
ref->elem = tplg_elem_lookup(&tplg->text_list,
|
|
Packit |
4a16fb |
ref->id, SND_TPLG_TYPE_TEXT, elem->index);
|
|
Packit |
4a16fb |
if (ref->elem)
|
|
Packit |
4a16fb |
copy_enum_texts(elem, ref->elem);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
} else if (ref->type == SND_TPLG_TYPE_DATA) {
|
|
Packit |
4a16fb |
err = tplg_copy_data(tplg, elem, ref);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
if (!ref->elem) {
|
|
Packit Service |
f36a15 |
SNDERR("cannot find '%s' referenced by"
|
|
Packit Service |
f36a15 |
" control '%s'", ref->id, elem->id);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* check referenced private data for a byte control */
|
|
Packit |
4a16fb |
static int tplg_build_bytes_control(snd_tplg_t *tplg, struct tplg_elem *elem)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
struct tplg_ref *ref;
|
|
Packit |
4a16fb |
struct list_head *base, *pos;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
base = &elem->ref_list;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
list_for_each(pos, base) {
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
ref = list_entry(pos, struct tplg_ref, list);
|
|
Packit |
4a16fb |
if (ref->elem)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (ref->type == SND_TPLG_TYPE_DATA) {
|
|
Packit |
4a16fb |
err = tplg_copy_data(tplg, elem, ref);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
int tplg_build_controls(snd_tplg_t *tplg)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
struct list_head *base, *pos;
|
|
Packit |
4a16fb |
struct tplg_elem *elem;
|
|
Packit |
4a16fb |
int err = 0;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
base = &tplg->mixer_list;
|
|
Packit |
4a16fb |
list_for_each(pos, base) {
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
elem = list_entry(pos, struct tplg_elem, list);
|
|
Packit |
4a16fb |
err = tplg_build_mixer_control(tplg, elem);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* add control to manifest */
|
|
Packit |
4a16fb |
tplg->manifest.control_elems++;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
base = &tplg->enum_list;
|
|
Packit |
4a16fb |
list_for_each(pos, base) {
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
elem = list_entry(pos, struct tplg_elem, list);
|
|
Packit |
4a16fb |
err = tplg_build_enum_control(tplg, elem);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* add control to manifest */
|
|
Packit |
4a16fb |
tplg->manifest.control_elems++;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
base = &tplg->bytes_ext_list;
|
|
Packit |
4a16fb |
list_for_each(pos, base) {
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
elem = list_entry(pos, struct tplg_elem, list);
|
|
Packit |
4a16fb |
err = tplg_build_bytes_control(tplg, elem);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* add control to manifest */
|
|
Packit |
4a16fb |
tplg->manifest.control_elems++;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
* Parse TLV of DBScale type.
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* Parse DBScale describing min, step, mute in DB.
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_config_iterator_t i, next;
|
|
Packit |
4a16fb |
snd_config_t *n;
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_ctl_tlv *tplg_tlv = elem->tlv;
|
|
Packit |
4a16fb |
struct snd_soc_tplg_tlv_dbscale *scale;
|
|
Packit Service |
f36a15 |
const char *id = NULL;
|
|
Packit Service |
f36a15 |
int val;
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
tplg_dbg(" scale: %s", elem->id);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
tplg_tlv->size = sizeof(struct snd_soc_tplg_ctl_tlv);
|
|
Packit |
4a16fb |
tplg_tlv->type = SNDRV_CTL_TLVT_DB_SCALE;
|
|
Packit |
4a16fb |
scale = &tplg_tlv->scale;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_config_for_each(i, next, cfg) {
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
n = snd_config_iterator_entry(i);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* get ID */
|
|
Packit Service |
f36a15 |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* get value */
|
|
Packit Service |
f36a15 |
if (tplg_get_integer(n, &val, 0))
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
tplg_dbg("\t%s = %i", id, val);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* get TLV data */
|
|
Packit |
4a16fb |
if (strcmp(id, "min") == 0)
|
|
Packit Service |
f36a15 |
scale->min = val;
|
|
Packit |
4a16fb |
else if (strcmp(id, "step") == 0)
|
|
Packit Service |
f36a15 |
scale->step = val;
|
|
Packit |
4a16fb |
else if (strcmp(id, "mute") == 0)
|
|
Packit Service |
f36a15 |
scale->mute = val;
|
|
Packit |
4a16fb |
else
|
|
Packit Service |
f36a15 |
SNDERR("unknown id '%s'", id);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* Parse TLV */
|
|
Packit |
4a16fb |
int tplg_parse_tlv(snd_tplg_t *tplg, snd_config_t *cfg,
|
|
Packit Service |
f36a15 |
void *private ATTRIBUTE_UNUSED)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_config_iterator_t i, next;
|
|
Packit |
4a16fb |
snd_config_t *n;
|
|
Packit |
4a16fb |
const char *id;
|
|
Packit |
4a16fb |
int err = 0;
|
|
Packit |
4a16fb |
struct tplg_elem *elem;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_TLV);
|
|
Packit |
4a16fb |
if (!elem)
|
|
Packit |
4a16fb |
return -ENOMEM;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_config_for_each(i, next, cfg) {
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
n = snd_config_iterator_entry(i);
|
|
Packit |
4a16fb |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "scale") == 0) {
|
|
Packit |
4a16fb |
err = tplg_parse_tlv_dbscale(n, elem);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit Service |
f36a15 |
SNDERR("failed to DBScale");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
/* save TLV data */
|
|
Packit Service |
f36a15 |
int tplg_save_tlv(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
|
|
Packit Service |
f36a15 |
struct tplg_elem *elem,
|
|
Packit Service |
f36a15 |
char **dst, const char *pfx)
|
|
Packit Service |
f36a15 |
{
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_ctl_tlv *tlv = elem->tlv;
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_tlv_dbscale *scale;
|
|
Packit Service |
f36a15 |
int err;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
if (tlv->type != SNDRV_CTL_TLVT_DB_SCALE) {
|
|
Packit Service |
f36a15 |
SNDERR("unknown TLV type");
|
|
Packit Service |
f36a15 |
return -EINVAL;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
scale = &tlv->scale;
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "\tscale {\n");
|
|
Packit Service |
f36a15 |
if (err >= 0 && scale->min)
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "\t\tmin %i\n", scale->min);
|
|
Packit Service |
f36a15 |
if (err >= 0 && scale->step > 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "\t\tstep %i\n", scale->step);
|
|
Packit Service |
f36a15 |
if (err >= 0 && scale->mute > 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "\t\tmute %i\n", scale->mute);
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "\t}\n");
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "}\n");
|
|
Packit Service |
f36a15 |
return err;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit |
4a16fb |
/* Parse Control Bytes */
|
|
Packit |
4a16fb |
int tplg_parse_control_bytes(snd_tplg_t *tplg,
|
|
Packit Service |
f36a15 |
snd_config_t *cfg,
|
|
Packit Service |
f36a15 |
void *private ATTRIBUTE_UNUSED)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
struct snd_soc_tplg_bytes_control *be;
|
|
Packit |
4a16fb |
struct tplg_elem *elem;
|
|
Packit |
4a16fb |
snd_config_iterator_t i, next;
|
|
Packit |
4a16fb |
snd_config_t *n;
|
|
Packit |
4a16fb |
const char *id, *val = NULL;
|
|
Packit Service |
f36a15 |
int err, ival;
|
|
Packit |
4a16fb |
bool access_set = false, tlv_set = false;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_BYTES);
|
|
Packit |
4a16fb |
if (!elem)
|
|
Packit |
4a16fb |
return -ENOMEM;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
be = elem->bytes_ext;
|
|
Packit |
4a16fb |
be->size = elem->size;
|
|
Packit |
4a16fb |
snd_strlcpy(be->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
|
|
Packit |
4a16fb |
be->hdr.type = SND_SOC_TPLG_TYPE_BYTES;
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
tplg_dbg(" Control Bytes: %s", elem->id);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_config_for_each(i, next, cfg) {
|
|
Packit |
4a16fb |
n = snd_config_iterator_entry(i);
|
|
Packit |
4a16fb |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* skip comments */
|
|
Packit |
4a16fb |
if (strcmp(id, "comment") == 0)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
if (id[0] == '#')
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "base") == 0) {
|
|
Packit Service |
f36a15 |
if (tplg_get_integer(n, &ival, 0))
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
be->base = ival;
|
|
Packit Service |
f36a15 |
tplg_dbg("\t%s: %d", id, be->base);
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "num_regs") == 0) {
|
|
Packit Service |
f36a15 |
if (tplg_get_integer(n, &ival, 0))
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
be->num_regs = ival;
|
|
Packit Service |
f36a15 |
tplg_dbg("\t%s: %d", id, be->num_regs);
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "max") == 0) {
|
|
Packit Service |
f36a15 |
if (tplg_get_integer(n, &ival, 0))
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
be->max = ival;
|
|
Packit Service |
f36a15 |
tplg_dbg("\t%s: %d", id, be->max);
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "mask") == 0) {
|
|
Packit Service |
f36a15 |
if (tplg_get_integer(n, &ival, 16))
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
be->mask = ival;
|
|
Packit Service |
f36a15 |
tplg_dbg("\t%s: %d", id, be->mask);
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "data") == 0) {
|
|
Packit Service |
f36a15 |
err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "tlv") == 0) {
|
|
Packit |
4a16fb |
if (snd_config_get_string(n, &val) < 0)
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
err = tplg_ref_add(elem, SND_TPLG_TYPE_TLV, val);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
tlv_set = true;
|
|
Packit Service |
f36a15 |
tplg_dbg("\t%s: %s", id, val);
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "ops") == 0) {
|
|
Packit |
4a16fb |
err = tplg_parse_compound(tplg, n, tplg_parse_ops,
|
|
Packit |
4a16fb |
&be->hdr);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "extops") == 0) {
|
|
Packit |
4a16fb |
err = tplg_parse_compound(tplg, n, tplg_parse_ext_ops,
|
|
Packit |
4a16fb |
be);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "access") == 0) {
|
|
Packit |
4a16fb |
err = parse_access(cfg, &be->hdr);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
access_set = true;
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* set CTL access to default values if none are provided */
|
|
Packit |
4a16fb |
if (!access_set) {
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
be->hdr.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
|
|
Packit |
4a16fb |
if (tlv_set)
|
|
Packit |
4a16fb |
be->hdr.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
/* save control bytes */
|
|
Packit Service |
f36a15 |
int tplg_save_control_bytes(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
|
|
Packit Service |
f36a15 |
struct tplg_elem *elem,
|
|
Packit Service |
f36a15 |
char **dst, const char *pfx)
|
|
Packit Service |
f36a15 |
{
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_bytes_control *be = elem->bytes_ext;
|
|
Packit Service |
f36a15 |
char pfx2[16];
|
|
Packit Service |
f36a15 |
int err;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
if (!be)
|
|
Packit Service |
f36a15 |
return 0;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
|
|
Packit Service |
f36a15 |
if (err < 0)
|
|
Packit Service |
f36a15 |
return err;
|
|
Packit Service |
f36a15 |
if (err >= 0 && elem->index > 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "\tindex %u\n", elem->index);
|
|
Packit Service |
f36a15 |
if (err >= 0 && be->base > 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "\tbase %u\n", be->base);
|
|
Packit Service |
f36a15 |
if (err >= 0 && be->num_regs > 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "\tnum_regs %u\n", be->num_regs);
|
|
Packit Service |
f36a15 |
if (err >= 0 && be->max > 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "\tmax %u\n", be->max);
|
|
Packit Service |
f36a15 |
if (err >= 0 && be->mask > 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "\tmask %u\n", be->mask);
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_ops(tplg, &be->hdr, dst, pfx2);
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_ext_ops(tplg, be, dst, pfx2);
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_access(tplg, &be->hdr, dst, pfx2);
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_TLV,
|
|
Packit Service |
f36a15 |
"tlv", dst, pfx2);
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA,
|
|
Packit Service |
f36a15 |
"data", dst, pfx2);
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "}\n");
|
|
Packit Service |
f36a15 |
return err;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit |
4a16fb |
/* Parse Control Enums. */
|
|
Packit |
4a16fb |
int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg,
|
|
Packit Service |
f36a15 |
void *private ATTRIBUTE_UNUSED)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
struct snd_soc_tplg_enum_control *ec;
|
|
Packit |
4a16fb |
struct tplg_elem *elem;
|
|
Packit |
4a16fb |
snd_config_iterator_t i, next;
|
|
Packit |
4a16fb |
snd_config_t *n;
|
|
Packit |
4a16fb |
const char *id, *val = NULL;
|
|
Packit |
4a16fb |
int err, j;
|
|
Packit |
4a16fb |
bool access_set = false;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_ENUM);
|
|
Packit |
4a16fb |
if (!elem)
|
|
Packit |
4a16fb |
return -ENOMEM;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
ec = elem->enum_ctrl;
|
|
Packit |
4a16fb |
snd_strlcpy(ec->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
|
|
Packit |
4a16fb |
ec->hdr.type = SND_SOC_TPLG_TYPE_ENUM;
|
|
Packit |
4a16fb |
ec->size = elem->size;
|
|
Packit |
4a16fb |
tplg->channel_idx = 0;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* set channel reg to default state */
|
|
Packit Service |
f36a15 |
for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++) {
|
|
Packit |
4a16fb |
ec->channel[j].reg = -1;
|
|
Packit Service |
f36a15 |
}
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
tplg_dbg(" Control Enum: %s", elem->id);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_config_for_each(i, next, cfg) {
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
n = snd_config_iterator_entry(i);
|
|
Packit |
4a16fb |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* skip comments */
|
|
Packit |
4a16fb |
if (strcmp(id, "comment") == 0)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
if (id[0] == '#')
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "texts") == 0) {
|
|
Packit |
4a16fb |
if (snd_config_get_string(n, &val) < 0)
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
tplg_ref_add(elem, SND_TPLG_TYPE_TEXT, val);
|
|
Packit Service |
f36a15 |
tplg_dbg("\t%s: %s", id, val);
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "channel") == 0) {
|
|
Packit |
4a16fb |
if (ec->num_channels >= SND_SOC_TPLG_MAX_CHAN) {
|
|
Packit Service |
f36a15 |
SNDERR("too many channels %s", elem->id);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
err = tplg_parse_compound(tplg, n, tplg_parse_channel,
|
|
Packit |
4a16fb |
ec->channel);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
ec->num_channels = tplg->channel_idx;
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "ops") == 0) {
|
|
Packit |
4a16fb |
err = tplg_parse_compound(tplg, n, tplg_parse_ops,
|
|
Packit |
4a16fb |
&ec->hdr);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "data") == 0) {
|
|
Packit Service |
f36a15 |
err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "access") == 0) {
|
|
Packit |
4a16fb |
err = parse_access(cfg, &ec->hdr);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
access_set = true;
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* set CTL access to default values if none are provided */
|
|
Packit |
4a16fb |
if (!access_set) {
|
|
Packit |
4a16fb |
ec->hdr.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
/* save control eunm */
|
|
Packit Service |
f36a15 |
int tplg_save_control_enum(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
|
|
Packit Service |
f36a15 |
struct tplg_elem *elem,
|
|
Packit Service |
f36a15 |
char **dst, const char *pfx)
|
|
Packit Service |
f36a15 |
{
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_enum_control *ec = elem->enum_ctrl;
|
|
Packit Service |
f36a15 |
char pfx2[16];
|
|
Packit Service |
f36a15 |
int err;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
if (!ec)
|
|
Packit Service |
f36a15 |
return 0;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
|
|
Packit Service |
f36a15 |
if (err < 0)
|
|
Packit Service |
f36a15 |
return err;
|
|
Packit Service |
f36a15 |
if (err >= 0 && elem->index > 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "\tindex %u\n", elem->index);
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_TEXT,
|
|
Packit Service |
f36a15 |
"texts", dst, pfx2);
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_channels(tplg, ec->channel, ec->num_channels,
|
|
Packit Service |
f36a15 |
dst, pfx2);
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_ops(tplg, &ec->hdr, dst, pfx2);
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_access(tplg, &ec->hdr, dst, pfx2);
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA,
|
|
Packit Service |
f36a15 |
"data", dst, pfx2);
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "}\n");
|
|
Packit Service |
f36a15 |
return err;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit |
4a16fb |
/* Parse Controls.
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* Mixer control. Supports multiple channels.
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
int tplg_parse_control_mixer(snd_tplg_t *tplg,
|
|
Packit Service |
f36a15 |
snd_config_t *cfg,
|
|
Packit Service |
f36a15 |
void *private ATTRIBUTE_UNUSED)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
struct snd_soc_tplg_mixer_control *mc;
|
|
Packit |
4a16fb |
struct tplg_elem *elem;
|
|
Packit |
4a16fb |
snd_config_iterator_t i, next;
|
|
Packit |
4a16fb |
snd_config_t *n;
|
|
Packit |
4a16fb |
const char *id, *val = NULL;
|
|
Packit Service |
f36a15 |
int err, j, ival;
|
|
Packit |
4a16fb |
bool access_set = false, tlv_set = false;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_MIXER);
|
|
Packit |
4a16fb |
if (!elem)
|
|
Packit |
4a16fb |
return -ENOMEM;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* init new mixer */
|
|
Packit |
4a16fb |
mc = elem->mixer_ctrl;
|
|
Packit |
4a16fb |
snd_strlcpy(mc->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
|
|
Packit |
4a16fb |
mc->hdr.type = SND_SOC_TPLG_TYPE_MIXER;
|
|
Packit |
4a16fb |
mc->size = elem->size;
|
|
Packit |
4a16fb |
tplg->channel_idx = 0;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* set channel reg to default state */
|
|
Packit |
4a16fb |
for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++)
|
|
Packit |
4a16fb |
mc->channel[j].reg = -1;
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
tplg_dbg(" Control Mixer: %s", elem->id);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* giterate trough each mixer elment */
|
|
Packit |
4a16fb |
snd_config_for_each(i, next, cfg) {
|
|
Packit |
4a16fb |
n = snd_config_iterator_entry(i);
|
|
Packit |
4a16fb |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* skip comments */
|
|
Packit |
4a16fb |
if (strcmp(id, "comment") == 0)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
if (id[0] == '#')
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "channel") == 0) {
|
|
Packit |
4a16fb |
if (mc->num_channels >= SND_SOC_TPLG_MAX_CHAN) {
|
|
Packit Service |
f36a15 |
SNDERR("too many channels %s", elem->id);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
err = tplg_parse_compound(tplg, n, tplg_parse_channel,
|
|
Packit |
4a16fb |
mc->channel);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
mc->num_channels = tplg->channel_idx;
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "max") == 0) {
|
|
Packit Service |
f36a15 |
if (tplg_get_integer(n, &ival, 0))
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
mc->max = ival;
|
|
Packit Service |
f36a15 |
tplg_dbg("\t%s: %d", id, mc->max);
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "invert") == 0) {
|
|
Packit Service |
f36a15 |
ival = snd_config_get_bool(n);
|
|
Packit Service |
f36a15 |
if (ival < 0)
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit Service |
f36a15 |
mc->invert = ival;
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
tplg_dbg("\t%s: %d", id, mc->invert);
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "ops") == 0) {
|
|
Packit |
4a16fb |
err = tplg_parse_compound(tplg, n, tplg_parse_ops,
|
|
Packit |
4a16fb |
&mc->hdr);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "tlv") == 0) {
|
|
Packit |
4a16fb |
if (snd_config_get_string(n, &val) < 0)
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
err = tplg_ref_add(elem, SND_TPLG_TYPE_TLV, val);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
tlv_set = true;
|
|
Packit Service |
f36a15 |
tplg_dbg("\t%s: %s", id, val);
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "data") == 0) {
|
|
Packit Service |
f36a15 |
err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "access") == 0) {
|
|
Packit |
4a16fb |
err = parse_access(cfg, &mc->hdr);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
access_set = true;
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* set CTL access to default values if none are provided */
|
|
Packit |
4a16fb |
if (!access_set) {
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
mc->hdr.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
|
|
Packit |
4a16fb |
if (tlv_set)
|
|
Packit |
4a16fb |
mc->hdr.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
int tplg_save_control_mixer(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
|
|
Packit Service |
f36a15 |
struct tplg_elem *elem, char **dst,
|
|
Packit Service |
f36a15 |
const char *pfx)
|
|
Packit |
4a16fb |
{
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_mixer_control *mc = elem->mixer_ctrl;
|
|
Packit Service |
f36a15 |
char pfx2[16];
|
|
Packit Service |
f36a15 |
int err;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
if (!mc)
|
|
Packit Service |
f36a15 |
return 0;
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
|
|
Packit Service |
f36a15 |
if (err < 0)
|
|
Packit Service |
f36a15 |
return err;
|
|
Packit Service |
f36a15 |
snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
|
|
Packit Service |
f36a15 |
if (err >= 0 && elem->index > 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "\tindex %u\n", elem->index);
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_channels(tplg, mc->channel, mc->num_channels,
|
|
Packit Service |
f36a15 |
dst, pfx2);
|
|
Packit Service |
f36a15 |
if (err >= 0 && mc->max > 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "\tmax %u\n", mc->max);
|
|
Packit Service |
f36a15 |
if (err >= 0 && mc->invert > 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "\tinvert 1\n", mc->max);
|
|
Packit Service |
f36a15 |
if (err >= 0 && mc->invert > 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "\tinvert 1\n", mc->max);
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_ops(tplg, &mc->hdr, dst, pfx2);
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_access(tplg, &mc->hdr, dst, pfx2);
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_TLV,
|
|
Packit Service |
f36a15 |
"tlv", dst, pfx2);
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA,
|
|
Packit Service |
f36a15 |
"data", dst, pfx2);
|
|
Packit Service |
f36a15 |
if (err >= 0)
|
|
Packit Service |
f36a15 |
err = tplg_save_printf(dst, pfx, "}\n");
|
|
Packit Service |
f36a15 |
return err;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
static int init_ctl_hdr(snd_tplg_t *tplg,
|
|
Packit Service |
f36a15 |
struct tplg_elem *parent,
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_ctl_hdr *hdr,
|
|
Packit Service |
f36a15 |
struct snd_tplg_ctl_template *t)
|
|
Packit Service |
f36a15 |
{
|
|
Packit Service |
f36a15 |
struct tplg_elem *elem;
|
|
Packit Service |
f36a15 |
int err;
|
|
Packit Service |
f36a15 |
|
|
Packit |
4a16fb |
hdr->size = sizeof(struct snd_soc_tplg_ctl_hdr);
|
|
Packit |
4a16fb |
hdr->type = t->type;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_strlcpy(hdr->name, t->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* clean up access flag */
|
|
Packit |
4a16fb |
if (t->access == 0)
|
|
Packit |
4a16fb |
t->access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
|
|
Packit |
4a16fb |
t->access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
|
Packit |
4a16fb |
SNDRV_CTL_ELEM_ACCESS_VOLATILE |
|
|
Packit |
4a16fb |
SNDRV_CTL_ELEM_ACCESS_INACTIVE |
|
|
Packit |
4a16fb |
SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE |
|
|
Packit |
4a16fb |
SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND |
|
|
Packit |
4a16fb |
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
hdr->access = t->access;
|
|
Packit |
4a16fb |
hdr->ops.get = t->ops.get;
|
|
Packit |
4a16fb |
hdr->ops.put = t->ops.put;
|
|
Packit |
4a16fb |
hdr->ops.info = t->ops.info;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* TLV */
|
|
Packit |
4a16fb |
if (hdr->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE
|
|
Packit |
4a16fb |
&& !(hdr->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)) {
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
struct snd_tplg_tlv_template *tlvt = t->tlv;
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_ctl_tlv *tlv;
|
|
Packit |
4a16fb |
struct snd_tplg_tlv_dbscale_template *scalet;
|
|
Packit |
4a16fb |
struct snd_soc_tplg_tlv_dbscale *scale;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (!tlvt) {
|
|
Packit Service |
f36a15 |
SNDERR("missing TLV data");
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
elem = tplg_elem_new_common(tplg, NULL, parent->id,
|
|
Packit Service |
f36a15 |
SND_TPLG_TYPE_TLV);
|
|
Packit Service |
f36a15 |
if (!elem)
|
|
Packit Service |
f36a15 |
return -ENOMEM;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
tlv = elem->tlv;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
err = tplg_ref_add(parent, SND_TPLG_TYPE_TLV, parent->id);
|
|
Packit Service |
f36a15 |
if (err < 0)
|
|
Packit Service |
f36a15 |
return err;
|
|
Packit Service |
f36a15 |
|
|
Packit |
4a16fb |
tlv->size = sizeof(struct snd_soc_tplg_ctl_tlv);
|
|
Packit |
4a16fb |
tlv->type = tlvt->type;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
switch (tlvt->type) {
|
|
Packit |
4a16fb |
case SNDRV_CTL_TLVT_DB_SCALE:
|
|
Packit |
4a16fb |
scalet = container_of(tlvt,
|
|
Packit |
4a16fb |
struct snd_tplg_tlv_dbscale_template, hdr);
|
|
Packit |
4a16fb |
scale = &tlv->scale;
|
|
Packit |
4a16fb |
scale->min = scalet->min;
|
|
Packit |
4a16fb |
scale->step = scalet->step;
|
|
Packit |
4a16fb |
scale->mute = scalet->mute;
|
|
Packit |
4a16fb |
break;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* TODO: add support for other TLV types */
|
|
Packit |
4a16fb |
default:
|
|
Packit Service |
f36a15 |
SNDERR("unsupported TLV type %d", tlv->type);
|
|
Packit |
4a16fb |
break;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
int tplg_add_mixer(snd_tplg_t *tplg, struct snd_tplg_mixer_template *mixer,
|
|
Packit Service |
f36a15 |
struct tplg_elem **e)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
struct snd_soc_tplg_mixer_control *mc;
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_private *priv;
|
|
Packit |
4a16fb |
struct tplg_elem *elem;
|
|
Packit |
4a16fb |
int ret, i, num_channels;
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
tplg_dbg(" Control Mixer: %s", mixer->hdr.name);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (mixer->hdr.type != SND_SOC_TPLG_TYPE_MIXER) {
|
|
Packit Service |
f36a15 |
SNDERR("invalid mixer type %d", mixer->hdr.type);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
elem = tplg_elem_new_common(tplg, NULL, mixer->hdr.name,
|
|
Packit |
4a16fb |
SND_TPLG_TYPE_MIXER);
|
|
Packit |
4a16fb |
if (!elem)
|
|
Packit |
4a16fb |
return -ENOMEM;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* init new mixer */
|
|
Packit |
4a16fb |
mc = elem->mixer_ctrl;
|
|
Packit |
4a16fb |
mc->size = elem->size;
|
|
Packit Service |
f36a15 |
ret = init_ctl_hdr(tplg, elem, &mc->hdr, &mixer->hdr);
|
|
Packit |
4a16fb |
if (ret < 0) {
|
|
Packit |
4a16fb |
tplg_elem_free(elem);
|
|
Packit |
4a16fb |
return ret;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
mc->min = mixer->min;
|
|
Packit |
4a16fb |
mc->max = mixer->max;
|
|
Packit |
4a16fb |
mc->platform_max = mixer->platform_max;
|
|
Packit |
4a16fb |
mc->invert = mixer->invert;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* set channel reg to default state */
|
|
Packit |
4a16fb |
for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++)
|
|
Packit |
4a16fb |
mc->channel[i].reg = -1;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
num_channels = mixer->map ? mixer->map->num_channels : 0;
|
|
Packit |
4a16fb |
mc->num_channels = num_channels;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
for (i = 0; i < num_channels; i++) {
|
|
Packit |
4a16fb |
struct snd_tplg_channel_elem *channel = &mixer->map->channel[i];
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
mc->channel[i].size = sizeof(mc->channel[0]);
|
|
Packit |
4a16fb |
mc->channel[i].reg = channel->reg;
|
|
Packit |
4a16fb |
mc->channel[i].shift = channel->shift;
|
|
Packit |
4a16fb |
mc->channel[i].id = channel->id;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* priv data */
|
|
Packit Service |
f36a15 |
priv = mixer->priv;
|
|
Packit Service |
f36a15 |
if (priv && priv->size > 0) {
|
|
Packit Service |
f36a15 |
ret = tplg_add_data(tplg, elem, priv,
|
|
Packit Service |
f36a15 |
sizeof(*priv) + priv->size);
|
|
Packit Service |
f36a15 |
if (ret < 0)
|
|
Packit Service |
f36a15 |
return ret;
|
|
Packit Service |
f36a15 |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (e)
|
|
Packit |
4a16fb |
*e = elem;
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
int tplg_add_enum(snd_tplg_t *tplg, struct snd_tplg_enum_template *enum_ctl,
|
|
Packit Service |
f36a15 |
struct tplg_elem **e)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
struct snd_soc_tplg_enum_control *ec;
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_private *priv;
|
|
Packit |
4a16fb |
struct tplg_elem *elem;
|
|
Packit Service |
f36a15 |
int ret, i, num_items, num_channels;
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
tplg_dbg(" Control Enum: %s", enum_ctl->hdr.name);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (enum_ctl->hdr.type != SND_SOC_TPLG_TYPE_ENUM) {
|
|
Packit Service |
f36a15 |
SNDERR("invalid enum type %d", enum_ctl->hdr.type);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
elem = tplg_elem_new_common(tplg, NULL, enum_ctl->hdr.name,
|
|
Packit |
4a16fb |
SND_TPLG_TYPE_ENUM);
|
|
Packit |
4a16fb |
if (!elem)
|
|
Packit |
4a16fb |
return -ENOMEM;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
ec = elem->enum_ctrl;
|
|
Packit |
4a16fb |
ec->size = elem->size;
|
|
Packit Service |
f36a15 |
ret = init_ctl_hdr(tplg, elem, &ec->hdr, &enum_ctl->hdr);
|
|
Packit |
4a16fb |
if (ret < 0) {
|
|
Packit |
4a16fb |
tplg_elem_free(elem);
|
|
Packit |
4a16fb |
return ret;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
num_items = enum_ctl->items < SND_SOC_TPLG_NUM_TEXTS ?
|
|
Packit |
4a16fb |
enum_ctl->items : SND_SOC_TPLG_NUM_TEXTS;
|
|
Packit |
4a16fb |
ec->items = num_items;
|
|
Packit |
4a16fb |
ec->mask = enum_ctl->mask;
|
|
Packit |
4a16fb |
ec->count = enum_ctl->items;
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
/* set channel reg to default state */
|
|
Packit Service |
f36a15 |
for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++)
|
|
Packit Service |
f36a15 |
ec->channel[i].reg = -1;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
num_channels = enum_ctl->map ? enum_ctl->map->num_channels : 0;
|
|
Packit Service |
f36a15 |
ec->num_channels = num_channels;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
for (i = 0; i < num_channels; i++) {
|
|
Packit Service |
f36a15 |
struct snd_tplg_channel_elem *channel = &enum_ctl->map->channel[i];
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
ec->channel[i].size = sizeof(ec->channel[0]);
|
|
Packit Service |
f36a15 |
ec->channel[i].reg = channel->reg;
|
|
Packit Service |
f36a15 |
ec->channel[i].shift = channel->shift;
|
|
Packit Service |
f36a15 |
ec->channel[i].id = channel->id;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit |
4a16fb |
if (enum_ctl->texts != NULL) {
|
|
Packit |
4a16fb |
for (i = 0; i < num_items; i++) {
|
|
Packit |
4a16fb |
if (enum_ctl->texts[i] != NULL)
|
|
Packit |
4a16fb |
snd_strlcpy(ec->texts[i], enum_ctl->texts[i],
|
|
Packit |
4a16fb |
SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (enum_ctl->values != NULL) {
|
|
Packit |
4a16fb |
for (i = 0; i < num_items; i++) {
|
|
Packit |
4a16fb |
if (enum_ctl->values[i] == NULL)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
memcpy(&ec->values[i * sizeof(int) * ENUM_VAL_SIZE],
|
|
Packit |
4a16fb |
enum_ctl->values[i],
|
|
Packit |
4a16fb |
sizeof(int) * ENUM_VAL_SIZE);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
/* priv data */
|
|
Packit Service |
f36a15 |
priv = enum_ctl->priv;
|
|
Packit Service |
f36a15 |
if (priv && priv->size > 0) {
|
|
Packit Service |
f36a15 |
ret = tplg_add_data(tplg, elem, priv,
|
|
Packit Service |
f36a15 |
sizeof(*priv) + priv->size);
|
|
Packit Service |
f36a15 |
if (ret < 0)
|
|
Packit Service |
f36a15 |
return ret;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (e)
|
|
Packit |
4a16fb |
*e = elem;
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
int tplg_add_bytes(snd_tplg_t *tplg, struct snd_tplg_bytes_template *bytes_ctl,
|
|
Packit Service |
f36a15 |
struct tplg_elem **e)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
struct snd_soc_tplg_bytes_control *be;
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_private *priv;
|
|
Packit |
4a16fb |
struct tplg_elem *elem;
|
|
Packit |
4a16fb |
int ret;
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
tplg_dbg(" Control Bytes: %s", bytes_ctl->hdr.name);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (bytes_ctl->hdr.type != SND_SOC_TPLG_TYPE_BYTES) {
|
|
Packit Service |
f36a15 |
SNDERR("invalid bytes type %d", bytes_ctl->hdr.type);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
elem = tplg_elem_new_common(tplg, NULL, bytes_ctl->hdr.name,
|
|
Packit |
4a16fb |
SND_TPLG_TYPE_BYTES);
|
|
Packit |
4a16fb |
if (!elem)
|
|
Packit |
4a16fb |
return -ENOMEM;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
be = elem->bytes_ext;
|
|
Packit |
4a16fb |
be->size = elem->size;
|
|
Packit Service |
f36a15 |
ret = init_ctl_hdr(tplg, elem, &be->hdr, &bytes_ctl->hdr);
|
|
Packit |
4a16fb |
if (ret < 0) {
|
|
Packit |
4a16fb |
tplg_elem_free(elem);
|
|
Packit |
4a16fb |
return ret;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
be->max = bytes_ctl->max;
|
|
Packit |
4a16fb |
be->mask = bytes_ctl->mask;
|
|
Packit |
4a16fb |
be->base = bytes_ctl->base;
|
|
Packit |
4a16fb |
be->num_regs = bytes_ctl->num_regs;
|
|
Packit |
4a16fb |
be->ext_ops.put = bytes_ctl->ext_ops.put;
|
|
Packit |
4a16fb |
be->ext_ops.get = bytes_ctl->ext_ops.get;
|
|
Packit |
4a16fb |
|
|
Packit Service |
f36a15 |
/* priv data */
|
|
Packit Service |
f36a15 |
priv = bytes_ctl->priv;
|
|
Packit Service |
f36a15 |
if (priv && priv->size > 0) {
|
|
Packit Service |
f36a15 |
ret = tplg_add_data(tplg, elem, priv,
|
|
Packit Service |
f36a15 |
sizeof(*priv) + priv->size);
|
|
Packit Service |
f36a15 |
if (ret < 0)
|
|
Packit Service |
f36a15 |
return ret;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* check on TLV bytes control */
|
|
Packit |
4a16fb |
if (be->hdr.access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
|
|
Packit |
4a16fb |
if ((be->hdr.access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)
|
|
Packit |
4a16fb |
!= SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
|
|
Packit Service |
f36a15 |
SNDERR("Invalid TLV bytes control access 0x%x",
|
|
Packit |
4a16fb |
be->hdr.access);
|
|
Packit |
4a16fb |
tplg_elem_free(elem);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (!be->max) {
|
|
Packit |
4a16fb |
tplg_elem_free(elem);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (e)
|
|
Packit |
4a16fb |
*e = elem;
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
int tplg_add_mixer_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
return tplg_add_mixer(tplg, t->mixer, NULL);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
int tplg_add_enum_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
return tplg_add_enum(tplg, t->enum_ctl, NULL);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
int tplg_add_bytes_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
return tplg_add_bytes(tplg, t->bytes_ctl, NULL);
|
|
Packit |
4a16fb |
}
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
int tplg_decode_control_mixer1(snd_tplg_t *tplg,
|
|
Packit Service |
f36a15 |
struct list_head *heap,
|
|
Packit Service |
f36a15 |
struct snd_tplg_mixer_template *mt,
|
|
Packit Service |
f36a15 |
size_t pos,
|
|
Packit Service |
f36a15 |
void *bin, size_t size)
|
|
Packit Service |
f36a15 |
{
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_mixer_control *mc = bin;
|
|
Packit Service |
f36a15 |
struct snd_tplg_channel_map_template *map;
|
|
Packit Service |
f36a15 |
struct snd_tplg_tlv_dbscale_template *db;
|
|
Packit Service |
f36a15 |
int i;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
if (size < sizeof(*mc)) {
|
|
Packit Service |
f36a15 |
SNDERR("mixer: small size %d", size);
|
|
Packit Service |
f36a15 |
return -EINVAL;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
tplg_log(tplg, 'D', pos, "mixer: size %d TLV size %d private size %d",
|
|
Packit Service |
f36a15 |
mc->size, mc->hdr.tlv.size, mc->priv.size);
|
|
Packit Service |
f36a15 |
if (size != mc->size + mc->priv.size) {
|
|
Packit Service |
f36a15 |
SNDERR("mixer: unexpected element size %d", size);
|
|
Packit Service |
f36a15 |
return -EINVAL;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
memset(mt, 0, sizeof(*mt));
|
|
Packit Service |
f36a15 |
mt->hdr.type = mc->hdr.type;
|
|
Packit Service |
f36a15 |
mt->hdr.name = mc->hdr.name;
|
|
Packit Service |
f36a15 |
mt->hdr.access = mc->hdr.access;
|
|
Packit Service |
f36a15 |
mt->hdr.ops.get = mc->hdr.ops.get;
|
|
Packit Service |
f36a15 |
mt->hdr.ops.put = mc->hdr.ops.put;
|
|
Packit Service |
f36a15 |
mt->hdr.ops.info = mc->hdr.ops.info;
|
|
Packit Service |
f36a15 |
mt->min = mc->min;
|
|
Packit Service |
f36a15 |
mt->max = mc->max;
|
|
Packit Service |
f36a15 |
mt->platform_max = mc->platform_max;
|
|
Packit Service |
f36a15 |
tplg_log(tplg, 'D', pos, "mixer: name '%s' access 0x%x",
|
|
Packit Service |
f36a15 |
mt->hdr.name, mt->hdr.access);
|
|
Packit Service |
f36a15 |
if (mc->num_channels > 0) {
|
|
Packit Service |
f36a15 |
map = tplg_calloc(heap, sizeof(*map));
|
|
Packit Service |
f36a15 |
map->num_channels = mc->num_channels;
|
|
Packit Service |
f36a15 |
for (i = 0; i < map->num_channels; i++) {
|
|
Packit Service |
f36a15 |
map->channel[i].reg = mc->channel[i].reg;
|
|
Packit Service |
f36a15 |
map->channel[i].shift = mc->channel[i].shift;
|
|
Packit Service |
f36a15 |
map->channel[i].id = mc->channel[i].id;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
mt->map = map;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
if (mc->hdr.tlv.size == 0) {
|
|
Packit Service |
f36a15 |
/* nothing */
|
|
Packit Service |
f36a15 |
} else if (mc->hdr.tlv.size == sizeof(struct snd_soc_tplg_ctl_tlv)) {
|
|
Packit Service |
f36a15 |
if (mc->hdr.tlv.type != SNDRV_CTL_TLVT_DB_SCALE) {
|
|
Packit Service |
f36a15 |
SNDERR("mixer: unknown TLV type %d",
|
|
Packit Service |
f36a15 |
mc->hdr.tlv.type);
|
|
Packit Service |
f36a15 |
return -EINVAL;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
db = tplg_calloc(heap, sizeof(*db));
|
|
Packit Service |
f36a15 |
if (db == NULL)
|
|
Packit Service |
f36a15 |
return -ENOMEM;
|
|
Packit Service |
f36a15 |
mt->hdr.tlv_scale = db;
|
|
Packit Service |
f36a15 |
db->hdr.type = mc->hdr.tlv.type;
|
|
Packit Service |
f36a15 |
db->min = mc->hdr.tlv.scale.min;
|
|
Packit Service |
f36a15 |
db->step = mc->hdr.tlv.scale.step;
|
|
Packit Service |
f36a15 |
db->mute = mc->hdr.tlv.scale.mute;
|
|
Packit Service |
f36a15 |
tplg_log(tplg, 'D', pos, "mixer: dB scale TLV: min %d step %d mute %d",
|
|
Packit Service |
f36a15 |
db->min, db->step, db->mute);
|
|
Packit Service |
f36a15 |
} else {
|
|
Packit Service |
f36a15 |
SNDERR("mixer: wrong TLV size %d", mc->hdr.tlv.size);
|
|
Packit Service |
f36a15 |
return -EINVAL;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
mt->priv = &mc->priv;
|
|
Packit Service |
f36a15 |
tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_mixer_control, priv),
|
|
Packit Service |
f36a15 |
"mixer: private start");
|
|
Packit Service |
f36a15 |
return 0;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
int tplg_decode_control_mixer(snd_tplg_t *tplg,
|
|
Packit Service |
f36a15 |
size_t pos,
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_hdr *hdr,
|
|
Packit Service |
f36a15 |
void *bin, size_t size)
|
|
Packit Service |
f36a15 |
{
|
|
Packit Service |
f36a15 |
struct list_head heap;
|
|
Packit Service |
f36a15 |
snd_tplg_obj_template_t t;
|
|
Packit Service |
f36a15 |
struct snd_tplg_mixer_template mt;
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_mixer_control *mc;
|
|
Packit Service |
f36a15 |
size_t size2;
|
|
Packit Service |
f36a15 |
int err;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
err = tplg_decode_template(tplg, pos, hdr, &t);
|
|
Packit Service |
f36a15 |
if (err < 0)
|
|
Packit Service |
f36a15 |
return err;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
next:
|
|
Packit Service |
f36a15 |
if (size < sizeof(*mc)) {
|
|
Packit Service |
f36a15 |
SNDERR("mixer: small size %d", size);
|
|
Packit Service |
f36a15 |
return -EINVAL;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
INIT_LIST_HEAD(&heap;;
|
|
Packit Service |
f36a15 |
mc = bin;
|
|
Packit Service |
f36a15 |
size2 = mc->size + mc->priv.size;
|
|
Packit Service |
f36a15 |
if (size2 > size) {
|
|
Packit Service |
f36a15 |
SNDERR("mixer: wrong element size (%d, priv %d)",
|
|
Packit Service |
f36a15 |
mc->size, mc->priv.size);
|
|
Packit Service |
f36a15 |
return -EINVAL;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
err = tplg_decode_control_mixer1(tplg, &heap, &mt, pos, bin, size2);
|
|
Packit Service |
f36a15 |
if (err >= 0) {
|
|
Packit Service |
f36a15 |
t.mixer = &mt;
|
|
Packit Service |
f36a15 |
err = snd_tplg_add_object(tplg, &t);
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
tplg_free(&heap;;
|
|
Packit Service |
f36a15 |
if (err < 0)
|
|
Packit Service |
f36a15 |
return err;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
bin += size2;
|
|
Packit Service |
f36a15 |
size -= size2;
|
|
Packit Service |
f36a15 |
pos += size2;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
if (size > 0)
|
|
Packit Service |
f36a15 |
goto next;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
return 0;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
int tplg_decode_control_enum1(snd_tplg_t *tplg,
|
|
Packit Service |
f36a15 |
struct list_head *heap,
|
|
Packit Service |
f36a15 |
struct snd_tplg_enum_template *et,
|
|
Packit Service |
f36a15 |
size_t pos,
|
|
Packit Service |
f36a15 |
void *bin, size_t size)
|
|
Packit Service |
f36a15 |
{
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_enum_control *ec = bin;
|
|
Packit Service |
f36a15 |
struct snd_tplg_channel_map_template cmt;
|
|
Packit Service |
f36a15 |
int i;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
if (size < sizeof(*ec)) {
|
|
Packit Service |
f36a15 |
SNDERR("enum: small size %d", size);
|
|
Packit Service |
f36a15 |
return -EINVAL;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
tplg_log(tplg, 'D', pos, "enum: size %d private size %d",
|
|
Packit Service |
f36a15 |
ec->size, ec->priv.size);
|
|
Packit Service |
f36a15 |
if (size != ec->size + ec->priv.size) {
|
|
Packit Service |
f36a15 |
SNDERR("enum: unexpected element size %d", size);
|
|
Packit Service |
f36a15 |
return -EINVAL;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
if (ec->num_channels > SND_TPLG_MAX_CHAN ||
|
|
Packit Service |
f36a15 |
ec->num_channels > SND_SOC_TPLG_MAX_CHAN) {
|
|
Packit Service |
f36a15 |
SNDERR("enum: unexpected channel count %d", ec->num_channels);
|
|
Packit Service |
f36a15 |
return -EINVAL;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
if (ec->items > SND_SOC_TPLG_NUM_TEXTS) {
|
|
Packit Service |
f36a15 |
SNDERR("enum: unexpected texts count %d", ec->items);
|
|
Packit Service |
f36a15 |
return -EINVAL;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
memset(et, 0, sizeof(*et));
|
|
Packit Service |
f36a15 |
et->hdr.type = ec->hdr.type;
|
|
Packit Service |
f36a15 |
et->hdr.name = ec->hdr.name;
|
|
Packit Service |
f36a15 |
et->hdr.access = ec->hdr.access;
|
|
Packit Service |
f36a15 |
et->hdr.ops.get = ec->hdr.ops.get;
|
|
Packit Service |
f36a15 |
et->hdr.ops.put = ec->hdr.ops.put;
|
|
Packit Service |
f36a15 |
et->hdr.ops.info = ec->hdr.ops.info;
|
|
Packit Service |
f36a15 |
et->mask = ec->mask;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
if (ec->items > 0) {
|
|
Packit Service |
f36a15 |
et->items = ec->items;
|
|
Packit Service |
f36a15 |
et->texts = tplg_calloc(heap, sizeof(char *) * ec->items);
|
|
Packit Service |
f36a15 |
if (!et->texts)
|
|
Packit Service |
f36a15 |
return -ENOMEM;
|
|
Packit Service |
f36a15 |
for (i = 0; ec->items; i++) {
|
|
Packit Service |
f36a15 |
unsigned int j = i * sizeof(int) * ENUM_VAL_SIZE;
|
|
Packit Service |
f36a15 |
et->texts[i] = ec->texts[i];
|
|
Packit Service |
f36a15 |
et->values[i] = (int *)&ec->values[j];
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
et->map = &cm;;
|
|
Packit Service |
f36a15 |
memset(&cmt, 0, sizeof(cmt));
|
|
Packit Service |
f36a15 |
cmt.num_channels = ec->num_channels;
|
|
Packit Service |
f36a15 |
for (i = 0; i < cmt.num_channels; i++) {
|
|
Packit Service |
f36a15 |
struct snd_tplg_channel_elem *channel = &cmt.channel[i];
|
|
Packit Service |
f36a15 |
tplg_log(tplg, 'D', pos + ((void *)&ec->channel[i] - (void *)ec),
|
|
Packit Service |
f36a15 |
"enum: channel size %d", ec->channel[i].size);
|
|
Packit Service |
f36a15 |
channel->reg = ec->channel[i].reg;
|
|
Packit Service |
f36a15 |
channel->shift = ec->channel[i].shift;
|
|
Packit Service |
f36a15 |
channel->id = ec->channel[i].id;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
et->priv = &ec->priv;
|
|
Packit Service |
f36a15 |
return 0;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
int tplg_decode_control_enum(snd_tplg_t *tplg,
|
|
Packit Service |
f36a15 |
size_t pos,
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_hdr *hdr,
|
|
Packit Service |
f36a15 |
void *bin, size_t size)
|
|
Packit Service |
f36a15 |
{
|
|
Packit Service |
f36a15 |
struct list_head heap;
|
|
Packit Service |
f36a15 |
snd_tplg_obj_template_t t;
|
|
Packit Service |
f36a15 |
struct snd_tplg_enum_template et;
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_enum_control *ec;
|
|
Packit Service |
f36a15 |
size_t size2;
|
|
Packit Service |
f36a15 |
int err;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
err = tplg_decode_template(tplg, pos, hdr, &t);
|
|
Packit Service |
f36a15 |
if (err < 0)
|
|
Packit Service |
f36a15 |
return err;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
next:
|
|
Packit Service |
f36a15 |
if (size < sizeof(*ec)) {
|
|
Packit Service |
f36a15 |
SNDERR("enum: small size %d", size);
|
|
Packit Service |
f36a15 |
return -EINVAL;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
INIT_LIST_HEAD(&heap;;
|
|
Packit Service |
f36a15 |
ec = bin;
|
|
Packit Service |
f36a15 |
size2 = ec->size + ec->priv.size;
|
|
Packit Service |
f36a15 |
if (size2 > size) {
|
|
Packit Service |
f36a15 |
SNDERR("enum: wrong element size (%d, priv %d)",
|
|
Packit Service |
f36a15 |
ec->size, ec->priv.size);
|
|
Packit Service |
f36a15 |
return -EINVAL;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
err = tplg_decode_control_enum1(tplg, &heap, &et, pos, bin, size);
|
|
Packit Service |
f36a15 |
if (err >= 0) {
|
|
Packit Service |
f36a15 |
t.enum_ctl = &et;
|
|
Packit Service |
f36a15 |
err = snd_tplg_add_object(tplg, &t);
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
tplg_free(&heap;;
|
|
Packit Service |
f36a15 |
if (err < 0)
|
|
Packit Service |
f36a15 |
return err;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
bin += size2;
|
|
Packit Service |
f36a15 |
size -= size2;
|
|
Packit Service |
f36a15 |
pos += size2;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
if (size > 0)
|
|
Packit Service |
f36a15 |
goto next;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
return 0;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
int tplg_decode_control_bytes1(snd_tplg_t *tplg,
|
|
Packit Service |
f36a15 |
struct snd_tplg_bytes_template *bt,
|
|
Packit Service |
f36a15 |
size_t pos,
|
|
Packit Service |
f36a15 |
void *bin, size_t size)
|
|
Packit Service |
f36a15 |
{
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_bytes_control *bc = bin;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
if (size < sizeof(*bc)) {
|
|
Packit Service |
f36a15 |
SNDERR("bytes: small size %d", size);
|
|
Packit Service |
f36a15 |
return -EINVAL;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
tplg_log(tplg, 'D', pos, "control bytes: size %d private size %d",
|
|
Packit Service |
f36a15 |
bc->size, bc->priv.size);
|
|
Packit Service |
f36a15 |
if (size != bc->size + bc->priv.size) {
|
|
Packit Service |
f36a15 |
SNDERR("bytes: unexpected element size %d", size);
|
|
Packit Service |
f36a15 |
return -EINVAL;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
memset(bt, 0, sizeof(*bt));
|
|
Packit Service |
f36a15 |
bt->hdr.type = bc->hdr.type;
|
|
Packit Service |
f36a15 |
bt->hdr.name = bc->hdr.name;
|
|
Packit Service |
f36a15 |
bt->hdr.access = bc->hdr.access;
|
|
Packit Service |
f36a15 |
bt->hdr.ops.get = bc->hdr.ops.get;
|
|
Packit Service |
f36a15 |
bt->hdr.ops.put = bc->hdr.ops.put;
|
|
Packit Service |
f36a15 |
bt->hdr.ops.info = bc->hdr.ops.info;
|
|
Packit Service |
f36a15 |
bt->max = bc->max;
|
|
Packit Service |
f36a15 |
bt->mask = bc->mask;
|
|
Packit Service |
f36a15 |
bt->base = bc->base;
|
|
Packit Service |
f36a15 |
bt->num_regs = bc->num_regs;
|
|
Packit Service |
f36a15 |
bt->ext_ops.get = bc->ext_ops.get;
|
|
Packit Service |
f36a15 |
bt->ext_ops.put = bc->ext_ops.put;
|
|
Packit Service |
f36a15 |
bt->ext_ops.info = bc->ext_ops.info;
|
|
Packit Service |
f36a15 |
tplg_log(tplg, 'D', pos, "control bytes: name '%s' access 0x%x",
|
|
Packit Service |
f36a15 |
bt->hdr.name, bt->hdr.access);
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
bt->priv = &bc->priv;
|
|
Packit Service |
f36a15 |
return 0;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
int tplg_decode_control_bytes(snd_tplg_t *tplg,
|
|
Packit Service |
f36a15 |
size_t pos,
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_hdr *hdr,
|
|
Packit Service |
f36a15 |
void *bin, size_t size)
|
|
Packit Service |
f36a15 |
{
|
|
Packit Service |
f36a15 |
snd_tplg_obj_template_t t;
|
|
Packit Service |
f36a15 |
struct snd_tplg_bytes_template bt;
|
|
Packit Service |
f36a15 |
struct snd_soc_tplg_bytes_control *bc;
|
|
Packit Service |
f36a15 |
size_t size2;
|
|
Packit Service |
f36a15 |
int err;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
err = tplg_decode_template(tplg, pos, hdr, &t);
|
|
Packit Service |
f36a15 |
if (err < 0)
|
|
Packit Service |
f36a15 |
return err;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
next:
|
|
Packit Service |
f36a15 |
if (size < sizeof(*bc)) {
|
|
Packit Service |
f36a15 |
SNDERR("bytes: small size %d", size);
|
|
Packit Service |
f36a15 |
return -EINVAL;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
bc = bin;
|
|
Packit Service |
f36a15 |
size2 = bc->size + bc->priv.size;
|
|
Packit Service |
f36a15 |
if (size2 > size) {
|
|
Packit Service |
f36a15 |
SNDERR("bytes: wrong element size (%d, priv %d)",
|
|
Packit Service |
f36a15 |
bc->size, bc->priv.size);
|
|
Packit Service |
f36a15 |
return -EINVAL;
|
|
Packit Service |
f36a15 |
}
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
err = tplg_decode_control_bytes1(tplg, &bt, pos, bin, size);
|
|
Packit Service |
f36a15 |
if (err < 0)
|
|
Packit Service |
f36a15 |
return err;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
t.bytes_ctl = &bt;
|
|
Packit Service |
f36a15 |
err = snd_tplg_add_object(tplg, &t);
|
|
Packit Service |
f36a15 |
if (err < 0)
|
|
Packit Service |
f36a15 |
return err;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
bin += size2;
|
|
Packit Service |
f36a15 |
size -= size2;
|
|
Packit Service |
f36a15 |
pos += size2;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
if (size > 0)
|
|
Packit Service |
f36a15 |
goto next;
|
|
Packit Service |
f36a15 |
|
|
Packit Service |
f36a15 |
return 0;
|
|
Packit Service |
f36a15 |
}
|