Re: [PATCH] ASoC: codecs: dmic: Use channel map for configs with a single mic

From: Matthias Kaehlcke
Date: Thu Jan 04 2018 - 14:54:45 EST


+ Brian & Dylan

El Thu, Jan 04, 2018 at 11:48:48AM -0800 Matthias Kaehlcke ha dit:

> The DMIC DAI driver specifies a number of 1 to 8 channels for each DAI.
> The actual number of mics can currently not be configured in the device
> tree or audio glue, but is derived from the min/max channels of the CPU
> and codec DAI. A typical CPU DAI has two or more channels, in consequence
> a single mic is treated as a stereo/multi channel device, even though
> only one channel carries audio data.
>
> This change adds the option to specify the number of used DMIC channels
> in the device tree. If a single channel is used we export a channel map
> that marks all unused channels as invalid to indicate userspace that the
> capture device is mono.
>
> Signed-off-by: Matthias Kaehlcke <mka@xxxxxxxxxxxx>
> ---
> Documentation/devicetree/bindings/sound/dmic.txt | 2 +
> sound/soc/codecs/dmic.c | 72 +++++++++++++++++++++---
> 2 files changed, 65 insertions(+), 9 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/sound/dmic.txt b/Documentation/devicetree/bindings/sound/dmic.txt
> index 54c8ef6498a8..f7bf65611453 100644
> --- a/Documentation/devicetree/bindings/sound/dmic.txt
> +++ b/Documentation/devicetree/bindings/sound/dmic.txt
> @@ -7,10 +7,12 @@ Required properties:
>
> Optional properties:
> - dmicen-gpios: GPIO specifier for dmic to control start and stop
> + - num-channels: Number of microphones on this DAI
>
> Example node:
>
> dmic_codec: dmic@0 {
> compatible = "dmic-codec";
> dmicen-gpios = <&gpio4 3 GPIO_ACTIVE_HIGH>;
> + num-channels = <1>;
> };
> diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c
> index b88a1ee66f80..c705a25b138e 100644
> --- a/sound/soc/codecs/dmic.c
> +++ b/sound/soc/codecs/dmic.c
> @@ -29,24 +29,29 @@
> #include <sound/soc.h>
> #include <sound/soc-dapm.h>
>
> +struct dmic {
> + struct gpio_desc *gpio_en;
> + int channels;
> +};
> +
> static int dmic_daiops_trigger(struct snd_pcm_substream *substream,
> int cmd, struct snd_soc_dai *dai)
> {
> - struct gpio_desc *dmic_en = snd_soc_dai_get_drvdata(dai);
> + struct dmic *dmic = snd_soc_dai_get_drvdata(dai);
>
> - if (!dmic_en)
> + if (!dmic || !dmic->gpio_en)
> return 0;
>
> switch (cmd) {
> case SNDRV_PCM_TRIGGER_START:
> case SNDRV_PCM_TRIGGER_RESUME:
> case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> - gpiod_set_value(dmic_en, 1);
> + gpiod_set_value(dmic->gpio_en, 1);
> break;
> case SNDRV_PCM_TRIGGER_STOP:
> case SNDRV_PCM_TRIGGER_SUSPEND:
> case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> - gpiod_set_value(dmic_en, 0);
> + gpiod_set_value(dmic->gpio_en, 0);
> break;
> }
>
> @@ -57,6 +62,42 @@ static const struct snd_soc_dai_ops dmic_dai_ops = {
> .trigger = dmic_daiops_trigger,
> };
>
> +const struct snd_pcm_chmap_elem dmic_chmaps_single[] = {
> + { .channels = 1,
> + .map = { SNDRV_CHMAP_MONO } },
> + { .channels = 2,
> + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_NA } },
> + { .channels = 4,
> + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_NA,
> + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA } },
> + { .channels = 6,
> + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_NA,
> + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
> + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA } },
> + { .channels = 8,
> + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_NA,
> + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
> + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
> + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA } },
> + { }
> +};
> +
> +static int dmic_pcm_new(struct snd_soc_pcm_runtime *rtd,
> + struct snd_soc_dai *dai)
> +{
> + struct dmic *dmic = snd_soc_dai_get_drvdata(dai);
> + int err;
> +
> + if (dmic->channels == 1) {
> + err = snd_pcm_add_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_CAPTURE,
> + dmic_chmaps_single, 8, 0, NULL);
> + if (err < 0)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> static struct snd_soc_dai_driver dmic_dai = {
> .name = "dmic-hifi",
> .capture = {
> @@ -69,18 +110,31 @@ static struct snd_soc_dai_driver dmic_dai = {
> | SNDRV_PCM_FMTBIT_S16_LE,
> },
> .ops = &dmic_dai_ops,
> + .pcm_new = dmic_pcm_new,
> };
>
> static int dmic_codec_probe(struct snd_soc_codec *codec)
> {
> - struct gpio_desc *dmic_en;
> + struct dmic *dmic;
> + int err;
> + u32 pval;
> +
> + dmic = devm_kzalloc(codec->dev, sizeof(*dmic), GFP_KERNEL);
> + if (!dmic)
> + return -ENOMEM;
>
> - dmic_en = devm_gpiod_get_optional(codec->dev,
> + dmic->gpio_en = devm_gpiod_get_optional(codec->dev,
> "dmicen", GPIOD_OUT_LOW);
> - if (IS_ERR(dmic_en))
> - return PTR_ERR(dmic_en);
> + if (IS_ERR(dmic->gpio_en))
> + return PTR_ERR(dmic->gpio_en);
> +
> + err = of_property_read_u32(codec->dev->of_node, "num-channels", &pval);
> + if (!err)
> + dmic->channels = (int)pval;
> + else if (err != -ENOENT)
> + return err;
>
> - snd_soc_codec_set_drvdata(codec, dmic_en);
> + snd_soc_codec_set_drvdata(codec, dmic);
>
> return 0;
> }