Commit 15d97222 authored by Mark Brown's avatar Mark Brown

ASoC: Intel: avs: Add support for sending initial

Merge series from Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>:

In some cases it may be desirable to provide default initial
configuration for FW module using payload. To facilitate such solution
extend topology to contain initial config information, parse it and then
send data to FW if present.

Amadeusz Sławiński (3):
  ASoC: Intel: avs: UAPI: Add tokens for initial config feature
  ASoC: Intel: avs: Add topology parsing support for initial config
  ASoC: Intel: avs: Send initial config to module if present

 include/uapi/sound/intel/avs/tokens.h |   9 ++
 sound/soc/intel/avs/path.c            |  33 ++++++
 sound/soc/intel/avs/topology.c        | 164 +++++++++++++++++++++++++-
 sound/soc/intel/avs/topology.h        |  13 ++
 4 files changed, 217 insertions(+), 2 deletions(-)

--
2.34.1
parents fd38b4e4 8a49ef78
...@@ -19,6 +19,8 @@ enum avs_tplg_token { ...@@ -19,6 +19,8 @@ enum avs_tplg_token {
AVS_TKN_MANIFEST_NUM_MODCFGS_EXT_U32 = 6, AVS_TKN_MANIFEST_NUM_MODCFGS_EXT_U32 = 6,
AVS_TKN_MANIFEST_NUM_PPLCFGS_U32 = 7, AVS_TKN_MANIFEST_NUM_PPLCFGS_U32 = 7,
AVS_TKN_MANIFEST_NUM_BINDINGS_U32 = 8, AVS_TKN_MANIFEST_NUM_BINDINGS_U32 = 8,
AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32 = 9,
AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32 = 10,
/* struct avs_tplg_library */ /* struct avs_tplg_library */
AVS_TKN_LIBRARY_ID_U32 = 101, AVS_TKN_LIBRARY_ID_U32 = 101,
...@@ -109,6 +111,8 @@ enum avs_tplg_token { ...@@ -109,6 +111,8 @@ enum avs_tplg_token {
AVS_TKN_MOD_PROC_DOMAIN_U8 = 1705, AVS_TKN_MOD_PROC_DOMAIN_U8 = 1705,
AVS_TKN_MOD_MODCFG_EXT_ID_U32 = 1706, AVS_TKN_MOD_MODCFG_EXT_ID_U32 = 1706,
AVS_TKN_MOD_KCONTROL_ID_U32 = 1707, AVS_TKN_MOD_KCONTROL_ID_U32 = 1707,
AVS_TKN_MOD_INIT_CONFIG_NUM_IDS_U32 = 1708,
AVS_TKN_MOD_INIT_CONFIG_ID_U32 = 1709,
/* struct avs_tplg_path_template */ /* struct avs_tplg_path_template */
AVS_TKN_PATH_TMPL_ID_U32 = 1801, AVS_TKN_PATH_TMPL_ID_U32 = 1801,
...@@ -125,6 +129,11 @@ enum avs_tplg_token { ...@@ -125,6 +129,11 @@ enum avs_tplg_token {
/* struct avs_tplg_kcontrol */ /* struct avs_tplg_kcontrol */
AVS_TKN_KCONTROL_ID_U32 = 2301, AVS_TKN_KCONTROL_ID_U32 = 2301,
/* struct avs_tplg_init_config */
AVS_TKN_INIT_CONFIG_ID_U32 = 2401,
AVS_TKN_INIT_CONFIG_PARAM_U8 = 2402,
AVS_TKN_INIT_CONFIG_LENGTH_U32 = 2403,
}; };
#endif #endif
...@@ -547,6 +547,33 @@ static int avs_path_module_type_create(struct avs_dev *adev, struct avs_path_mod ...@@ -547,6 +547,33 @@ static int avs_path_module_type_create(struct avs_dev *adev, struct avs_path_mod
return avs_modext_create(adev, mod); return avs_modext_create(adev, mod);
} }
static int avs_path_module_send_init_configs(struct avs_dev *adev, struct avs_path_module *mod)
{
struct avs_soc_component *acomp;
acomp = to_avs_soc_component(mod->template->owner->owner->owner->owner->comp);
u32 num_ids = mod->template->num_config_ids;
u32 *ids = mod->template->config_ids;
for (int i = 0; i < num_ids; i++) {
struct avs_tplg_init_config *config = &acomp->tplg->init_configs[ids[i]];
size_t len = config->length;
void *data = config->data;
u32 param = config->param;
int ret;
ret = avs_ipc_set_large_config(adev, mod->module_id, mod->instance_id,
param, data, len);
if (ret) {
dev_err(adev->dev, "send initial module config failed: %d\n", ret);
return AVS_IPC_RET(ret);
}
}
return 0;
}
static void avs_path_module_free(struct avs_dev *adev, struct avs_path_module *mod) static void avs_path_module_free(struct avs_dev *adev, struct avs_path_module *mod)
{ {
kfree(mod); kfree(mod);
...@@ -580,6 +607,12 @@ avs_path_module_create(struct avs_dev *adev, ...@@ -580,6 +607,12 @@ avs_path_module_create(struct avs_dev *adev,
return ERR_PTR(ret); return ERR_PTR(ret);
} }
ret = avs_path_module_send_init_configs(adev, mod);
if (ret) {
kfree(mod);
return ERR_PTR(ret);
}
return mod; return mod;
} }
......
...@@ -1118,6 +1118,21 @@ static const struct avs_tplg_token_parser module_parsers[] = { ...@@ -1118,6 +1118,21 @@ static const struct avs_tplg_token_parser module_parsers[] = {
.offset = offsetof(struct avs_tplg_module, ctl_id), .offset = offsetof(struct avs_tplg_module, ctl_id),
.parse = avs_parse_byte_token, .parse = avs_parse_byte_token,
}, },
{
.token = AVS_TKN_MOD_INIT_CONFIG_NUM_IDS_U32,
.type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
.offset = offsetof(struct avs_tplg_module, num_config_ids),
.parse = avs_parse_byte_token,
},
};
static const struct avs_tplg_token_parser init_config_parsers[] = {
{
.token = AVS_TKN_MOD_INIT_CONFIG_ID_U32,
.type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
.offset = 0,
.parse = avs_parse_word_token,
},
}; };
static struct avs_tplg_module * static struct avs_tplg_module *
...@@ -1125,17 +1140,50 @@ avs_tplg_module_create(struct snd_soc_component *comp, struct avs_tplg_pipeline ...@@ -1125,17 +1140,50 @@ avs_tplg_module_create(struct snd_soc_component *comp, struct avs_tplg_pipeline
struct snd_soc_tplg_vendor_array *tuples, u32 block_size) struct snd_soc_tplg_vendor_array *tuples, u32 block_size)
{ {
struct avs_tplg_module *module; struct avs_tplg_module *module;
u32 esize;
int ret; int ret;
/* See where config id block starts. */
ret = avs_tplg_vendor_entry_size(tuples, block_size,
AVS_TKN_MOD_INIT_CONFIG_ID_U32, &esize);
if (ret)
return ERR_PTR(ret);
module = devm_kzalloc(comp->card->dev, sizeof(*module), GFP_KERNEL); module = devm_kzalloc(comp->card->dev, sizeof(*module), GFP_KERNEL);
if (!module) if (!module)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
ret = avs_parse_tokens(comp, module, module_parsers, ret = avs_parse_tokens(comp, module, module_parsers,
ARRAY_SIZE(module_parsers), tuples, block_size); ARRAY_SIZE(module_parsers), tuples, esize);
if (ret < 0) if (ret < 0)
return ERR_PTR(ret); return ERR_PTR(ret);
block_size -= esize;
/* Parse trailing config ids if any. */
if (block_size) {
u32 num_config_ids = module->num_config_ids;
u32 *config_ids;
if (!num_config_ids)
return ERR_PTR(-EINVAL);
config_ids = devm_kcalloc(comp->card->dev, num_config_ids, sizeof(*config_ids),
GFP_KERNEL);
if (!config_ids)
return ERR_PTR(-ENOMEM);
tuples = avs_tplg_vendor_array_at(tuples, esize);
ret = parse_dictionary_entries(comp, tuples, block_size,
config_ids, num_config_ids, sizeof(*config_ids),
AVS_TKN_MOD_INIT_CONFIG_ID_U32,
init_config_parsers,
ARRAY_SIZE(init_config_parsers));
if (ret)
return ERR_PTR(ret);
module->config_ids = config_ids;
}
module->owner = owner; module->owner = owner;
INIT_LIST_HEAD(&module->node); INIT_LIST_HEAD(&module->node);
...@@ -1416,6 +1464,82 @@ avs_tplg_path_template_create(struct snd_soc_component *comp, struct avs_tplg *o ...@@ -1416,6 +1464,82 @@ avs_tplg_path_template_create(struct snd_soc_component *comp, struct avs_tplg *o
return template; return template;
} }
static const struct avs_tplg_token_parser mod_init_config_parsers[] = {
{
.token = AVS_TKN_MOD_INIT_CONFIG_ID_U32,
.type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
.offset = offsetof(struct avs_tplg_init_config, id),
.parse = avs_parse_word_token,
},
{
.token = AVS_TKN_INIT_CONFIG_PARAM_U8,
.type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
.offset = offsetof(struct avs_tplg_init_config, param),
.parse = avs_parse_byte_token,
},
{
.token = AVS_TKN_INIT_CONFIG_LENGTH_U32,
.type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
.offset = offsetof(struct avs_tplg_init_config, length),
.parse = avs_parse_word_token,
},
};
static int avs_tplg_parse_initial_configs(struct snd_soc_component *comp,
struct snd_soc_tplg_vendor_array *tuples,
u32 block_size)
{
struct avs_soc_component *acomp = to_avs_soc_component(comp);
struct avs_tplg *tplg = acomp->tplg;
int ret, i;
/* Parse tuple section telling how many init configs there are. */
ret = parse_dictionary_header(comp, tuples, (void **)&tplg->init_configs,
&tplg->num_init_configs,
sizeof(*tplg->init_configs),
AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32);
if (ret)
return ret;
block_size -= le32_to_cpu(tuples->size);
/* With header parsed, move on to parsing entries. */
tuples = avs_tplg_vendor_array_next(tuples);
for (i = 0; i < tplg->num_init_configs && block_size > 0; i++) {
struct avs_tplg_init_config *config = &tplg->init_configs[i];
struct snd_soc_tplg_vendor_array *tmp;
void *init_config_data;
u32 esize;
/*
* Usually to get section length we search for first token of next group of data,
* but in this case we can't as tuples are followed by raw data.
*/
tmp = avs_tplg_vendor_array_next(tuples);
esize = le32_to_cpu(tuples->size) + le32_to_cpu(tmp->size);
ret = parse_dictionary_entries(comp, tuples, esize, config, 1, sizeof(*config),
AVS_TKN_MOD_INIT_CONFIG_ID_U32,
mod_init_config_parsers,
ARRAY_SIZE(mod_init_config_parsers));
block_size -= esize;
/* handle raw data section */
init_config_data = (void *)tuples + esize;
esize = config->length;
config->data = devm_kmemdup(comp->card->dev, init_config_data, esize, GFP_KERNEL);
if (!config->data)
return -ENOMEM;
tuples = init_config_data + esize;
block_size -= esize;
}
return 0;
}
static int avs_route_load(struct snd_soc_component *comp, int index, static int avs_route_load(struct snd_soc_component *comp, int index,
struct snd_soc_dapm_route *route) struct snd_soc_dapm_route *route)
{ {
...@@ -1571,6 +1695,7 @@ static int avs_manifest(struct snd_soc_component *comp, int index, ...@@ -1571,6 +1695,7 @@ static int avs_manifest(struct snd_soc_component *comp, int index,
struct snd_soc_tplg_vendor_array *tuples = manifest->priv.array; struct snd_soc_tplg_vendor_array *tuples = manifest->priv.array;
struct avs_soc_component *acomp = to_avs_soc_component(comp); struct avs_soc_component *acomp = to_avs_soc_component(comp);
size_t remaining = le32_to_cpu(manifest->priv.size); size_t remaining = le32_to_cpu(manifest->priv.size);
bool has_init_config = true;
u32 offset; u32 offset;
int ret; int ret;
...@@ -1668,8 +1793,43 @@ static int avs_manifest(struct snd_soc_component *comp, int index, ...@@ -1668,8 +1793,43 @@ static int avs_manifest(struct snd_soc_component *comp, int index,
remaining -= offset; remaining -= offset;
tuples = avs_tplg_vendor_array_at(tuples, offset); tuples = avs_tplg_vendor_array_at(tuples, offset);
ret = avs_tplg_vendor_array_lookup(tuples, remaining,
AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32, &offset);
if (ret) {
dev_err(comp->dev, "condpath lookup failed: %d\n", ret);
return ret;
}
/* Bindings dictionary. */ /* Bindings dictionary. */
return avs_tplg_parse_bindings(comp, tuples, remaining); ret = avs_tplg_parse_bindings(comp, tuples, offset);
if (ret < 0)
return ret;
remaining -= offset;
tuples = avs_tplg_vendor_array_at(tuples, offset);
ret = avs_tplg_vendor_array_lookup(tuples, remaining,
AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32, &offset);
if (ret == -ENOENT) {
dev_dbg(comp->dev, "init config lookup failed: %d\n", ret);
has_init_config = false;
} else if (ret) {
dev_err(comp->dev, "init config lookup failed: %d\n", ret);
return ret;
}
if (!has_init_config)
return 0;
remaining -= offset;
tuples = avs_tplg_vendor_array_at(tuples, offset);
/* Initial configs dictionary. */
ret = avs_tplg_parse_initial_configs(comp, tuples, remaining);
if (ret < 0)
return ret;
return 0;
} }
#define AVS_CONTROL_OPS_VOLUME 257 #define AVS_CONTROL_OPS_VOLUME 257
......
...@@ -33,6 +33,9 @@ struct avs_tplg { ...@@ -33,6 +33,9 @@ struct avs_tplg {
u32 num_pplcfgs; u32 num_pplcfgs;
struct avs_tplg_binding *bindings; struct avs_tplg_binding *bindings;
u32 num_bindings; u32 num_bindings;
u32 num_condpath_tmpls;
struct avs_tplg_init_config *init_configs;
u32 num_init_configs;
struct list_head path_tmpl_list; struct list_head path_tmpl_list;
}; };
...@@ -147,6 +150,14 @@ struct avs_tplg_path_template { ...@@ -147,6 +150,14 @@ struct avs_tplg_path_template {
struct list_head node; struct list_head node;
}; };
struct avs_tplg_init_config {
u32 id;
u8 param;
size_t length;
void *data;
};
struct avs_tplg_path { struct avs_tplg_path {
u32 id; u32 id;
...@@ -183,6 +194,8 @@ struct avs_tplg_module { ...@@ -183,6 +194,8 @@ struct avs_tplg_module {
u8 domain; u8 domain;
struct avs_tplg_modcfg_ext *cfg_ext; struct avs_tplg_modcfg_ext *cfg_ext;
u32 ctl_id; u32 ctl_id;
u32 num_config_ids;
u32 *config_ids;
struct avs_tplg_pipeline *owner; struct avs_tplg_pipeline *owner;
/* Pipeline modules management. */ /* Pipeline modules management. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment