topology: Parse HW configurations of physical DAI links in text conf file

Users can configure the runtime supported HW configurations of a physical
link by SectionHWConfig. A physical link can refer multiple HW config
sections in SectionLink.

Signed-off-by: Mengdong Lin <mengdong.lin@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Mengdong Lin 2016-11-06 13:14:04 +08:00 committed by Takashi Iwai
parent 6b4d775b97
commit f7bf8b0cc8
5 changed files with 236 additions and 0 deletions

View file

@ -162,6 +162,34 @@ static int build_link(snd_tplg_t *tplg, struct tplg_elem *elem)
if (err < 0)
return err;
/* hw configs */
base = &elem->ref_list;
list_for_each(pos, base) {
ref = list_entry(pos, struct tplg_ref, list);
switch (ref->type) {
case SND_TPLG_TYPE_HW_CONFIG:
ref->elem = tplg_elem_lookup(&tplg->hw_cfg_list,
ref->id, SND_TPLG_TYPE_HW_CONFIG);
if (!ref->elem) {
SNDERR("error: cannot find HW config '%s'"
" referenced by link '%s'\n",
ref->id, elem->id);
return -EINVAL;
}
memcpy(&link->hw_config[num_hw_configs],
ref->elem->hw_cfg,
sizeof(struct snd_soc_tplg_hw_config));
num_hw_configs++;
break;
default:
break;
}
}
/* add link to manifest */
tplg->manifest.dai_link_elems++;
@ -523,6 +551,54 @@ int tplg_parse_pcm(snd_tplg_t *tplg,
return 0;
}
/* parse physical link runtime supported HW configs in text conf file */
static int parse_hw_config_refs(snd_tplg_t *tplg, snd_config_t *cfg,
struct tplg_elem *elem)
{
struct snd_soc_tplg_link_config *link = elem->link;
snd_config_type_t type;
snd_config_iterator_t i, next;
snd_config_t *n;
const char *id, *val = NULL;
if (snd_config_get_id(cfg, &id) < 0)
return -EINVAL;
type = snd_config_get_type(cfg);
/* refer to a single HW config */
if (type == SND_CONFIG_TYPE_STRING) {
if (snd_config_get_string(cfg, &val) < 0)
return -EINVAL;
link->num_hw_configs = 1;
return tplg_ref_add(elem, SND_TPLG_TYPE_HW_CONFIG, val);
}
if (type != SND_CONFIG_TYPE_COMPOUND) {
SNDERR("error: compound type expected for %s", id);
return -EINVAL;
}
/* refer to a list of HW configs */
snd_config_for_each(i, next, cfg) {
const char *val;
n = snd_config_iterator_entry(i);
if (snd_config_get_string(n, &val) < 0)
continue;
if (link->num_hw_configs >= SND_SOC_TPLG_HW_CONFIG_MAX) {
SNDERR("error: exceed max hw configs for link %s", id);
return -EINVAL;
}
link->num_hw_configs++;
return tplg_ref_add(elem, SND_TPLG_TYPE_HW_CONFIG, val);
}
return 0;
}
/* Parse a physical link element in text conf file */
int tplg_parse_link(snd_tplg_t *tplg,
snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
@ -573,6 +649,21 @@ int tplg_parse_link(snd_tplg_t *tplg,
continue;
}
if (strcmp(id, "hw_configs") == 0) {
err = parse_hw_config_refs(tplg, n, elem);
if (err < 0)
return err;
continue;
}
if (strcmp(id, "default_hw_conf_id") == 0) {
if (snd_config_get_string(n, &val) < 0)
return -EINVAL;
link->default_hw_config_id = atoi(val);
continue;
}
if (strcmp(id, "data") == 0) {
err = tplg_parse_data_refs(n, elem);
if (err < 0)
@ -638,6 +729,110 @@ int tplg_parse_cc(snd_tplg_t *tplg,
return 0;
}
static int get_audio_hw_format(const char *val)
{
if (!strlen(val))
return -EINVAL;
if (!strcmp(val, "I2S"))
return SND_SOC_DAI_FORMAT_I2S;
if (!strcmp(val, "RIGHT_J"))
return SND_SOC_DAI_FORMAT_RIGHT_J;
if (!strcmp(val, "LEFT_J"))
return SND_SOC_DAI_FORMAT_LEFT_J;
if (!strcmp(val, "DSP_A"))
return SND_SOC_DAI_FORMAT_DSP_A;
if (!strcmp(val, "LEFT_B"))
return SND_SOC_DAI_FORMAT_DSP_B;
if (!strcmp(val, "AC97"))
return SND_SOC_DAI_FORMAT_AC97;
if (!strcmp(val, "PDM"))
return SND_SOC_DAI_FORMAT_PDM;
SNDERR("error: invalid audio HW format %s\n", val);
return -EINVAL;
}
int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg,
void *private ATTRIBUTE_UNUSED)
{
struct snd_soc_tplg_hw_config *hw_cfg;
struct tplg_elem *elem;
snd_config_iterator_t i, next;
snd_config_t *n;
const char *id, *val = NULL;
int ret;
elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_HW_CONFIG);
if (!elem)
return -ENOMEM;
hw_cfg = elem->hw_cfg;
hw_cfg->size = elem->size;
tplg_dbg(" Link HW config: %s\n", elem->id);
snd_config_for_each(i, next, cfg) {
n = snd_config_iterator_entry(i);
if (snd_config_get_id(n, &id) < 0)
continue;
/* skip comments */
if (strcmp(id, "comment") == 0)
continue;
if (id[0] == '#')
continue;
if (strcmp(id, "id") == 0) {
if (snd_config_get_string(n, &val) < 0)
return -EINVAL;
hw_cfg->id = atoi(val);
tplg_dbg("\t%s: %d\n", id, hw_cfg->id);
continue;
}
if (strcmp(id, "format") == 0) {
if (snd_config_get_string(n, &val) < 0)
return -EINVAL;
ret = get_audio_hw_format(val);
if (ret < 0)
return ret;
hw_cfg->fmt = ret;
continue;
}
if (strcmp(id, "bclk") == 0) {
if (snd_config_get_string(n, &val) < 0)
return -EINVAL;
if (!strcmp(val, "master"))
hw_cfg->bclk_master = true;
continue;
}
if (strcmp(id, "fsync") == 0) {
if (snd_config_get_string(n, &val) < 0)
return -EINVAL;
if (!strcmp(val, "master"))
hw_cfg->fsync_master = true;
continue;
}
}
return 0;
}
/* copy stream object */
static void tplg_add_stream_object(struct snd_soc_tplg_stream *strm,
struct snd_tplg_stream_template *strm_tpl)