2019-05-30 02:57:50 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2011-01-06 17:00:37 +03:00
/*
* dmic . c - - SoC audio for Generic Digital MICs
*
* Author : Liam Girdwood < lrg @ slimlogic . co . uk >
*/
2018-02-16 20:53:12 +03:00
# include <linux/delay.h>
2017-08-17 05:24:44 +03:00
# include <linux/gpio.h>
# include <linux/gpio/consumer.h>
2011-01-06 17:00:37 +03:00
# include <linux/platform_device.h>
# include <linux/slab.h>
2011-07-15 20:38:28 +04:00
# include <linux/module.h>
2011-01-06 17:00:37 +03:00
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/soc.h>
# include <sound/soc-dapm.h>
2018-11-28 09:52:45 +03:00
# define MAX_MODESWITCH_DELAY 70
static int modeswitch_delay ;
module_param ( modeswitch_delay , uint , 0644 ) ;
2018-11-28 09:52:46 +03:00
static int wakeup_delay ;
module_param ( wakeup_delay , uint , 0644 ) ;
2018-02-16 20:53:12 +03:00
struct dmic {
struct gpio_desc * gpio_en ;
int wakeup_delay ;
2018-11-28 09:52:45 +03:00
/* Delay after DMIC mode switch */
int modeswitch_delay ;
} ;
2019-01-05 05:02:29 +03:00
static int dmic_daiops_trigger ( struct snd_pcm_substream * substream ,
int cmd , struct snd_soc_dai * dai )
2018-11-28 09:52:45 +03:00
{
struct snd_soc_component * component = dai - > component ;
struct dmic * dmic = snd_soc_component_get_drvdata ( component ) ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_STOP :
if ( dmic - > modeswitch_delay )
mdelay ( dmic - > modeswitch_delay ) ;
break ;
}
return 0 ;
}
static const struct snd_soc_dai_ops dmic_dai_ops = {
. trigger = dmic_daiops_trigger ,
2018-02-16 20:53:12 +03:00
} ;
2017-08-17 05:24:44 +03:00
2018-02-16 20:53:12 +03:00
static int dmic_aif_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event ) {
2018-02-21 05:06:29 +03:00
struct snd_soc_component * component = snd_soc_dapm_to_component ( w - > dapm ) ;
struct dmic * dmic = snd_soc_component_get_drvdata ( component ) ;
2017-08-17 05:24:44 +03:00
2018-02-16 20:53:12 +03:00
switch ( event ) {
case SND_SOC_DAPM_POST_PMU :
if ( dmic - > gpio_en )
2020-04-22 11:35:50 +03:00
gpiod_set_value_cansleep ( dmic - > gpio_en , 1 ) ;
2018-02-16 20:53:12 +03:00
if ( dmic - > wakeup_delay )
msleep ( dmic - > wakeup_delay ) ;
2017-08-17 05:24:44 +03:00
break ;
2018-02-16 20:53:12 +03:00
case SND_SOC_DAPM_POST_PMD :
if ( dmic - > gpio_en )
2020-04-22 11:35:50 +03:00
gpiod_set_value_cansleep ( dmic - > gpio_en , 0 ) ;
2017-08-17 05:24:44 +03:00
break ;
}
return 0 ;
}
2011-01-06 17:00:37 +03:00
static struct snd_soc_dai_driver dmic_dai = {
. name = " dmic-hifi " ,
. capture = {
. stream_name = " Capture " ,
. channels_min = 1 ,
. channels_max = 8 ,
. rates = SNDRV_PCM_RATE_CONTINUOUS ,
. formats = SNDRV_PCM_FMTBIT_S32_LE
| SNDRV_PCM_FMTBIT_S24_LE
2022-04-18 06:18:30 +03:00
| SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_DSD_U8
| SNDRV_PCM_FMTBIT_DSD_U16_LE
| SNDRV_PCM_FMTBIT_DSD_U32_LE ,
2011-01-06 17:00:37 +03:00
} ,
2018-11-28 09:52:45 +03:00
. ops = & dmic_dai_ops ,
2011-01-06 17:00:37 +03:00
} ;
2018-01-29 07:40:29 +03:00
static int dmic_component_probe ( struct snd_soc_component * component )
2017-08-17 05:24:44 +03:00
{
2018-02-16 20:53:12 +03:00
struct dmic * dmic ;
dmic = devm_kzalloc ( component - > dev , sizeof ( * dmic ) , GFP_KERNEL ) ;
if ( ! dmic )
return - ENOMEM ;
dmic - > gpio_en = devm_gpiod_get_optional ( component - > dev ,
" dmicen " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( dmic - > gpio_en ) )
return PTR_ERR ( dmic - > gpio_en ) ;
2017-08-17 05:24:44 +03:00
2018-02-16 20:53:12 +03:00
device_property_read_u32 ( component - > dev , " wakeup-delay-ms " ,
& dmic - > wakeup_delay ) ;
2018-11-28 09:52:45 +03:00
device_property_read_u32 ( component - > dev , " modeswitch-delay-ms " ,
& dmic - > modeswitch_delay ) ;
2018-11-28 09:52:46 +03:00
if ( wakeup_delay )
dmic - > wakeup_delay = wakeup_delay ;
2018-11-28 09:52:45 +03:00
if ( modeswitch_delay )
dmic - > modeswitch_delay = modeswitch_delay ;
if ( dmic - > modeswitch_delay > MAX_MODESWITCH_DELAY )
dmic - > modeswitch_delay = MAX_MODESWITCH_DELAY ;
2017-08-17 05:24:44 +03:00
2018-02-16 20:53:12 +03:00
snd_soc_component_set_drvdata ( component , dmic ) ;
2017-08-17 05:24:44 +03:00
return 0 ;
}
2011-05-12 19:26:20 +04:00
static const struct snd_soc_dapm_widget dmic_dapm_widgets [ ] = {
2018-02-16 20:53:12 +03:00
SND_SOC_DAPM_AIF_OUT_E ( " DMIC AIF " , " Capture " , 0 ,
SND_SOC_NOPM , 0 , 0 , dmic_aif_event ,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD ) ,
2011-05-12 19:26:20 +04:00
SND_SOC_DAPM_INPUT ( " DMic " ) ,
} ;
static const struct snd_soc_dapm_route intercon [ ] = {
{ " DMIC AIF " , NULL , " DMic " } ,
} ;
2018-01-29 07:40:29 +03:00
static const struct snd_soc_component_driver soc_dmic = {
. probe = dmic_component_probe ,
. dapm_widgets = dmic_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( dmic_dapm_widgets ) ,
. dapm_routes = intercon ,
. num_dapm_routes = ARRAY_SIZE ( intercon ) ,
. idle_bias_on = 1 ,
. use_pmdown_time = 1 ,
. endianness = 1 ,
2011-05-12 19:26:20 +04:00
} ;
2011-01-06 17:00:37 +03:00
2012-12-07 18:26:37 +04:00
static int dmic_dev_probe ( struct platform_device * pdev )
2011-01-06 17:00:37 +03:00
{
2018-01-05 23:39:57 +03:00
int err ;
u32 chans ;
struct snd_soc_dai_driver * dai_drv = & dmic_dai ;
if ( pdev - > dev . of_node ) {
err = of_property_read_u32 ( pdev - > dev . of_node , " num-channels " , & chans ) ;
2018-01-20 02:36:50 +03:00
if ( err & & ( err ! = - EINVAL ) )
2018-01-05 23:39:57 +03:00
return err ;
if ( ! err ) {
if ( chans < 1 | | chans > 8 )
return - EINVAL ;
dai_drv = devm_kzalloc ( & pdev - > dev , sizeof ( * dai_drv ) , GFP_KERNEL ) ;
if ( ! dai_drv )
return - ENOMEM ;
memcpy ( dai_drv , & dmic_dai , sizeof ( * dai_drv ) ) ;
dai_drv - > capture . channels_max = chans ;
}
}
2018-01-29 07:40:29 +03:00
return devm_snd_soc_register_component ( & pdev - > dev ,
2018-01-05 23:39:57 +03:00
& soc_dmic , dai_drv , 1 ) ;
2011-01-06 17:00:37 +03:00
}
MODULE_ALIAS ( " platform:dmic-codec " ) ;
2017-07-25 11:48:14 +03:00
static const struct of_device_id dmic_dev_match [ ] = {
{ . compatible = " dmic-codec " } ,
{ }
} ;
2018-08-29 18:00:49 +03:00
MODULE_DEVICE_TABLE ( of , dmic_dev_match ) ;
2017-07-25 11:48:14 +03:00
2011-01-06 17:00:37 +03:00
static struct platform_driver dmic_driver = {
. driver = {
. name = " dmic-codec " ,
2017-07-25 11:48:14 +03:00
. of_match_table = dmic_dev_match ,
2011-01-06 17:00:37 +03:00
} ,
. probe = dmic_dev_probe ,
} ;
2011-11-24 02:52:08 +04:00
module_platform_driver ( dmic_driver ) ;
2011-01-06 17:00:37 +03:00
MODULE_DESCRIPTION ( " Generic DMIC driver " ) ;
MODULE_AUTHOR ( " Liam Girdwood <lrg@slimlogic.co.uk> " ) ;
MODULE_LICENSE ( " GPL " ) ;