From 583958fa2e522139a5fe8b039069e31502c20d31 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Tue, 11 Oct 2016 14:36:42 +0800 Subject: ASoC: topology: Make manifest backward compatible from ABI v4 This patch adds support for old version 4 of manifest. Topology ABI v5 added new fields to manifest while user space may still uses v4. So kernel will check the ABI version by comparing the object size. If user space uses v4 of manifest, kernel will create a latest version of manifest from the old one, and use the new one internally and free it later. Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 87 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 79 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 6b05047a4134..c832b24b0cde 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -53,6 +53,23 @@ #define SOC_TPLG_PASS_START SOC_TPLG_PASS_MANIFEST #define SOC_TPLG_PASS_END SOC_TPLG_PASS_BE_DAI + +/* + * Old version of ABI structs, supported for backward compatibility. + */ + +/* Manifest v4 */ +struct snd_soc_tplg_manifest_v4 { + __le32 size; /* in bytes of this structure */ + __le32 control_elems; /* number of control elements */ + __le32 widget_elems; /* number of widget elements */ + __le32 graph_elems; /* number of graph elements */ + __le32 pcm_elems; /* number of PCM elements */ + __le32 dai_link_elems; /* number of DAI link elements */ + struct snd_soc_tplg_private priv; +} __packed; + +/* topology context */ struct soc_tplg { const struct firmware *fw; @@ -1798,27 +1815,81 @@ static int soc_tplg_be_dai_elems_load(struct soc_tplg *tplg, return 0; } +/** + * manifest_new_ver - Create a new version of manifest from the old version + * of source. + * @toplogy: topology context + * @src: old version of manifest as a source + * @manifest: latest version of manifest created from the source + * + * Support from vesion 4. Users need free the returned manifest manually. + */ +static int manifest_new_ver(struct soc_tplg *tplg, + struct snd_soc_tplg_manifest *src, + struct snd_soc_tplg_manifest **manifest) +{ + struct snd_soc_tplg_manifest *dest; + struct snd_soc_tplg_manifest_v4 *src_v4; + + *manifest = NULL; + + if (src->size != sizeof(*src_v4)) { + dev_err(tplg->dev, "ASoC: invalid manifest size\n"); + return -EINVAL; + } + + dev_warn(tplg->dev, "ASoC: old version of manifest\n"); + + src_v4 = (struct snd_soc_tplg_manifest_v4 *)src; + dest = kzalloc(sizeof(*dest) + src_v4->priv.size, GFP_KERNEL); + if (!dest) + return -ENOMEM; + + dest->size = sizeof(*dest); /* size of latest abi version */ + dest->control_elems = src_v4->control_elems; + dest->widget_elems = src_v4->widget_elems; + dest->graph_elems = src_v4->graph_elems; + dest->pcm_elems = src_v4->pcm_elems; + dest->dai_link_elems = src_v4->dai_link_elems; + dest->priv.size = src_v4->priv.size; + if (dest->priv.size) + memcpy(dest->priv.data, src_v4->priv.data, + src_v4->priv.size); + + *manifest = dest; + return 0; +} static int soc_tplg_manifest_load(struct soc_tplg *tplg, struct snd_soc_tplg_hdr *hdr) { - struct snd_soc_tplg_manifest *manifest; + struct snd_soc_tplg_manifest *manifest, *_manifest; + bool abi_match; + int err; if (tplg->pass != SOC_TPLG_PASS_MANIFEST) return 0; manifest = (struct snd_soc_tplg_manifest *)tplg->pos; - if (manifest->size != sizeof(*manifest)) { - dev_err(tplg->dev, "ASoC: invalid manifest size\n"); - return -EINVAL; - } - tplg->pos += sizeof(struct snd_soc_tplg_manifest); + /* check ABI version by size, create a new manifest if abi not match */ + if (manifest->size == sizeof(*manifest)) { + abi_match = true; + _manifest = manifest; + } else { + abi_match = false; + err = manifest_new_ver(tplg, manifest, &_manifest); + if (err < 0) + return err; + } + /* pass control to component driver for optional further init */ if (tplg->comp && tplg->ops && tplg->ops->manifest) - return tplg->ops->manifest(tplg->comp, manifest); + return tplg->ops->manifest(tplg->comp, _manifest); + + if (!abi_match) /* free the duplicated one */ + kfree(_manifest); - dev_err(tplg->dev, "ASoC: Firmware manifest not supported\n"); return 0; } -- cgit v1.2.3 From 55726dc95b78da32454878ac2b8a76daa53db396 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Thu, 3 Nov 2016 01:00:16 +0800 Subject: ASoC: topology: Make PCM backward compatible from ABI v4 This patch adds support for old version 4 of PCMs (FE DAI & DAI links). Topology ABI v5 added new fields to stream caps and thus changed PCMs. Since user space may still uses v4, kernel will check the ABI version by comparing the object size. If user space uses v4 of PCMs, kernel will create the latest version of PCMs from the old version, and use the new version internally to create FE DAI & DAI links. Because these new created PCM elements will be freed later, kernel need duplicate the name strings of DAI driver and DAI links when creating them. Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 145 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 133 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index c832b24b0cde..03edde57bfea 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -53,7 +53,6 @@ #define SOC_TPLG_PASS_START SOC_TPLG_PASS_MANIFEST #define SOC_TPLG_PASS_END SOC_TPLG_PASS_BE_DAI - /* * Old version of ABI structs, supported for backward compatibility. */ @@ -69,6 +68,39 @@ struct snd_soc_tplg_manifest_v4 { struct snd_soc_tplg_private priv; } __packed; +/* Stream Capabilities v4 */ +struct snd_soc_tplg_stream_caps_v4 { + __le32 size; /* in bytes of this structure */ + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + __le64 formats; /* supported formats SNDRV_PCM_FMTBIT_* */ + __le32 rates; /* supported rates SNDRV_PCM_RATE_* */ + __le32 rate_min; /* min rate */ + __le32 rate_max; /* max rate */ + __le32 channels_min; /* min channels */ + __le32 channels_max; /* max channels */ + __le32 periods_min; /* min number of periods */ + __le32 periods_max; /* max number of periods */ + __le32 period_size_min; /* min period size bytes */ + __le32 period_size_max; /* max period size bytes */ + __le32 buffer_size_min; /* min buffer size bytes */ + __le32 buffer_size_max; /* max buffer size bytes */ +} __packed; + +/* PCM v4 */ +struct snd_soc_tplg_pcm_v4 { + __le32 size; /* in bytes of this structure */ + char pcm_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + char dai_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + __le32 pcm_id; /* unique ID - used to match with DAI link */ + __le32 dai_id; /* unique ID - used to match */ + __le32 playback; /* supports playback mode */ + __le32 capture; /* supports capture mode */ + __le32 compress; /* 1 = compressed; 0 = PCM */ + struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* for DAI link */ + __le32 num_streams; /* number of streams */ + struct snd_soc_tplg_stream_caps_v4 caps[2]; /* playback and capture for DAI */ +} __packed; + /* topology context */ struct soc_tplg { const struct firmware *fw; @@ -1692,38 +1724,127 @@ static int soc_tplg_pcm_create(struct soc_tplg *tplg, return soc_tplg_link_create(tplg, pcm); } +/* copy stream caps from the old version 4 of source */ +static void stream_caps_new_ver(struct snd_soc_tplg_stream_caps *dest, + struct snd_soc_tplg_stream_caps_v4 *src) +{ + dest->size = sizeof(*dest); + memcpy(dest->name, src->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + dest->formats = src->formats; + dest->rates = src->rates; + dest->rate_min = src->rate_min; + dest->rate_max = src->rate_max; + dest->channels_min = src->channels_min; + dest->channels_max = src->channels_max; + dest->periods_min = src->periods_min; + dest->periods_max = src->periods_max; + dest->period_size_min = src->period_size_min; + dest->period_size_max = src->period_size_max; + dest->buffer_size_min = src->buffer_size_min; + dest->buffer_size_max = src->buffer_size_max; +} + +/** + * pcm_new_ver - Create the new version of PCM from the old version. + * @tplg: topology context + * @src: older version of pcm as a source + * @pcm: latest version of pcm created from the source + * + * Support from vesion 4. User should free the returned pcm manually. + */ +static int pcm_new_ver(struct soc_tplg *tplg, + struct snd_soc_tplg_pcm *src, + struct snd_soc_tplg_pcm **pcm) +{ + struct snd_soc_tplg_pcm *dest; + struct snd_soc_tplg_pcm_v4 *src_v4; + int i; + + *pcm = NULL; + + if (src->size != sizeof(*src_v4)) { + dev_err(tplg->dev, "ASoC: invalid PCM size\n"); + return -EINVAL; + } + + dev_warn(tplg->dev, "ASoC: old version of PCM\n"); + src_v4 = (struct snd_soc_tplg_pcm_v4 *)src; + dest = kzalloc(sizeof(*dest), GFP_KERNEL); + if (!dest) + return -ENOMEM; + + dest->size = sizeof(*dest); /* size of latest abi version */ + memcpy(dest->pcm_name, src_v4->pcm_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + memcpy(dest->dai_name, src_v4->dai_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + dest->pcm_id = src_v4->pcm_id; + dest->dai_id = src_v4->dai_id; + dest->playback = src_v4->playback; + dest->capture = src_v4->capture; + dest->compress = src_v4->compress; + 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)); + + for (i = 0; i < 2; i++) + stream_caps_new_ver(&dest->caps[i], &src_v4->caps[i]); + + *pcm = dest; + return 0; +} + static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg, struct snd_soc_tplg_hdr *hdr) { - struct snd_soc_tplg_pcm *pcm; + struct snd_soc_tplg_pcm *pcm, *_pcm; int count = hdr->count; - int i; + int i, err; + bool abi_match; if (tplg->pass != SOC_TPLG_PASS_PCM_DAI) return 0; + /* check the element size and count */ + pcm = (struct snd_soc_tplg_pcm *)tplg->pos; + if (pcm->size > sizeof(struct snd_soc_tplg_pcm) + || pcm->size < sizeof(struct snd_soc_tplg_pcm_v4)) { + dev_err(tplg->dev, "ASoC: invalid size %d for PCM elems\n", + pcm->size); + return -EINVAL; + } + if (soc_tplg_check_elem_count(tplg, - sizeof(struct snd_soc_tplg_pcm), count, + pcm->size, count, hdr->payload_size, "PCM DAI")) { dev_err(tplg->dev, "ASoC: invalid count %d for PCM DAI elems\n", count); return -EINVAL; } - /* create the FE DAIs and DAI links */ - pcm = (struct snd_soc_tplg_pcm *)tplg->pos; for (i = 0; i < count; i++) { - if (pcm->size != sizeof(*pcm)) { - dev_err(tplg->dev, "ASoC: invalid pcm size\n"); - return -EINVAL; + pcm = (struct snd_soc_tplg_pcm *)tplg->pos; + + /* check ABI version by size, create a new version of pcm + * if abi not match. + */ + if (pcm->size == sizeof(*pcm)) { + abi_match = true; + _pcm = pcm; + } else { + abi_match = false; + err = pcm_new_ver(tplg, pcm, &_pcm); } - soc_tplg_pcm_create(tplg, pcm); - pcm++; + /* create the FE DAIs and DAI links */ + soc_tplg_pcm_create(tplg, _pcm); + + if (!abi_match) + kfree(_pcm); /* free the duplicated one */ + + tplg->pos += pcm->size; /* offset by version-specific size */ } dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count); - tplg->pos += sizeof(struct snd_soc_tplg_pcm) * count; return 0; } -- cgit v1.2.3 From 8f27c4abc2cd7a31adb896a6b4861cdcb921d063 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Thu, 3 Nov 2016 01:02:59 +0800 Subject: ASoC: topology: Only use valid names of PCM for the kernel DAI & DAI link User space may not always set a valid FE DAI driver's name, FE DAI link's name, stream name or cpu DAI name. In such cases, there are all ZERO in these name string buffers of a topology PCM object. This patch will only duplicate valid name strings for kernel FE DAI driver and DAI link when creating them from topology, and free the name strings when destroying them. Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 03edde57bfea..0f1c8ebf8cda 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -523,6 +523,7 @@ static void remove_dai(struct snd_soc_component *comp, if (dobj->ops && dobj->ops->dai_unload) dobj->ops->dai_unload(comp, dobj); + kfree(dai_drv->name); list_del(&dobj->list); kfree(dai_drv); } @@ -540,6 +541,10 @@ static void remove_link(struct snd_soc_component *comp, if (dobj->ops && dobj->ops->link_unload) dobj->ops->link_unload(comp, dobj); + kfree(link->name); + kfree(link->stream_name); + kfree(link->cpu_dai_name); + list_del(&dobj->list); snd_soc_remove_dai_link(comp->card, link); kfree(link); @@ -1638,7 +1643,8 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, if (dai_drv == NULL) return -ENOMEM; - dai_drv->name = pcm->dai_name; + if (strlen(pcm->dai_name)) + dai_drv->name = kstrdup(pcm->dai_name, GFP_KERNEL); dai_drv->id = pcm->dai_id; if (pcm->playback) { @@ -1681,11 +1687,15 @@ static int soc_tplg_link_create(struct soc_tplg *tplg, if (link == NULL) return -ENOMEM; - link->name = pcm->pcm_name; - link->stream_name = pcm->pcm_name; + if (strlen(pcm->pcm_name)) { + link->name = kstrdup(pcm->pcm_name, GFP_KERNEL); + link->stream_name = kstrdup(pcm->pcm_name, GFP_KERNEL); + } link->id = pcm->pcm_id; - link->cpu_dai_name = pcm->dai_name; + if (strlen(pcm->dai_name)) + link->cpu_dai_name = kstrdup(pcm->dai_name, GFP_KERNEL); + link->codec_name = "snd-soc-dummy"; link->codec_dai_name = "snd-soc-dummy-dai"; -- cgit v1.2.3 From 288b8da7e992f0b86b283f98e92885781ffdcaee Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Thu, 3 Nov 2016 01:03:17 +0800 Subject: ASoC: topology: Support topology file of ABI v4 Users start to use topology ABI from v4. ABI v5 updated existing manifest and PCM elements. Two previous patches can support these ABI updates in a backward compatible way. So if the topology file from user space is generated by ABI v4, kernel will no longer quit but continue parsing. Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown --- include/uapi/sound/asoc.h | 3 ++- sound/soc/soc-topology.c | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index 8d9814b17c46..69cae7198b18 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -83,7 +83,8 @@ #define SND_SOC_TPLG_NUM_TEXTS 16 /* ABI version */ -#define SND_SOC_TPLG_ABI_VERSION 0x5 +#define SND_SOC_TPLG_ABI_VERSION 0x5 /* current version */ +#define SND_SOC_TPLG_ABI_VERSION_MIN 0x4 /* oldest version supported */ /* Max size of TLV data */ #define SND_SOC_TPLG_TLV_SIZE 32 diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 0f1c8ebf8cda..2f9b64ec5904 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -2056,7 +2056,9 @@ static int soc_valid_header(struct soc_tplg *tplg, return -EINVAL; } - if (hdr->abi != SND_SOC_TPLG_ABI_VERSION) { + /* Support ABI from version 4 */ + if (hdr->abi > SND_SOC_TPLG_ABI_VERSION + || hdr->abi < SND_SOC_TPLG_ABI_VERSION_MIN) { dev_err(tplg->dev, "ASoC: pass %d invalid ABI version got 0x%x need 0x%x at offset 0x%lx size 0x%zx.\n", tplg->pass, hdr->abi, -- cgit v1.2.3 From 717a8e7235377fa50f074c407cc5d0486aa15aa2 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Thu, 3 Nov 2016 01:03:34 +0800 Subject: ASoC: topology: ABI - Add flags and private data to PCM This is the remaining update to PCM ABI object of version 5. The flags will be applied to FE (Front End) links and can also be used by physical links. The private data is reserved for future extension, so offset update will add the private data size. Now user space is using ABI v4, and the previous patch "ASoC: topology: make PCM backward compatible from ABI v4" can assure the backward compatibility. Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown --- include/uapi/sound/asoc.h | 8 ++++++++ sound/soc/soc-topology.c | 28 ++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index 69cae7198b18..aeb408241bc3 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -146,6 +146,11 @@ #define SND_SOC_DAI_FORMAT_MSB SND_SOC_DAI_FORMAT_LEFT_J #define SND_SOC_DAI_FORMAT_LSB SND_SOC_DAI_FORMAT_RIGHT_J +/* DAI link flags */ +#define SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES (1 << 0) +#define SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS (1 << 1) +#define SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS (1 << 2) + /* * Block Header. * This header precedes all object and object arrays below. @@ -456,6 +461,9 @@ struct snd_soc_tplg_pcm { struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* for DAI link */ __le32 num_streams; /* number of streams */ struct snd_soc_tplg_stream_caps caps[2]; /* playback and capture for DAI */ + __le32 flag_mask; /* bitmask of flags to configure */ + __le32 flags; /* SND_SOC_TPLG_LNK_FLGBIT_* flag value */ + struct snd_soc_tplg_private priv; } __attribute__((packed)); diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 2f9b64ec5904..8baa1761b874 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1676,6 +1676,24 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, return snd_soc_register_dai(tplg->comp, dai_drv); } +static void set_link_flags(struct snd_soc_dai_link *link, + unsigned int flag_mask, unsigned int flags) +{ + 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; +} + /* create the FE DAI link */ static int soc_tplg_link_create(struct soc_tplg *tplg, struct snd_soc_tplg_pcm *pcm) @@ -1703,6 +1721,8 @@ static int soc_tplg_link_create(struct soc_tplg *tplg, link->dynamic = 1; link->dpcm_playback = pcm->playback; link->dpcm_capture = pcm->capture; + if (pcm->flag_mask) + set_link_flags(link, pcm->flag_mask, pcm->flags); /* pass control to component driver for optional further init */ ret = soc_tplg_dai_link_load(tplg, link); @@ -1848,10 +1868,14 @@ 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 + */ + tplg->pos += pcm->size + _pcm->priv.size; + if (!abi_match) kfree(_pcm); /* free the duplicated one */ - - tplg->pos += pcm->size; /* offset by version-specific size */ } dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count); -- cgit v1.2.3 From 17fb175520e5497d71351aa66a125324fcb625a7 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Thu, 3 Nov 2016 01:04:12 +0800 Subject: ASoC: Define API to find a dai link Define the API to find an existing DAI link of the soc card by matching the ID, name and stream name. Some cards may use unique ID for each DAI link, so matching ID is enough, and name or stream name are not necessary. But user need to specify name or stream name as well if not sure whether link ID is unique since most cards use 0 as the default link ID. Topology can use this API to find an existing BE link and configure it. Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown --- include/sound/soc.h | 3 +++ sound/soc/soc-core.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) (limited to 'sound') diff --git a/include/sound/soc.h b/include/sound/soc.h index 4f1c784e44f6..c3a38ee2b006 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1671,6 +1671,9 @@ int snd_soc_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); void snd_soc_remove_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); +struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card, + int id, const char *name, + const char *stream_name); int snd_soc_register_dai(struct snd_soc_component *component, struct snd_soc_dai_driver *dai_drv); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c0bbcd903261..a7a1ca40bcf5 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -972,6 +972,48 @@ struct snd_soc_dai *snd_soc_find_dai( } EXPORT_SYMBOL_GPL(snd_soc_find_dai); + +/** + * snd_soc_find_dai_link - Find a DAI link + * + * @card: soc card + * @id: DAI link ID to match + * @name: DAI link name to match, optional + * @stream name: DAI link stream name to match, optional + * + * This function will search all existing DAI links of the soc card to + * find the link of the same ID. Since DAI links may not have their + * unique ID, so name and stream name should also match if being + * specified. + * + * Return: pointer of DAI link, or NULL if not found. + */ +struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card, + int id, const char *name, + const char *stream_name) +{ + struct snd_soc_dai_link *link, *_link; + + lockdep_assert_held(&client_mutex); + + list_for_each_entry_safe(link, _link, &card->dai_link_list, list) { + if (link->id != id) + continue; + + if (name && (!link->name || strcmp(name, link->name))) + continue; + + if (stream_name && (!link->stream_name + || strcmp(stream_name, link->stream_name))) + continue; + + return link; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_find_dai_link); + static bool soc_is_dai_link_bound(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) { -- cgit v1.2.3 From 593d9e52f9bb84bb4d8965278632e8ec7319f336 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Thu, 3 Nov 2016 01:04:27 +0800 Subject: ASoC: topology: Add support to configure existing physical DAI links Topology will find an existing physical link (including BE link for DPCM) by checking its ID, name and stream name, and configure its physical audio format and flags. This support is backward compatible for old ABI v4. Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 206 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 204 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 8baa1761b874..b15a2049efcf 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 /* * 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: -- cgit v1.2.3 From ab4bc5eed8e9c9fa36d80d58c55300dd1eef29a3 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Thu, 3 Nov 2016 01:04:42 +0800 Subject: ASoC: topology: Rename the function to create a FE link Just code refactoring. The function soc_tplg_link_create() will create a front end link, not a physical link. So rename it to soc_tplg_fe_link_create(). Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index b15a2049efcf..eea28ade5b32 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1704,7 +1704,7 @@ static void set_link_flags(struct snd_soc_dai_link *link, } /* create the FE DAI link */ -static int soc_tplg_link_create(struct soc_tplg *tplg, +static int soc_tplg_fe_link_create(struct soc_tplg *tplg, struct snd_soc_tplg_pcm *pcm) { struct snd_soc_dai_link *link; @@ -1760,7 +1760,7 @@ static int soc_tplg_pcm_create(struct soc_tplg *tplg, if (ret < 0) return ret; - return soc_tplg_link_create(tplg, pcm); + return soc_tplg_fe_link_create(tplg, pcm); } /* copy stream caps from the old version 4 of source */ -- cgit v1.2.3 From 3fbf793510c7628248a965972112fab958e6e3cf Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Thu, 3 Nov 2016 01:05:01 +0800 Subject: ASoC: topology: ABI - Rename struct and type for physical DAIs Rename the ABI struct and type because they are for configuring physical DAIs, not only backend DAIs since users may not need DPCM: - Rename struct snd_soc_tplg_be_dai to snd_soc_tplg_dai. - Rename type SND_SOC_TPLG_TYPE_BE_DAI to SND_SOC_TPLG_TYPE_DAI. This code refactoring is backward compatible because: - Both layout of the struct and type value has no change. Kernel can find the same type value and map to same data layout. - This struct is not in ABI v4 at all. Now the user space uses ABI v4. Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown --- include/uapi/sound/asoc.h | 15 ++++++++------- sound/soc/soc-topology.c | 8 ++++---- 2 files changed, 12 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index ed28ebc392d1..3c3fcc86b9f4 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -111,8 +111,8 @@ #define SND_SOC_TPLG_TYPE_CODEC_LINK 9 #define SND_SOC_TPLG_TYPE_BACKEND_LINK 10 #define SND_SOC_TPLG_TYPE_PDATA 11 -#define SND_SOC_TPLG_TYPE_BE_DAI 12 -#define SND_SOC_TPLG_TYPE_MAX SND_SOC_TPLG_TYPE_BE_DAI +#define SND_SOC_TPLG_TYPE_DAI 12 +#define SND_SOC_TPLG_TYPE_MAX SND_SOC_TPLG_TYPE_DAI /* vendor block IDs - please add new vendor types to end */ #define SND_SOC_TPLG_TYPE_VENDOR_FW 1000 @@ -131,7 +131,7 @@ #define SND_SOC_TPLG_TUPLE_TYPE_WORD 4 #define SND_SOC_TPLG_TUPLE_TYPE_SHORT 5 -/* BE DAI flags */ +/* DAI flags */ #define SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES (1 << 0) #define SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS (1 << 1) #define SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS (1 << 2) @@ -527,16 +527,17 @@ struct snd_soc_tplg_link_config { } __attribute__((packed)); /* - * Describes SW/FW specific features of BE DAI. + * Describes SW/FW specific features of physical DAI. + * It can be used to configure backend DAIs for DPCM. * - * File block representation for BE DAI :- + * File block representation for physical DAI :- * +-----------------------------------+-----+ * | struct snd_soc_tplg_hdr | 1 | * +-----------------------------------+-----+ - * | struct snd_soc_tplg_be_dai | N | + * | struct snd_soc_tplg_dai | N | * +-----------------------------------+-----+ */ -struct snd_soc_tplg_be_dai { +struct snd_soc_tplg_dai { __le32 size; /* in bytes of this structure */ char dai_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; /* name - used to match */ __le32 dai_id; /* unique ID - used to match */ diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index eea28ade5b32..a839dc857e0d 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -2090,7 +2090,7 @@ static int soc_tplg_link_elems_load(struct soc_tplg *tplg, * platform driver should specify the BE DAI name and ID for matching. */ static int soc_tplg_be_dai_config(struct soc_tplg *tplg, - struct snd_soc_tplg_be_dai *be) + struct snd_soc_tplg_dai *be) { struct snd_soc_dai_link_component dai_component = {0}; struct snd_soc_dai *dai; @@ -2145,7 +2145,7 @@ static int soc_tplg_be_dai_config(struct soc_tplg *tplg, static int soc_tplg_be_dai_elems_load(struct soc_tplg *tplg, struct snd_soc_tplg_hdr *hdr) { - struct snd_soc_tplg_be_dai *be; + struct snd_soc_tplg_dai *be; int count = hdr->count; int i; @@ -2154,7 +2154,7 @@ static int soc_tplg_be_dai_elems_load(struct soc_tplg *tplg, /* config the existing BE DAIs */ for (i = 0; i < count; i++) { - be = (struct snd_soc_tplg_be_dai *)tplg->pos; + be = (struct snd_soc_tplg_dai *)tplg->pos; if (be->size != sizeof(*be)) { dev_err(tplg->dev, "ASoC: invalid BE DAI size\n"); return -EINVAL; @@ -2328,7 +2328,7 @@ static int soc_tplg_load_header(struct soc_tplg *tplg, return soc_tplg_dapm_widget_elems_load(tplg, hdr); case SND_SOC_TPLG_TYPE_PCM: return soc_tplg_pcm_elems_load(tplg, hdr); - case SND_SOC_TPLG_TYPE_BE_DAI: + case SND_SOC_TPLG_TYPE_DAI: return soc_tplg_be_dai_elems_load(tplg, hdr); case SND_SOC_TPLG_TYPE_DAI_LINK: case SND_SOC_TPLG_TYPE_BACKEND_LINK: -- cgit v1.2.3 From 9aa3f03473ce1187062bb0c68aeacf7b3ac52831 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Thu, 3 Nov 2016 01:05:15 +0800 Subject: ASoC: topology: Rename functions & variables for physical DAIs Code refactoring. These functions and variables are for configuring physical DAIs, not only backend DAIs since users may not need DPCM. So remove 'be' from the function names, and rename variables 'be' to 'd' or 'dai'. Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 57 ++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 28 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index a839dc857e0d..f44f12eb1798 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -2081,16 +2081,16 @@ static int soc_tplg_link_elems_load(struct soc_tplg *tplg, return 0; } -/* * - * soc_tplg_be_dai_config - Find and configure an existing BE DAI. +/** + * soc_tplg_dai_config - Find and configure an existing physical DAI. * @tplg: topology context - * @be: topology BE DAI configs. + * @d: physical DAI configs. * - * The BE dai should already be registered by the platform driver. The - * platform driver should specify the BE DAI name and ID for matching. + * The physical dai should already be registered by the platform driver. + * The platform driver should specify the DAI name and ID for matching. */ -static int soc_tplg_be_dai_config(struct soc_tplg *tplg, - struct snd_soc_tplg_dai *be) +static int soc_tplg_dai_config(struct soc_tplg *tplg, + struct snd_soc_tplg_dai *d) { struct snd_soc_dai_link_component dai_component = {0}; struct snd_soc_dai *dai; @@ -2099,17 +2099,17 @@ static int soc_tplg_be_dai_config(struct soc_tplg *tplg, struct snd_soc_tplg_stream_caps *caps; int ret; - dai_component.dai_name = be->dai_name; + dai_component.dai_name = d->dai_name; dai = snd_soc_find_dai(&dai_component); if (!dai) { - dev_err(tplg->dev, "ASoC: BE DAI %s not registered\n", - be->dai_name); + dev_err(tplg->dev, "ASoC: physical DAI %s not registered\n", + d->dai_name); return -EINVAL; } - if (be->dai_id != dai->id) { - dev_err(tplg->dev, "ASoC: BE DAI %s id mismatch\n", - be->dai_name); + if (d->dai_id != dai->id) { + dev_err(tplg->dev, "ASoC: physical DAI %s id mismatch\n", + d->dai_name); return -EINVAL; } @@ -2117,20 +2117,20 @@ static int soc_tplg_be_dai_config(struct soc_tplg *tplg, if (!dai_drv) return -EINVAL; - if (be->playback) { + if (d->playback) { stream = &dai_drv->playback; - caps = &be->caps[SND_SOC_TPLG_STREAM_PLAYBACK]; + caps = &d->caps[SND_SOC_TPLG_STREAM_PLAYBACK]; set_stream_info(stream, caps); } - if (be->capture) { + if (d->capture) { stream = &dai_drv->capture; - caps = &be->caps[SND_SOC_TPLG_STREAM_CAPTURE]; + caps = &d->caps[SND_SOC_TPLG_STREAM_CAPTURE]; set_stream_info(stream, caps); } - if (be->flag_mask) - set_dai_flags(dai_drv, be->flag_mask, be->flags); + if (d->flag_mask) + set_dai_flags(dai_drv, d->flag_mask, d->flags); /* pass control to component driver for optional further init */ ret = soc_tplg_dai_load(tplg, dai_drv); @@ -2142,10 +2142,11 @@ static int soc_tplg_be_dai_config(struct soc_tplg *tplg, return 0; } -static int soc_tplg_be_dai_elems_load(struct soc_tplg *tplg, - struct snd_soc_tplg_hdr *hdr) +/* load physical DAI elements */ +static int soc_tplg_dai_elems_load(struct soc_tplg *tplg, + struct snd_soc_tplg_hdr *hdr) { - struct snd_soc_tplg_dai *be; + struct snd_soc_tplg_dai *dai; int count = hdr->count; int i; @@ -2154,14 +2155,14 @@ static int soc_tplg_be_dai_elems_load(struct soc_tplg *tplg, /* config the existing BE DAIs */ for (i = 0; i < count; i++) { - be = (struct snd_soc_tplg_dai *)tplg->pos; - if (be->size != sizeof(*be)) { - dev_err(tplg->dev, "ASoC: invalid BE DAI size\n"); + dai = (struct snd_soc_tplg_dai *)tplg->pos; + if (dai->size != sizeof(*dai)) { + dev_err(tplg->dev, "ASoC: invalid physical DAI size\n"); return -EINVAL; } - soc_tplg_be_dai_config(tplg, be); - tplg->pos += (sizeof(*be) + be->priv.size); + soc_tplg_dai_config(tplg, dai); + tplg->pos += (sizeof(*dai) + dai->priv.size); } dev_dbg(tplg->dev, "ASoC: Configure %d BE DAIs\n", count); @@ -2329,7 +2330,7 @@ static int soc_tplg_load_header(struct soc_tplg *tplg, case SND_SOC_TPLG_TYPE_PCM: return soc_tplg_pcm_elems_load(tplg, hdr); case SND_SOC_TPLG_TYPE_DAI: - return soc_tplg_be_dai_elems_load(tplg, hdr); + return soc_tplg_dai_elems_load(tplg, hdr); case SND_SOC_TPLG_TYPE_DAI_LINK: case SND_SOC_TPLG_TYPE_BACKEND_LINK: /* physical link configurations */ -- cgit v1.2.3 From 6ff67ccafdf4c782489de1ccc47e1ec8d8480b63 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Thu, 3 Nov 2016 01:05:32 +0800 Subject: ASoC: topology: ABI - Add voice wake up flag for DAI links Add a new flag bit SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP to link flags. If a link is used for voice wake up, users can set this flag bit and topology will set the link's 'ignore_suspend' to true. This ABI update is backward compatible. Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown --- include/uapi/sound/asoc.h | 1 + sound/soc/soc-topology.c | 5 +++++ 2 files changed, 6 insertions(+) (limited to 'sound') diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index 3c3fcc86b9f4..6a4280c6e860 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -155,6 +155,7 @@ #define SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES (1 << 0) #define SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS (1 << 1) #define SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS (1 << 2) +#define SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP (1 << 3) /* * Block Header. diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index f44f12eb1798..8772fd994e82 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1701,6 +1701,11 @@ static void set_link_flags(struct snd_soc_dai_link *link, link->symmetric_samplebits = flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS ? 1 : 0; + + if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP) + link->ignore_suspend = + flags & SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP ? + 1 : 0; } /* create the FE DAI link */ -- cgit v1.2.3 From dbab1cb88e84813254091d0d02ab83d9929e6a27 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Sat, 5 Nov 2016 08:42:14 +0800 Subject: ASoC: topology: Check name strings of physical DAI links Check if the name strings are properly terminated, and only use valid name strings to find existing physical DAI links to configure. Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 8772fd994e82..4dfdc656cce6 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1994,10 +1994,24 @@ static int soc_tplg_link_config(struct soc_tplg *tplg, { struct snd_soc_dai_link *link; const char *name, *stream_name; + size_t len; int ret; - name = strlen(cfg->name) ? cfg->name : NULL; - stream_name = strlen(cfg->stream_name) ? cfg->stream_name : NULL; + len = strnlen(cfg->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + if (len == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; + else if (len) + name = cfg->name; + else + name = NULL; + + len = strnlen(cfg->stream_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + if (len == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; + else if (len) + stream_name = cfg->stream_name; + else + stream_name = NULL; link = snd_soc_find_dai_link(tplg->comp->card, cfg->id, name, stream_name); -- cgit v1.2.3 From 4090d63b49d7d440ff984db3715d5c780209e8e8 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 10 Nov 2016 15:34:52 +0000 Subject: ASoC: wm2200: Correct types of mixer texts and values The core expects "const char * const" and "unsigned int" for enum controls, the mixer control definitions in wm2200 use "const char *" and "int". This patch corrects the type of these arrays. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm2200.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index 606bf88abfc4..d83dab57a1d1 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -999,7 +999,7 @@ static DECLARE_TLV_DB_SCALE(in_tlv, -6300, 100, 0); static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); static DECLARE_TLV_DB_SCALE(out_tlv, -6400, 100, 0); -static const char *wm2200_mixer_texts[] = { +static const char * const wm2200_mixer_texts[] = { "None", "Tone Generator", "AEC Loopback", @@ -1033,7 +1033,7 @@ static const char *wm2200_mixer_texts[] = { "DSP2.6", }; -static int wm2200_mixer_values[] = { +static unsigned int wm2200_mixer_values[] = { 0x00, 0x04, /* Tone */ 0x08, /* AEC */ -- cgit v1.2.3 From a4a1d79ea75eec48e086648678b62a8220fe3723 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 10 Nov 2016 09:55:54 +0200 Subject: ASoC: tlv320aic31xx: Add missing of_device_id for dac3100 The compatible table was not updated when the support for DAC3100 was added. Signed-off-by: Peter Ujfalusi Reviewed-by: Jyri Sarha Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic31xx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index be1a64bfd320..8880f6be3336 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -1253,6 +1253,7 @@ static const struct of_device_id tlv320aic31xx_of_match[] = { { .compatible = "ti,tlv320aic3110" }, { .compatible = "ti,tlv320aic3120" }, { .compatible = "ti,tlv320aic3111" }, + { .compatible = "ti,tlv320dac3100" }, {}, }; MODULE_DEVICE_TABLE(of, tlv320aic31xx_of_match); -- cgit v1.2.3 From 4e2cc814eb2755df286ea890078fb636e1aa0f69 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 10 Nov 2016 09:55:55 +0200 Subject: ASoC: tlv320aic31xx: Add support for tlv320dac3101 The DAC3101 is mostly identical to DAC3100 with the exception that it has stereo speaker AMP instead of mono used in DAC3100. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/tlv320aic31xx.txt | 1 + sound/soc/codecs/tlv320aic31xx.c | 2 ++ sound/soc/codecs/tlv320aic31xx.h | 1 + 3 files changed, 4 insertions(+) (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/tlv320aic31xx.txt b/Documentation/devicetree/bindings/sound/tlv320aic31xx.txt index 9340d2ddcc54..6fbba562eaa7 100644 --- a/Documentation/devicetree/bindings/sound/tlv320aic31xx.txt +++ b/Documentation/devicetree/bindings/sound/tlv320aic31xx.txt @@ -12,6 +12,7 @@ Required properties: "ti,tlv320aic3120" - TLV320AIC3120 (mono speaker amp, MiniDSP) "ti,tlv320aic3111" - TLV320AIC3111 (stereo speaker amp, MiniDSP) "ti,tlv320dac3100" - TLV320DAC3100 (no ADC, mono speaker amp, no MiniDSP) + "ti,tlv320dac3101" - TLV320DAC3101 (no ADC, stereo speaker amp, no MiniDSP) - reg - - I2C slave address - HPVDD-supply, SPRVDD-supply, SPLVDD-supply, AVDD-supply, IOVDD-supply, diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 8880f6be3336..f8a90ba8cd71 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -1254,6 +1254,7 @@ static const struct of_device_id tlv320aic31xx_of_match[] = { { .compatible = "ti,tlv320aic3120" }, { .compatible = "ti,tlv320aic3111" }, { .compatible = "ti,tlv320dac3100" }, + { .compatible = "ti,tlv320dac3101" }, {}, }; MODULE_DEVICE_TABLE(of, tlv320aic31xx_of_match); @@ -1380,6 +1381,7 @@ static const struct i2c_device_id aic31xx_i2c_id[] = { { "tlv320aic3120", AIC3120 }, { "tlv320aic3111", AIC3111 }, { "tlv320dac3100", DAC3100 }, + { "tlv320dac3101", DAC3101 }, { } }; MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id); diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h index 5acd5b69fb83..730fb2058869 100644 --- a/sound/soc/codecs/tlv320aic31xx.h +++ b/sound/soc/codecs/tlv320aic31xx.h @@ -32,6 +32,7 @@ enum aic31xx_type { AIC3120 = AIC31XX_MINIDSP_BIT, AIC3111 = (AIC31XX_STEREO_CLASS_D_BIT | AIC31XX_MINIDSP_BIT), DAC3100 = DAC31XX_BIT, + DAC3101 = DAC31XX_BIT | AIC31XX_STEREO_CLASS_D_BIT, }; struct aic31xx_pdata { -- cgit v1.2.3 From 4a5cf1320a9501261919cf96af300b1df3ad0210 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 17 Nov 2016 01:12:30 +0000 Subject: ASoC: uda1380: Remove #if IS_ENABLED(CONFIG_I2C) uda1380 driver doesn't work without CONFIG_I2C anyway. Let's remove #if IS_ENABLED(CONFIG_I2C), and use module_i2c_driver(). And, this patch adds "depends on I2C" to Kconfig Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 + sound/soc/codecs/uda1380.c | 22 +--------------------- 2 files changed, 2 insertions(+), 21 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index c67667bb970f..98b9a0c35d6b 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -874,6 +874,7 @@ config SND_SOC_UDA134X config SND_SOC_UDA1380 tristate + depends on I2C config SND_SOC_WL1273 tristate diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 533e3bb444e4..8e52439a58fd 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -775,7 +775,6 @@ static struct snd_soc_codec_driver soc_codec_dev_uda1380 = { }, }; -#if IS_ENABLED(CONFIG_I2C) static int uda1380_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -815,27 +814,8 @@ static struct i2c_driver uda1380_i2c_driver = { .remove = uda1380_i2c_remove, .id_table = uda1380_i2c_id, }; -#endif -static int __init uda1380_modinit(void) -{ - int ret = 0; -#if IS_ENABLED(CONFIG_I2C) - ret = i2c_add_driver(&uda1380_i2c_driver); - if (ret != 0) - pr_err("Failed to register UDA1380 I2C driver: %d\n", ret); -#endif - return ret; -} -module_init(uda1380_modinit); - -static void __exit uda1380_exit(void) -{ -#if IS_ENABLED(CONFIG_I2C) - i2c_del_driver(&uda1380_i2c_driver); -#endif -} -module_exit(uda1380_exit); +module_i2c_driver(uda1380_i2c_driver); MODULE_AUTHOR("Giorgio Padrin"); MODULE_DESCRIPTION("Audio support for codec Philips UDA1380"); -- cgit v1.2.3 From f196e9ac07ed1212ce7d106c07a25019c9c1ee18 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 17 Nov 2016 01:13:35 +0000 Subject: ASoC: wm8523: Remove #if IS_ENABLED(CONFIG_I2C) wm8523 driver doesn't work without CONFIG_I2C anyway. Let's remove #if IS_ENABLED(CONFIG_I2C), and use module_i2c_driver(). Signed-off-by: Kuninori Morimoto Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8523.c | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index deb2e075428e..6d0a2723bfde 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -446,7 +446,6 @@ static const struct regmap_config wm8523_regmap = { .volatile_reg = wm8523_volatile_register, }; -#if IS_ENABLED(CONFIG_I2C) static int wm8523_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -543,29 +542,8 @@ static struct i2c_driver wm8523_i2c_driver = { .remove = wm8523_i2c_remove, .id_table = wm8523_i2c_id, }; -#endif -static int __init wm8523_modinit(void) -{ - int ret; -#if IS_ENABLED(CONFIG_I2C) - ret = i2c_add_driver(&wm8523_i2c_driver); - if (ret != 0) { - printk(KERN_ERR "Failed to register WM8523 I2C driver: %d\n", - ret); - } -#endif - return 0; -} -module_init(wm8523_modinit); - -static void __exit wm8523_exit(void) -{ -#if IS_ENABLED(CONFIG_I2C) - i2c_del_driver(&wm8523_i2c_driver); -#endif -} -module_exit(wm8523_exit); +module_i2c_driver(wm8523_i2c_driver); MODULE_DESCRIPTION("ASoC WM8523 driver"); MODULE_AUTHOR("Mark Brown "); -- cgit v1.2.3 From 4185d4be22a3fbf8e065437883ced195e692642b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 23 Nov 2016 10:30:10 +0100 Subject: ASoC: uda1380: Remove unused DAI ID defines The DAI ID defines are back from the time when DAIs were referenced by a numerical ID. These days a string is used for matching instead and the defines are unused. The last user of these defines was removed in commit f0fba2ad1b6b ("ASoC: multi-component - ASoC Multi-Component Support"). So remove the defines as well. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/uda1380.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/uda1380.h b/sound/soc/codecs/uda1380.h index 942e3927c72b..69a326ac3c1a 100644 --- a/sound/soc/codecs/uda1380.h +++ b/sound/soc/codecs/uda1380.h @@ -72,8 +72,4 @@ #define R22_SKIP_DCFIL 0x0002 #define R23_AGC_EN 0x0001 -#define UDA1380_DAI_DUPLEX 0 /* playback and capture on single DAI */ -#define UDA1380_DAI_PLAYBACK 1 /* playback DAI */ -#define UDA1380_DAI_CAPTURE 2 /* capture DAI */ - #endif /* _UDA1380_H */ -- cgit v1.2.3 From 222e728ca29b5976accd1fc520a716d168ee2ca5 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 23 Nov 2016 10:30:11 +0100 Subject: ASoC: uda1380: Request GPIOs at bus probe time Resources should be requested when the device is probed on the control bus rather then when the CODEC is bound to the sound card. This allows things like probe deferring and device managed allocations to work. So move the GPIO request calls from the CODEC probe to the bus probe and also make them managed along the way. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/uda1380.c | 55 +++++++++++++++++----------------------------- 1 file changed, 20 insertions(+), 35 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 8e52439a58fd..2918fdb95e58 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -698,25 +698,10 @@ static int uda1380_probe(struct snd_soc_codec *codec) codec->hw_write = (hw_write_t)i2c_master_send; codec->control_data = uda1380->control_data; - if (!pdata) - return -EINVAL; - - if (gpio_is_valid(pdata->gpio_reset)) { - ret = gpio_request_one(pdata->gpio_reset, GPIOF_OUT_INIT_LOW, - "uda1380 reset"); - if (ret) - goto err_out; - } - - if (gpio_is_valid(pdata->gpio_power)) { - ret = gpio_request_one(pdata->gpio_power, GPIOF_OUT_INIT_LOW, - "uda1380 power"); - if (ret) - goto err_free_gpio; - } else { + if (!gpio_is_valid(pdata->gpio_power)) { ret = uda1380_reset(codec); if (ret) - goto err_free_gpio; + return ret; } INIT_WORK(&uda1380->work, uda1380_flush_work); @@ -732,29 +717,11 @@ static int uda1380_probe(struct snd_soc_codec *codec) break; } - return 0; - -err_free_gpio: - if (gpio_is_valid(pdata->gpio_reset)) - gpio_free(pdata->gpio_reset); -err_out: - return ret; -} - -/* power down chip */ -static int uda1380_remove(struct snd_soc_codec *codec) -{ - struct uda1380_platform_data *pdata =codec->dev->platform_data; - - gpio_free(pdata->gpio_reset); - gpio_free(pdata->gpio_power); - return 0; } static struct snd_soc_codec_driver soc_codec_dev_uda1380 = { .probe = uda1380_probe, - .remove = uda1380_remove, .read = uda1380_read_reg_cache, .write = uda1380_write, .set_bias_level = uda1380_set_bias_level, @@ -778,14 +745,32 @@ static struct snd_soc_codec_driver soc_codec_dev_uda1380 = { static int uda1380_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct uda1380_platform_data *pdata = i2c->dev.platform_data; struct uda1380_priv *uda1380; int ret; + if (!pdata) + return -EINVAL; + uda1380 = devm_kzalloc(&i2c->dev, sizeof(struct uda1380_priv), GFP_KERNEL); if (uda1380 == NULL) return -ENOMEM; + if (gpio_is_valid(pdata->gpio_reset)) { + ret = devm_gpio_request_one(&i2c->dev, pdata->gpio_reset, + GPIOF_OUT_INIT_LOW, "uda1380 reset"); + if (ret) + return ret; + } + + if (gpio_is_valid(pdata->gpio_power)) { + ret = devm_gpio_request_one(&i2c->dev, pdata->gpio_power, + GPIOF_OUT_INIT_LOW, "uda1380 power"); + if (ret) + return ret; + } + i2c_set_clientdata(i2c, uda1380); uda1380->control_data = i2c; -- cgit v1.2.3 From 1a7dd6e2f192960229dd3113a35c74690e503ea2 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 25 Nov 2016 16:09:10 +0800 Subject: ASoC: topology: Allow a widget to have multiple enum controls This patch can create multiple enumerated mixer controls for a widget. Previously topology kernel driver assumes a widget can have only one emumerated mixer control. We need to remove this restriction for Broxton. Its firmware modules (widgets) may need multiple enum controls based on the channel and MIC combination. No ABI change is needed. The ABI allows a widget to embed multiple controls. Reported-by: G Kranthi Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 163 +++++++++++++++++++++++++---------------------- 1 file changed, 87 insertions(+), 76 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 4dfdc656cce6..63e1a50f2161 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -486,21 +486,24 @@ static void remove_widget(struct snd_soc_component *comp, dobj->ops->widget_unload(comp, dobj); /* - * Dynamic Widgets either have 1 enum kcontrol or 1..N mixers. + * Dynamic Widgets either have 1..N enum kcontrols or mixers. * The enum may either have an array of values or strings. */ if (dobj->widget.kcontrol_enum) { /* enumerated widget mixer */ - struct soc_enum *se = - (struct soc_enum *)w->kcontrols[0]->private_value; + for (i = 0; i < w->num_kcontrols; i++) { + struct snd_kcontrol *kcontrol = w->kcontrols[i]; + struct soc_enum *se = + (struct soc_enum *)kcontrol->private_value; - snd_ctl_remove(card, w->kcontrols[0]); + snd_ctl_remove(card, kcontrol); - kfree(se->dobj.control.dvalues); - for (i = 0; i < se->items; i++) - kfree(se->dobj.control.dtexts[i]); + kfree(se->dobj.control.dvalues); + for (i = 0; i < se->items; i++) + kfree(se->dobj.control.dtexts[i]); - kfree(se); + kfree(se); + } kfree(w->kcontrol_news); } else { /* non enumerated widget mixer */ @@ -1256,98 +1259,105 @@ err: } static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create( - struct soc_tplg *tplg) + struct soc_tplg *tplg, int num_kcontrols) { struct snd_kcontrol_new *kc; struct snd_soc_tplg_enum_control *ec; struct soc_enum *se; - int i, err; - - ec = (struct snd_soc_tplg_enum_control *)tplg->pos; - tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + - ec->priv.size); - - /* validate kcontrol */ - if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) - return NULL; + int i, j, err; - kc = kzalloc(sizeof(*kc), GFP_KERNEL); + kc = kcalloc(num_kcontrols, sizeof(*kc), GFP_KERNEL); if (kc == NULL) return NULL; - se = kzalloc(sizeof(*se), GFP_KERNEL); - if (se == NULL) - goto err; + for (i = 0; i < num_kcontrols; i++) { + ec = (struct snd_soc_tplg_enum_control *)tplg->pos; + /* validate kcontrol */ + if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return NULL; + + se = kzalloc(sizeof(*se), GFP_KERNEL); + if (se == NULL) + goto err; - dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n", - ec->hdr.name); + dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n", + ec->hdr.name); - kc->name = ec->hdr.name; - kc->private_value = (long)se; - kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc->access = ec->hdr.access; + kc[i].name = ec->hdr.name; + kc[i].private_value = (long)se; + kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc[i].access = ec->hdr.access; - /* we only support FL/FR channel mapping atm */ - se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); - se->shift_l = tplc_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FL); - se->shift_r = tplc_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FR); + /* we only support FL/FR channel mapping atm */ + se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); + se->shift_l = tplc_chan_get_shift(tplg, ec->channel, + SNDRV_CHMAP_FL); + se->shift_r = tplc_chan_get_shift(tplg, ec->channel, + SNDRV_CHMAP_FR); - se->items = ec->items; - se->mask = ec->mask; - se->dobj.index = tplg->index; + se->items = ec->items; + se->mask = ec->mask; + se->dobj.index = tplg->index; - switch (ec->hdr.ops.info) { - case SND_SOC_TPLG_CTL_ENUM_VALUE: - case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: - err = soc_tplg_denum_create_values(se, ec); - if (err < 0) { - dev_err(tplg->dev, "ASoC: could not create values for %s\n", - ec->hdr.name); + switch (ec->hdr.ops.info) { + case SND_SOC_TPLG_CTL_ENUM_VALUE: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: + err = soc_tplg_denum_create_values(se, ec); + if (err < 0) { + dev_err(tplg->dev, "ASoC: could not create values for %s\n", + ec->hdr.name); + goto err_se; + } + /* fall through to create texts */ + case SND_SOC_TPLG_CTL_ENUM: + case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: + err = soc_tplg_denum_create_texts(se, ec); + if (err < 0) { + dev_err(tplg->dev, "ASoC: could not create texts for %s\n", + ec->hdr.name); + goto err_se; + } + break; + default: + dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n", + ec->hdr.ops.info, ec->hdr.name); goto err_se; } - /* fall through to create texts */ - case SND_SOC_TPLG_CTL_ENUM: - case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: - case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: - err = soc_tplg_denum_create_texts(se, ec); + + /* map io handlers */ + err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc[i], tplg); + if (err) { + soc_control_err(tplg, &ec->hdr, ec->hdr.name); + goto err_se; + } + + /* pass control to driver for optional further init */ + err = soc_tplg_init_kcontrol(tplg, &kc[i], + (struct snd_soc_tplg_ctl_hdr *)ec); if (err < 0) { - dev_err(tplg->dev, "ASoC: could not create texts for %s\n", + dev_err(tplg->dev, "ASoC: failed to init %s\n", ec->hdr.name); goto err_se; } - break; - default: - dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n", - ec->hdr.ops.info, ec->hdr.name); - goto err_se; - } - /* map io handlers */ - err = soc_tplg_kcontrol_bind_io(&ec->hdr, kc, tplg); - if (err) { - soc_control_err(tplg, &ec->hdr, ec->hdr.name); - goto err_se; - } - - /* pass control to driver for optional further init */ - err = soc_tplg_init_kcontrol(tplg, kc, - (struct snd_soc_tplg_ctl_hdr *)ec); - if (err < 0) { - dev_err(tplg->dev, "ASoC: failed to init %s\n", - ec->hdr.name); - goto err_se; + tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + + ec->priv.size); } return kc; err_se: - /* free values and texts */ - kfree(se->dobj.control.dvalues); - for (i = 0; i < ec->items; i++) - kfree(se->dobj.control.dtexts[i]); + for (; i >= 0; i--) { + /* free values and texts */ + se = (struct soc_enum *)kc[i].private_value; + kfree(se->dobj.control.dvalues); + for (j = 0; j < ec->items; j++) + kfree(se->dobj.control.dtexts[j]); - kfree(se); + kfree(se); + } err: kfree(kc); @@ -1499,9 +1509,10 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: template.dobj.widget.kcontrol_enum = 1; - template.num_kcontrols = 1; + template.num_kcontrols = w->num_kcontrols; template.kcontrol_news = - soc_tplg_dapm_widget_denum_create(tplg); + soc_tplg_dapm_widget_denum_create(tplg, + template.num_kcontrols); if (!template.kcontrol_news) { ret = -ENOMEM; goto hdr_err; -- cgit v1.2.3 From eea3dd4f1247aa8654194fb19ade22c94c42e41a Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 25 Nov 2016 16:09:17 +0800 Subject: ASoC: topology: Only free TLV for volume mixers of a widget This patch will check the type of embedded controls for a widget, and only free the TLV of volume mixers. Bytes controls don't have TLV. Just free the private value which is used as struct soc_mixer_control for volume mixers or soc_bytes_ext for bytes controls. No need to cast to these types before freeing it. Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown --- include/sound/soc-topology.h | 2 +- sound/soc/soc-topology.c | 25 +++++++++++++++++-------- 2 files changed, 18 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h index b897b9d63161..f9cc7b9271ac 100644 --- a/include/sound/soc-topology.h +++ b/include/sound/soc-topology.h @@ -53,7 +53,7 @@ struct snd_soc_dobj_control { /* dynamic widget object */ struct snd_soc_dobj_widget { - unsigned int kcontrol_enum:1; /* this widget is an enum kcontrol */ + unsigned int kcontrol_type; /* kcontrol type: mixer, enum, bytes */ }; /* generic dynamic object - all dynamic objects belong to this struct */ diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 63e1a50f2161..11feb19e9730 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -489,7 +489,7 @@ static void remove_widget(struct snd_soc_component *comp, * Dynamic Widgets either have 1..N enum kcontrols or mixers. * The enum may either have an array of values or strings. */ - if (dobj->widget.kcontrol_enum) { + if (dobj->widget.kcontrol_type == SND_SOC_TPLG_TYPE_ENUM) { /* enumerated widget mixer */ for (i = 0; i < w->num_kcontrols; i++) { struct snd_kcontrol *kcontrol = w->kcontrols[i]; @@ -506,16 +506,21 @@ static void remove_widget(struct snd_soc_component *comp, } kfree(w->kcontrol_news); } else { - /* non enumerated widget mixer */ + /* volume mixer or bytes controls */ for (i = 0; i < w->num_kcontrols; i++) { struct snd_kcontrol *kcontrol = w->kcontrols[i]; - struct soc_mixer_control *sm = - (struct soc_mixer_control *) kcontrol->private_value; - kfree(w->kcontrols[i]->tlv.p); + if (dobj->widget.kcontrol_type + == SND_SOC_TPLG_TYPE_MIXER) + kfree(kcontrol->tlv.p); - snd_ctl_remove(card, w->kcontrols[i]); - kfree(sm); + snd_ctl_remove(card, kcontrol); + + /* Private value is used as struct soc_mixer_control + * for volume mixers or soc_bytes_ext for bytes + * controls. + */ + kfree((void *)kcontrol->private_value); } kfree(w->kcontrol_news); } @@ -1439,6 +1444,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, struct snd_soc_dapm_widget template, *widget; struct snd_soc_tplg_ctl_hdr *control_hdr; struct snd_soc_card *card = tplg->comp->card; + unsigned int kcontrol_type; int ret = 0; if (strnlen(w->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == @@ -1494,6 +1500,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, case SND_SOC_TPLG_CTL_VOLSW_XR_SX: case SND_SOC_TPLG_CTL_RANGE: case SND_SOC_TPLG_DAPM_CTL_VOLSW: + kcontrol_type = SND_SOC_TPLG_TYPE_MIXER; /* volume mixer */ template.num_kcontrols = w->num_kcontrols; template.kcontrol_news = soc_tplg_dapm_widget_dmixer_create(tplg, @@ -1508,7 +1515,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: - template.dobj.widget.kcontrol_enum = 1; + kcontrol_type = SND_SOC_TPLG_TYPE_ENUM; /* enumerated mixer */ template.num_kcontrols = w->num_kcontrols; template.kcontrol_news = soc_tplg_dapm_widget_denum_create(tplg, @@ -1519,6 +1526,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, } break; case SND_SOC_TPLG_CTL_BYTES: + kcontrol_type = SND_SOC_TPLG_TYPE_BYTES; /* bytes control */ template.num_kcontrols = w->num_kcontrols; template.kcontrol_news = soc_tplg_dapm_widget_dbytes_create(tplg, @@ -1555,6 +1563,7 @@ widget: } widget->dobj.type = SND_SOC_DOBJ_WIDGET; + widget->dobj.widget.kcontrol_type = kcontrol_type; widget->dobj.ops = tplg->ops; widget->dobj.index = tplg->index; kfree(template.sname); -- cgit v1.2.3 From dd5abb742071a962be4403f171063ed1e9d4202d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 9 Dec 2016 12:51:46 +0100 Subject: ASoC: topology: avoid uninitialized kcontrol_type When num_kcontrols is zero, widget->dobj.widget.kcontrol_type gets set to an uninitialized local variable: sound/soc/soc-topology.c: In function 'soc_tplg_dapm_widget_create': sound/soc/soc-topology.c:1566:36: error: 'kcontrol_type' may be used uninitialized in this function [-Werror=maybe-uninitialized] I could not figure out which of the valid types would be appropriate here, so this sets it to '0', which is invalid but at least well-defined here. There is probably a better way to address the issue. Fixes: eea3dd4f1247 ("ASoC: topology: Only free TLV for volume mixers of a widget") Signed-off-by: Arnd Bergmann Reported-by: Dan Carpenter Reviewed-by: Takashi Sakamoto Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 11feb19e9730..65670b2b408c 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1485,6 +1485,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, tplg->pos += (sizeof(struct snd_soc_tplg_dapm_widget) + w->priv.size); if (w->num_kcontrols == 0) { + kcontrol_type = 0; template.num_kcontrols = 0; goto widget; } -- cgit v1.2.3