linux/sound/soc/codecs/dmic.c
<<
>>
Prefs
   1/*
   2 * dmic.c  --  SoC audio for Generic Digital MICs
   3 *
   4 * Author: Liam Girdwood <lrg@slimlogic.co.uk>
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License
   8 * version 2 as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful, but
  11 * WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13 * General Public License for more details.
  14 *
  15 * You should have received a copy of the GNU General Public License
  16 * along with this program; if not, write to the Free Software
  17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  18 * 02110-1301 USA
  19 *
  20 */
  21
  22#include <linux/delay.h>
  23#include <linux/gpio.h>
  24#include <linux/gpio/consumer.h>
  25#include <linux/platform_device.h>
  26#include <linux/slab.h>
  27#include <linux/module.h>
  28#include <sound/core.h>
  29#include <sound/pcm.h>
  30#include <sound/soc.h>
  31#include <sound/soc-dapm.h>
  32
  33struct dmic {
  34        struct gpio_desc *gpio_en;
  35        int wakeup_delay;
  36};
  37
  38static int dmic_aif_event(struct snd_soc_dapm_widget *w,
  39                          struct snd_kcontrol *kcontrol, int event) {
  40        struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
  41        struct dmic *dmic = snd_soc_component_get_drvdata(component);
  42
  43        switch (event) {
  44        case SND_SOC_DAPM_POST_PMU:
  45                if (dmic->gpio_en)
  46                        gpiod_set_value(dmic->gpio_en, 1);
  47
  48                if (dmic->wakeup_delay)
  49                        msleep(dmic->wakeup_delay);
  50                break;
  51        case SND_SOC_DAPM_POST_PMD:
  52                if (dmic->gpio_en)
  53                        gpiod_set_value(dmic->gpio_en, 0);
  54                break;
  55        }
  56
  57        return 0;
  58}
  59
  60static struct snd_soc_dai_driver dmic_dai = {
  61        .name = "dmic-hifi",
  62        .capture = {
  63                .stream_name = "Capture",
  64                .channels_min = 1,
  65                .channels_max = 8,
  66                .rates = SNDRV_PCM_RATE_CONTINUOUS,
  67                .formats = SNDRV_PCM_FMTBIT_S32_LE
  68                        | SNDRV_PCM_FMTBIT_S24_LE
  69                        | SNDRV_PCM_FMTBIT_S16_LE,
  70        },
  71};
  72
  73static int dmic_component_probe(struct snd_soc_component *component)
  74{
  75        struct dmic *dmic;
  76
  77        dmic = devm_kzalloc(component->dev, sizeof(*dmic), GFP_KERNEL);
  78        if (!dmic)
  79                return -ENOMEM;
  80
  81        dmic->gpio_en = devm_gpiod_get_optional(component->dev,
  82                                                "dmicen", GPIOD_OUT_LOW);
  83        if (IS_ERR(dmic->gpio_en))
  84                return PTR_ERR(dmic->gpio_en);
  85
  86        device_property_read_u32(component->dev, "wakeup-delay-ms",
  87                                 &dmic->wakeup_delay);
  88
  89        snd_soc_component_set_drvdata(component, dmic);
  90
  91        return 0;
  92}
  93
  94static const struct snd_soc_dapm_widget dmic_dapm_widgets[] = {
  95        SND_SOC_DAPM_AIF_OUT_E("DMIC AIF", "Capture", 0,
  96                               SND_SOC_NOPM, 0, 0, dmic_aif_event,
  97                               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
  98        SND_SOC_DAPM_INPUT("DMic"),
  99};
 100
 101static const struct snd_soc_dapm_route intercon[] = {
 102        {"DMIC AIF", NULL, "DMic"},
 103};
 104
 105static const struct snd_soc_component_driver soc_dmic = {
 106        .probe                  = dmic_component_probe,
 107        .dapm_widgets           = dmic_dapm_widgets,
 108        .num_dapm_widgets       = ARRAY_SIZE(dmic_dapm_widgets),
 109        .dapm_routes            = intercon,
 110        .num_dapm_routes        = ARRAY_SIZE(intercon),
 111        .idle_bias_on           = 1,
 112        .use_pmdown_time        = 1,
 113        .endianness             = 1,
 114        .non_legacy_dai_naming  = 1,
 115};
 116
 117static int dmic_dev_probe(struct platform_device *pdev)
 118{
 119        int err;
 120        u32 chans;
 121        struct snd_soc_dai_driver *dai_drv = &dmic_dai;
 122
 123        if (pdev->dev.of_node) {
 124                err = of_property_read_u32(pdev->dev.of_node, "num-channels", &chans);
 125                if (err && (err != -EINVAL))
 126                        return err;
 127
 128                if (!err) {
 129                        if (chans < 1 || chans > 8)
 130                                return -EINVAL;
 131
 132                        dai_drv = devm_kzalloc(&pdev->dev, sizeof(*dai_drv), GFP_KERNEL);
 133                        if (!dai_drv)
 134                                return -ENOMEM;
 135
 136                        memcpy(dai_drv, &dmic_dai, sizeof(*dai_drv));
 137                        dai_drv->capture.channels_max = chans;
 138                }
 139        }
 140
 141        return devm_snd_soc_register_component(&pdev->dev,
 142                        &soc_dmic, dai_drv, 1);
 143}
 144
 145MODULE_ALIAS("platform:dmic-codec");
 146
 147static const struct of_device_id dmic_dev_match[] = {
 148        {.compatible = "dmic-codec"},
 149        {}
 150};
 151
 152static struct platform_driver dmic_driver = {
 153        .driver = {
 154                .name = "dmic-codec",
 155                .of_match_table = dmic_dev_match,
 156        },
 157        .probe = dmic_dev_probe,
 158};
 159
 160module_platform_driver(dmic_driver);
 161
 162MODULE_DESCRIPTION("Generic DMIC driver");
 163MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
 164MODULE_LICENSE("GPL");
 165