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 33bc0a7dbcSJenny TC #define MAX_MODESWITCH_DELAY 70 34bc0a7dbcSJenny TC static int modeswitch_delay; 35bc0a7dbcSJenny TC module_param(modeswitch_delay, uint, 0644); 36bc0a7dbcSJenny TC 37*f6f30a60SJenny TC static int wakeup_delay; 38*f6f30a60SJenny TC module_param(wakeup_delay, uint, 0644); 39*f6f30a60SJenny TC 4005c9b302SMatthias Kaehlcke struct dmic { 4105c9b302SMatthias Kaehlcke struct gpio_desc *gpio_en; 4205c9b302SMatthias Kaehlcke int wakeup_delay; 43bc0a7dbcSJenny TC /* Delay after DMIC mode switch */ 44bc0a7dbcSJenny TC int modeswitch_delay; 45bc0a7dbcSJenny TC }; 46bc0a7dbcSJenny TC 47bc0a7dbcSJenny TC int dmic_daiops_trigger(struct snd_pcm_substream *substream, 48bc0a7dbcSJenny TC int cmd, struct snd_soc_dai *dai) 49bc0a7dbcSJenny TC { 50bc0a7dbcSJenny TC struct snd_soc_component *component = dai->component; 51bc0a7dbcSJenny TC struct dmic *dmic = snd_soc_component_get_drvdata(component); 52bc0a7dbcSJenny TC 53bc0a7dbcSJenny TC switch (cmd) { 54bc0a7dbcSJenny TC case SNDRV_PCM_TRIGGER_STOP: 55bc0a7dbcSJenny TC if (dmic->modeswitch_delay) 56bc0a7dbcSJenny TC mdelay(dmic->modeswitch_delay); 57bc0a7dbcSJenny TC 58bc0a7dbcSJenny TC break; 59bc0a7dbcSJenny TC } 60bc0a7dbcSJenny TC 61bc0a7dbcSJenny TC return 0; 62bc0a7dbcSJenny TC } 63bc0a7dbcSJenny TC 64bc0a7dbcSJenny TC static const struct snd_soc_dai_ops dmic_dai_ops = { 65bc0a7dbcSJenny TC .trigger = dmic_daiops_trigger, 6623c7159aShuang lin }; 6723c7159aShuang lin 6805c9b302SMatthias Kaehlcke static int dmic_aif_event(struct snd_soc_dapm_widget *w, 6905c9b302SMatthias Kaehlcke struct snd_kcontrol *kcontrol, int event) { 7015b7c5deSKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 7115b7c5deSKuninori Morimoto struct dmic *dmic = snd_soc_component_get_drvdata(component); 7205c9b302SMatthias Kaehlcke 7305c9b302SMatthias Kaehlcke switch (event) { 7405c9b302SMatthias Kaehlcke case SND_SOC_DAPM_POST_PMU: 7505c9b302SMatthias Kaehlcke if (dmic->gpio_en) 7605c9b302SMatthias Kaehlcke gpiod_set_value(dmic->gpio_en, 1); 7705c9b302SMatthias Kaehlcke 7805c9b302SMatthias Kaehlcke if (dmic->wakeup_delay) 7905c9b302SMatthias Kaehlcke msleep(dmic->wakeup_delay); 8005c9b302SMatthias Kaehlcke break; 8105c9b302SMatthias Kaehlcke case SND_SOC_DAPM_POST_PMD: 8205c9b302SMatthias Kaehlcke if (dmic->gpio_en) 8305c9b302SMatthias Kaehlcke gpiod_set_value(dmic->gpio_en, 0); 8405c9b302SMatthias Kaehlcke break; 8505c9b302SMatthias Kaehlcke } 8605c9b302SMatthias Kaehlcke 8705c9b302SMatthias Kaehlcke return 0; 8805c9b302SMatthias Kaehlcke } 8905c9b302SMatthias Kaehlcke 90a710770eSDavid Lambert static struct snd_soc_dai_driver dmic_dai = { 91a710770eSDavid Lambert .name = "dmic-hifi", 92a710770eSDavid Lambert .capture = { 93a710770eSDavid Lambert .stream_name = "Capture", 94a710770eSDavid Lambert .channels_min = 1, 95a710770eSDavid Lambert .channels_max = 8, 96a710770eSDavid Lambert .rates = SNDRV_PCM_RATE_CONTINUOUS, 97a710770eSDavid Lambert .formats = SNDRV_PCM_FMTBIT_S32_LE 98a710770eSDavid Lambert | SNDRV_PCM_FMTBIT_S24_LE 99a710770eSDavid Lambert | SNDRV_PCM_FMTBIT_S16_LE, 100a710770eSDavid Lambert }, 101bc0a7dbcSJenny TC .ops = &dmic_dai_ops, 102a710770eSDavid Lambert }; 103a710770eSDavid Lambert 1046d6c3946SKuninori Morimoto static int dmic_component_probe(struct snd_soc_component *component) 10523c7159aShuang lin { 10605c9b302SMatthias Kaehlcke struct dmic *dmic; 10723c7159aShuang lin 10805c9b302SMatthias Kaehlcke dmic = devm_kzalloc(component->dev, sizeof(*dmic), GFP_KERNEL); 10905c9b302SMatthias Kaehlcke if (!dmic) 11005c9b302SMatthias Kaehlcke return -ENOMEM; 11105c9b302SMatthias Kaehlcke 11205c9b302SMatthias Kaehlcke dmic->gpio_en = devm_gpiod_get_optional(component->dev, 11323c7159aShuang lin "dmicen", GPIOD_OUT_LOW); 11405c9b302SMatthias Kaehlcke if (IS_ERR(dmic->gpio_en)) 11505c9b302SMatthias Kaehlcke return PTR_ERR(dmic->gpio_en); 11623c7159aShuang lin 11705c9b302SMatthias Kaehlcke device_property_read_u32(component->dev, "wakeup-delay-ms", 11805c9b302SMatthias Kaehlcke &dmic->wakeup_delay); 119bc0a7dbcSJenny TC device_property_read_u32(component->dev, "modeswitch-delay-ms", 120bc0a7dbcSJenny TC &dmic->modeswitch_delay); 121*f6f30a60SJenny TC if (wakeup_delay) 122*f6f30a60SJenny TC dmic->wakeup_delay = wakeup_delay; 123bc0a7dbcSJenny TC if (modeswitch_delay) 124bc0a7dbcSJenny TC dmic->modeswitch_delay = modeswitch_delay; 125bc0a7dbcSJenny TC 126bc0a7dbcSJenny TC if (dmic->modeswitch_delay > MAX_MODESWITCH_DELAY) 127bc0a7dbcSJenny TC dmic->modeswitch_delay = MAX_MODESWITCH_DELAY; 12805c9b302SMatthias Kaehlcke 12905c9b302SMatthias Kaehlcke snd_soc_component_set_drvdata(component, dmic); 13023c7159aShuang lin 13123c7159aShuang lin return 0; 13223c7159aShuang lin } 13323c7159aShuang lin 134d5e4b0adSMisael Lopez Cruz static const struct snd_soc_dapm_widget dmic_dapm_widgets[] = { 13505c9b302SMatthias Kaehlcke SND_SOC_DAPM_AIF_OUT_E("DMIC AIF", "Capture", 0, 13605c9b302SMatthias Kaehlcke SND_SOC_NOPM, 0, 0, dmic_aif_event, 13705c9b302SMatthias Kaehlcke SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), 138d5e4b0adSMisael Lopez Cruz SND_SOC_DAPM_INPUT("DMic"), 139d5e4b0adSMisael Lopez Cruz }; 140d5e4b0adSMisael Lopez Cruz 141d5e4b0adSMisael Lopez Cruz static const struct snd_soc_dapm_route intercon[] = { 142d5e4b0adSMisael Lopez Cruz {"DMIC AIF", NULL, "DMic"}, 143d5e4b0adSMisael Lopez Cruz }; 144d5e4b0adSMisael Lopez Cruz 1456d6c3946SKuninori Morimoto static const struct snd_soc_component_driver soc_dmic = { 1466d6c3946SKuninori Morimoto .probe = dmic_component_probe, 147a85f9da7SLars-Peter Clausen .dapm_widgets = dmic_dapm_widgets, 148a85f9da7SLars-Peter Clausen .num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets), 149a85f9da7SLars-Peter Clausen .dapm_routes = intercon, 150a85f9da7SLars-Peter Clausen .num_dapm_routes = ARRAY_SIZE(intercon), 1516d6c3946SKuninori Morimoto .idle_bias_on = 1, 1526d6c3946SKuninori Morimoto .use_pmdown_time = 1, 1536d6c3946SKuninori Morimoto .endianness = 1, 1546d6c3946SKuninori Morimoto .non_legacy_dai_naming = 1, 155d5e4b0adSMisael Lopez Cruz }; 156a710770eSDavid Lambert 1577a79e94eSBill Pemberton static int dmic_dev_probe(struct platform_device *pdev) 158a710770eSDavid Lambert { 1597fb59e94SMatthias Kaehlcke int err; 1607fb59e94SMatthias Kaehlcke u32 chans; 1617fb59e94SMatthias Kaehlcke struct snd_soc_dai_driver *dai_drv = &dmic_dai; 1627fb59e94SMatthias Kaehlcke 1637fb59e94SMatthias Kaehlcke if (pdev->dev.of_node) { 1647fb59e94SMatthias Kaehlcke err = of_property_read_u32(pdev->dev.of_node, "num-channels", &chans); 16535b84bf0SMatthias Kaehlcke if (err && (err != -EINVAL)) 1667fb59e94SMatthias Kaehlcke return err; 1677fb59e94SMatthias Kaehlcke 1687fb59e94SMatthias Kaehlcke if (!err) { 1697fb59e94SMatthias Kaehlcke if (chans < 1 || chans > 8) 1707fb59e94SMatthias Kaehlcke return -EINVAL; 1717fb59e94SMatthias Kaehlcke 1727fb59e94SMatthias Kaehlcke dai_drv = devm_kzalloc(&pdev->dev, sizeof(*dai_drv), GFP_KERNEL); 1737fb59e94SMatthias Kaehlcke if (!dai_drv) 1747fb59e94SMatthias Kaehlcke return -ENOMEM; 1757fb59e94SMatthias Kaehlcke 1767fb59e94SMatthias Kaehlcke memcpy(dai_drv, &dmic_dai, sizeof(*dai_drv)); 1777fb59e94SMatthias Kaehlcke dai_drv->capture.channels_max = chans; 1787fb59e94SMatthias Kaehlcke } 1797fb59e94SMatthias Kaehlcke } 1807fb59e94SMatthias Kaehlcke 1816d6c3946SKuninori Morimoto return devm_snd_soc_register_component(&pdev->dev, 1827fb59e94SMatthias Kaehlcke &soc_dmic, dai_drv, 1); 183a710770eSDavid Lambert } 184a710770eSDavid Lambert 185a710770eSDavid Lambert MODULE_ALIAS("platform:dmic-codec"); 186a710770eSDavid Lambert 18729685e20SArnaud Pouliquen static const struct of_device_id dmic_dev_match[] = { 18829685e20SArnaud Pouliquen {.compatible = "dmic-codec"}, 18929685e20SArnaud Pouliquen {} 19029685e20SArnaud Pouliquen }; 191cb06a037SJerome Brunet MODULE_DEVICE_TABLE(of, dmic_dev_match); 19229685e20SArnaud Pouliquen 193a710770eSDavid Lambert static struct platform_driver dmic_driver = { 194a710770eSDavid Lambert .driver = { 195a710770eSDavid Lambert .name = "dmic-codec", 19629685e20SArnaud Pouliquen .of_match_table = dmic_dev_match, 197a710770eSDavid Lambert }, 198a710770eSDavid Lambert .probe = dmic_dev_probe, 199a710770eSDavid Lambert }; 200a710770eSDavid Lambert 2015bbcc3c0SMark Brown module_platform_driver(dmic_driver); 202a710770eSDavid Lambert 203a710770eSDavid Lambert MODULE_DESCRIPTION("Generic DMIC driver"); 204a710770eSDavid Lambert MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>"); 205a710770eSDavid Lambert MODULE_LICENSE("GPL"); 206