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 2223c7159aShuang lin #include <linux/gpio.h> 2323c7159aShuang lin #include <linux/gpio/consumer.h> 24a710770eSDavid Lambert #include <linux/platform_device.h> 25a710770eSDavid Lambert #include <linux/slab.h> 26da155d5bSPaul Gortmaker #include <linux/module.h> 27a710770eSDavid Lambert #include <sound/core.h> 28a710770eSDavid Lambert #include <sound/pcm.h> 29a710770eSDavid Lambert #include <sound/soc.h> 30a710770eSDavid Lambert #include <sound/soc-dapm.h> 31a710770eSDavid Lambert 3223c7159aShuang lin static int dmic_daiops_trigger(struct snd_pcm_substream *substream, 3323c7159aShuang lin int cmd, struct snd_soc_dai *dai) 3423c7159aShuang lin { 3523c7159aShuang lin struct gpio_desc *dmic_en = snd_soc_dai_get_drvdata(dai); 3623c7159aShuang lin 3723c7159aShuang lin if (!dmic_en) 3823c7159aShuang lin return 0; 3923c7159aShuang lin 4023c7159aShuang lin switch (cmd) { 4123c7159aShuang lin case SNDRV_PCM_TRIGGER_START: 4223c7159aShuang lin case SNDRV_PCM_TRIGGER_RESUME: 4323c7159aShuang lin case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 4423c7159aShuang lin gpiod_set_value(dmic_en, 1); 4523c7159aShuang lin break; 4623c7159aShuang lin case SNDRV_PCM_TRIGGER_STOP: 4723c7159aShuang lin case SNDRV_PCM_TRIGGER_SUSPEND: 4823c7159aShuang lin case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 4923c7159aShuang lin gpiod_set_value(dmic_en, 0); 5023c7159aShuang lin break; 5123c7159aShuang lin } 5223c7159aShuang lin 5323c7159aShuang lin return 0; 5423c7159aShuang lin } 5523c7159aShuang lin 5623c7159aShuang lin static const struct snd_soc_dai_ops dmic_dai_ops = { 5723c7159aShuang lin .trigger = dmic_daiops_trigger, 5823c7159aShuang lin }; 5923c7159aShuang lin 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 }, 7123c7159aShuang lin .ops = &dmic_dai_ops, 72a710770eSDavid Lambert }; 73a710770eSDavid Lambert 74*6d6c3946SKuninori Morimoto static int dmic_component_probe(struct snd_soc_component *component) 7523c7159aShuang lin { 7623c7159aShuang lin struct gpio_desc *dmic_en; 7723c7159aShuang lin 78*6d6c3946SKuninori Morimoto dmic_en = devm_gpiod_get_optional(component->dev, 7923c7159aShuang lin "dmicen", GPIOD_OUT_LOW); 8023c7159aShuang lin if (IS_ERR(dmic_en)) 8123c7159aShuang lin return PTR_ERR(dmic_en); 8223c7159aShuang lin 83*6d6c3946SKuninori Morimoto snd_soc_component_set_drvdata(component, dmic_en); 8423c7159aShuang lin 8523c7159aShuang lin return 0; 8623c7159aShuang lin } 8723c7159aShuang lin 88d5e4b0adSMisael Lopez Cruz static const struct snd_soc_dapm_widget dmic_dapm_widgets[] = { 89d5e4b0adSMisael Lopez Cruz SND_SOC_DAPM_AIF_OUT("DMIC AIF", "Capture", 0, 90d5e4b0adSMisael Lopez Cruz SND_SOC_NOPM, 0, 0), 91d5e4b0adSMisael Lopez Cruz SND_SOC_DAPM_INPUT("DMic"), 92d5e4b0adSMisael Lopez Cruz }; 93d5e4b0adSMisael Lopez Cruz 94d5e4b0adSMisael Lopez Cruz static const struct snd_soc_dapm_route intercon[] = { 95d5e4b0adSMisael Lopez Cruz {"DMIC AIF", NULL, "DMic"}, 96d5e4b0adSMisael Lopez Cruz }; 97d5e4b0adSMisael Lopez Cruz 98*6d6c3946SKuninori Morimoto static const struct snd_soc_component_driver soc_dmic = { 99*6d6c3946SKuninori Morimoto .probe = dmic_component_probe, 100a85f9da7SLars-Peter Clausen .dapm_widgets = dmic_dapm_widgets, 101a85f9da7SLars-Peter Clausen .num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets), 102a85f9da7SLars-Peter Clausen .dapm_routes = intercon, 103a85f9da7SLars-Peter Clausen .num_dapm_routes = ARRAY_SIZE(intercon), 104*6d6c3946SKuninori Morimoto .idle_bias_on = 1, 105*6d6c3946SKuninori Morimoto .use_pmdown_time = 1, 106*6d6c3946SKuninori Morimoto .endianness = 1, 107*6d6c3946SKuninori Morimoto .non_legacy_dai_naming = 1, 108d5e4b0adSMisael Lopez Cruz }; 109a710770eSDavid Lambert 1107a79e94eSBill Pemberton static int dmic_dev_probe(struct platform_device *pdev) 111a710770eSDavid Lambert { 1127fb59e94SMatthias Kaehlcke int err; 1137fb59e94SMatthias Kaehlcke u32 chans; 1147fb59e94SMatthias Kaehlcke struct snd_soc_dai_driver *dai_drv = &dmic_dai; 1157fb59e94SMatthias Kaehlcke 1167fb59e94SMatthias Kaehlcke if (pdev->dev.of_node) { 1177fb59e94SMatthias Kaehlcke err = of_property_read_u32(pdev->dev.of_node, "num-channels", &chans); 11835b84bf0SMatthias Kaehlcke if (err && (err != -EINVAL)) 1197fb59e94SMatthias Kaehlcke return err; 1207fb59e94SMatthias Kaehlcke 1217fb59e94SMatthias Kaehlcke if (!err) { 1227fb59e94SMatthias Kaehlcke if (chans < 1 || chans > 8) 1237fb59e94SMatthias Kaehlcke return -EINVAL; 1247fb59e94SMatthias Kaehlcke 1257fb59e94SMatthias Kaehlcke dai_drv = devm_kzalloc(&pdev->dev, sizeof(*dai_drv), GFP_KERNEL); 1267fb59e94SMatthias Kaehlcke if (!dai_drv) 1277fb59e94SMatthias Kaehlcke return -ENOMEM; 1287fb59e94SMatthias Kaehlcke 1297fb59e94SMatthias Kaehlcke memcpy(dai_drv, &dmic_dai, sizeof(*dai_drv)); 1307fb59e94SMatthias Kaehlcke dai_drv->capture.channels_max = chans; 1317fb59e94SMatthias Kaehlcke } 1327fb59e94SMatthias Kaehlcke } 1337fb59e94SMatthias Kaehlcke 134*6d6c3946SKuninori Morimoto return devm_snd_soc_register_component(&pdev->dev, 1357fb59e94SMatthias Kaehlcke &soc_dmic, dai_drv, 1); 136a710770eSDavid Lambert } 137a710770eSDavid Lambert 138a710770eSDavid Lambert MODULE_ALIAS("platform:dmic-codec"); 139a710770eSDavid Lambert 14029685e20SArnaud Pouliquen static const struct of_device_id dmic_dev_match[] = { 14129685e20SArnaud Pouliquen {.compatible = "dmic-codec"}, 14229685e20SArnaud Pouliquen {} 14329685e20SArnaud Pouliquen }; 14429685e20SArnaud Pouliquen 145a710770eSDavid Lambert static struct platform_driver dmic_driver = { 146a710770eSDavid Lambert .driver = { 147a710770eSDavid Lambert .name = "dmic-codec", 14829685e20SArnaud Pouliquen .of_match_table = dmic_dev_match, 149a710770eSDavid Lambert }, 150a710770eSDavid Lambert .probe = dmic_dev_probe, 151a710770eSDavid Lambert }; 152a710770eSDavid Lambert 1535bbcc3c0SMark Brown module_platform_driver(dmic_driver); 154a710770eSDavid Lambert 155a710770eSDavid Lambert MODULE_DESCRIPTION("Generic DMIC driver"); 156a710770eSDavid Lambert MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>"); 157a710770eSDavid Lambert MODULE_LICENSE("GPL"); 158