2010-05-27 16:57:41 +04:00
/*
* cs42l51 . c
*
* ASoC Driver for Cirrus Logic CS42L51 codecs
*
* Copyright ( c ) 2010 Arnaud Patard < apatard @ mandriva . com >
*
* Based on cs4270 . c - Copyright ( c ) Freescale Semiconductor
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* 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 .
*
* For now :
* - Only I2C is support . Not SPI
* - master mode * NOT * supported
*/
2018-10-19 18:56:35 +03:00
# include <linux/clk.h>
2010-05-27 16:57:41 +04:00
# include <linux/module.h>
# include <linux/slab.h>
# include <sound/core.h>
# include <sound/soc.h>
# include <sound/tlv.h>
# include <sound/initval.h>
# include <sound/pcm_params.h>
# include <sound/pcm.h>
2019-04-03 16:23:33 +03:00
# include <linux/gpio/consumer.h>
2014-02-11 23:24:31 +04:00
# include <linux/regmap.h>
2019-04-03 16:23:32 +03:00
# include <linux/regulator/consumer.h>
2010-05-27 16:57:41 +04:00
# include "cs42l51.h"
enum master_slave_mode {
MODE_SLAVE ,
MODE_SLAVE_AUTO ,
MODE_MASTER ,
} ;
2019-04-03 16:23:32 +03:00
static const char * const cs42l51_supply_names [ ] = {
" VL " ,
" VD " ,
" VA " ,
" VAHP " ,
} ;
2010-05-27 16:57:41 +04:00
struct cs42l51_private {
unsigned int mclk ;
2018-10-19 18:56:35 +03:00
struct clk * mclk_handle ;
2010-05-27 16:57:41 +04:00
unsigned int audio_mode ; /* The mode (I2S or left-justified) */
enum master_slave_mode func ;
2019-04-03 16:23:32 +03:00
struct regulator_bulk_data supplies [ ARRAY_SIZE ( cs42l51_supply_names ) ] ;
2019-04-03 16:23:33 +03:00
struct gpio_desc * reset_gpio ;
2019-04-03 16:23:36 +03:00
struct regmap * regmap ;
2010-05-27 16:57:41 +04:00
} ;
# define CS42L51_FORMATS ( \
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE )
static int cs42l51_get_chan_mix ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2018-01-29 06:59:02 +03:00
struct snd_soc_component * component = snd_soc_kcontrol_component ( kcontrol ) ;
unsigned long value = snd_soc_component_read32 ( component , CS42L51_PCM_MIXER ) & 3 ;
2010-05-27 16:57:41 +04:00
switch ( value ) {
default :
case 0 :
2016-02-29 20:02:59 +03:00
ucontrol - > value . enumerated . item [ 0 ] = 0 ;
2010-05-27 16:57:41 +04:00
break ;
/* same value : (L+R)/2 and (R+L)/2 */
case 1 :
case 2 :
2016-02-29 20:02:59 +03:00
ucontrol - > value . enumerated . item [ 0 ] = 1 ;
2010-05-27 16:57:41 +04:00
break ;
case 3 :
2016-02-29 20:02:59 +03:00
ucontrol - > value . enumerated . item [ 0 ] = 2 ;
2010-05-27 16:57:41 +04:00
break ;
}
return 0 ;
}
# define CHAN_MIX_NORMAL 0x00
# define CHAN_MIX_BOTH 0x55
# define CHAN_MIX_SWAP 0xFF
static int cs42l51_set_chan_mix ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2018-01-29 06:59:02 +03:00
struct snd_soc_component * component = snd_soc_kcontrol_component ( kcontrol ) ;
2010-05-27 16:57:41 +04:00
unsigned char val ;
2016-02-29 20:02:59 +03:00
switch ( ucontrol - > value . enumerated . item [ 0 ] ) {
2010-05-27 16:57:41 +04:00
default :
case 0 :
val = CHAN_MIX_NORMAL ;
break ;
case 1 :
val = CHAN_MIX_BOTH ;
break ;
case 2 :
val = CHAN_MIX_SWAP ;
break ;
}
2018-01-29 06:59:02 +03:00
snd_soc_component_write ( component , CS42L51_PCM_MIXER , val ) ;
2010-05-27 16:57:41 +04:00
return 1 ;
}
static const DECLARE_TLV_DB_SCALE ( adc_pcm_tlv , - 5150 , 50 , 0 ) ;
static const DECLARE_TLV_DB_SCALE ( tone_tlv , - 1050 , 150 , 0 ) ;
2014-03-19 19:40:02 +04:00
static const DECLARE_TLV_DB_SCALE ( aout_tlv , - 10200 , 50 , 0 ) ;
2010-05-27 16:57:41 +04:00
static const DECLARE_TLV_DB_SCALE ( boost_tlv , 1600 , 1600 , 0 ) ;
2019-04-03 16:23:37 +03:00
static const DECLARE_TLV_DB_SCALE ( adc_boost_tlv , 2000 , 2000 , 0 ) ;
2010-05-27 16:57:41 +04:00
static const char * chan_mix [ ] = {
" L R " ,
" L+R " ,
" R L " ,
} ;
2014-02-18 13:44:57 +04:00
static SOC_ENUM_SINGLE_EXT_DECL ( cs42l51_chan_mix , chan_mix ) ;
2010-05-27 16:57:41 +04:00
static const struct snd_kcontrol_new cs42l51_snd_controls [ ] = {
SOC_DOUBLE_R_SX_TLV ( " PCM Playback Volume " ,
CS42L51_PCMA_VOL , CS42L51_PCMB_VOL ,
2014-03-19 19:40:02 +04:00
0 , 0x19 , 0x7F , adc_pcm_tlv ) ,
2010-05-27 16:57:41 +04:00
SOC_DOUBLE_R ( " PCM Playback Switch " ,
CS42L51_PCMA_VOL , CS42L51_PCMB_VOL , 7 , 1 , 1 ) ,
SOC_DOUBLE_R_SX_TLV ( " Analog Playback Volume " ,
CS42L51_AOUTA_VOL , CS42L51_AOUTB_VOL ,
2012-03-30 19:43:55 +04:00
0 , 0x34 , 0xE4 , aout_tlv ) ,
2010-05-27 16:57:41 +04:00
SOC_DOUBLE_R_SX_TLV ( " ADC Mixer Volume " ,
CS42L51_ADCA_VOL , CS42L51_ADCB_VOL ,
2014-03-19 19:40:02 +04:00
0 , 0x19 , 0x7F , adc_pcm_tlv ) ,
2010-05-27 16:57:41 +04:00
SOC_DOUBLE_R ( " ADC Mixer Switch " ,
CS42L51_ADCA_VOL , CS42L51_ADCB_VOL , 7 , 1 , 1 ) ,
SOC_SINGLE ( " Playback Deemphasis Switch " , CS42L51_DAC_CTL , 3 , 1 , 0 ) ,
SOC_SINGLE ( " Auto-Mute Switch " , CS42L51_DAC_CTL , 2 , 1 , 0 ) ,
SOC_SINGLE ( " Soft Ramp Switch " , CS42L51_DAC_CTL , 1 , 1 , 0 ) ,
SOC_SINGLE ( " Zero Cross Switch " , CS42L51_DAC_CTL , 0 , 0 , 0 ) ,
SOC_DOUBLE_TLV ( " Mic Boost Volume " ,
CS42L51_MIC_CTL , 0 , 1 , 1 , 0 , boost_tlv ) ,
2019-04-03 16:23:37 +03:00
SOC_DOUBLE_TLV ( " ADC Boost Volume " ,
CS42L51_MIC_CTL , 5 , 6 , 1 , 0 , adc_boost_tlv ) ,
2010-05-27 16:57:41 +04:00
SOC_SINGLE_TLV ( " Bass Volume " , CS42L51_TONE_CTL , 0 , 0xf , 1 , tone_tlv ) ,
SOC_SINGLE_TLV ( " Treble Volume " , CS42L51_TONE_CTL , 4 , 0xf , 1 , tone_tlv ) ,
SOC_ENUM_EXT ( " PCM channel mixer " ,
cs42l51_chan_mix ,
cs42l51_get_chan_mix , cs42l51_set_chan_mix ) ,
} ;
/*
* to power down , one must :
* 1. ) Enable the PDN bit
* 2. ) enable power - down for the select channels
* 3. ) disable the PDN bit .
*/
static int cs42l51_pdn_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
2018-01-29 06:59:02 +03:00
struct snd_soc_component * component = snd_soc_dapm_to_component ( w - > dapm ) ;
2014-11-20 23:05:38 +03:00
2010-05-27 16:57:41 +04:00
switch ( event ) {
case SND_SOC_DAPM_PRE_PMD :
2018-01-29 06:59:02 +03:00
snd_soc_component_update_bits ( component , CS42L51_POWER_CTL1 ,
2011-11-01 11:17:57 +04:00
CS42L51_POWER_CTL1_PDN ,
CS42L51_POWER_CTL1_PDN ) ;
2010-05-27 16:57:41 +04:00
break ;
default :
case SND_SOC_DAPM_POST_PMD :
2018-01-29 06:59:02 +03:00
snd_soc_component_update_bits ( component , CS42L51_POWER_CTL1 ,
2011-11-01 11:17:57 +04:00
CS42L51_POWER_CTL1_PDN , 0 ) ;
2010-05-27 16:57:41 +04:00
break ;
}
return 0 ;
}
static const char * cs42l51_dac_names [ ] = { " Direct PCM " ,
" DSP PCM " , " ADC " } ;
2014-02-18 13:44:57 +04:00
static SOC_ENUM_SINGLE_DECL ( cs42l51_dac_mux_enum ,
CS42L51_DAC_CTL , 6 , cs42l51_dac_names ) ;
2010-05-27 16:57:41 +04:00
static const struct snd_kcontrol_new cs42l51_dac_mux_controls =
SOC_DAPM_ENUM ( " Route " , cs42l51_dac_mux_enum ) ;
static const char * cs42l51_adcl_names [ ] = { " AIN1 Left " , " AIN2 Left " ,
" MIC Left " , " MIC+preamp Left " } ;
2014-02-18 13:44:57 +04:00
static SOC_ENUM_SINGLE_DECL ( cs42l51_adcl_mux_enum ,
CS42L51_ADC_INPUT , 4 , cs42l51_adcl_names ) ;
2010-05-27 16:57:41 +04:00
static const struct snd_kcontrol_new cs42l51_adcl_mux_controls =
SOC_DAPM_ENUM ( " Route " , cs42l51_adcl_mux_enum ) ;
static const char * cs42l51_adcr_names [ ] = { " AIN1 Right " , " AIN2 Right " ,
" MIC Right " , " MIC+preamp Right " } ;
2014-02-18 13:44:57 +04:00
static SOC_ENUM_SINGLE_DECL ( cs42l51_adcr_mux_enum ,
CS42L51_ADC_INPUT , 6 , cs42l51_adcr_names ) ;
2010-05-27 16:57:41 +04:00
static const struct snd_kcontrol_new cs42l51_adcr_mux_controls =
SOC_DAPM_ENUM ( " Route " , cs42l51_adcr_mux_enum ) ;
static const struct snd_soc_dapm_widget cs42l51_dapm_widgets [ ] = {
2019-04-03 16:23:35 +03:00
SND_SOC_DAPM_SUPPLY ( " Mic Bias " , CS42L51_MIC_POWER_CTL , 1 , 1 , NULL ,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD ) ,
2010-05-27 16:57:41 +04:00
SND_SOC_DAPM_PGA_E ( " Left PGA " , CS42L51_POWER_CTL1 , 3 , 1 , NULL , 0 ,
cs42l51_pdn_event , SND_SOC_DAPM_PRE_POST_PMD ) ,
SND_SOC_DAPM_PGA_E ( " Right PGA " , CS42L51_POWER_CTL1 , 4 , 1 , NULL , 0 ,
cs42l51_pdn_event , SND_SOC_DAPM_PRE_POST_PMD ) ,
SND_SOC_DAPM_ADC_E ( " Left ADC " , " Left HiFi Capture " ,
CS42L51_POWER_CTL1 , 1 , 1 ,
cs42l51_pdn_event , SND_SOC_DAPM_PRE_POST_PMD ) ,
SND_SOC_DAPM_ADC_E ( " Right ADC " , " Right HiFi Capture " ,
CS42L51_POWER_CTL1 , 2 , 1 ,
cs42l51_pdn_event , SND_SOC_DAPM_PRE_POST_PMD ) ,
SND_SOC_DAPM_DAC_E ( " Left DAC " , " Left HiFi Playback " ,
CS42L51_POWER_CTL1 , 5 , 1 ,
cs42l51_pdn_event , SND_SOC_DAPM_PRE_POST_PMD ) ,
SND_SOC_DAPM_DAC_E ( " Right DAC " , " Right HiFi Playback " ,
CS42L51_POWER_CTL1 , 6 , 1 ,
cs42l51_pdn_event , SND_SOC_DAPM_PRE_POST_PMD ) ,
/* analog/mic */
SND_SOC_DAPM_INPUT ( " AIN1L " ) ,
SND_SOC_DAPM_INPUT ( " AIN1R " ) ,
SND_SOC_DAPM_INPUT ( " AIN2L " ) ,
SND_SOC_DAPM_INPUT ( " AIN2R " ) ,
SND_SOC_DAPM_INPUT ( " MICL " ) ,
SND_SOC_DAPM_INPUT ( " MICR " ) ,
SND_SOC_DAPM_MIXER ( " Mic Preamp Left " ,
CS42L51_MIC_POWER_CTL , 2 , 1 , NULL , 0 ) ,
SND_SOC_DAPM_MIXER ( " Mic Preamp Right " ,
CS42L51_MIC_POWER_CTL , 3 , 1 , NULL , 0 ) ,
/* HP */
SND_SOC_DAPM_OUTPUT ( " HPL " ) ,
SND_SOC_DAPM_OUTPUT ( " HPR " ) ,
/* mux */
SND_SOC_DAPM_MUX ( " DAC Mux " , SND_SOC_NOPM , 0 , 0 ,
& cs42l51_dac_mux_controls ) ,
SND_SOC_DAPM_MUX ( " PGA-ADC Mux Left " , SND_SOC_NOPM , 0 , 0 ,
& cs42l51_adcl_mux_controls ) ,
SND_SOC_DAPM_MUX ( " PGA-ADC Mux Right " , SND_SOC_NOPM , 0 , 0 ,
& cs42l51_adcr_mux_controls ) ,
} ;
2018-10-15 17:03:36 +03:00
static const struct snd_soc_dapm_widget cs42l51_dapm_mclk_widgets [ ] = {
SND_SOC_DAPM_CLOCK_SUPPLY ( " MCLK " )
} ;
2010-05-27 16:57:41 +04:00
static const struct snd_soc_dapm_route cs42l51_routes [ ] = {
{ " HPL " , NULL , " Left DAC " } ,
{ " HPR " , NULL , " Right DAC " } ,
{ " Left ADC " , NULL , " Left PGA " } ,
{ " Right ADC " , NULL , " Right PGA " } ,
{ " Mic Preamp Left " , NULL , " MICL " } ,
{ " Mic Preamp Right " , NULL , " MICR " } ,
{ " PGA-ADC Mux Left " , " AIN1 Left " , " AIN1L " } ,
{ " PGA-ADC Mux Left " , " AIN2 Left " , " AIN2L " } ,
{ " PGA-ADC Mux Left " , " MIC Left " , " MICL " } ,
{ " PGA-ADC Mux Left " , " MIC+preamp Left " , " Mic Preamp Left " } ,
{ " PGA-ADC Mux Right " , " AIN1 Right " , " AIN1R " } ,
{ " PGA-ADC Mux Right " , " AIN2 Right " , " AIN2R " } ,
{ " PGA-ADC Mux Right " , " MIC Right " , " MICR " } ,
{ " PGA-ADC Mux Right " , " MIC+preamp Right " , " Mic Preamp Right " } ,
{ " Left PGA " , NULL , " PGA-ADC Mux Left " } ,
{ " Right PGA " , NULL , " PGA-ADC Mux Right " } ,
} ;
static int cs42l51_set_dai_fmt ( struct snd_soc_dai * codec_dai ,
unsigned int format )
{
2018-01-29 06:59:02 +03:00
struct snd_soc_component * component = codec_dai - > component ;
struct cs42l51_private * cs42l51 = snd_soc_component_get_drvdata ( component ) ;
2010-05-27 16:57:41 +04:00
switch ( format & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_I2S :
case SND_SOC_DAIFMT_LEFT_J :
case SND_SOC_DAIFMT_RIGHT_J :
cs42l51 - > audio_mode = format & SND_SOC_DAIFMT_FORMAT_MASK ;
break ;
default :
2018-01-29 06:59:02 +03:00
dev_err ( component - > dev , " invalid DAI format \n " ) ;
2011-10-06 03:29:56 +04:00
return - EINVAL ;
2010-05-27 16:57:41 +04:00
}
switch ( format & SND_SOC_DAIFMT_MASTER_MASK ) {
case SND_SOC_DAIFMT_CBM_CFM :
cs42l51 - > func = MODE_MASTER ;
break ;
case SND_SOC_DAIFMT_CBS_CFS :
cs42l51 - > func = MODE_SLAVE_AUTO ;
break ;
default :
2018-01-29 06:59:02 +03:00
dev_err ( component - > dev , " Unknown master/slave configuration \n " ) ;
2011-10-06 03:29:56 +04:00
return - EINVAL ;
2010-05-27 16:57:41 +04:00
}
2011-10-06 03:29:56 +04:00
return 0 ;
2010-05-27 16:57:41 +04:00
}
struct cs42l51_ratios {
unsigned int ratio ;
unsigned char speed_mode ;
unsigned char mclk ;
} ;
static struct cs42l51_ratios slave_ratios [ ] = {
{ 512 , CS42L51_QSM_MODE , 0 } , { 768 , CS42L51_QSM_MODE , 0 } ,
{ 1024 , CS42L51_QSM_MODE , 0 } , { 1536 , CS42L51_QSM_MODE , 0 } ,
{ 2048 , CS42L51_QSM_MODE , 0 } , { 3072 , CS42L51_QSM_MODE , 0 } ,
{ 256 , CS42L51_HSM_MODE , 0 } , { 384 , CS42L51_HSM_MODE , 0 } ,
{ 512 , CS42L51_HSM_MODE , 0 } , { 768 , CS42L51_HSM_MODE , 0 } ,
{ 1024 , CS42L51_HSM_MODE , 0 } , { 1536 , CS42L51_HSM_MODE , 0 } ,
{ 128 , CS42L51_SSM_MODE , 0 } , { 192 , CS42L51_SSM_MODE , 0 } ,
{ 256 , CS42L51_SSM_MODE , 0 } , { 384 , CS42L51_SSM_MODE , 0 } ,
{ 512 , CS42L51_SSM_MODE , 0 } , { 768 , CS42L51_SSM_MODE , 0 } ,
{ 128 , CS42L51_DSM_MODE , 0 } , { 192 , CS42L51_DSM_MODE , 0 } ,
{ 256 , CS42L51_DSM_MODE , 0 } , { 384 , CS42L51_DSM_MODE , 0 } ,
} ;
static struct cs42l51_ratios slave_auto_ratios [ ] = {
{ 1024 , CS42L51_QSM_MODE , 0 } , { 1536 , CS42L51_QSM_MODE , 0 } ,
{ 2048 , CS42L51_QSM_MODE , 1 } , { 3072 , CS42L51_QSM_MODE , 1 } ,
{ 512 , CS42L51_HSM_MODE , 0 } , { 768 , CS42L51_HSM_MODE , 0 } ,
{ 1024 , CS42L51_HSM_MODE , 1 } , { 1536 , CS42L51_HSM_MODE , 1 } ,
{ 256 , CS42L51_SSM_MODE , 0 } , { 384 , CS42L51_SSM_MODE , 0 } ,
{ 512 , CS42L51_SSM_MODE , 1 } , { 768 , CS42L51_SSM_MODE , 1 } ,
{ 128 , CS42L51_DSM_MODE , 0 } , { 192 , CS42L51_DSM_MODE , 0 } ,
{ 256 , CS42L51_DSM_MODE , 1 } , { 384 , CS42L51_DSM_MODE , 1 } ,
} ;
2019-04-03 16:23:34 +03:00
/*
* Master mode mclk / fs ratios .
* Recommended configurations are SSM for 4 - 50 khz and DSM for 50 - 100 kHz ranges
* The table below provides support of following ratios :
* 128 : SSM ( % 128 ) with div2 disabled
* 256 : SSM ( % 128 ) with div2 enabled
* In both cases , if sampling rate is above 50 kHz , SSM is overridden
* with DSM ( % 128 ) configuration
*/
static struct cs42l51_ratios master_ratios [ ] = {
{ 128 , CS42L51_SSM_MODE , 0 } , { 256 , CS42L51_SSM_MODE , 1 } ,
} ;
2010-05-27 16:57:41 +04:00
static int cs42l51_set_dai_sysclk ( struct snd_soc_dai * codec_dai ,
int clk_id , unsigned int freq , int dir )
{
2018-01-29 06:59:02 +03:00
struct snd_soc_component * component = codec_dai - > component ;
struct cs42l51_private * cs42l51 = snd_soc_component_get_drvdata ( component ) ;
2010-05-27 16:57:41 +04:00
cs42l51 - > mclk = freq ;
return 0 ;
}
static int cs42l51_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
2018-01-29 06:59:02 +03:00
struct snd_soc_component * component = dai - > component ;
struct cs42l51_private * cs42l51 = snd_soc_component_get_drvdata ( component ) ;
2010-05-27 16:57:41 +04:00
int ret ;
unsigned int i ;
unsigned int rate ;
unsigned int ratio ;
struct cs42l51_ratios * ratios = NULL ;
int nr_ratios = 0 ;
2019-04-03 16:23:34 +03:00
int intf_ctl , power_ctl , fmt , mode ;
2010-05-27 16:57:41 +04:00
switch ( cs42l51 - > func ) {
case MODE_MASTER :
2019-04-03 16:23:34 +03:00
ratios = master_ratios ;
nr_ratios = ARRAY_SIZE ( master_ratios ) ;
break ;
2010-05-27 16:57:41 +04:00
case MODE_SLAVE :
ratios = slave_ratios ;
nr_ratios = ARRAY_SIZE ( slave_ratios ) ;
break ;
case MODE_SLAVE_AUTO :
ratios = slave_auto_ratios ;
nr_ratios = ARRAY_SIZE ( slave_auto_ratios ) ;
break ;
}
/* Figure out which MCLK/LRCK ratio to use */
rate = params_rate ( params ) ; /* Sampling rate, in Hz */
ratio = cs42l51 - > mclk / rate ; /* MCLK/LRCK ratio */
for ( i = 0 ; i < nr_ratios ; i + + ) {
if ( ratios [ i ] . ratio = = ratio )
break ;
}
if ( i = = nr_ratios ) {
/* We did not find a matching ratio */
2018-01-29 06:59:02 +03:00
dev_err ( component - > dev , " could not find matching ratio \n " ) ;
2010-05-27 16:57:41 +04:00
return - EINVAL ;
}
2018-01-29 06:59:02 +03:00
intf_ctl = snd_soc_component_read32 ( component , CS42L51_INTF_CTL ) ;
power_ctl = snd_soc_component_read32 ( component , CS42L51_MIC_POWER_CTL ) ;
2010-05-27 16:57:41 +04:00
intf_ctl & = ~ ( CS42L51_INTF_CTL_MASTER | CS42L51_INTF_CTL_ADC_I2S
| CS42L51_INTF_CTL_DAC_FORMAT ( 7 ) ) ;
power_ctl & = ~ ( CS42L51_MIC_POWER_CTL_SPEED ( 3 )
| CS42L51_MIC_POWER_CTL_MCLK_DIV2 ) ;
switch ( cs42l51 - > func ) {
case MODE_MASTER :
intf_ctl | = CS42L51_INTF_CTL_MASTER ;
2019-04-03 16:23:34 +03:00
mode = ratios [ i ] . speed_mode ;
/* Force DSM mode if sampling rate is above 50kHz */
if ( rate > 50000 )
mode = CS42L51_DSM_MODE ;
power_ctl | = CS42L51_MIC_POWER_CTL_SPEED ( mode ) ;
/*
* Auto detect mode is not applicable for master mode and has to
* be disabled . Otherwise SPEED [ 1 : 0 ] bits will be ignored .
*/
power_ctl & = ~ CS42L51_MIC_POWER_CTL_AUTO ;
2010-05-27 16:57:41 +04:00
break ;
case MODE_SLAVE :
power_ctl | = CS42L51_MIC_POWER_CTL_SPEED ( ratios [ i ] . speed_mode ) ;
break ;
case MODE_SLAVE_AUTO :
power_ctl | = CS42L51_MIC_POWER_CTL_AUTO ;
break ;
}
switch ( cs42l51 - > audio_mode ) {
case SND_SOC_DAIFMT_I2S :
intf_ctl | = CS42L51_INTF_CTL_ADC_I2S ;
intf_ctl | = CS42L51_INTF_CTL_DAC_FORMAT ( CS42L51_DAC_DIF_I2S ) ;
break ;
case SND_SOC_DAIFMT_LEFT_J :
intf_ctl | = CS42L51_INTF_CTL_DAC_FORMAT ( CS42L51_DAC_DIF_LJ24 ) ;
break ;
case SND_SOC_DAIFMT_RIGHT_J :
2014-01-08 23:48:20 +04:00
switch ( params_width ( params ) ) {
case 16 :
2010-05-27 16:57:41 +04:00
fmt = CS42L51_DAC_DIF_RJ16 ;
break ;
2014-01-08 23:48:20 +04:00
case 18 :
2010-05-27 16:57:41 +04:00
fmt = CS42L51_DAC_DIF_RJ18 ;
break ;
2014-01-08 23:48:20 +04:00
case 20 :
2010-05-27 16:57:41 +04:00
fmt = CS42L51_DAC_DIF_RJ20 ;
break ;
2014-01-08 23:48:20 +04:00
case 24 :
2010-05-27 16:57:41 +04:00
fmt = CS42L51_DAC_DIF_RJ24 ;
break ;
default :
2018-01-29 06:59:02 +03:00
dev_err ( component - > dev , " unknown format \n " ) ;
2010-05-27 16:57:41 +04:00
return - EINVAL ;
}
intf_ctl | = CS42L51_INTF_CTL_DAC_FORMAT ( fmt ) ;
break ;
default :
2018-01-29 06:59:02 +03:00
dev_err ( component - > dev , " unknown format \n " ) ;
2010-05-27 16:57:41 +04:00
return - EINVAL ;
}
if ( ratios [ i ] . mclk )
power_ctl | = CS42L51_MIC_POWER_CTL_MCLK_DIV2 ;
2018-01-29 06:59:02 +03:00
ret = snd_soc_component_write ( component , CS42L51_INTF_CTL , intf_ctl ) ;
2010-05-27 16:57:41 +04:00
if ( ret < 0 )
return ret ;
2018-01-29 06:59:02 +03:00
ret = snd_soc_component_write ( component , CS42L51_MIC_POWER_CTL , power_ctl ) ;
2010-05-27 16:57:41 +04:00
if ( ret < 0 )
return ret ;
return 0 ;
}
static int cs42l51_dai_mute ( struct snd_soc_dai * dai , int mute )
{
2018-01-29 06:59:02 +03:00
struct snd_soc_component * component = dai - > component ;
2010-05-27 16:57:41 +04:00
int reg ;
int mask = CS42L51_DAC_OUT_CTL_DACA_MUTE | CS42L51_DAC_OUT_CTL_DACB_MUTE ;
2018-01-29 06:59:02 +03:00
reg = snd_soc_component_read32 ( component , CS42L51_DAC_OUT_CTL ) ;
2010-05-27 16:57:41 +04:00
if ( mute )
reg | = mask ;
else
reg & = ~ mask ;
2018-01-29 06:59:02 +03:00
return snd_soc_component_write ( component , CS42L51_DAC_OUT_CTL , reg ) ;
2010-05-27 16:57:41 +04:00
}
2019-03-29 18:37:37 +03:00
static int cs42l51_of_xlate_dai_id ( struct snd_soc_component * component ,
struct device_node * endpoint )
{
/* return dai id 0, whatever the endpoint index */
return 0 ;
}
2011-11-23 14:40:40 +04:00
static const struct snd_soc_dai_ops cs42l51_dai_ops = {
2010-05-27 16:57:41 +04:00
. hw_params = cs42l51_hw_params ,
. set_sysclk = cs42l51_set_dai_sysclk ,
. set_fmt = cs42l51_set_dai_fmt ,
. digital_mute = cs42l51_dai_mute ,
} ;
2010-03-17 23:15:21 +03:00
static struct snd_soc_dai_driver cs42l51_dai = {
. name = " cs42l51-hifi " ,
2010-05-27 16:57:41 +04:00
. playback = {
. stream_name = " Playback " ,
. channels_min = 1 ,
. channels_max = 2 ,
. rates = SNDRV_PCM_RATE_8000_96000 ,
. formats = CS42L51_FORMATS ,
} ,
. capture = {
. stream_name = " Capture " ,
. channels_min = 1 ,
. channels_max = 2 ,
. rates = SNDRV_PCM_RATE_8000_96000 ,
. formats = CS42L51_FORMATS ,
} ,
. ops = & cs42l51_dai_ops ,
} ;
2018-01-29 06:59:02 +03:00
static int cs42l51_component_probe ( struct snd_soc_component * component )
2010-05-27 16:57:41 +04:00
{
2010-03-17 23:15:21 +03:00
int ret , reg ;
2018-10-15 17:03:36 +03:00
struct snd_soc_dapm_context * dapm ;
2018-10-19 18:56:35 +03:00
struct cs42l51_private * cs42l51 ;
2018-10-15 17:03:36 +03:00
2018-10-19 18:56:35 +03:00
cs42l51 = snd_soc_component_get_drvdata ( component ) ;
2018-10-15 17:03:36 +03:00
dapm = snd_soc_component_get_dapm ( component ) ;
2018-10-19 18:56:35 +03:00
if ( cs42l51 - > mclk_handle )
snd_soc_dapm_new_controls ( dapm , cs42l51_dapm_mclk_widgets , 1 ) ;
2010-05-27 16:57:41 +04:00
2010-03-17 23:15:21 +03:00
/*
* DAC configuration
* - Use signal processor
* - auto mute
* - vol changes immediate
* - no de - emphasize
*/
reg = CS42L51_DAC_CTL_DATA_SEL ( 1 )
| CS42L51_DAC_CTL_AMUTE | CS42L51_DAC_CTL_DACSZ ( 0 ) ;
2018-01-29 06:59:02 +03:00
ret = snd_soc_component_write ( component , CS42L51_DAC_CTL , reg ) ;
2010-03-17 23:15:21 +03:00
if ( ret < 0 )
return ret ;
2010-05-27 16:57:41 +04:00
return 0 ;
}
2018-01-29 06:59:02 +03:00
static const struct snd_soc_component_driver soc_component_device_cs42l51 = {
. probe = cs42l51_component_probe ,
. controls = cs42l51_snd_controls ,
. num_controls = ARRAY_SIZE ( cs42l51_snd_controls ) ,
. dapm_widgets = cs42l51_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( cs42l51_dapm_widgets ) ,
. dapm_routes = cs42l51_routes ,
. num_dapm_routes = ARRAY_SIZE ( cs42l51_routes ) ,
2019-03-29 18:37:37 +03:00
. of_xlate_dai_id = cs42l51_of_xlate_dai_id ,
2018-01-29 06:59:02 +03:00
. idle_bias_on = 1 ,
. use_pmdown_time = 1 ,
. endianness = 1 ,
. non_legacy_dai_naming = 1 ,
2010-03-17 23:15:21 +03:00
} ;
2010-05-27 16:57:41 +04:00
2019-04-03 16:23:36 +03:00
static bool cs42l51_writeable_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case CS42L51_POWER_CTL1 :
case CS42L51_MIC_POWER_CTL :
case CS42L51_INTF_CTL :
case CS42L51_MIC_CTL :
case CS42L51_ADC_CTL :
case CS42L51_ADC_INPUT :
case CS42L51_DAC_OUT_CTL :
case CS42L51_DAC_CTL :
case CS42L51_ALC_PGA_CTL :
case CS42L51_ALC_PGB_CTL :
case CS42L51_ADCA_ATT :
case CS42L51_ADCB_ATT :
case CS42L51_ADCA_VOL :
case CS42L51_ADCB_VOL :
case CS42L51_PCMA_VOL :
case CS42L51_PCMB_VOL :
case CS42L51_BEEP_FREQ :
case CS42L51_BEEP_VOL :
case CS42L51_BEEP_CONF :
case CS42L51_TONE_CTL :
case CS42L51_AOUTA_VOL :
case CS42L51_AOUTB_VOL :
case CS42L51_PCM_MIXER :
case CS42L51_LIMIT_THRES_DIS :
case CS42L51_LIMIT_REL :
case CS42L51_LIMIT_ATT :
case CS42L51_ALC_EN :
case CS42L51_ALC_REL :
case CS42L51_ALC_THRES :
case CS42L51_NOISE_CONF :
case CS42L51_CHARGE_FREQ :
return true ;
default :
return false ;
}
}
static bool cs42l51_volatile_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case CS42L51_STATUS :
return true ;
default :
return false ;
}
}
static bool cs42l51_readable_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case CS42L51_CHIP_REV_ID :
case CS42L51_POWER_CTL1 :
case CS42L51_MIC_POWER_CTL :
case CS42L51_INTF_CTL :
case CS42L51_MIC_CTL :
case CS42L51_ADC_CTL :
case CS42L51_ADC_INPUT :
case CS42L51_DAC_OUT_CTL :
case CS42L51_DAC_CTL :
case CS42L51_ALC_PGA_CTL :
case CS42L51_ALC_PGB_CTL :
case CS42L51_ADCA_ATT :
case CS42L51_ADCB_ATT :
case CS42L51_ADCA_VOL :
case CS42L51_ADCB_VOL :
case CS42L51_PCMA_VOL :
case CS42L51_PCMB_VOL :
case CS42L51_BEEP_FREQ :
case CS42L51_BEEP_VOL :
case CS42L51_BEEP_CONF :
case CS42L51_TONE_CTL :
case CS42L51_AOUTA_VOL :
case CS42L51_AOUTB_VOL :
case CS42L51_PCM_MIXER :
case CS42L51_LIMIT_THRES_DIS :
case CS42L51_LIMIT_REL :
case CS42L51_LIMIT_ATT :
case CS42L51_ALC_EN :
case CS42L51_ALC_REL :
case CS42L51_ALC_THRES :
case CS42L51_NOISE_CONF :
case CS42L51_STATUS :
case CS42L51_CHARGE_FREQ :
return true ;
default :
return false ;
}
}
2014-04-16 00:49:33 +04:00
const struct regmap_config cs42l51_regmap = {
2019-04-03 16:23:36 +03:00
. reg_bits = 8 ,
. reg_stride = 1 ,
. val_bits = 8 ,
. use_single_write = true ,
. readable_reg = cs42l51_readable_reg ,
. volatile_reg = cs42l51_volatile_reg ,
. writeable_reg = cs42l51_writeable_reg ,
2014-02-11 23:24:31 +04:00
. max_register = CS42L51_CHARGE_FREQ ,
. cache_type = REGCACHE_RBTREE ,
} ;
2014-04-16 00:49:33 +04:00
EXPORT_SYMBOL_GPL ( cs42l51_regmap ) ;
2014-02-11 23:24:31 +04:00
2014-04-16 00:49:33 +04:00
int cs42l51_probe ( struct device * dev , struct regmap * regmap )
2010-05-27 16:57:41 +04:00
{
2010-03-17 23:15:21 +03:00
struct cs42l51_private * cs42l51 ;
2014-02-11 23:24:31 +04:00
unsigned int val ;
2019-04-03 16:23:32 +03:00
int ret , i ;
2010-05-27 16:57:41 +04:00
2014-04-16 00:49:33 +04:00
if ( IS_ERR ( regmap ) )
return PTR_ERR ( regmap ) ;
cs42l51 = devm_kzalloc ( dev , sizeof ( struct cs42l51_private ) ,
GFP_KERNEL ) ;
if ( ! cs42l51 )
return - ENOMEM ;
dev_set_drvdata ( dev , cs42l51 ) ;
2019-04-03 16:23:36 +03:00
cs42l51 - > regmap = regmap ;
2014-02-11 23:24:31 +04:00
2018-10-19 18:56:35 +03:00
cs42l51 - > mclk_handle = devm_clk_get ( dev , " MCLK " ) ;
if ( IS_ERR ( cs42l51 - > mclk_handle ) ) {
if ( PTR_ERR ( cs42l51 - > mclk_handle ) ! = - ENOENT )
return PTR_ERR ( cs42l51 - > mclk_handle ) ;
cs42l51 - > mclk_handle = NULL ;
}
2019-04-03 16:23:32 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( cs42l51 - > supplies ) ; i + + )
cs42l51 - > supplies [ i ] . supply = cs42l51_supply_names [ i ] ;
ret = devm_regulator_bulk_get ( dev , ARRAY_SIZE ( cs42l51 - > supplies ) ,
cs42l51 - > supplies ) ;
if ( ret ! = 0 ) {
dev_err ( dev , " Failed to request supplies: %d \n " , ret ) ;
return ret ;
}
ret = regulator_bulk_enable ( ARRAY_SIZE ( cs42l51 - > supplies ) ,
cs42l51 - > supplies ) ;
if ( ret ! = 0 ) {
dev_err ( dev , " Failed to enable supplies: %d \n " , ret ) ;
return ret ;
}
2019-04-03 16:23:33 +03:00
cs42l51 - > reset_gpio = devm_gpiod_get_optional ( dev , " reset " ,
GPIOD_OUT_LOW ) ;
if ( IS_ERR ( cs42l51 - > reset_gpio ) )
return PTR_ERR ( cs42l51 - > reset_gpio ) ;
if ( cs42l51 - > reset_gpio ) {
dev_dbg ( dev , " Release reset gpio \n " ) ;
gpiod_set_value_cansleep ( cs42l51 - > reset_gpio , 0 ) ;
mdelay ( 2 ) ;
}
2010-03-17 23:15:21 +03:00
/* Verify that we have a CS42L51 */
2014-02-11 23:24:31 +04:00
ret = regmap_read ( regmap , CS42L51_CHIP_REV_ID , & val ) ;
2010-03-17 23:15:21 +03:00
if ( ret < 0 ) {
2014-04-16 00:49:33 +04:00
dev_err ( dev , " failed to read I2C \n " ) ;
2010-03-17 23:15:21 +03:00
goto error ;
}
2014-02-11 23:24:31 +04:00
if ( ( val ! = CS42L51_MK_CHIP_REV ( CS42L51_CHIP_ID , CS42L51_CHIP_REV_A ) ) & &
( val ! = CS42L51_MK_CHIP_REV ( CS42L51_CHIP_ID , CS42L51_CHIP_REV_B ) ) ) {
2014-04-16 00:49:33 +04:00
dev_err ( dev , " Invalid chip id: %x \n " , val ) ;
2010-03-17 23:15:21 +03:00
ret = - ENODEV ;
goto error ;
}
2014-04-17 12:35:43 +04:00
dev_info ( dev , " Cirrus Logic CS42L51, Revision: %02X \n " ,
val & CS42L51_CHIP_REV_MASK ) ;
2010-03-17 23:15:21 +03:00
2018-01-29 06:59:02 +03:00
ret = devm_snd_soc_register_component ( dev ,
& soc_component_device_cs42l51 , & cs42l51_dai , 1 ) ;
2019-04-03 16:23:32 +03:00
if ( ret < 0 )
goto error ;
return 0 ;
2010-03-17 23:15:21 +03:00
error :
2019-04-03 16:23:32 +03:00
regulator_bulk_disable ( ARRAY_SIZE ( cs42l51 - > supplies ) ,
cs42l51 - > supplies ) ;
2010-03-17 23:15:21 +03:00
return ret ;
}
2014-04-16 00:49:33 +04:00
EXPORT_SYMBOL_GPL ( cs42l51_probe ) ;
2010-03-17 23:15:21 +03:00
2019-04-03 16:23:32 +03:00
int cs42l51_remove ( struct device * dev )
{
struct cs42l51_private * cs42l51 = dev_get_drvdata ( dev ) ;
2019-04-03 16:23:33 +03:00
gpiod_set_value_cansleep ( cs42l51 - > reset_gpio , 1 ) ;
2019-04-03 16:23:32 +03:00
return regulator_bulk_disable ( ARRAY_SIZE ( cs42l51 - > supplies ) ,
cs42l51 - > supplies ) ;
}
EXPORT_SYMBOL_GPL ( cs42l51_remove ) ;
2019-04-03 16:23:36 +03:00
int __maybe_unused cs42l51_suspend ( struct device * dev )
{
struct cs42l51_private * cs42l51 = dev_get_drvdata ( dev ) ;
regcache_cache_only ( cs42l51 - > regmap , true ) ;
regcache_mark_dirty ( cs42l51 - > regmap ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( cs42l51_suspend ) ;
int __maybe_unused cs42l51_resume ( struct device * dev )
{
struct cs42l51_private * cs42l51 = dev_get_drvdata ( dev ) ;
regcache_cache_only ( cs42l51 - > regmap , false ) ;
return regcache_sync ( cs42l51 - > regmap ) ;
}
EXPORT_SYMBOL_GPL ( cs42l51_resume ) ;
2014-11-12 17:40:44 +03:00
const struct of_device_id cs42l51_of_match [ ] = {
2014-01-30 21:14:05 +04:00
{ . compatible = " cirrus,cs42l51 " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , cs42l51_of_match ) ;
2014-11-12 17:40:44 +03:00
EXPORT_SYMBOL_GPL ( cs42l51_of_match ) ;
2010-09-09 16:10:33 +04:00
MODULE_AUTHOR ( " Arnaud Patard <arnaud.patard@rtp-net.org> " ) ;
2010-05-27 16:57:41 +04:00
MODULE_DESCRIPTION ( " Cirrus Logic CS42L51 ALSA SoC Codec Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;