2011-01-19 21:22:06 +03:00
/*
* CS4271 ASoC codec driver
*
* Copyright ( c ) 2010 Alexander Sverdlin < subaparts @ yandex . ru >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* This driver support CS4271 codec being master or slave , working
* in control port mode , connected either via SPI or I2C .
* The data format accepted is I2S or left - justified .
* DAPM support not implemented .
*/
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/delay.h>
# include <linux/gpio.h>
2013-10-11 15:53:56 +04:00
# include <linux/of.h>
2012-09-28 03:36:44 +04:00
# include <linux/of_device.h>
# include <linux/of_gpio.h>
# include <sound/pcm.h>
# include <sound/soc.h>
# include <sound/tlv.h>
2011-01-19 21:22:06 +03:00
# include <sound/cs4271.h>
2014-10-06 19:09:47 +04:00
# include "cs4271.h"
2011-01-19 21:22:06 +03:00
# define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE )
2011-03-07 20:29:36 +03:00
# define CS4271_PCM_RATES SNDRV_PCM_RATE_8000_192000
2011-01-19 21:22:06 +03:00
/*
* CS4271 registers
*/
2013-03-08 02:53:12 +04:00
# define CS4271_MODE1 0x01 /* Mode Control 1 */
# define CS4271_DACCTL 0x02 /* DAC Control */
# define CS4271_DACVOL 0x03 /* DAC Volume & Mixing Control */
# define CS4271_VOLA 0x04 /* DAC Channel A Volume Control */
# define CS4271_VOLB 0x05 /* DAC Channel B Volume Control */
# define CS4271_ADCCTL 0x06 /* ADC Control */
# define CS4271_MODE2 0x07 /* Mode Control 2 */
# define CS4271_CHIPID 0x08 /* Chip ID */
2011-01-19 21:22:06 +03:00
# define CS4271_FIRSTREG CS4271_MODE1
# define CS4271_LASTREG CS4271_MODE2
# define CS4271_NR_REGS ((CS4271_LASTREG & 0xFF) + 1)
/* Bit masks for the CS4271 registers */
# define CS4271_MODE1_MODE_MASK 0xC0
# define CS4271_MODE1_MODE_1X 0x00
# define CS4271_MODE1_MODE_2X 0x80
# define CS4271_MODE1_MODE_4X 0xC0
# define CS4271_MODE1_DIV_MASK 0x30
# define CS4271_MODE1_DIV_1 0x00
# define CS4271_MODE1_DIV_15 0x10
# define CS4271_MODE1_DIV_2 0x20
# define CS4271_MODE1_DIV_3 0x30
# define CS4271_MODE1_MASTER 0x08
# define CS4271_MODE1_DAC_DIF_MASK 0x07
# define CS4271_MODE1_DAC_DIF_LJ 0x00
# define CS4271_MODE1_DAC_DIF_I2S 0x01
# define CS4271_MODE1_DAC_DIF_RJ16 0x02
# define CS4271_MODE1_DAC_DIF_RJ24 0x03
# define CS4271_MODE1_DAC_DIF_RJ20 0x04
# define CS4271_MODE1_DAC_DIF_RJ18 0x05
# define CS4271_DACCTL_AMUTE 0x80
# define CS4271_DACCTL_IF_SLOW 0x40
# define CS4271_DACCTL_DEM_MASK 0x30
# define CS4271_DACCTL_DEM_DIS 0x00
# define CS4271_DACCTL_DEM_441 0x10
# define CS4271_DACCTL_DEM_48 0x20
# define CS4271_DACCTL_DEM_32 0x30
# define CS4271_DACCTL_SVRU 0x08
# define CS4271_DACCTL_SRD 0x04
# define CS4271_DACCTL_INVA 0x02
# define CS4271_DACCTL_INVB 0x01
# define CS4271_DACVOL_BEQUA 0x40
# define CS4271_DACVOL_SOFT 0x20
# define CS4271_DACVOL_ZEROC 0x10
# define CS4271_DACVOL_ATAPI_MASK 0x0F
# define CS4271_DACVOL_ATAPI_M_M 0x00
# define CS4271_DACVOL_ATAPI_M_BR 0x01
# define CS4271_DACVOL_ATAPI_M_BL 0x02
# define CS4271_DACVOL_ATAPI_M_BLR2 0x03
# define CS4271_DACVOL_ATAPI_AR_M 0x04
# define CS4271_DACVOL_ATAPI_AR_BR 0x05
# define CS4271_DACVOL_ATAPI_AR_BL 0x06
# define CS4271_DACVOL_ATAPI_AR_BLR2 0x07
# define CS4271_DACVOL_ATAPI_AL_M 0x08
# define CS4271_DACVOL_ATAPI_AL_BR 0x09
# define CS4271_DACVOL_ATAPI_AL_BL 0x0A
# define CS4271_DACVOL_ATAPI_AL_BLR2 0x0B
# define CS4271_DACVOL_ATAPI_ALR2_M 0x0C
# define CS4271_DACVOL_ATAPI_ALR2_BR 0x0D
# define CS4271_DACVOL_ATAPI_ALR2_BL 0x0E
# define CS4271_DACVOL_ATAPI_ALR2_BLR2 0x0F
# define CS4271_VOLA_MUTE 0x80
# define CS4271_VOLA_VOL_MASK 0x7F
# define CS4271_VOLB_MUTE 0x80
# define CS4271_VOLB_VOL_MASK 0x7F
# define CS4271_ADCCTL_DITHER16 0x20
# define CS4271_ADCCTL_ADC_DIF_MASK 0x10
# define CS4271_ADCCTL_ADC_DIF_LJ 0x00
# define CS4271_ADCCTL_ADC_DIF_I2S 0x10
# define CS4271_ADCCTL_MUTEA 0x08
# define CS4271_ADCCTL_MUTEB 0x04
# define CS4271_ADCCTL_HPFDA 0x02
# define CS4271_ADCCTL_HPFDB 0x01
# define CS4271_MODE2_LOOP 0x10
# define CS4271_MODE2_MUTECAEQUB 0x08
# define CS4271_MODE2_FREEZE 0x04
# define CS4271_MODE2_CPEN 0x02
# define CS4271_MODE2_PDN 0x01
# define CS4271_CHIPID_PART_MASK 0xF0
# define CS4271_CHIPID_REV_MASK 0x0F
/*
* Default CS4271 power - up configuration
* Array contains non - existing in hw register at address 0
* Array do not include Chip ID , as codec driver does not use
* registers read operations at all
*/
2013-03-08 02:53:12 +04:00
static const struct reg_default cs4271_reg_defaults [ ] = {
{ CS4271_MODE1 , 0 , } ,
{ CS4271_DACCTL , CS4271_DACCTL_AMUTE , } ,
{ CS4271_DACVOL , CS4271_DACVOL_SOFT | CS4271_DACVOL_ATAPI_AL_BR , } ,
{ CS4271_VOLA , 0 , } ,
{ CS4271_VOLB , 0 , } ,
{ CS4271_ADCCTL , 0 , } ,
{ CS4271_MODE2 , 0 , } ,
2011-01-19 21:22:06 +03:00
} ;
2013-03-08 02:53:12 +04:00
static bool cs4271_volatile_reg ( struct device * dev , unsigned int reg )
{
return reg = = CS4271_CHIPID ;
}
2011-01-19 21:22:06 +03:00
struct cs4271_private {
unsigned int mclk ;
bool master ;
bool deemph ;
2013-03-08 02:53:12 +04:00
struct regmap * regmap ;
2011-01-19 21:22:06 +03:00
/* Current sample rate for de-emphasis control */
int rate ;
/* GPIO driving Reset pin, if any */
int gpio_nreset ;
/* GPIO that disable serial bus, if any */
int gpio_disable ;
2012-12-10 13:30:04 +04:00
/* enable soft reset workaround */
bool enable_soft_reset ;
2011-01-19 21:22:06 +03:00
} ;
2013-08-11 16:15:10 +04:00
static const struct snd_soc_dapm_widget cs4271_dapm_widgets [ ] = {
SND_SOC_DAPM_INPUT ( " AINA " ) ,
SND_SOC_DAPM_INPUT ( " AINB " ) ,
SND_SOC_DAPM_OUTPUT ( " AOUTA+ " ) ,
SND_SOC_DAPM_OUTPUT ( " AOUTA- " ) ,
SND_SOC_DAPM_OUTPUT ( " AOUTB+ " ) ,
SND_SOC_DAPM_OUTPUT ( " AOUTB- " ) ,
} ;
static const struct snd_soc_dapm_route cs4271_dapm_routes [ ] = {
{ " Capture " , NULL , " AINA " } ,
{ " Capture " , NULL , " AINB " } ,
{ " AOUTA+ " , NULL , " Playback " } ,
{ " AOUTA- " , NULL , " Playback " } ,
{ " AOUTB+ " , NULL , " Playback " } ,
{ " AOUTB- " , NULL , " Playback " } ,
} ;
2011-01-19 21:22:06 +03:00
/*
* @ freq is the desired MCLK rate
* MCLK rate should ( c ) be the sample rate , multiplied by one of the
* ratios listed in cs4271_mclk_fs_ratios table
*/
static int cs4271_set_dai_sysclk ( struct snd_soc_dai * codec_dai ,
int clk_id , unsigned int freq , int dir )
{
struct snd_soc_codec * codec = codec_dai - > codec ;
struct cs4271_private * cs4271 = snd_soc_codec_get_drvdata ( codec ) ;
cs4271 - > mclk = freq ;
return 0 ;
}
static int cs4271_set_dai_fmt ( struct snd_soc_dai * codec_dai ,
unsigned int format )
{
struct snd_soc_codec * codec = codec_dai - > codec ;
struct cs4271_private * cs4271 = snd_soc_codec_get_drvdata ( codec ) ;
unsigned int val = 0 ;
2011-01-21 22:22:07 +03:00
int ret ;
2011-01-19 21:22:06 +03:00
switch ( format & SND_SOC_DAIFMT_MASTER_MASK ) {
case SND_SOC_DAIFMT_CBS_CFS :
cs4271 - > master = 0 ;
break ;
case SND_SOC_DAIFMT_CBM_CFM :
cs4271 - > master = 1 ;
val | = CS4271_MODE1_MASTER ;
break ;
default :
dev_err ( codec - > dev , " Invalid DAI format \n " ) ;
return - EINVAL ;
}
switch ( format & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_LEFT_J :
val | = CS4271_MODE1_DAC_DIF_LJ ;
2013-03-08 02:53:12 +04:00
ret = regmap_update_bits ( cs4271 - > regmap , CS4271_ADCCTL ,
2011-01-19 21:22:06 +03:00
CS4271_ADCCTL_ADC_DIF_MASK , CS4271_ADCCTL_ADC_DIF_LJ ) ;
2011-01-21 22:22:07 +03:00
if ( ret < 0 )
return ret ;
2011-01-19 21:22:06 +03:00
break ;
case SND_SOC_DAIFMT_I2S :
val | = CS4271_MODE1_DAC_DIF_I2S ;
2013-03-08 02:53:12 +04:00
ret = regmap_update_bits ( cs4271 - > regmap , CS4271_ADCCTL ,
2011-01-19 21:22:06 +03:00
CS4271_ADCCTL_ADC_DIF_MASK , CS4271_ADCCTL_ADC_DIF_I2S ) ;
2011-01-21 22:22:07 +03:00
if ( ret < 0 )
return ret ;
2011-01-19 21:22:06 +03:00
break ;
default :
dev_err ( codec - > dev , " Invalid DAI format \n " ) ;
return - EINVAL ;
}
2013-03-08 02:53:12 +04:00
ret = regmap_update_bits ( cs4271 - > regmap , CS4271_MODE1 ,
2011-01-19 21:22:06 +03:00
CS4271_MODE1_DAC_DIF_MASK | CS4271_MODE1_MASTER , val ) ;
2011-01-21 22:22:07 +03:00
if ( ret < 0 )
return ret ;
2011-01-19 21:22:06 +03:00
return 0 ;
}
static int cs4271_deemph [ ] = { 0 , 44100 , 48000 , 32000 } ;
static int cs4271_set_deemph ( struct snd_soc_codec * codec )
{
struct cs4271_private * cs4271 = snd_soc_codec_get_drvdata ( codec ) ;
2011-01-21 22:22:07 +03:00
int i , ret ;
2011-01-19 21:22:06 +03:00
int val = CS4271_DACCTL_DEM_DIS ;
if ( cs4271 - > deemph ) {
/* Find closest de-emphasis freq */
val = 1 ;
for ( i = 2 ; i < ARRAY_SIZE ( cs4271_deemph ) ; i + + )
if ( abs ( cs4271_deemph [ i ] - cs4271 - > rate ) <
abs ( cs4271_deemph [ val ] - cs4271 - > rate ) )
val = i ;
val < < = 4 ;
}
2013-03-08 02:53:12 +04:00
ret = regmap_update_bits ( cs4271 - > regmap , CS4271_DACCTL ,
2011-01-19 21:22:06 +03:00
CS4271_DACCTL_DEM_MASK , val ) ;
2011-01-21 22:22:07 +03:00
if ( ret < 0 )
return ret ;
return 0 ;
2011-01-19 21:22:06 +03:00
}
static int cs4271_get_deemph ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2014-03-18 12:02:04 +04:00
struct snd_soc_codec * codec = snd_soc_kcontrol_codec ( kcontrol ) ;
2011-01-19 21:22:06 +03:00
struct cs4271_private * cs4271 = snd_soc_codec_get_drvdata ( codec ) ;
2015-03-10 14:39:05 +03:00
ucontrol - > value . integer . value [ 0 ] = cs4271 - > deemph ;
2011-01-19 21:22:06 +03:00
return 0 ;
}
static int cs4271_put_deemph ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2014-03-18 12:02:04 +04:00
struct snd_soc_codec * codec = snd_soc_kcontrol_codec ( kcontrol ) ;
2011-01-19 21:22:06 +03:00
struct cs4271_private * cs4271 = snd_soc_codec_get_drvdata ( codec ) ;
2015-03-10 14:39:05 +03:00
cs4271 - > deemph = ucontrol - > value . integer . value [ 0 ] ;
2011-01-19 21:22:06 +03:00
return cs4271_set_deemph ( codec ) ;
}
2011-03-07 20:29:45 +03:00
struct cs4271_clk_cfg {
bool master ; /* codec mode */
u8 speed_mode ; /* codec speed mode: 1x, 2x, 4x */
unsigned short ratio ; /* MCLK / sample rate */
u8 ratio_mask ; /* ratio bit mask for Master mode */
} ;
static struct cs4271_clk_cfg cs4271_clk_tab [ ] = {
{ 1 , CS4271_MODE1_MODE_1X , 256 , CS4271_MODE1_DIV_1 } ,
{ 1 , CS4271_MODE1_MODE_1X , 384 , CS4271_MODE1_DIV_15 } ,
{ 1 , CS4271_MODE1_MODE_1X , 512 , CS4271_MODE1_DIV_2 } ,
{ 1 , CS4271_MODE1_MODE_1X , 768 , CS4271_MODE1_DIV_3 } ,
{ 1 , CS4271_MODE1_MODE_2X , 128 , CS4271_MODE1_DIV_1 } ,
{ 1 , CS4271_MODE1_MODE_2X , 192 , CS4271_MODE1_DIV_15 } ,
{ 1 , CS4271_MODE1_MODE_2X , 256 , CS4271_MODE1_DIV_2 } ,
{ 1 , CS4271_MODE1_MODE_2X , 384 , CS4271_MODE1_DIV_3 } ,
{ 1 , CS4271_MODE1_MODE_4X , 64 , CS4271_MODE1_DIV_1 } ,
{ 1 , CS4271_MODE1_MODE_4X , 96 , CS4271_MODE1_DIV_15 } ,
{ 1 , CS4271_MODE1_MODE_4X , 128 , CS4271_MODE1_DIV_2 } ,
{ 1 , CS4271_MODE1_MODE_4X , 192 , CS4271_MODE1_DIV_3 } ,
{ 0 , CS4271_MODE1_MODE_1X , 256 , CS4271_MODE1_DIV_1 } ,
{ 0 , CS4271_MODE1_MODE_1X , 384 , CS4271_MODE1_DIV_1 } ,
{ 0 , CS4271_MODE1_MODE_1X , 512 , CS4271_MODE1_DIV_1 } ,
{ 0 , CS4271_MODE1_MODE_1X , 768 , CS4271_MODE1_DIV_2 } ,
{ 0 , CS4271_MODE1_MODE_1X , 1024 , CS4271_MODE1_DIV_2 } ,
{ 0 , CS4271_MODE1_MODE_2X , 128 , CS4271_MODE1_DIV_1 } ,
{ 0 , CS4271_MODE1_MODE_2X , 192 , CS4271_MODE1_DIV_1 } ,
{ 0 , CS4271_MODE1_MODE_2X , 256 , CS4271_MODE1_DIV_1 } ,
{ 0 , CS4271_MODE1_MODE_2X , 384 , CS4271_MODE1_DIV_2 } ,
{ 0 , CS4271_MODE1_MODE_2X , 512 , CS4271_MODE1_DIV_2 } ,
{ 0 , CS4271_MODE1_MODE_4X , 64 , CS4271_MODE1_DIV_1 } ,
{ 0 , CS4271_MODE1_MODE_4X , 96 , CS4271_MODE1_DIV_1 } ,
{ 0 , CS4271_MODE1_MODE_4X , 128 , CS4271_MODE1_DIV_1 } ,
{ 0 , CS4271_MODE1_MODE_4X , 192 , CS4271_MODE1_DIV_2 } ,
{ 0 , CS4271_MODE1_MODE_4X , 256 , CS4271_MODE1_DIV_2 } ,
} ;
# define CS4171_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab)
2011-01-19 21:22:06 +03:00
static int cs4271_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
2012-04-04 18:58:16 +04:00
struct snd_soc_codec * codec = dai - > codec ;
2011-01-19 21:22:06 +03:00
struct cs4271_private * cs4271 = snd_soc_codec_get_drvdata ( codec ) ;
2011-01-21 22:22:07 +03:00
int i , ret ;
unsigned int ratio , val ;
2011-01-19 21:22:06 +03:00
2012-12-10 13:30:04 +04:00
if ( cs4271 - > enable_soft_reset ) {
/*
* Put the codec in soft reset and back again in case it ' s not
* currently streaming data . This way of bringing the codec in
* sync to the current clocks is not explicitly documented in
* the data sheet , but it seems to work fine , and in contrast
* to a read hardware reset , we don ' t have to sync back all
* registers every time .
*/
if ( ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK & &
! dai - > capture_active ) | |
( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE & &
! dai - > playback_active ) ) {
2013-03-08 02:53:12 +04:00
ret = regmap_update_bits ( cs4271 - > regmap , CS4271_MODE2 ,
CS4271_MODE2_PDN ,
CS4271_MODE2_PDN ) ;
2012-12-10 13:30:04 +04:00
if ( ret < 0 )
return ret ;
2013-03-08 02:53:12 +04:00
ret = regmap_update_bits ( cs4271 - > regmap , CS4271_MODE2 ,
CS4271_MODE2_PDN , 0 ) ;
2012-12-10 13:30:04 +04:00
if ( ret < 0 )
return ret ;
}
}
2011-01-19 21:22:06 +03:00
cs4271 - > rate = params_rate ( params ) ;
2011-03-07 20:29:45 +03:00
/* Configure DAC */
if ( cs4271 - > rate < 50000 )
val = CS4271_MODE1_MODE_1X ;
else if ( cs4271 - > rate < 100000 )
val = CS4271_MODE1_MODE_2X ;
else
val = CS4271_MODE1_MODE_4X ;
2011-01-19 21:22:06 +03:00
ratio = cs4271 - > mclk / cs4271 - > rate ;
for ( i = 0 ; i < CS4171_NR_RATIOS ; i + + )
2011-03-07 20:29:45 +03:00
if ( ( cs4271_clk_tab [ i ] . master = = cs4271 - > master ) & &
( cs4271_clk_tab [ i ] . speed_mode = = val ) & &
( cs4271_clk_tab [ i ] . ratio = = ratio ) )
2011-01-19 21:22:06 +03:00
break ;
2011-03-07 20:29:45 +03:00
if ( i = = CS4171_NR_RATIOS ) {
2011-01-19 21:22:06 +03:00
dev_err ( codec - > dev , " Invalid sample rate \n " ) ;
return - EINVAL ;
}
2011-03-07 20:29:45 +03:00
val | = cs4271_clk_tab [ i ] . ratio_mask ;
2011-01-19 21:22:06 +03:00
2013-03-08 02:53:12 +04:00
ret = regmap_update_bits ( cs4271 - > regmap , CS4271_MODE1 ,
2011-01-19 21:22:06 +03:00
CS4271_MODE1_MODE_MASK | CS4271_MODE1_DIV_MASK , val ) ;
2011-01-21 22:22:07 +03:00
if ( ret < 0 )
return ret ;
2011-01-19 21:22:06 +03:00
return cs4271_set_deemph ( codec ) ;
}
2013-03-21 23:43:54 +04:00
static int cs4271_mute_stream ( struct snd_soc_dai * dai , int mute , int stream )
2011-01-19 21:22:06 +03:00
{
struct snd_soc_codec * codec = dai - > codec ;
2013-03-08 02:53:12 +04:00
struct cs4271_private * cs4271 = snd_soc_codec_get_drvdata ( codec ) ;
2011-01-21 22:22:07 +03:00
int ret ;
2011-01-19 21:22:06 +03:00
int val_a = 0 ;
int val_b = 0 ;
2013-03-21 23:43:54 +04:00
if ( stream ! = SNDRV_PCM_STREAM_PLAYBACK )
return 0 ;
2011-01-19 21:22:06 +03:00
if ( mute ) {
val_a = CS4271_VOLA_MUTE ;
val_b = CS4271_VOLB_MUTE ;
}
2013-03-08 02:53:12 +04:00
ret = regmap_update_bits ( cs4271 - > regmap , CS4271_VOLA ,
CS4271_VOLA_MUTE , val_a ) ;
2011-01-21 22:22:07 +03:00
if ( ret < 0 )
return ret ;
2013-03-08 02:53:12 +04:00
ret = regmap_update_bits ( cs4271 - > regmap , CS4271_VOLB ,
CS4271_VOLB_MUTE , val_b ) ;
2011-01-21 22:22:07 +03:00
if ( ret < 0 )
return ret ;
2011-01-19 21:22:06 +03:00
return 0 ;
}
/* CS4271 controls */
static DECLARE_TLV_DB_SCALE ( cs4271_dac_tlv , - 12700 , 100 , 0 ) ;
static const struct snd_kcontrol_new cs4271_snd_controls [ ] = {
SOC_DOUBLE_R_TLV ( " Master Playback Volume " , CS4271_VOLA , CS4271_VOLB ,
0 , 0x7F , 1 , cs4271_dac_tlv ) ,
SOC_SINGLE ( " Digital Loopback Switch " , CS4271_MODE2 , 4 , 1 , 0 ) ,
SOC_SINGLE ( " Soft Ramp Switch " , CS4271_DACVOL , 5 , 1 , 0 ) ,
SOC_SINGLE ( " Zero Cross Switch " , CS4271_DACVOL , 4 , 1 , 0 ) ,
SOC_SINGLE_BOOL_EXT ( " De-emphasis Switch " , 0 ,
cs4271_get_deemph , cs4271_put_deemph ) ,
SOC_SINGLE ( " Auto-Mute Switch " , CS4271_DACCTL , 7 , 1 , 0 ) ,
SOC_SINGLE ( " Slow Roll Off Filter Switch " , CS4271_DACCTL , 6 , 1 , 0 ) ,
SOC_SINGLE ( " Soft Volume Ramp-Up Switch " , CS4271_DACCTL , 3 , 1 , 0 ) ,
SOC_SINGLE ( " Soft Ramp-Down Switch " , CS4271_DACCTL , 2 , 1 , 0 ) ,
SOC_SINGLE ( " Left Channel Inversion Switch " , CS4271_DACCTL , 1 , 1 , 0 ) ,
SOC_SINGLE ( " Right Channel Inversion Switch " , CS4271_DACCTL , 0 , 1 , 0 ) ,
SOC_DOUBLE ( " Master Capture Switch " , CS4271_ADCCTL , 3 , 2 , 1 , 1 ) ,
SOC_SINGLE ( " Dither 16-Bit Data Switch " , CS4271_ADCCTL , 5 , 1 , 0 ) ,
SOC_DOUBLE ( " High Pass Filter Switch " , CS4271_ADCCTL , 1 , 0 , 1 , 1 ) ,
SOC_DOUBLE_R ( " Master Playback Switch " , CS4271_VOLA , CS4271_VOLB ,
7 , 1 , 1 ) ,
} ;
2011-11-23 14:40:40 +04:00
static const struct snd_soc_dai_ops cs4271_dai_ops = {
2011-01-19 21:22:06 +03:00
. hw_params = cs4271_hw_params ,
. set_sysclk = cs4271_set_dai_sysclk ,
. set_fmt = cs4271_set_dai_fmt ,
2013-03-21 23:43:54 +04:00
. mute_stream = cs4271_mute_stream ,
2011-01-19 21:22:06 +03:00
} ;
2011-01-26 14:35:28 +03:00
static struct snd_soc_dai_driver cs4271_dai = {
2011-01-19 21:22:06 +03:00
. name = " cs4271-hifi " ,
. playback = {
. stream_name = " Playback " ,
. channels_min = 2 ,
. channels_max = 2 ,
2011-03-07 20:29:36 +03:00
. rates = CS4271_PCM_RATES ,
2011-01-19 21:22:06 +03:00
. formats = CS4271_PCM_FORMATS ,
} ,
. capture = {
. stream_name = " Capture " ,
. channels_min = 2 ,
. channels_max = 2 ,
2011-03-07 20:29:36 +03:00
. rates = CS4271_PCM_RATES ,
2011-01-19 21:22:06 +03:00
. formats = CS4271_PCM_FORMATS ,
} ,
. ops = & cs4271_dai_ops ,
. symmetric_rates = 1 ,
} ;
# ifdef CONFIG_PM
2011-12-02 13:18:28 +04:00
static int cs4271_soc_suspend ( struct snd_soc_codec * codec )
2011-01-19 21:22:06 +03:00
{
2011-01-21 22:22:07 +03:00
int ret ;
2013-03-08 02:53:12 +04:00
struct cs4271_private * cs4271 = snd_soc_codec_get_drvdata ( codec ) ;
2011-01-19 21:22:06 +03:00
/* Set power-down bit */
2013-03-08 02:53:12 +04:00
ret = regmap_update_bits ( cs4271 - > regmap , CS4271_MODE2 ,
CS4271_MODE2_PDN , CS4271_MODE2_PDN ) ;
2011-01-21 22:22:07 +03:00
if ( ret < 0 )
return ret ;
2013-03-08 02:53:12 +04:00
2011-01-19 21:22:06 +03:00
return 0 ;
}
static int cs4271_soc_resume ( struct snd_soc_codec * codec )
{
2011-01-21 22:22:07 +03:00
int ret ;
2013-03-08 02:53:12 +04:00
struct cs4271_private * cs4271 = snd_soc_codec_get_drvdata ( codec ) ;
2011-01-19 21:22:06 +03:00
/* Restore codec state */
2013-03-08 02:53:12 +04:00
ret = regcache_sync ( cs4271 - > regmap ) ;
2011-01-21 22:22:07 +03:00
if ( ret < 0 )
return ret ;
2013-03-08 02:53:12 +04:00
2011-01-19 21:22:06 +03:00
/* then disable the power-down bit */
2013-03-08 02:53:12 +04:00
ret = regmap_update_bits ( cs4271 - > regmap , CS4271_MODE2 ,
CS4271_MODE2_PDN , 0 ) ;
2011-01-21 22:22:07 +03:00
if ( ret < 0 )
return ret ;
2013-03-08 02:53:12 +04:00
2011-01-19 21:22:06 +03:00
return 0 ;
}
# else
# define cs4271_soc_suspend NULL
# define cs4271_soc_resume NULL
# endif /* CONFIG_PM */
2012-09-28 03:36:44 +04:00
# ifdef CONFIG_OF
2014-10-06 19:09:47 +04:00
const struct of_device_id cs4271_dt_ids [ ] = {
2012-09-28 03:36:44 +04:00
{ . compatible = " cirrus,cs4271 " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , cs4271_dt_ids ) ;
2014-10-06 19:09:47 +04:00
EXPORT_SYMBOL_GPL ( cs4271_dt_ids ) ;
2012-09-28 03:36:44 +04:00
# endif
2014-10-06 19:09:47 +04:00
static int cs4271_codec_probe ( struct snd_soc_codec * codec )
2011-01-19 21:22:06 +03:00
{
struct cs4271_private * cs4271 = snd_soc_codec_get_drvdata ( codec ) ;
struct cs4271_platform_data * cs4271plat = codec - > dev - > platform_data ;
int ret ;
2012-11-30 14:28:55 +04:00
bool amutec_eq_bmutec = false ;
2011-01-19 21:22:06 +03:00
2012-09-28 03:36:44 +04:00
# ifdef CONFIG_OF
2012-10-04 16:03:23 +04:00
if ( of_match_device ( cs4271_dt_ids , codec - > dev ) ) {
2012-11-30 14:28:56 +04:00
if ( of_get_property ( codec - > dev - > of_node ,
2012-10-04 16:03:23 +04:00
" cirrus,amutec-eq-bmutec " , NULL ) )
2012-11-30 14:28:55 +04:00
amutec_eq_bmutec = true ;
2012-12-10 13:30:04 +04:00
if ( of_get_property ( codec - > dev - > of_node ,
" cirrus,enable-soft-reset " , NULL ) )
cs4271 - > enable_soft_reset = true ;
2012-10-04 16:03:23 +04:00
}
2012-09-28 03:36:44 +04:00
# endif
2012-10-04 16:03:23 +04:00
if ( cs4271plat ) {
amutec_eq_bmutec = cs4271plat - > amutec_eq_bmutec ;
2012-12-10 13:30:04 +04:00
cs4271 - > enable_soft_reset = cs4271plat - > enable_soft_reset ;
2012-10-04 16:03:23 +04:00
}
2011-01-19 21:22:06 +03:00
2014-02-19 17:05:54 +04:00
if ( gpio_is_valid ( cs4271 - > gpio_nreset ) ) {
2011-01-19 21:22:06 +03:00
/* Reset codec */
2014-02-19 17:05:54 +04:00
gpio_direction_output ( cs4271 - > gpio_nreset , 0 ) ;
2015-04-02 11:17:40 +03:00
mdelay ( 1 ) ;
2014-02-19 17:05:54 +04:00
gpio_set_value ( cs4271 - > gpio_nreset , 1 ) ;
2011-01-19 21:22:06 +03:00
/* Give the codec time to wake up */
2015-04-02 11:17:40 +03:00
mdelay ( 1 ) ;
2011-01-19 21:22:06 +03:00
}
2013-03-08 02:53:12 +04:00
ret = regmap_update_bits ( cs4271 - > regmap , CS4271_MODE2 ,
CS4271_MODE2_PDN | CS4271_MODE2_CPEN ,
CS4271_MODE2_PDN | CS4271_MODE2_CPEN ) ;
2011-01-21 22:22:07 +03:00
if ( ret < 0 )
return ret ;
2013-03-08 02:53:12 +04:00
ret = regmap_update_bits ( cs4271 - > regmap , CS4271_MODE2 ,
CS4271_MODE2_PDN , 0 ) ;
2011-01-21 22:22:07 +03:00
if ( ret < 0 )
return ret ;
2011-01-19 21:22:06 +03:00
/* Power-up sequence requires 85 uS */
udelay ( 85 ) ;
2012-10-04 16:03:23 +04:00
if ( amutec_eq_bmutec )
2013-03-08 02:53:12 +04:00
regmap_update_bits ( cs4271 - > regmap , CS4271_MODE2 ,
CS4271_MODE2_MUTECAEQUB ,
CS4271_MODE2_MUTECAEQUB ) ;
2012-10-04 16:03:23 +04:00
2013-08-11 16:12:13 +04:00
return 0 ;
2011-01-19 21:22:06 +03:00
}
2014-10-06 19:09:47 +04:00
static int cs4271_codec_remove ( struct snd_soc_codec * codec )
2011-01-19 21:22:06 +03:00
{
struct cs4271_private * cs4271 = snd_soc_codec_get_drvdata ( codec ) ;
2012-11-10 22:52:50 +04:00
if ( gpio_is_valid ( cs4271 - > gpio_nreset ) )
2011-01-19 21:22:06 +03:00
/* Set codec to the reset state */
2012-11-10 22:52:50 +04:00
gpio_set_value ( cs4271 - > gpio_nreset , 0 ) ;
2011-01-19 21:22:06 +03:00
return 0 ;
} ;
2011-01-26 14:35:28 +03:00
static struct snd_soc_codec_driver soc_codec_dev_cs4271 = {
2014-10-06 19:09:47 +04:00
. probe = cs4271_codec_probe ,
. remove = cs4271_codec_remove ,
2011-01-19 21:22:06 +03:00
. suspend = cs4271_soc_suspend ,
. resume = cs4271_soc_resume ,
2013-08-11 16:12:13 +04:00
. controls = cs4271_snd_controls ,
. num_controls = ARRAY_SIZE ( cs4271_snd_controls ) ,
2013-08-11 16:15:10 +04:00
. dapm_widgets = cs4271_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( cs4271_dapm_widgets ) ,
. dapm_routes = cs4271_dapm_routes ,
. num_dapm_routes = ARRAY_SIZE ( cs4271_dapm_routes ) ,
2011-01-19 21:22:06 +03:00
} ;
2014-02-19 17:05:54 +04:00
static int cs4271_common_probe ( struct device * dev ,
struct cs4271_private * * c )
2011-01-19 21:22:06 +03:00
{
2014-02-19 17:05:54 +04:00
struct cs4271_platform_data * cs4271plat = dev - > platform_data ;
2011-01-19 21:22:06 +03:00
struct cs4271_private * cs4271 ;
2014-02-19 17:05:54 +04:00
cs4271 = devm_kzalloc ( dev , sizeof ( * cs4271 ) , GFP_KERNEL ) ;
2011-01-19 21:22:06 +03:00
if ( ! cs4271 )
return - ENOMEM ;
2014-02-19 17:05:54 +04:00
if ( of_match_device ( cs4271_dt_ids , dev ) )
cs4271 - > gpio_nreset =
of_get_named_gpio ( dev - > of_node , " reset-gpio " , 0 ) ;
if ( cs4271plat )
cs4271 - > gpio_nreset = cs4271plat - > gpio_nreset ;
if ( gpio_is_valid ( cs4271 - > gpio_nreset ) ) {
int ret ;
ret = devm_gpio_request ( dev , cs4271 - > gpio_nreset ,
" CS4271 Reset " ) ;
if ( ret < 0 )
return ret ;
}
* c = cs4271 ;
return 0 ;
}
2014-10-06 19:09:47 +04:00
const struct regmap_config cs4271_regmap_config = {
2014-03-05 20:53:31 +04:00
. max_register = CS4271_LASTREG ,
. reg_defaults = cs4271_reg_defaults ,
. num_reg_defaults = ARRAY_SIZE ( cs4271_reg_defaults ) ,
. cache_type = REGCACHE_RBTREE ,
. volatile_reg = cs4271_volatile_reg ,
} ;
2014-10-06 19:09:47 +04:00
EXPORT_SYMBOL_GPL ( cs4271_regmap_config ) ;
2014-03-05 20:53:31 +04:00
2014-10-06 19:09:47 +04:00
int cs4271_probe ( struct device * dev , struct regmap * regmap )
2014-02-19 17:05:54 +04:00
{
struct cs4271_private * cs4271 ;
int ret ;
2014-10-06 19:09:47 +04:00
if ( IS_ERR ( regmap ) )
return PTR_ERR ( regmap ) ;
2011-01-19 21:22:06 +03:00
2014-10-06 19:09:47 +04:00
ret = cs4271_common_probe ( dev , & cs4271 ) ;
2014-02-19 17:05:54 +04:00
if ( ret < 0 )
return ret ;
2011-01-19 21:22:06 +03:00
2014-10-06 19:09:47 +04:00
dev_set_drvdata ( dev , cs4271 ) ;
cs4271 - > regmap = regmap ;
2011-01-19 21:22:06 +03:00
2014-10-06 19:09:47 +04:00
return snd_soc_register_codec ( dev , & soc_codec_dev_cs4271 , & cs4271_dai ,
1 ) ;
2011-01-19 21:22:06 +03:00
}
2014-10-06 19:09:47 +04:00
EXPORT_SYMBOL_GPL ( cs4271_probe ) ;
2011-01-19 21:22:06 +03:00
MODULE_AUTHOR ( " Alexander Sverdlin <subaparts@yandex.ru> " ) ;
MODULE_DESCRIPTION ( " Cirrus Logic CS4271 ALSA SoC Codec Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;