2014-03-05 12:34:34 +04:00
/*
* SiRF audio codec driver
*
* Copyright ( c ) 2011 Cambridge Silicon Radio Limited , a CSR plc group company .
*
* Licensed under GPLv2 or later .
*/
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/pm_runtime.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/io.h>
# include <linux/regmap.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/initval.h>
# include <sound/tlv.h>
# include <sound/soc.h>
# include <sound/dmaengine_pcm.h>
# include "sirf-audio-codec.h"
struct sirf_audio_codec {
struct clk * clk ;
struct regmap * regmap ;
u32 reg_ctrl0 , reg_ctrl1 ;
} ;
static const char * const input_mode_mux [ ] = { " Single-ended " ,
" Differential " } ;
static const struct soc_enum input_mode_mux_enum =
SOC_ENUM_SINGLE ( AUDIO_IC_CODEC_CTRL1 , 4 , 2 , input_mode_mux ) ;
static const struct snd_kcontrol_new sirf_audio_codec_input_mode_control =
SOC_DAPM_ENUM ( " Route " , input_mode_mux_enum ) ;
static const DECLARE_TLV_DB_SCALE ( playback_vol_tlv , - 12400 , 100 , 0 ) ;
static const DECLARE_TLV_DB_SCALE ( capture_vol_tlv_prima2 , 500 , 100 , 0 ) ;
static const DECLARE_TLV_DB_RANGE ( capture_vol_tlv_atlas6 ,
0 , 7 , TLV_DB_SCALE_ITEM ( - 100 , 100 , 0 ) ,
0x22 , 0x3F , TLV_DB_SCALE_ITEM ( 700 , 100 , 0 ) ,
) ;
static struct snd_kcontrol_new volume_controls_atlas6 [ ] = {
SOC_DOUBLE_TLV ( " Playback Volume " , AUDIO_IC_CODEC_CTRL0 , 21 , 14 ,
0x7F , 0 , playback_vol_tlv ) ,
SOC_DOUBLE_TLV ( " Capture Volume " , AUDIO_IC_CODEC_CTRL1 , 16 , 10 ,
0x3F , 0 , capture_vol_tlv_atlas6 ) ,
} ;
static struct snd_kcontrol_new volume_controls_prima2 [ ] = {
SOC_DOUBLE_TLV ( " Speaker Volume " , AUDIO_IC_CODEC_CTRL0 , 21 , 14 ,
0x7F , 0 , playback_vol_tlv ) ,
SOC_DOUBLE_TLV ( " Capture Volume " , AUDIO_IC_CODEC_CTRL1 , 15 , 10 ,
0x1F , 0 , capture_vol_tlv_prima2 ) ,
} ;
static struct snd_kcontrol_new left_input_path_controls [ ] = {
SOC_DAPM_SINGLE ( " Line Left Switch " , AUDIO_IC_CODEC_CTRL1 , 6 , 1 , 0 ) ,
SOC_DAPM_SINGLE ( " Mic Left Switch " , AUDIO_IC_CODEC_CTRL1 , 3 , 1 , 0 ) ,
} ;
static struct snd_kcontrol_new right_input_path_controls [ ] = {
SOC_DAPM_SINGLE ( " Line Right Switch " , AUDIO_IC_CODEC_CTRL1 , 5 , 1 , 0 ) ,
SOC_DAPM_SINGLE ( " Mic Right Switch " , AUDIO_IC_CODEC_CTRL1 , 2 , 1 , 0 ) ,
} ;
static struct snd_kcontrol_new left_dac_to_hp_left_amp_switch_control =
SOC_DAPM_SINGLE ( " Switch " , AUDIO_IC_CODEC_CTRL0 , 9 , 1 , 0 ) ;
static struct snd_kcontrol_new left_dac_to_hp_right_amp_switch_control =
SOC_DAPM_SINGLE ( " Switch " , AUDIO_IC_CODEC_CTRL0 , 8 , 1 , 0 ) ;
static struct snd_kcontrol_new right_dac_to_hp_left_amp_switch_control =
SOC_DAPM_SINGLE ( " Switch " , AUDIO_IC_CODEC_CTRL0 , 7 , 1 , 0 ) ;
static struct snd_kcontrol_new right_dac_to_hp_right_amp_switch_control =
SOC_DAPM_SINGLE ( " Switch " , AUDIO_IC_CODEC_CTRL0 , 6 , 1 , 0 ) ;
static struct snd_kcontrol_new left_dac_to_speaker_lineout_switch_control =
SOC_DAPM_SINGLE ( " Switch " , AUDIO_IC_CODEC_CTRL0 , 11 , 1 , 0 ) ;
static struct snd_kcontrol_new right_dac_to_speaker_lineout_switch_control =
SOC_DAPM_SINGLE ( " Switch " , AUDIO_IC_CODEC_CTRL0 , 10 , 1 , 0 ) ;
/* After enable adc, Delay 200ms to avoid pop noise */
static int adc_enable_delay_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
switch ( event ) {
case SND_SOC_DAPM_POST_PMU :
msleep ( 200 ) ;
break ;
default :
break ;
}
return 0 ;
}
static void enable_and_reset_codec ( struct regmap * regmap ,
u32 codec_enable_bits , u32 codec_reset_bits )
{
regmap_update_bits ( regmap , AUDIO_IC_CODEC_CTRL1 ,
codec_enable_bits | codec_reset_bits ,
2014-05-08 12:29:49 +04:00
codec_enable_bits ) ;
2014-03-05 12:34:34 +04:00
msleep ( 20 ) ;
regmap_update_bits ( regmap , AUDIO_IC_CODEC_CTRL1 ,
codec_reset_bits , codec_reset_bits ) ;
}
static int atlas6_codec_enable_and_reset_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
# define ATLAS6_CODEC_ENABLE_BITS (1 << 29)
# define ATLAS6_CODEC_RESET_BITS (1 << 28)
2014-11-20 23:28:15 +03:00
struct snd_soc_codec * codec = snd_soc_dapm_to_codec ( w - > dapm ) ;
struct sirf_audio_codec * sirf_audio_codec = snd_soc_codec_get_drvdata ( codec ) ;
2014-03-05 12:34:34 +04:00
switch ( event ) {
case SND_SOC_DAPM_PRE_PMU :
enable_and_reset_codec ( sirf_audio_codec - > regmap ,
ATLAS6_CODEC_ENABLE_BITS , ATLAS6_CODEC_RESET_BITS ) ;
break ;
case SND_SOC_DAPM_POST_PMD :
regmap_update_bits ( sirf_audio_codec - > regmap ,
2014-05-08 12:29:49 +04:00
AUDIO_IC_CODEC_CTRL1 , ATLAS6_CODEC_ENABLE_BITS , 0 ) ;
2014-03-05 12:34:34 +04:00
break ;
default :
break ;
}
return 0 ;
}
static int prima2_codec_enable_and_reset_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
# define PRIMA2_CODEC_ENABLE_BITS (1 << 27)
# define PRIMA2_CODEC_RESET_BITS (1 << 26)
2014-11-20 23:28:15 +03:00
struct snd_soc_codec * codec = snd_soc_dapm_to_codec ( w - > dapm ) ;
struct sirf_audio_codec * sirf_audio_codec = snd_soc_codec_get_drvdata ( codec ) ;
2014-03-05 12:34:34 +04:00
switch ( event ) {
case SND_SOC_DAPM_POST_PMU :
enable_and_reset_codec ( sirf_audio_codec - > regmap ,
PRIMA2_CODEC_ENABLE_BITS , PRIMA2_CODEC_RESET_BITS ) ;
break ;
case SND_SOC_DAPM_POST_PMD :
regmap_update_bits ( sirf_audio_codec - > regmap ,
2014-05-08 12:29:49 +04:00
AUDIO_IC_CODEC_CTRL1 , PRIMA2_CODEC_ENABLE_BITS , 0 ) ;
2014-03-05 12:34:34 +04:00
break ;
default :
break ;
}
return 0 ;
}
static const struct snd_soc_dapm_widget atlas6_output_driver_dapm_widgets [ ] = {
SND_SOC_DAPM_OUT_DRV ( " HP Left Driver " , AUDIO_IC_CODEC_CTRL1 ,
25 , 0 , NULL , 0 ) ,
SND_SOC_DAPM_OUT_DRV ( " HP Right Driver " , AUDIO_IC_CODEC_CTRL1 ,
26 , 0 , NULL , 0 ) ,
SND_SOC_DAPM_OUT_DRV ( " Speaker Driver " , AUDIO_IC_CODEC_CTRL1 ,
27 , 0 , NULL , 0 ) ,
} ;
static const struct snd_soc_dapm_widget prima2_output_driver_dapm_widgets [ ] = {
SND_SOC_DAPM_OUT_DRV ( " HP Left Driver " , AUDIO_IC_CODEC_CTRL1 ,
23 , 0 , NULL , 0 ) ,
SND_SOC_DAPM_OUT_DRV ( " HP Right Driver " , AUDIO_IC_CODEC_CTRL1 ,
24 , 0 , NULL , 0 ) ,
SND_SOC_DAPM_OUT_DRV ( " Speaker Driver " , AUDIO_IC_CODEC_CTRL1 ,
25 , 0 , NULL , 0 ) ,
} ;
static const struct snd_soc_dapm_widget atlas6_codec_clock_dapm_widget =
SND_SOC_DAPM_SUPPLY ( " codecclk " , SND_SOC_NOPM , 0 , 0 ,
atlas6_codec_enable_and_reset_event ,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD ) ;
static const struct snd_soc_dapm_widget prima2_codec_clock_dapm_widget =
SND_SOC_DAPM_SUPPLY ( " codecclk " , SND_SOC_NOPM , 0 , 0 ,
prima2_codec_enable_and_reset_event ,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD ) ;
static const struct snd_soc_dapm_widget sirf_audio_codec_dapm_widgets [ ] = {
SND_SOC_DAPM_DAC ( " DAC left " , NULL , AUDIO_IC_CODEC_CTRL0 , 1 , 0 ) ,
SND_SOC_DAPM_DAC ( " DAC right " , NULL , AUDIO_IC_CODEC_CTRL0 , 0 , 0 ) ,
SND_SOC_DAPM_SWITCH ( " Left dac to hp left amp " , SND_SOC_NOPM , 0 , 0 ,
& left_dac_to_hp_left_amp_switch_control ) ,
SND_SOC_DAPM_SWITCH ( " Left dac to hp right amp " , SND_SOC_NOPM , 0 , 0 ,
& left_dac_to_hp_right_amp_switch_control ) ,
SND_SOC_DAPM_SWITCH ( " Right dac to hp left amp " , SND_SOC_NOPM , 0 , 0 ,
& right_dac_to_hp_left_amp_switch_control ) ,
SND_SOC_DAPM_SWITCH ( " Right dac to hp right amp " , SND_SOC_NOPM , 0 , 0 ,
& right_dac_to_hp_right_amp_switch_control ) ,
SND_SOC_DAPM_OUT_DRV ( " HP amp left driver " , AUDIO_IC_CODEC_CTRL0 , 3 , 0 ,
NULL , 0 ) ,
SND_SOC_DAPM_OUT_DRV ( " HP amp right driver " , AUDIO_IC_CODEC_CTRL0 , 3 , 0 ,
NULL , 0 ) ,
SND_SOC_DAPM_SWITCH ( " Left dac to speaker lineout " , SND_SOC_NOPM , 0 , 0 ,
& left_dac_to_speaker_lineout_switch_control ) ,
SND_SOC_DAPM_SWITCH ( " Right dac to speaker lineout " , SND_SOC_NOPM , 0 , 0 ,
& right_dac_to_speaker_lineout_switch_control ) ,
SND_SOC_DAPM_OUT_DRV ( " Speaker amp driver " , AUDIO_IC_CODEC_CTRL0 , 4 , 0 ,
NULL , 0 ) ,
SND_SOC_DAPM_OUTPUT ( " HPOUTL " ) ,
SND_SOC_DAPM_OUTPUT ( " HPOUTR " ) ,
SND_SOC_DAPM_OUTPUT ( " SPKOUT " ) ,
SND_SOC_DAPM_ADC_E ( " ADC left " , NULL , AUDIO_IC_CODEC_CTRL1 , 8 , 0 ,
adc_enable_delay_event , SND_SOC_DAPM_POST_PMU ) ,
SND_SOC_DAPM_ADC_E ( " ADC right " , NULL , AUDIO_IC_CODEC_CTRL1 , 7 , 0 ,
adc_enable_delay_event , SND_SOC_DAPM_POST_PMU ) ,
SND_SOC_DAPM_MIXER ( " Left PGA mixer " , AUDIO_IC_CODEC_CTRL1 , 1 , 0 ,
& left_input_path_controls [ 0 ] ,
ARRAY_SIZE ( left_input_path_controls ) ) ,
SND_SOC_DAPM_MIXER ( " Right PGA mixer " , AUDIO_IC_CODEC_CTRL1 , 0 , 0 ,
& right_input_path_controls [ 0 ] ,
ARRAY_SIZE ( right_input_path_controls ) ) ,
SND_SOC_DAPM_MUX ( " Mic input mode mux " , SND_SOC_NOPM , 0 , 0 ,
& sirf_audio_codec_input_mode_control ) ,
SND_SOC_DAPM_MICBIAS ( " Mic Bias " , AUDIO_IC_CODEC_PWR , 3 , 0 ) ,
SND_SOC_DAPM_INPUT ( " MICIN1 " ) ,
SND_SOC_DAPM_INPUT ( " MICIN2 " ) ,
SND_SOC_DAPM_INPUT ( " LINEIN1 " ) ,
SND_SOC_DAPM_INPUT ( " LINEIN2 " ) ,
SND_SOC_DAPM_SUPPLY ( " HSL Phase Opposite " , AUDIO_IC_CODEC_CTRL0 ,
30 , 0 , NULL , 0 ) ,
} ;
static const struct snd_soc_dapm_route sirf_audio_codec_map [ ] = {
{ " SPKOUT " , NULL , " Speaker Driver " } ,
{ " Speaker Driver " , NULL , " Speaker amp driver " } ,
{ " Speaker amp driver " , NULL , " Left dac to speaker lineout " } ,
{ " Speaker amp driver " , NULL , " Right dac to speaker lineout " } ,
{ " Left dac to speaker lineout " , " Switch " , " DAC left " } ,
{ " Right dac to speaker lineout " , " Switch " , " DAC right " } ,
{ " HPOUTL " , NULL , " HP Left Driver " } ,
{ " HPOUTR " , NULL , " HP Right Driver " } ,
{ " HP Left Driver " , NULL , " HP amp left driver " } ,
{ " HP Right Driver " , NULL , " HP amp right driver " } ,
{ " HP amp left driver " , NULL , " Right dac to hp left amp " } ,
{ " HP amp right driver " , NULL , " Right dac to hp right amp " } ,
{ " HP amp left driver " , NULL , " Left dac to hp left amp " } ,
{ " HP amp right driver " , NULL , " Right dac to hp right amp " } ,
{ " Right dac to hp left amp " , " Switch " , " DAC left " } ,
{ " Right dac to hp right amp " , " Switch " , " DAC right " } ,
{ " Left dac to hp left amp " , " Switch " , " DAC left " } ,
{ " Left dac to hp right amp " , " Switch " , " DAC right " } ,
{ " DAC left " , NULL , " codecclk " } ,
{ " DAC right " , NULL , " codecclk " } ,
{ " DAC left " , NULL , " Playback " } ,
{ " DAC right " , NULL , " Playback " } ,
{ " DAC left " , NULL , " HSL Phase Opposite " } ,
{ " DAC right " , NULL , " HSL Phase Opposite " } ,
{ " Capture " , NULL , " ADC left " } ,
{ " Capture " , NULL , " ADC right " } ,
{ " ADC left " , NULL , " codecclk " } ,
{ " ADC right " , NULL , " codecclk " } ,
{ " ADC left " , NULL , " Left PGA mixer " } ,
{ " ADC right " , NULL , " Right PGA mixer " } ,
{ " Left PGA mixer " , " Line Left Switch " , " LINEIN2 " } ,
{ " Right PGA mixer " , " Line Right Switch " , " LINEIN1 " } ,
{ " Left PGA mixer " , " Mic Left Switch " , " MICIN2 " } ,
{ " Right PGA mixer " , " Mic Right Switch " , " Mic input mode mux " } ,
{ " Mic input mode mux " , " Single-ended " , " MICIN1 " } ,
{ " Mic input mode mux " , " Differential " , " MICIN1 " } ,
} ;
2014-03-20 11:46:19 +04:00
static void sirf_audio_codec_tx_enable ( struct sirf_audio_codec * sirf_audio_codec )
{
regmap_update_bits ( sirf_audio_codec - > regmap , AUDIO_PORT_IC_TXFIFO_OP ,
AUDIO_FIFO_RESET , AUDIO_FIFO_RESET ) ;
regmap_update_bits ( sirf_audio_codec - > regmap , AUDIO_PORT_IC_TXFIFO_OP ,
AUDIO_FIFO_RESET , ~ AUDIO_FIFO_RESET ) ;
regmap_write ( sirf_audio_codec - > regmap , AUDIO_PORT_IC_TXFIFO_INT_MSK , 0 ) ;
regmap_write ( sirf_audio_codec - > regmap , AUDIO_PORT_IC_TXFIFO_OP , 0 ) ;
regmap_update_bits ( sirf_audio_codec - > regmap , AUDIO_PORT_IC_TXFIFO_OP ,
AUDIO_FIFO_START , AUDIO_FIFO_START ) ;
regmap_update_bits ( sirf_audio_codec - > regmap ,
AUDIO_PORT_IC_CODEC_TX_CTRL , IC_TX_ENABLE , IC_TX_ENABLE ) ;
}
static void sirf_audio_codec_tx_disable ( struct sirf_audio_codec * sirf_audio_codec )
{
regmap_write ( sirf_audio_codec - > regmap , AUDIO_PORT_IC_TXFIFO_OP , 0 ) ;
regmap_update_bits ( sirf_audio_codec - > regmap ,
AUDIO_PORT_IC_CODEC_TX_CTRL , IC_TX_ENABLE , ~ IC_TX_ENABLE ) ;
}
static void sirf_audio_codec_rx_enable ( struct sirf_audio_codec * sirf_audio_codec ,
int channels )
{
regmap_update_bits ( sirf_audio_codec - > regmap , AUDIO_PORT_IC_RXFIFO_OP ,
AUDIO_FIFO_RESET , AUDIO_FIFO_RESET ) ;
regmap_update_bits ( sirf_audio_codec - > regmap , AUDIO_PORT_IC_RXFIFO_OP ,
AUDIO_FIFO_RESET , ~ AUDIO_FIFO_RESET ) ;
regmap_write ( sirf_audio_codec - > regmap ,
AUDIO_PORT_IC_RXFIFO_INT_MSK , 0 ) ;
regmap_write ( sirf_audio_codec - > regmap , AUDIO_PORT_IC_RXFIFO_OP , 0 ) ;
regmap_update_bits ( sirf_audio_codec - > regmap , AUDIO_PORT_IC_RXFIFO_OP ,
AUDIO_FIFO_START , AUDIO_FIFO_START ) ;
if ( channels = = 1 )
regmap_update_bits ( sirf_audio_codec - > regmap ,
AUDIO_PORT_IC_CODEC_RX_CTRL ,
IC_RX_ENABLE_MONO , IC_RX_ENABLE_MONO ) ;
else
regmap_update_bits ( sirf_audio_codec - > regmap ,
AUDIO_PORT_IC_CODEC_RX_CTRL ,
IC_RX_ENABLE_STEREO , IC_RX_ENABLE_STEREO ) ;
}
static void sirf_audio_codec_rx_disable ( struct sirf_audio_codec * sirf_audio_codec )
{
regmap_update_bits ( sirf_audio_codec - > regmap ,
AUDIO_PORT_IC_CODEC_RX_CTRL ,
IC_RX_ENABLE_STEREO , ~ IC_RX_ENABLE_STEREO ) ;
}
2014-03-05 12:34:34 +04:00
static int sirf_audio_codec_trigger ( struct snd_pcm_substream * substream ,
int cmd ,
struct snd_soc_dai * dai )
{
struct snd_soc_codec * codec = dai - > codec ;
2014-03-20 11:46:19 +04:00
struct sirf_audio_codec * sirf_audio_codec = snd_soc_codec_get_drvdata ( codec ) ;
int playback = substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ;
2014-03-05 12:34:34 +04:00
/*
* This is a workaround , When stop playback ,
* need disable HP amp , avoid the current noise .
*/
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
2014-03-20 11:46:19 +04:00
if ( playback ) {
snd_soc_update_bits ( codec , AUDIO_IC_CODEC_CTRL0 ,
IC_HSLEN | IC_HSREN , 0 ) ;
sirf_audio_codec_tx_disable ( sirf_audio_codec ) ;
} else
sirf_audio_codec_rx_disable ( sirf_audio_codec ) ;
2014-03-05 12:34:34 +04:00
break ;
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
2014-03-20 11:46:19 +04:00
if ( playback ) {
sirf_audio_codec_tx_enable ( sirf_audio_codec ) ;
snd_soc_update_bits ( codec , AUDIO_IC_CODEC_CTRL0 ,
IC_HSLEN | IC_HSREN , IC_HSLEN | IC_HSREN ) ;
} else
sirf_audio_codec_rx_enable ( sirf_audio_codec ,
substream - > runtime - > channels ) ;
2014-03-05 12:34:34 +04:00
break ;
default :
return - EINVAL ;
}
return 0 ;
}
2015-07-15 08:50:49 +03:00
static const struct snd_soc_dai_ops sirf_audio_codec_dai_ops = {
2014-03-05 12:34:34 +04:00
. trigger = sirf_audio_codec_trigger ,
} ;
2015-07-15 08:50:49 +03:00
static struct snd_soc_dai_driver sirf_audio_codec_dai = {
2014-03-05 12:34:34 +04:00
. name = " sirf-audio-codec " ,
. playback = {
. stream_name = " Playback " ,
. channels_min = 2 ,
. channels_max = 2 ,
. rates = SNDRV_PCM_RATE_48000 ,
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
} ,
. capture = {
. stream_name = " Capture " ,
. channels_min = 1 ,
. channels_max = 2 ,
. rates = SNDRV_PCM_RATE_48000 ,
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
} ,
. ops = & sirf_audio_codec_dai_ops ,
} ;
static int sirf_audio_codec_probe ( struct snd_soc_codec * codec )
{
2015-05-11 10:42:36 +03:00
struct snd_soc_dapm_context * dapm = snd_soc_codec_get_dapm ( codec ) ;
2014-03-05 12:34:34 +04:00
pm_runtime_enable ( codec - > dev ) ;
if ( of_device_is_compatible ( codec - > dev - > of_node , " sirf,prima2-audio-codec " ) ) {
snd_soc_dapm_new_controls ( dapm ,
prima2_output_driver_dapm_widgets ,
ARRAY_SIZE ( prima2_output_driver_dapm_widgets ) ) ;
snd_soc_dapm_new_controls ( dapm ,
& prima2_codec_clock_dapm_widget , 1 ) ;
return snd_soc_add_codec_controls ( codec ,
volume_controls_prima2 ,
ARRAY_SIZE ( volume_controls_prima2 ) ) ;
}
if ( of_device_is_compatible ( codec - > dev - > of_node , " sirf,atlas6-audio-codec " ) ) {
snd_soc_dapm_new_controls ( dapm ,
atlas6_output_driver_dapm_widgets ,
ARRAY_SIZE ( atlas6_output_driver_dapm_widgets ) ) ;
snd_soc_dapm_new_controls ( dapm ,
& atlas6_codec_clock_dapm_widget , 1 ) ;
return snd_soc_add_codec_controls ( codec ,
volume_controls_atlas6 ,
ARRAY_SIZE ( volume_controls_atlas6 ) ) ;
}
return - EINVAL ;
}
static int sirf_audio_codec_remove ( struct snd_soc_codec * codec )
{
pm_runtime_disable ( codec - > dev ) ;
return 0 ;
}
static struct snd_soc_codec_driver soc_codec_device_sirf_audio_codec = {
. probe = sirf_audio_codec_probe ,
. remove = sirf_audio_codec_remove ,
. dapm_widgets = sirf_audio_codec_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( sirf_audio_codec_dapm_widgets ) ,
. dapm_routes = sirf_audio_codec_map ,
. num_dapm_routes = ARRAY_SIZE ( sirf_audio_codec_map ) ,
. idle_bias_off = true ,
} ;
static const struct of_device_id sirf_audio_codec_of_match [ ] = {
{ . compatible = " sirf,prima2-audio-codec " } ,
{ . compatible = " sirf,atlas6-audio-codec " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , sirf_audio_codec_of_match ) ;
static const struct regmap_config sirf_audio_codec_regmap_config = {
. reg_bits = 32 ,
. reg_stride = 4 ,
. val_bits = 32 ,
2014-03-20 11:46:19 +04:00
. max_register = AUDIO_PORT_IC_RXFIFO_INT_MSK ,
2014-03-05 12:34:34 +04:00
. cache_type = REGCACHE_NONE ,
} ;
static int sirf_audio_codec_driver_probe ( struct platform_device * pdev )
{
int ret ;
struct sirf_audio_codec * sirf_audio_codec ;
void __iomem * base ;
struct resource * mem_res ;
const struct of_device_id * match ;
match = of_match_node ( sirf_audio_codec_of_match , pdev - > dev . of_node ) ;
sirf_audio_codec = devm_kzalloc ( & pdev - > dev ,
sizeof ( struct sirf_audio_codec ) , GFP_KERNEL ) ;
if ( ! sirf_audio_codec )
return - ENOMEM ;
platform_set_drvdata ( pdev , sirf_audio_codec ) ;
mem_res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
base = devm_ioremap_resource ( & pdev - > dev , mem_res ) ;
2014-07-30 04:04:12 +04:00
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
2014-03-05 12:34:34 +04:00
sirf_audio_codec - > regmap = devm_regmap_init_mmio ( & pdev - > dev , base ,
& sirf_audio_codec_regmap_config ) ;
if ( IS_ERR ( sirf_audio_codec - > regmap ) )
return PTR_ERR ( sirf_audio_codec - > regmap ) ;
sirf_audio_codec - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( sirf_audio_codec - > clk ) ) {
dev_err ( & pdev - > dev , " Get clock failed. \n " ) ;
return PTR_ERR ( sirf_audio_codec - > clk ) ;
}
ret = clk_prepare_enable ( sirf_audio_codec - > clk ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Enable clock failed. \n " ) ;
return ret ;
}
ret = snd_soc_register_codec ( & ( pdev - > dev ) ,
& soc_codec_device_sirf_audio_codec ,
& sirf_audio_codec_dai , 1 ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Register Audio Codec dai failed. \n " ) ;
goto err_clk_put ;
}
/*
* Always open charge pump , if not , when the charge pump closed the
* adc will not stable
*/
regmap_update_bits ( sirf_audio_codec - > regmap , AUDIO_IC_CODEC_CTRL0 ,
IC_CPFREQ , IC_CPFREQ ) ;
if ( of_device_is_compatible ( pdev - > dev . of_node , " sirf,atlas6-audio-codec " ) )
regmap_update_bits ( sirf_audio_codec - > regmap ,
AUDIO_IC_CODEC_CTRL0 , IC_CPEN , IC_CPEN ) ;
return 0 ;
err_clk_put :
clk_disable_unprepare ( sirf_audio_codec - > clk ) ;
return ret ;
}
static int sirf_audio_codec_driver_remove ( struct platform_device * pdev )
{
struct sirf_audio_codec * sirf_audio_codec = platform_get_drvdata ( pdev ) ;
clk_disable_unprepare ( sirf_audio_codec - > clk ) ;
snd_soc_unregister_codec ( & ( pdev - > dev ) ) ;
return 0 ;
}
# ifdef CONFIG_PM_SLEEP
static int sirf_audio_codec_suspend ( struct device * dev )
{
struct sirf_audio_codec * sirf_audio_codec = dev_get_drvdata ( dev ) ;
regmap_read ( sirf_audio_codec - > regmap , AUDIO_IC_CODEC_CTRL0 ,
& sirf_audio_codec - > reg_ctrl0 ) ;
regmap_read ( sirf_audio_codec - > regmap , AUDIO_IC_CODEC_CTRL1 ,
& sirf_audio_codec - > reg_ctrl1 ) ;
clk_disable_unprepare ( sirf_audio_codec - > clk ) ;
return 0 ;
}
static int sirf_audio_codec_resume ( struct device * dev )
{
struct sirf_audio_codec * sirf_audio_codec = dev_get_drvdata ( dev ) ;
int ret ;
ret = clk_prepare_enable ( sirf_audio_codec - > clk ) ;
if ( ret )
return ret ;
regmap_write ( sirf_audio_codec - > regmap , AUDIO_IC_CODEC_CTRL0 ,
sirf_audio_codec - > reg_ctrl0 ) ;
regmap_write ( sirf_audio_codec - > regmap , AUDIO_IC_CODEC_CTRL1 ,
sirf_audio_codec - > reg_ctrl1 ) ;
return 0 ;
}
# endif
static const struct dev_pm_ops sirf_audio_codec_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( sirf_audio_codec_suspend , sirf_audio_codec_resume )
} ;
static struct platform_driver sirf_audio_codec_driver = {
. driver = {
. name = " sirf-audio-codec " ,
. of_match_table = sirf_audio_codec_of_match ,
. pm = & sirf_audio_codec_pm_ops ,
} ,
. probe = sirf_audio_codec_driver_probe ,
. remove = sirf_audio_codec_driver_remove ,
} ;
module_platform_driver ( sirf_audio_codec_driver ) ;
MODULE_DESCRIPTION ( " SiRF audio codec driver " ) ;
MODULE_AUTHOR ( " RongJun Ying <Rongjun.Ying@csr.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;