From: Mengdong Lin mengdong.lin@linux.intel.com
Topology will check with ASoC if a BE (Backend) link already exists by checking its ID. If the BE link doesn't exist, topology will create it and the link_load ops and card's add_dai_link will be called for machine specific init.
Signed-off-by: Mengdong Lin mengdong.lin@linux.intel.com
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 6a1a9c2..fd2c36b 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -49,9 +49,10 @@ #define SOC_TPLG_PASS_GRAPH 5 #define SOC_TPLG_PASS_PINS 6 #define SOC_TPLG_PASS_BE_DAI 7 +#define SOC_TPLG_PASS_LINK 8
#define SOC_TPLG_PASS_START SOC_TPLG_PASS_MANIFEST -#define SOC_TPLG_PASS_END SOC_TPLG_PASS_BE_DAI +#define SOC_TPLG_PASS_END SOC_TPLG_PASS_LINK
struct soc_tplg { const struct firmware *fw; @@ -493,6 +494,7 @@ static void remove_link(struct snd_soc_component *comp,
list_del(&dobj->list); snd_soc_remove_dai_link(comp->card, link); + kfree(link->codecs); kfree(link); }
@@ -1631,6 +1633,29 @@ static void set_link_flags(struct snd_soc_dai_link *link, if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_IGNORE_POWERDOWN_TIME) link->ignore_pmdown_time = flags & SND_SOC_TPLG_LNK_FLGBIT_IGNORE_POWERDOWN_TIME ? 1 : 0; + + if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES) + link->symmetric_rates = + flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES ? 1 : 0; + + if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS) + link->symmetric_channels = + flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS ? + 1 : 0; + + if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS) + link->symmetric_samplebits = + flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS ? + 1 : 0; + + if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_DPCM_PLAYBACK) + link->dpcm_playback = + flags & SND_SOC_TPLG_LNK_FLGBIT_DPCM_PLAYBACK ? 1 : 0; + + if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_DPCM_CAPTURE) + link->dpcm_capture = + flags & SND_SOC_TPLG_LNK_FLGBIT_DPCM_CAPTURE ? 1 : 0; + }
/* create the FE DAI link */ @@ -1865,6 +1890,189 @@ static int soc_tplg_be_dai_elems_load(struct soc_tplg *tplg, return 0; }
+/* * + * set_be_link_hw_format - Set the HW audio format of the BE DAI link. + * @tplg: topology context + * @cfg: topology BE DAI link configs. + * + * Topology context contains a list of supported HW formats (configs) and + * a default format ID for the BE link. This function will use this default ID + * to choose the HW format to set the link's DAI format for init. + */ +static void set_be_link_hw_format(struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg) +{ + struct snd_soc_tplg_hw_config *hw_config; + unsigned char bclk_master, fsync_master; + unsigned char invert_bclk, invert_fsync; + int i; + + for (i = 0; i < cfg->num_hw_configs; i++) { + hw_config = &cfg->hw_config[i]; + if (hw_config->id != cfg->default_hw_config_id) + continue; + + link->dai_fmt = hw_config->fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + /* clock signal polarity */ + invert_bclk = hw_config->invert_bclk; + invert_fsync = hw_config->invert_fsync; + if (!invert_bclk && !invert_fsync) + link->dai_fmt |= SND_SOC_DAIFMT_NB_NF; + else if (!invert_bclk && invert_fsync) + link->dai_fmt |= SND_SOC_DAIFMT_NB_IF; + else if (invert_bclk && !invert_fsync) + link->dai_fmt |= SND_SOC_DAIFMT_IB_NF; + else + link->dai_fmt |= SND_SOC_DAIFMT_IB_IF; + + /* clock masters */ + bclk_master = hw_config->bclk_master; + fsync_master = hw_config->fsync_master; + if (!bclk_master && !fsync_master) + link->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + else if (bclk_master && !fsync_master) + link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; + else if (!bclk_master && fsync_master) + link->dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; + else + link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + + return; + } +} + +/* create a BE DAI link */ +static int soc_tplg_be_link_create(struct soc_tplg *tplg, + struct snd_soc_tplg_link_config *cfg) +{ + struct snd_soc_dai_link *link; + int i, ret = 0; + + dev_dbg(tplg->dev, + "Adding BE link %s, stream name %s\n", + cfg->name, cfg->stream_name); + + link = kzalloc(sizeof(struct snd_soc_dai_link), GFP_KERNEL); + if (link == NULL) + return -ENOMEM; + + /* id and names */ + if (strlen(cfg->name)) + link->name = cfg->name; + if (strlen(cfg->stream_name)) + link->stream_name = cfg->stream_name; + link->id = cfg->id; + + /* components */ + if (strlen(cfg->cpu.name)) + link->cpu_name = cfg->cpu.name; + link->cpu_dai_name = cfg->cpu.dai_name; + + if (!cfg->num_codecs || !cfg->codecs) { + ret = -EINVAL; + goto err; + } + + link->num_codecs = cfg->num_codecs; + link->codecs = kcalloc(link->num_codecs, + sizeof(struct snd_soc_dai_link_component), + GFP_KERNEL); + if (!link->codecs) { + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < link->num_codecs ; i++) { + if (strlen(cfg->codecs[i].name)) + link->codecs[i].name = cfg->codecs[i].name; + link->codecs[i].dai_name = cfg->codecs[i].dai_name; + } + + /* hw format */ + if (cfg->num_hw_configs) + set_be_link_hw_format(link, cfg); + + /* flags */ + link->no_pcm = 1; + if (cfg->flag_mask) + set_link_flags(link, cfg->flag_mask, cfg->flags); + + /* pass control to component driver for optional further init */ + ret = soc_tplg_dai_link_load(tplg, link); + if (ret < 0) { + dev_err(tplg->dev, "ASoC: BE link loading failed\n"); + goto err; + } + + link->dobj.index = tplg->index; + link->dobj.ops = tplg->ops; + link->dobj.type = SND_SOC_DOBJ_DAI_LINK; + list_add(&link->dobj.list, &tplg->comp->dobj_list); + + snd_soc_add_dai_link(tplg->comp->card, link); + return 0; + +err: + if (link) { + kfree(link->codecs); + kfree(link->params); + kfree(link); + } + + return ret; +} + +/* Config an existing or create a new BE DAI link */ +static int soc_tplg_be_link_config(struct soc_tplg *tplg, + struct snd_soc_tplg_link_config *cfg) +{ + struct snd_soc_dai_link *link; + + link = snd_soc_find_dai_link(tplg->comp->card, cfg->id); + if (!link) + return soc_tplg_be_link_create(tplg, cfg); + + /* TODO: configure existing BE links. No user request atm. */ + return 0; +} + + +/* Load BE link elements from the topology context */ +static int soc_tplg_be_link_elems_load(struct soc_tplg *tplg, + struct snd_soc_tplg_hdr *hdr) +{ + struct snd_soc_tplg_link_config *link; + int count = hdr->count; + int i; + + if (tplg->pass != SOC_TPLG_PASS_LINK) { + tplg->pos += hdr->size + hdr->payload_size; + return 0; + }; + + if (soc_tplg_check_elem_count(tplg, + sizeof(struct snd_soc_tplg_link_config), count, + hdr->payload_size, "BE DAI link")) { + dev_err(tplg->dev, "ASoC: invalid count %d for BE DAI link elems\n", + count); + return -EINVAL; + } + + /* config or create the BE DAI links */ + for (i = 0; i < count; i++) { + link = (struct snd_soc_tplg_link_config *)tplg->pos; + if (link->size != sizeof(*link)) { + dev_err(tplg->dev, "ASoC: invalid link config size\n"); + return -EINVAL; + } + + soc_tplg_be_link_config(tplg, link); + tplg->pos += (sizeof(*link) + link->priv.size); + } + + return 0; +}
static int soc_tplg_manifest_load(struct soc_tplg *tplg, struct snd_soc_tplg_hdr *hdr) @@ -1971,6 +2179,8 @@ static int soc_tplg_load_header(struct soc_tplg *tplg, return soc_tplg_pcm_elems_load(tplg, hdr); case SND_SOC_TPLG_TYPE_BE_DAI: return soc_tplg_be_dai_elems_load(tplg, hdr); + case SND_SOC_TPLG_TYPE_BACKEND_LINK: + return soc_tplg_be_link_elems_load(tplg, hdr); case SND_SOC_TPLG_TYPE_MANIFEST: return soc_tplg_manifest_load(tplg, hdr); default: