|
@@ -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
|
|
|
|
|
|
/*
|
|
|
* Old version of ABI structs, supported for backward compatibility.
|
|
@@ -101,6 +102,14 @@ struct snd_soc_tplg_pcm_v4 {
|
|
|
struct snd_soc_tplg_stream_caps_v4 caps[2]; /* playback and capture for DAI */
|
|
|
} __packed;
|
|
|
|
|
|
+/* Physical link config v4 */
|
|
|
+struct snd_soc_tplg_link_config_v4 {
|
|
|
+ __le32 size; /* in bytes of this structure */
|
|
|
+ __le32 id; /* unique ID - used to match */
|
|
|
+ struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* supported configs playback and captrure */
|
|
|
+ __le32 num_streams; /* number of streams */
|
|
|
+} __packed;
|
|
|
+
|
|
|
/* topology context */
|
|
|
struct soc_tplg {
|
|
|
const struct firmware *fw;
|
|
@@ -1868,7 +1877,6 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
|
|
|
/* create the FE DAIs and DAI links */
|
|
|
soc_tplg_pcm_create(tplg, _pcm);
|
|
|
|
|
|
-
|
|
|
/* offset by version-specific struct size and
|
|
|
* real priv data size
|
|
|
*/
|
|
@@ -1883,6 +1891,196 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * set_link_hw_format - Set the HW audio format of the physical DAI link.
|
|
|
+ * @tplg: topology context
|
|
|
+ * @cfg: physical link configs.
|
|
|
+ *
|
|
|
+ * Topology context contains a list of supported HW formats (configs) and
|
|
|
+ * a default format ID for the physical 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_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;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * link_new_ver - Create a new physical link config from the old
|
|
|
+ * version of source.
|
|
|
+ * @toplogy: topology context
|
|
|
+ * @src: old version of phyical link config as a source
|
|
|
+ * @link: latest version of physical link config created from the source
|
|
|
+ *
|
|
|
+ * Support from vesion 4. User need free the returned link config manually.
|
|
|
+ */
|
|
|
+static int link_new_ver(struct soc_tplg *tplg,
|
|
|
+ struct snd_soc_tplg_link_config *src,
|
|
|
+ struct snd_soc_tplg_link_config **link)
|
|
|
+{
|
|
|
+ struct snd_soc_tplg_link_config *dest;
|
|
|
+ struct snd_soc_tplg_link_config_v4 *src_v4;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ *link = NULL;
|
|
|
+
|
|
|
+ if (src->size != sizeof(struct snd_soc_tplg_link_config_v4)) {
|
|
|
+ dev_err(tplg->dev, "ASoC: invalid physical link config size\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ dev_warn(tplg->dev, "ASoC: old version of physical link config\n");
|
|
|
+
|
|
|
+ src_v4 = (struct snd_soc_tplg_link_config_v4 *)src;
|
|
|
+ dest = kzalloc(sizeof(*dest), GFP_KERNEL);
|
|
|
+ if (!dest)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ dest->size = sizeof(*dest);
|
|
|
+ dest->id = src_v4->id;
|
|
|
+ dest->num_streams = src_v4->num_streams;
|
|
|
+ for (i = 0; i < dest->num_streams; i++)
|
|
|
+ memcpy(&dest->stream[i], &src_v4->stream[i],
|
|
|
+ sizeof(struct snd_soc_tplg_stream));
|
|
|
+
|
|
|
+ *link = dest;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* Find and configure an existing physical DAI link */
|
|
|
+static int soc_tplg_link_config(struct soc_tplg *tplg,
|
|
|
+ struct snd_soc_tplg_link_config *cfg)
|
|
|
+{
|
|
|
+ struct snd_soc_dai_link *link;
|
|
|
+ const char *name, *stream_name;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ name = strlen(cfg->name) ? cfg->name : NULL;
|
|
|
+ stream_name = strlen(cfg->stream_name) ? cfg->stream_name : NULL;
|
|
|
+
|
|
|
+ link = snd_soc_find_dai_link(tplg->comp->card, cfg->id,
|
|
|
+ name, stream_name);
|
|
|
+ if (!link) {
|
|
|
+ dev_err(tplg->dev, "ASoC: physical link %s (id %d) not exist\n",
|
|
|
+ name, cfg->id);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* hw format */
|
|
|
+ if (cfg->num_hw_configs)
|
|
|
+ set_link_hw_format(link, cfg);
|
|
|
+
|
|
|
+ /* flags */
|
|
|
+ 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: physical link loading failed\n");
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/* Load physical link config elements from the topology context */
|
|
|
+static int soc_tplg_link_elems_load(struct soc_tplg *tplg,
|
|
|
+ struct snd_soc_tplg_hdr *hdr)
|
|
|
+{
|
|
|
+ struct snd_soc_tplg_link_config *link, *_link;
|
|
|
+ int count = hdr->count;
|
|
|
+ int i, ret;
|
|
|
+ bool abi_match;
|
|
|
+
|
|
|
+ if (tplg->pass != SOC_TPLG_PASS_LINK) {
|
|
|
+ tplg->pos += hdr->size + hdr->payload_size;
|
|
|
+ return 0;
|
|
|
+ };
|
|
|
+
|
|
|
+ /* check the element size and count */
|
|
|
+ link = (struct snd_soc_tplg_link_config *)tplg->pos;
|
|
|
+ if (link->size > sizeof(struct snd_soc_tplg_link_config)
|
|
|
+ || link->size < sizeof(struct snd_soc_tplg_link_config_v4)) {
|
|
|
+ dev_err(tplg->dev, "ASoC: invalid size %d for physical link elems\n",
|
|
|
+ link->size);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (soc_tplg_check_elem_count(tplg,
|
|
|
+ link->size, count,
|
|
|
+ hdr->payload_size, "physical link config")) {
|
|
|
+ dev_err(tplg->dev, "ASoC: invalid count %d for physical link elems\n",
|
|
|
+ count);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* config physical DAI links */
|
|
|
+ for (i = 0; i < count; i++) {
|
|
|
+ link = (struct snd_soc_tplg_link_config *)tplg->pos;
|
|
|
+ if (link->size == sizeof(*link)) {
|
|
|
+ abi_match = true;
|
|
|
+ _link = link;
|
|
|
+ } else {
|
|
|
+ abi_match = false;
|
|
|
+ ret = link_new_ver(tplg, link, &_link);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = soc_tplg_link_config(tplg, _link);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ /* offset by version-specific struct size and
|
|
|
+ * real priv data size
|
|
|
+ */
|
|
|
+ tplg->pos += link->size + _link->priv.size;
|
|
|
+
|
|
|
+ if (!abi_match)
|
|
|
+ kfree(_link); /* free the duplicated one */
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/* *
|
|
|
* soc_tplg_be_dai_config - Find and configure an existing BE DAI.
|
|
|
* @tplg: topology context
|
|
@@ -2132,6 +2330,10 @@ 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_DAI_LINK:
|
|
|
+ case SND_SOC_TPLG_TYPE_BACKEND_LINK:
|
|
|
+ /* physical link configurations */
|
|
|
+ return soc_tplg_link_elems_load(tplg, hdr);
|
|
|
case SND_SOC_TPLG_TYPE_MANIFEST:
|
|
|
return soc_tplg_manifest_load(tplg, hdr);
|
|
|
default:
|