1a710770eSDavid Lambert /* 2a710770eSDavid Lambert * dmic.c -- SoC audio for Generic Digital MICs 3a710770eSDavid Lambert * 4a710770eSDavid Lambert * Author: Liam Girdwood <lrg@slimlogic.co.uk> 5a710770eSDavid Lambert * 6a710770eSDavid Lambert * This program is free software; you can redistribute it and/or 7a710770eSDavid Lambert * modify it under the terms of the GNU General Public License 8a710770eSDavid Lambert * version 2 as published by the Free Software Foundation. 9a710770eSDavid Lambert * 10a710770eSDavid Lambert * This program is distributed in the hope that it will be useful, but 11a710770eSDavid Lambert * WITHOUT ANY WARRANTY; without even the implied warranty of 12a710770eSDavid Lambert * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13a710770eSDavid Lambert * General Public License for more details. 14a710770eSDavid Lambert * 15a710770eSDavid Lambert * You should have received a copy of the GNU General Public License 16a710770eSDavid Lambert * along with this program; if not, write to the Free Software 17a710770eSDavid Lambert * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 18a710770eSDavid Lambert * 02110-1301 USA 19a710770eSDavid Lambert * 20a710770eSDavid Lambert */ 21a710770eSDavid Lambert 2205c9b302SMatthias Kaehlcke #include <linux/delay.h> 2323c7159aShuang lin #include <linux/gpio.h> 2423c7159aShuang lin #include <linux/gpio/consumer.h> 25a710770eSDavid Lambert #include <linux/platform_device.h> 26a710770eSDavid Lambert #include <linux/slab.h> 27da155d5bSPaul Gortmaker #include <linux/module.h> 28a710770eSDavid Lambert #include <sound/core.h> 29a710770eSDavid Lambert #include <sound/pcm.h> 30a710770eSDavid Lambert #include <sound/soc.h> 31a710770eSDavid Lambert #include <sound/soc-dapm.h> 32a710770eSDavid Lambert 3305c9b302SMatthias Kaehlcke struct dmic { 3405c9b302SMatthias Kaehlcke struct gpio_desc *gpio_en; 3505c9b302SMatthias Kaehlcke int wakeup_delay; 3623c7159aShuang lin }; 3723c7159aShuang lin 3805c9b302SMatthias Kaehlcke static int dmic_aif_event(struct snd_soc_dapm_widget *w, 3905c9b302SMatthias Kaehlcke struct snd_kcontrol *kcontrol, int event) { 40*15b7c5deSKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 41*15b7c5deSKuninori Morimoto struct dmic *dmic = snd_soc_component_get_drvdata(component); 4205c9b302SMatthias Kaehlcke 4305c9b302SMatthias Kaehlcke switch (event) { 4405c9b302SMatthias Kaehlcke case SND_SOC_DAPM_POST_PMU: 4505c9b302SMatthias Kaehlcke if (dmic->gpio_en) 4605c9b302SMatthias Kaehlcke gpiod_set_value(dmic->gpio_en, 1); 4705c9b302SMatthias Kaehlcke 4805c9b302SMatthias Kaehlcke if (dmic->wakeup_delay) 4905c9b302SMatthias Kaehlcke msleep(dmic->wakeup_delay); 5005c9b302SMatthias Kaehlcke break; 5105c9b302SMatthias Kaehlcke case SND_SOC_DAPM_POST_PMD: 5205c9b302SMatthias Kaehlcke if (dmic->gpio_en) 5305c9b302SMatthias Kaehlcke gpiod_set_value(dmic->gpio_en, 0); 5405c9b302SMatthias Kaehlcke break; 5505c9b302SMatthias Kaehlcke } 5605c9b302SMatthias Kaehlcke 5705c9b302SMatthias Kaehlcke return 0; 5805c9b302SMatthias Kaehlcke } 5905c9b302SMatthias Kaehlcke 60a710770eSDavid Lambert static struct snd_soc_dai_driver dmic_dai = { 61a710770eSDavid Lambert .name = "dmic-hifi", 62a710770eSDavid Lambert .capture = { 63a710770eSDavid Lambert .stream_name = "Capture", 64a710770eSDavid Lambert .channels_min = 1, 65a710770eSDavid Lambert .channels_max = 8, 66a710770eSDavid Lambert .rates = SNDRV_PCM_RATE_CONTINUOUS, 67a710770eSDavid Lambert .formats = SNDRV_PCM_FMTBIT_S32_LE 68a710770eSDavid Lambert | SNDRV_PCM_FMTBIT_S24_LE 69a710770eSDavid Lambert | SNDRV_PCM_FMTBIT_S16_LE, 70a710770eSDavid Lambert }, 71a710770eSDavid Lambert }; 72a710770eSDavid Lambert 736d6c3946SKuninori Morimoto static int dmic_component_probe(struct snd_soc_component *component) 7423c7159aShuang lin { 7505c9b302SMatthias Kaehlcke struct dmic *dmic; 7623c7159aShuang lin 7705c9b302SMatthias Kaehlcke dmic = devm_kzalloc(component->dev, sizeof(*dmic), GFP_KERNEL); 7805c9b302SMatthias Kaehlcke if (!dmic) 7905c9b302SMatthias Kaehlcke return -ENOMEM; 8005c9b302SMatthias Kaehlcke 8105c9b302SMatthias Kaehlcke dmic->gpio_en = devm_gpiod_get_optional(component->dev, 8223c7159aShuang lin "dmicen", GPIOD_OUT_LOW); 8305c9b302SMatthias Kaehlcke if (IS_ERR(dmic->gpio_en)) 8405c9b302SMatthias Kaehlcke return PTR_ERR(dmic->gpio_en); 8523c7159aShuang lin 8605c9b302SMatthias Kaehlcke device_property_read_u32(component->dev, "wakeup-delay-ms", 8705c9b302SMatthias Kaehlcke &dmic->wakeup_delay); 8805c9b302SMatthias Kaehlcke 8905c9b302SMatthias Kaehlcke snd_soc_component_set_drvdata(component, dmic); 9023c7159aShuang lin 9123c7159aShuang lin return 0; 9223c7159aShuang lin } 9323c7159aShuang lin 94d5e4b0adSMisael Lopez Cruz static const struct snd_soc_dapm_widget dmic_dapm_widgets[] = { 9505c9b302SMatthias Kaehlcke SND_SOC_DAPM_AIF_OUT_E("DMIC AIF", "Capture", 0, 9605c9b302SMatthias Kaehlcke SND_SOC_NOPM, 0, 0, dmic_aif_event, 9705c9b302SMatthias Kaehlcke SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), 98d5e4b0adSMisael Lopez Cruz SND_SOC_DAPM_INPUT("DMic"), 99d5e4b0adSMisael Lopez Cruz }; 100d5e4b0adSMisael Lopez Cruz 101d5e4b0adSMisael Lopez Cruz static const struct snd_soc_dapm_route intercon[] = { 102d5e4b0adSMisael Lopez Cruz {"DMIC AIF", NULL, "DMic"}, 103d5e4b0adSMisael Lopez Cruz }; 104d5e4b0adSMisael Lopez Cruz 1056d6c3946SKuninori Morimoto static const struct snd_soc_component_driver soc_dmic = { 1066d6c3946SKuninori Morimoto .probe = dmic_component_probe, 107a85f9da7SLars-Peter Clausen .dapm_widgets = dmic_dapm_widgets, 108a85f9da7SLars-Peter Clausen .num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets), 109a85f9da7SLars-Peter Clausen .dapm_routes = intercon, 110a85f9da7SLars-Peter Clausen .num_dapm_routes = ARRAY_SIZE(intercon), 1116d6c3946SKuninori Morimoto .idle_bias_on = 1, 1126d6c3946SKuninori Morimoto .use_pmdown_time = 1, 1136d6c3946SKuninori Morimoto .endianness = 1, 1146d6c3946SKuninori Morimoto .non_legacy_dai_naming = 1, 115d5e4b0adSMisael Lopez Cruz }; 116a710770eSDavid Lambert 1177a79e94eSBill Pemberton static int dmic_dev_probe(struct platform_device *pdev) 118a710770eSDavid Lambert { 1197fb59e94SMatthias Kaehlcke int err; 1207fb59e94SMatthias Kaehlcke u32 chans; 1217fb59e94SMatthias Kaehlcke struct snd_soc_dai_driver *dai_drv = &dmic_dai; 1227fb59e94SMatthias Kaehlcke 1237fb59e94SMatthias Kaehlcke if (pdev->dev.of_node) { 1247fb59e94SMatthias Kaehlcke err = of_property_read_u32(pdev->dev.of_node, "num-channels", &chans); 12535b84bf0SMatthias Kaehlcke if (err && (err != -EINVAL)) 1267fb59e94SMatthias Kaehlcke return err; 1277fb59e94SMatthias Kaehlcke 1287fb59e94SMatthias Kaehlcke if (!err) { 1297fb59e94SMatthias Kaehlcke if (chans < 1 || chans > 8) 1307fb59e94SMatthias Kaehlcke return -EINVAL; 1317fb59e94SMatthias Kaehlcke 1327fb59e94SMatthias Kaehlcke dai_drv = devm_kzalloc(&pdev->dev, sizeof(*dai_drv), GFP_KERNEL); 1337fb59e94SMatthias Kaehlcke if (!dai_drv) 1347fb59e94SMatthias Kaehlcke return -ENOMEM; 1357fb59e94SMatthias Kaehlcke 1367fb59e94SMatthias Kaehlcke memcpy(dai_drv, &dmic_dai, sizeof(*dai_drv)); 1377fb59e94SMatthias Kaehlcke dai_drv->capture.channels_max = chans; 1387fb59e94SMatthias Kaehlcke } 1397fb59e94SMatthias Kaehlcke } 1407fb59e94SMatthias Kaehlcke 1416d6c3946SKuninori Morimoto return devm_snd_soc_register_component(&pdev->dev, 1427fb59e94SMatthias Kaehlcke &soc_dmic, dai_drv, 1); 143a710770eSDavid Lambert } 144a710770eSDavid Lambert 145a710770eSDavid Lambert MODULE_ALIAS("platform:dmic-codec"); 146a710770eSDavid Lambert 14729685e20SArnaud Pouliquen static const struct of_device_id dmic_dev_match[] = { 14829685e20SArnaud Pouliquen {.compatible = "dmic-codec"}, 14929685e20SArnaud Pouliquen {} 15029685e20SArnaud Pouliquen }; 15129685e20SArnaud Pouliquen 152a710770eSDavid Lambert static struct platform_driver dmic_driver = { 153a710770eSDavid Lambert .driver = { 154a710770eSDavid Lambert .name = "dmic-codec", 15529685e20SArnaud Pouliquen .of_match_table = dmic_dev_match, 156a710770eSDavid Lambert }, 157a710770eSDavid Lambert .probe = dmic_dev_probe, 158a710770eSDavid Lambert }; 159a710770eSDavid Lambert 1605bbcc3c0SMark Brown module_platform_driver(dmic_driver); 161a710770eSDavid Lambert 162a710770eSDavid Lambert MODULE_DESCRIPTION("Generic DMIC driver"); 163a710770eSDavid Lambert MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>"); 164a710770eSDavid Lambert MODULE_LICENSE("GPL"); 165