2011-02-23 21:08:21 +03:00
/*
* sgtl5000 . c - - SGTL5000 ALSA SoC Audio driver
*
* Copyright 2010 - 2011 Freescale Semiconductor , Inc . All Rights Reserved .
*
* 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 .
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/slab.h>
# include <linux/pm.h>
# include <linux/i2c.h>
# include <linux/clk.h>
# include <linux/platform_device.h>
# include <linux/regulator/driver.h>
# include <linux/regulator/machine.h>
# include <linux/regulator/consumer.h>
# include <sound/core.h>
# include <sound/tlv.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/soc.h>
# include <sound/soc-dapm.h>
# include <sound/initval.h>
# include "sgtl5000.h"
# define SGTL5000_DAP_REG_OFFSET 0x0100
# define SGTL5000_MAX_REG_OFFSET 0x013A
/* default value of sgtl5000 registers except DAP */
static const u16 sgtl5000_regs [ SGTL5000_MAX_REG_OFFSET > > 1 ] = {
0xa011 , /* 0x0000, CHIP_ID. 11 stand for revison 17 */
0x0000 , /* 0x0002, CHIP_DIG_POWER. */
0x0008 , /* 0x0004, CHIP_CKL_CTRL */
0x0010 , /* 0x0006, CHIP_I2S_CTRL */
0x0000 , /* 0x0008, reserved */
0x0008 , /* 0x000A, CHIP_SSS_CTRL */
0x0000 , /* 0x000C, reserved */
0x020c , /* 0x000E, CHIP_ADCDAC_CTRL */
0x3c3c , /* 0x0010, CHIP_DAC_VOL */
0x0000 , /* 0x0012, reserved */
0x015f , /* 0x0014, CHIP_PAD_STRENGTH */
0x0000 , /* 0x0016, reserved */
0x0000 , /* 0x0018, reserved */
0x0000 , /* 0x001A, reserved */
0x0000 , /* 0x001E, reserved */
0x0000 , /* 0x0020, CHIP_ANA_ADC_CTRL */
0x1818 , /* 0x0022, CHIP_ANA_HP_CTRL */
0x0111 , /* 0x0024, CHIP_ANN_CTRL */
0x0000 , /* 0x0026, CHIP_LINREG_CTRL */
0x0000 , /* 0x0028, CHIP_REF_CTRL */
0x0000 , /* 0x002A, CHIP_MIC_CTRL */
0x0000 , /* 0x002C, CHIP_LINE_OUT_CTRL */
0x0404 , /* 0x002E, CHIP_LINE_OUT_VOL */
0x7060 , /* 0x0030, CHIP_ANA_POWER */
0x5000 , /* 0x0032, CHIP_PLL_CTRL */
0x0000 , /* 0x0034, CHIP_CLK_TOP_CTRL */
0x0000 , /* 0x0036, CHIP_ANA_STATUS */
0x0000 , /* 0x0038, reserved */
0x0000 , /* 0x003A, CHIP_ANA_TEST2 */
0x0000 , /* 0x003C, CHIP_SHORT_CTRL */
0x0000 , /* reserved */
} ;
/* default value of dap registers */
static const u16 sgtl5000_dap_regs [ ] = {
0x0000 , /* 0x0100, DAP_CONTROL */
0x0000 , /* 0x0102, DAP_PEQ */
0x0040 , /* 0x0104, DAP_BASS_ENHANCE */
0x051f , /* 0x0106, DAP_BASS_ENHANCE_CTRL */
0x0000 , /* 0x0108, DAP_AUDIO_EQ */
0x0040 , /* 0x010A, DAP_SGTL_SURROUND */
0x0000 , /* 0x010C, DAP_FILTER_COEF_ACCESS */
0x0000 , /* 0x010E, DAP_COEF_WR_B0_MSB */
0x0000 , /* 0x0110, DAP_COEF_WR_B0_LSB */
0x0000 , /* 0x0112, reserved */
0x0000 , /* 0x0114, reserved */
0x002f , /* 0x0116, DAP_AUDIO_EQ_BASS_BAND0 */
0x002f , /* 0x0118, DAP_AUDIO_EQ_BAND0 */
0x002f , /* 0x011A, DAP_AUDIO_EQ_BAND2 */
0x002f , /* 0x011C, DAP_AUDIO_EQ_BAND3 */
0x002f , /* 0x011E, DAP_AUDIO_EQ_TREBLE_BAND4 */
0x8000 , /* 0x0120, DAP_MAIN_CHAN */
0x0000 , /* 0x0122, DAP_MIX_CHAN */
0x0510 , /* 0x0124, DAP_AVC_CTRL */
0x1473 , /* 0x0126, DAP_AVC_THRESHOLD */
0x0028 , /* 0x0128, DAP_AVC_ATTACK */
0x0050 , /* 0x012A, DAP_AVC_DECAY */
0x0000 , /* 0x012C, DAP_COEF_WR_B1_MSB */
0x0000 , /* 0x012E, DAP_COEF_WR_B1_LSB */
0x0000 , /* 0x0130, DAP_COEF_WR_B2_MSB */
0x0000 , /* 0x0132, DAP_COEF_WR_B2_LSB */
0x0000 , /* 0x0134, DAP_COEF_WR_A1_MSB */
0x0000 , /* 0x0136, DAP_COEF_WR_A1_LSB */
0x0000 , /* 0x0138, DAP_COEF_WR_A2_MSB */
0x0000 , /* 0x013A, DAP_COEF_WR_A2_LSB */
} ;
/* regulator supplies for sgtl5000, VDDD is an optional external supply */
enum sgtl5000_regulator_supplies {
VDDA ,
VDDIO ,
VDDD ,
SGTL5000_SUPPLY_NUM
} ;
/* vddd is optional supply */
static const char * supply_names [ SGTL5000_SUPPLY_NUM ] = {
" VDDA " ,
" VDDIO " ,
" VDDD "
} ;
# define LDO_CONSUMER_NAME "VDDD_LDO"
# define LDO_VOLTAGE 1200000
static struct regulator_consumer_supply ldo_consumer [ ] = {
REGULATOR_SUPPLY ( LDO_CONSUMER_NAME , NULL ) ,
} ;
2011-02-28 17:33:01 +03:00
static struct regulator_init_data ldo_init_data = {
2011-02-23 21:08:21 +03:00
. constraints = {
. min_uV = 850000 ,
. max_uV = 1600000 ,
. valid_modes_mask = REGULATOR_MODE_NORMAL ,
. valid_ops_mask = REGULATOR_CHANGE_STATUS ,
} ,
. num_consumer_supplies = 1 ,
. consumer_supplies = & ldo_consumer [ 0 ] ,
} ;
/*
* sgtl5000 internal ldo regulator ,
* enabled when VDDD not provided
*/
struct ldo_regulator {
struct regulator_desc desc ;
struct regulator_dev * dev ;
int voltage ;
void * codec_data ;
bool enabled ;
} ;
/* sgtl5000 private structure in codec */
struct sgtl5000_priv {
int sysclk ; /* sysclk rate */
int master ; /* i2s master or not */
int fmt ; /* i2s data format */
struct regulator_bulk_data supplies [ SGTL5000_SUPPLY_NUM ] ;
struct ldo_regulator * ldo ;
} ;
/*
* mic_bias power on / off share the same register bits with
* output impedance of mic bias , when power on mic bias , we
* need reclaim it to impedance value .
* 0x0 = Powered off
* 0x1 = 2 Kohm
* 0x2 = 4 Kohm
* 0x3 = 8 Kohm
*/
static int mic_bias_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
switch ( event ) {
case SND_SOC_DAPM_POST_PMU :
/* change mic bias resistor to 4Kohm */
snd_soc_update_bits ( w - > codec , SGTL5000_CHIP_MIC_CTRL ,
SGTL5000_BIAS_R_4k , SGTL5000_BIAS_R_4k ) ;
break ;
case SND_SOC_DAPM_PRE_PMD :
/*
* SGTL5000_BIAS_R_8k as mask to clean the two bits
* of mic bias and output impedance
*/
snd_soc_update_bits ( w - > codec , SGTL5000_CHIP_MIC_CTRL ,
SGTL5000_BIAS_R_8k , 0 ) ;
break ;
}
return 0 ;
}
/*
* using codec assist to small pop , hp_powerup or lineout_powerup
* should stay setting until vag_powerup is fully ramped down ,
* vag fully ramped down require 400 ms .
*/
static int small_pop_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
switch ( event ) {
case SND_SOC_DAPM_PRE_PMU :
snd_soc_update_bits ( w - > codec , SGTL5000_CHIP_ANA_POWER ,
SGTL5000_VAG_POWERUP , SGTL5000_VAG_POWERUP ) ;
break ;
case SND_SOC_DAPM_PRE_PMD :
snd_soc_update_bits ( w - > codec , SGTL5000_CHIP_ANA_POWER ,
SGTL5000_VAG_POWERUP , 0 ) ;
msleep ( 400 ) ;
break ;
default :
break ;
}
return 0 ;
}
/* input sources for ADC */
static const char * adc_mux_text [ ] = {
" MIC_IN " , " LINE_IN "
} ;
static const struct soc_enum adc_enum =
SOC_ENUM_SINGLE ( SGTL5000_CHIP_ANA_CTRL , 2 , 2 , adc_mux_text ) ;
static const struct snd_kcontrol_new adc_mux =
SOC_DAPM_ENUM ( " Capture Mux " , adc_enum ) ;
/* input sources for DAC */
static const char * dac_mux_text [ ] = {
" DAC " , " LINE_IN "
} ;
static const struct soc_enum dac_enum =
SOC_ENUM_SINGLE ( SGTL5000_CHIP_ANA_CTRL , 6 , 2 , dac_mux_text ) ;
static const struct snd_kcontrol_new dac_mux =
SOC_DAPM_ENUM ( " Headphone Mux " , dac_enum ) ;
static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets [ ] = {
SND_SOC_DAPM_INPUT ( " LINE_IN " ) ,
SND_SOC_DAPM_INPUT ( " MIC_IN " ) ,
SND_SOC_DAPM_OUTPUT ( " HP_OUT " ) ,
SND_SOC_DAPM_OUTPUT ( " LINE_OUT " ) ,
SND_SOC_DAPM_MICBIAS_E ( " Mic Bias " , SGTL5000_CHIP_MIC_CTRL , 8 , 0 ,
mic_bias_event ,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD ) ,
SND_SOC_DAPM_PGA_E ( " HP " , SGTL5000_CHIP_ANA_POWER , 4 , 0 , NULL , 0 ,
small_pop_event ,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD ) ,
SND_SOC_DAPM_PGA_E ( " LO " , SGTL5000_CHIP_ANA_POWER , 0 , 0 , NULL , 0 ,
small_pop_event ,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD ) ,
SND_SOC_DAPM_MUX ( " Capture Mux " , SND_SOC_NOPM , 0 , 0 , & adc_mux ) ,
SND_SOC_DAPM_MUX ( " Headphone Mux " , SND_SOC_NOPM , 0 , 0 , & dac_mux ) ,
/* aif for i2s input */
SND_SOC_DAPM_AIF_IN ( " AIFIN " , " Playback " ,
0 , SGTL5000_CHIP_DIG_POWER ,
0 , 0 ) ,
/* aif for i2s output */
SND_SOC_DAPM_AIF_OUT ( " AIFOUT " , " Capture " ,
0 , SGTL5000_CHIP_DIG_POWER ,
1 , 0 ) ,
SND_SOC_DAPM_ADC ( " ADC " , " Capture " , SGTL5000_CHIP_ANA_POWER , 1 , 0 ) ,
SND_SOC_DAPM_DAC ( " DAC " , " Playback " , SGTL5000_CHIP_ANA_POWER , 3 , 0 ) ,
} ;
/* routes for sgtl5000 */
static const struct snd_soc_dapm_route audio_map [ ] = {
{ " Capture Mux " , " LINE_IN " , " LINE_IN " } , /* line_in --> adc_mux */
{ " Capture Mux " , " MIC_IN " , " MIC_IN " } , /* mic_in --> adc_mux */
{ " ADC " , NULL , " Capture Mux " } , /* adc_mux --> adc */
{ " AIFOUT " , NULL , " ADC " } , /* adc --> i2s_out */
{ " DAC " , NULL , " AIFIN " } , /* i2s-->dac,skip audio mux */
{ " Headphone Mux " , " DAC " , " DAC " } , /* dac --> hp_mux */
{ " LO " , NULL , " DAC " } , /* dac --> line_out */
{ " Headphone Mux " , " LINE_IN " , " LINE_IN " } , /* line_in --> hp_mux */
{ " HP " , NULL , " Headphone Mux " } , /* hp_mux --> hp */
{ " LINE_OUT " , NULL , " LO " } ,
{ " HP_OUT " , NULL , " HP " } ,
} ;
/* custom function to fetch info of PCM playback volume */
static int dac_info_volsw ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
{
uinfo - > type = SNDRV_CTL_ELEM_TYPE_INTEGER ;
uinfo - > count = 2 ;
uinfo - > value . integer . min = 0 ;
uinfo - > value . integer . max = 0xfc - 0x3c ;
return 0 ;
}
/*
* custom function to get of PCM playback volume
*
* dac volume register
* 15 - - - - - - - - - - - - - 8 - 7 - - - - - - - - - - - - - - 0
* | R channel vol | L channel vol |
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* PCM volume with 0.5017 dB steps from 0 to - 90 dB
*
* register values map to dB
* 0x3B and less = Reserved
* 0x3C = 0 dB
* 0x3D = - 0.5 dB
* 0xF0 = - 90 dB
* 0xFC and greater = Muted
*
* register value map to userspace value
*
* register value 0x3c ( 0 dB ) 0xf0 ( - 90 dB ) 0xfc
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* userspace value 0xc0 0
*/
static int dac_get_volsw ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
int reg ;
int l ;
int r ;
reg = snd_soc_read ( codec , SGTL5000_CHIP_DAC_VOL ) ;
/* get left channel volume */
l = ( reg & SGTL5000_DAC_VOL_LEFT_MASK ) > > SGTL5000_DAC_VOL_LEFT_SHIFT ;
/* get right channel volume */
r = ( reg & SGTL5000_DAC_VOL_RIGHT_MASK ) > > SGTL5000_DAC_VOL_RIGHT_SHIFT ;
/* make sure value fall in (0x3c,0xfc) */
l = clamp ( l , 0x3c , 0xfc ) ;
r = clamp ( r , 0x3c , 0xfc ) ;
/* invert it and map to userspace value */
l = 0xfc - l ;
r = 0xfc - r ;
ucontrol - > value . integer . value [ 0 ] = l ;
ucontrol - > value . integer . value [ 1 ] = r ;
return 0 ;
}
/*
* custom function to put of PCM playback volume
*
* dac volume register
* 15 - - - - - - - - - - - - - 8 - 7 - - - - - - - - - - - - - - 0
* | R channel vol | L channel vol |
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* PCM volume with 0.5017 dB steps from 0 to - 90 dB
*
* register values map to dB
* 0x3B and less = Reserved
* 0x3C = 0 dB
* 0x3D = - 0.5 dB
* 0xF0 = - 90 dB
* 0xFC and greater = Muted
*
* userspace value map to register value
*
* userspace value 0xc0 0
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* register value 0x3c ( 0 dB ) 0xf0 ( - 90 dB ) 0xfc
*/
static int dac_put_volsw ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
int reg ;
int l ;
int r ;
l = ucontrol - > value . integer . value [ 0 ] ;
r = ucontrol - > value . integer . value [ 1 ] ;
/* make sure userspace volume fall in (0, 0xfc-0x3c) */
l = clamp ( l , 0 , 0xfc - 0x3c ) ;
r = clamp ( r , 0 , 0xfc - 0x3c ) ;
/* invert it, get the value can be set to register */
l = 0xfc - l ;
r = 0xfc - r ;
/* shift to get the register value */
reg = l < < SGTL5000_DAC_VOL_LEFT_SHIFT |
r < < SGTL5000_DAC_VOL_RIGHT_SHIFT ;
snd_soc_write ( codec , SGTL5000_CHIP_DAC_VOL , reg ) ;
return 0 ;
}
static const DECLARE_TLV_DB_SCALE ( capture_6db_attenuate , - 600 , 600 , 0 ) ;
/* tlv for mic gain, 0db 20db 30db 40db */
static const unsigned int mic_gain_tlv [ ] = {
TLV_DB_RANGE_HEAD ( 4 ) ,
0 , 0 , TLV_DB_SCALE_ITEM ( 0 , 0 , 0 ) ,
1 , 3 , TLV_DB_SCALE_ITEM ( 2000 , 1000 , 0 ) ,
} ;
/* tlv for hp volume, -51.5db to 12.0db, step .5db */
static const DECLARE_TLV_DB_SCALE ( headphone_volume , - 5150 , 50 , 0 ) ;
static const struct snd_kcontrol_new sgtl5000_snd_controls [ ] = {
/* SOC_DOUBLE_S8_TLV with invert */
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " PCM Playback Volume " ,
. access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
SNDRV_CTL_ELEM_ACCESS_READWRITE ,
. info = dac_info_volsw ,
. get = dac_get_volsw ,
. put = dac_put_volsw ,
} ,
SOC_DOUBLE ( " Capture Volume " , SGTL5000_CHIP_ANA_ADC_CTRL , 0 , 4 , 0xf , 0 ) ,
SOC_SINGLE_TLV ( " Capture Attenuate Switch (-6dB) " ,
SGTL5000_CHIP_ANA_ADC_CTRL ,
8 , 2 , 0 , capture_6db_attenuate ) ,
SOC_SINGLE ( " Capture ZC Switch " , SGTL5000_CHIP_ANA_CTRL , 1 , 1 , 0 ) ,
SOC_DOUBLE_TLV ( " Headphone Playback Volume " ,
SGTL5000_CHIP_ANA_HP_CTRL ,
0 , 8 ,
0x7f , 1 ,
headphone_volume ) ,
SOC_SINGLE ( " Headphone Playback ZC Switch " , SGTL5000_CHIP_ANA_CTRL ,
5 , 1 , 0 ) ,
SOC_SINGLE_TLV ( " Mic Volume " , SGTL5000_CHIP_MIC_CTRL ,
0 , 4 , 0 , mic_gain_tlv ) ,
} ;
/* mute the codec used by alsa core */
static int sgtl5000_digital_mute ( struct snd_soc_dai * codec_dai , int mute )
{
struct snd_soc_codec * codec = codec_dai - > codec ;
u16 adcdac_ctrl = SGTL5000_DAC_MUTE_LEFT | SGTL5000_DAC_MUTE_RIGHT ;
snd_soc_update_bits ( codec , SGTL5000_CHIP_ADCDAC_CTRL ,
adcdac_ctrl , mute ? adcdac_ctrl : 0 ) ;
return 0 ;
}
/* set codec format */
static int sgtl5000_set_dai_fmt ( struct snd_soc_dai * codec_dai , unsigned int fmt )
{
struct snd_soc_codec * codec = codec_dai - > codec ;
struct sgtl5000_priv * sgtl5000 = snd_soc_codec_get_drvdata ( codec ) ;
u16 i2sctl = 0 ;
sgtl5000 - > master = 0 ;
/*
* i2s clock and frame master setting .
* ONLY support :
* - clock and frame slave ,
* - clock and frame master
*/
switch ( fmt & SND_SOC_DAIFMT_MASTER_MASK ) {
case SND_SOC_DAIFMT_CBS_CFS :
break ;
case SND_SOC_DAIFMT_CBM_CFM :
i2sctl | = SGTL5000_I2S_MASTER ;
sgtl5000 - > master = 1 ;
break ;
default :
return - EINVAL ;
}
/* setting i2s data format */
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_DSP_A :
i2sctl | = SGTL5000_I2S_MODE_PCM ;
break ;
case SND_SOC_DAIFMT_DSP_B :
i2sctl | = SGTL5000_I2S_MODE_PCM ;
i2sctl | = SGTL5000_I2S_LRALIGN ;
break ;
case SND_SOC_DAIFMT_I2S :
i2sctl | = SGTL5000_I2S_MODE_I2S_LJ ;
break ;
case SND_SOC_DAIFMT_RIGHT_J :
i2sctl | = SGTL5000_I2S_MODE_RJ ;
i2sctl | = SGTL5000_I2S_LRPOL ;
break ;
case SND_SOC_DAIFMT_LEFT_J :
i2sctl | = SGTL5000_I2S_MODE_I2S_LJ ;
i2sctl | = SGTL5000_I2S_LRALIGN ;
break ;
default :
return - EINVAL ;
}
sgtl5000 - > fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK ;
/* Clock inversion */
switch ( fmt & SND_SOC_DAIFMT_INV_MASK ) {
case SND_SOC_DAIFMT_NB_NF :
break ;
case SND_SOC_DAIFMT_IB_NF :
i2sctl | = SGTL5000_I2S_SCLK_INV ;
break ;
default :
return - EINVAL ;
}
snd_soc_write ( codec , SGTL5000_CHIP_I2S_CTRL , i2sctl ) ;
return 0 ;
}
/* set codec sysclk */
static int sgtl5000_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 sgtl5000_priv * sgtl5000 = snd_soc_codec_get_drvdata ( codec ) ;
switch ( clk_id ) {
case SGTL5000_SYSCLK :
sgtl5000 - > sysclk = freq ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
/*
* set clock according to i2s frame clock ,
* sgtl5000 provide 2 clock sources .
* 1. sys_mclk . sample freq can only configure to
* 1 / 256 , 1 / 384 , 1 / 512 of sys_mclk .
* 2. pll . can derive any audio clocks .
*
* clock setting rules :
* 1. in slave mode , only sys_mclk can use .
* 2. as constraint by sys_mclk , sample freq should
* set to 32 k , 44.1 k and above .
* 3. using sys_mclk prefer to pll to save power .
*/
static int sgtl5000_set_clock ( struct snd_soc_codec * codec , int frame_rate )
{
struct sgtl5000_priv * sgtl5000 = snd_soc_codec_get_drvdata ( codec ) ;
int clk_ctl = 0 ;
int sys_fs ; /* sample freq */
/*
* sample freq should be divided by frame clock ,
* if frame clock lower than 44.1 khz , sample feq should set to
* 32 khz or 44.1 khz .
*/
switch ( frame_rate ) {
case 8000 :
case 16000 :
sys_fs = 32000 ;
break ;
case 11025 :
case 22050 :
sys_fs = 44100 ;
break ;
default :
sys_fs = frame_rate ;
break ;
}
/* set divided factor of frame clock */
switch ( sys_fs / frame_rate ) {
case 4 :
clk_ctl | = SGTL5000_RATE_MODE_DIV_4 < < SGTL5000_RATE_MODE_SHIFT ;
break ;
case 2 :
clk_ctl | = SGTL5000_RATE_MODE_DIV_2 < < SGTL5000_RATE_MODE_SHIFT ;
break ;
case 1 :
clk_ctl | = SGTL5000_RATE_MODE_DIV_1 < < SGTL5000_RATE_MODE_SHIFT ;
break ;
default :
return - EINVAL ;
}
/* set the sys_fs according to frame rate */
switch ( sys_fs ) {
case 32000 :
clk_ctl | = SGTL5000_SYS_FS_32k < < SGTL5000_SYS_FS_SHIFT ;
break ;
case 44100 :
clk_ctl | = SGTL5000_SYS_FS_44_1k < < SGTL5000_SYS_FS_SHIFT ;
break ;
case 48000 :
clk_ctl | = SGTL5000_SYS_FS_48k < < SGTL5000_SYS_FS_SHIFT ;
break ;
case 96000 :
clk_ctl | = SGTL5000_SYS_FS_96k < < SGTL5000_SYS_FS_SHIFT ;
break ;
default :
dev_err ( codec - > dev , " frame rate %d not supported \n " ,
frame_rate ) ;
return - EINVAL ;
}
/*
* calculate the divider of mclk / sample_freq ,
* factor of freq = 96 k can only be 256 , since mclk in range ( 12 m , 27 m )
*/
switch ( sgtl5000 - > sysclk / sys_fs ) {
case 256 :
clk_ctl | = SGTL5000_MCLK_FREQ_256FS < <
SGTL5000_MCLK_FREQ_SHIFT ;
break ;
case 384 :
clk_ctl | = SGTL5000_MCLK_FREQ_384FS < <
SGTL5000_MCLK_FREQ_SHIFT ;
break ;
case 512 :
clk_ctl | = SGTL5000_MCLK_FREQ_512FS < <
SGTL5000_MCLK_FREQ_SHIFT ;
break ;
default :
/* if mclk not satisify the divider, use pll */
if ( sgtl5000 - > master ) {
clk_ctl | = SGTL5000_MCLK_FREQ_PLL < <
SGTL5000_MCLK_FREQ_SHIFT ;
} else {
dev_err ( codec - > dev ,
" PLL not supported in slave mode \n " ) ;
return - EINVAL ;
}
}
/* if using pll, please check manual 6.4.2 for detail */
if ( ( clk_ctl & SGTL5000_MCLK_FREQ_MASK ) = = SGTL5000_MCLK_FREQ_PLL ) {
u64 out , t ;
int div2 ;
int pll_ctl ;
unsigned int in , int_div , frac_div ;
if ( sgtl5000 - > sysclk > 17000000 ) {
div2 = 1 ;
in = sgtl5000 - > sysclk / 2 ;
} else {
div2 = 0 ;
in = sgtl5000 - > sysclk ;
}
if ( sys_fs = = 44100 )
out = 180633600 ;
else
out = 196608000 ;
t = do_div ( out , in ) ;
int_div = out ;
t * = 2048 ;
do_div ( t , in ) ;
frac_div = t ;
pll_ctl = int_div < < SGTL5000_PLL_INT_DIV_SHIFT |
frac_div < < SGTL5000_PLL_FRAC_DIV_SHIFT ;
snd_soc_write ( codec , SGTL5000_CHIP_PLL_CTRL , pll_ctl ) ;
if ( div2 )
snd_soc_update_bits ( codec ,
SGTL5000_CHIP_CLK_TOP_CTRL ,
SGTL5000_INPUT_FREQ_DIV2 ,
SGTL5000_INPUT_FREQ_DIV2 ) ;
else
snd_soc_update_bits ( codec ,
SGTL5000_CHIP_CLK_TOP_CTRL ,
SGTL5000_INPUT_FREQ_DIV2 ,
0 ) ;
/* power up pll */
snd_soc_update_bits ( codec , SGTL5000_CHIP_ANA_POWER ,
SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP ,
SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP ) ;
} else {
/* power down pll */
snd_soc_update_bits ( codec , SGTL5000_CHIP_ANA_POWER ,
SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP ,
0 ) ;
}
/* if using pll, clk_ctrl must be set after pll power up */
snd_soc_write ( codec , SGTL5000_CHIP_CLK_CTRL , clk_ctl ) ;
return 0 ;
}
/*
* Set PCM DAI bit size and sample rate .
* input : params_rate , params_fmt
*/
static int sgtl5000_pcm_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
struct snd_soc_codec * codec = rtd - > codec ;
struct sgtl5000_priv * sgtl5000 = snd_soc_codec_get_drvdata ( codec ) ;
int channels = params_channels ( params ) ;
int i2s_ctl = 0 ;
int stereo ;
int ret ;
/* sysclk should already set */
if ( ! sgtl5000 - > sysclk ) {
dev_err ( codec - > dev , " %s: set sysclk first! \n " , __func__ ) ;
return - EFAULT ;
}
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
stereo = SGTL5000_DAC_STEREO ;
else
stereo = SGTL5000_ADC_STEREO ;
/* set mono to save power */
snd_soc_update_bits ( codec , SGTL5000_CHIP_ANA_POWER , stereo ,
channels = = 1 ? 0 : stereo ) ;
/* set codec clock base on lrclk */
ret = sgtl5000_set_clock ( codec , params_rate ( params ) ) ;
if ( ret )
return ret ;
/* set i2s data format */
switch ( params_format ( params ) ) {
case SNDRV_PCM_FORMAT_S16_LE :
if ( sgtl5000 - > fmt = = SND_SOC_DAIFMT_RIGHT_J )
return - EINVAL ;
i2s_ctl | = SGTL5000_I2S_DLEN_16 < < SGTL5000_I2S_DLEN_SHIFT ;
i2s_ctl | = SGTL5000_I2S_SCLKFREQ_32FS < <
SGTL5000_I2S_SCLKFREQ_SHIFT ;
break ;
case SNDRV_PCM_FORMAT_S20_3LE :
i2s_ctl | = SGTL5000_I2S_DLEN_20 < < SGTL5000_I2S_DLEN_SHIFT ;
i2s_ctl | = SGTL5000_I2S_SCLKFREQ_64FS < <
SGTL5000_I2S_SCLKFREQ_SHIFT ;
break ;
case SNDRV_PCM_FORMAT_S24_LE :
i2s_ctl | = SGTL5000_I2S_DLEN_24 < < SGTL5000_I2S_DLEN_SHIFT ;
i2s_ctl | = SGTL5000_I2S_SCLKFREQ_64FS < <
SGTL5000_I2S_SCLKFREQ_SHIFT ;
break ;
case SNDRV_PCM_FORMAT_S32_LE :
if ( sgtl5000 - > fmt = = SND_SOC_DAIFMT_RIGHT_J )
return - EINVAL ;
i2s_ctl | = SGTL5000_I2S_DLEN_32 < < SGTL5000_I2S_DLEN_SHIFT ;
i2s_ctl | = SGTL5000_I2S_SCLKFREQ_64FS < <
SGTL5000_I2S_SCLKFREQ_SHIFT ;
break ;
default :
return - EINVAL ;
}
snd_soc_update_bits ( codec , SGTL5000_CHIP_I2S_CTRL , i2s_ctl , i2s_ctl ) ;
return 0 ;
}
static int ldo_regulator_is_enabled ( struct regulator_dev * dev )
{
struct ldo_regulator * ldo = rdev_get_drvdata ( dev ) ;
return ldo - > enabled ;
}
static int ldo_regulator_enable ( struct regulator_dev * dev )
{
struct ldo_regulator * ldo = rdev_get_drvdata ( dev ) ;
struct snd_soc_codec * codec = ( struct snd_soc_codec * ) ldo - > codec_data ;
int reg ;
if ( ldo_regulator_is_enabled ( dev ) )
return 0 ;
/* set regulator value firstly */
reg = ( 1600 - ldo - > voltage / 1000 ) / 50 ;
reg = clamp ( reg , 0x0 , 0xf ) ;
/* amend the voltage value, unit: uV */
ldo - > voltage = ( 1600 - reg * 50 ) * 1000 ;
/* set voltage to register */
snd_soc_update_bits ( codec , SGTL5000_CHIP_LINREG_CTRL ,
( 0x1 < < 4 ) - 1 , reg ) ;
snd_soc_update_bits ( codec , SGTL5000_CHIP_ANA_POWER ,
SGTL5000_LINEREG_D_POWERUP ,
SGTL5000_LINEREG_D_POWERUP ) ;
/* when internal ldo enabled, simple digital power can be disabled */
snd_soc_update_bits ( codec , SGTL5000_CHIP_ANA_POWER ,
SGTL5000_LINREG_SIMPLE_POWERUP ,
0 ) ;
ldo - > enabled = 1 ;
return 0 ;
}
static int ldo_regulator_disable ( struct regulator_dev * dev )
{
struct ldo_regulator * ldo = rdev_get_drvdata ( dev ) ;
struct snd_soc_codec * codec = ( struct snd_soc_codec * ) ldo - > codec_data ;
snd_soc_update_bits ( codec , SGTL5000_CHIP_ANA_POWER ,
SGTL5000_LINEREG_D_POWERUP ,
0 ) ;
/* clear voltage info */
snd_soc_update_bits ( codec , SGTL5000_CHIP_LINREG_CTRL ,
( 0x1 < < 4 ) - 1 , 0 ) ;
ldo - > enabled = 0 ;
return 0 ;
}
static int ldo_regulator_get_voltage ( struct regulator_dev * dev )
{
struct ldo_regulator * ldo = rdev_get_drvdata ( dev ) ;
return ldo - > voltage ;
}
static struct regulator_ops ldo_regulator_ops = {
. is_enabled = ldo_regulator_is_enabled ,
. enable = ldo_regulator_enable ,
. disable = ldo_regulator_disable ,
. get_voltage = ldo_regulator_get_voltage ,
} ;
static int ldo_regulator_register ( struct snd_soc_codec * codec ,
struct regulator_init_data * init_data ,
int voltage )
{
struct ldo_regulator * ldo ;
ldo = kzalloc ( sizeof ( struct ldo_regulator ) , GFP_KERNEL ) ;
if ( ! ldo ) {
dev_err ( codec - > dev , " failed to allocate ldo_regulator \n " ) ;
return - ENOMEM ;
}
ldo - > desc . name = kstrdup ( dev_name ( codec - > dev ) , GFP_KERNEL ) ;
if ( ! ldo - > desc . name ) {
kfree ( ldo ) ;
dev_err ( codec - > dev , " failed to allocate decs name memory \n " ) ;
return - ENOMEM ;
}
ldo - > desc . type = REGULATOR_VOLTAGE ;
ldo - > desc . owner = THIS_MODULE ;
ldo - > desc . ops = & ldo_regulator_ops ;
ldo - > desc . n_voltages = 1 ;
ldo - > codec_data = codec ;
ldo - > voltage = voltage ;
ldo - > dev = regulator_register ( & ldo - > desc , codec - > dev ,
init_data , ldo ) ;
if ( IS_ERR ( ldo - > dev ) ) {
2011-03-08 14:39:24 +03:00
int ret = PTR_ERR ( ldo - > dev ) ;
2011-02-23 21:08:21 +03:00
dev_err ( codec - > dev , " failed to register regulator \n " ) ;
kfree ( ldo - > desc . name ) ;
kfree ( ldo ) ;
2011-03-08 14:39:24 +03:00
return ret ;
2011-02-23 21:08:21 +03:00
}
return 0 ;
}
static int ldo_regulator_remove ( struct snd_soc_codec * codec )
{
struct sgtl5000_priv * sgtl5000 = snd_soc_codec_get_drvdata ( codec ) ;
struct ldo_regulator * ldo = sgtl5000 - > ldo ;
if ( ! ldo )
return 0 ;
regulator_unregister ( ldo - > dev ) ;
kfree ( ldo - > desc . name ) ;
kfree ( ldo ) ;
return 0 ;
}
/*
* set dac bias
* common state changes :
* startup :
* off - - > standby - - > prepare - - > on
* standby - - > prepare - - > on
*
* stop :
* on - - > prepare - - > standby
*/
static int sgtl5000_set_bias_level ( struct snd_soc_codec * codec ,
enum snd_soc_bias_level level )
{
int ret ;
struct sgtl5000_priv * sgtl5000 = snd_soc_codec_get_drvdata ( codec ) ;
switch ( level ) {
case SND_SOC_BIAS_ON :
case SND_SOC_BIAS_PREPARE :
break ;
case SND_SOC_BIAS_STANDBY :
if ( codec - > dapm . bias_level = = SND_SOC_BIAS_OFF ) {
ret = regulator_bulk_enable (
ARRAY_SIZE ( sgtl5000 - > supplies ) ,
sgtl5000 - > supplies ) ;
if ( ret )
return ret ;
udelay ( 10 ) ;
}
break ;
case SND_SOC_BIAS_OFF :
regulator_bulk_disable ( ARRAY_SIZE ( sgtl5000 - > supplies ) ,
sgtl5000 - > supplies ) ;
break ;
}
codec - > dapm . bias_level = level ;
return 0 ;
}
# define SGTL5000_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE )
2011-02-28 17:33:01 +03:00
static struct snd_soc_dai_ops sgtl5000_ops = {
2011-02-23 21:08:21 +03:00
. hw_params = sgtl5000_pcm_hw_params ,
. digital_mute = sgtl5000_digital_mute ,
. set_fmt = sgtl5000_set_dai_fmt ,
. set_sysclk = sgtl5000_set_dai_sysclk ,
} ;
static struct snd_soc_dai_driver sgtl5000_dai = {
. name = " sgtl5000 " ,
. playback = {
. stream_name = " Playback " ,
. channels_min = 1 ,
. channels_max = 2 ,
/*
* only support 8 ~ 48 K + 96 K ,
* TODO modify hw_param to support more
*/
. rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000 ,
. formats = SGTL5000_FORMATS ,
} ,
. capture = {
. stream_name = " Capture " ,
. channels_min = 1 ,
. channels_max = 2 ,
. rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000 ,
. formats = SGTL5000_FORMATS ,
} ,
. ops = & sgtl5000_ops ,
. symmetric_rates = 1 ,
} ;
static int sgtl5000_volatile_register ( struct snd_soc_codec * codec ,
unsigned int reg )
{
switch ( reg ) {
case SGTL5000_CHIP_ID :
case SGTL5000_CHIP_ADCDAC_CTRL :
case SGTL5000_CHIP_ANA_STATUS :
return 1 ;
}
return 0 ;
}
# ifdef CONFIG_SUSPEND
static int sgtl5000_suspend ( struct snd_soc_codec * codec , pm_message_t state )
{
sgtl5000_set_bias_level ( codec , SND_SOC_BIAS_OFF ) ;
return 0 ;
}
/*
* restore all sgtl5000 registers ,
* since a big hole between dap and regular registers ,
* we will restore them respectively .
*/
static int sgtl5000_restore_regs ( struct snd_soc_codec * codec )
{
u16 * cache = codec - > reg_cache ;
int i ;
int regular_regs = SGTL5000_CHIP_SHORT_CTRL > > 1 ;
/* restore regular registers */
for ( i = 0 ; i < regular_regs ; i + + ) {
int reg = i < < 1 ;
/* this regs depends on the others */
if ( reg = = SGTL5000_CHIP_ANA_POWER | |
reg = = SGTL5000_CHIP_CLK_CTRL | |
reg = = SGTL5000_CHIP_LINREG_CTRL | |
reg = = SGTL5000_CHIP_LINE_OUT_CTRL | |
reg = = SGTL5000_CHIP_CLK_CTRL )
continue ;
snd_soc_write ( codec , reg , cache [ i ] ) ;
}
/* restore dap registers */
for ( i = SGTL5000_DAP_REG_OFFSET > > 1 ;
i < SGTL5000_MAX_REG_OFFSET > > 1 ; i + + ) {
int reg = i < < 1 ;
snd_soc_write ( codec , reg , cache [ i ] ) ;
}
/*
* restore power and other regs according
* to set_power ( ) and set_clock ( )
*/
snd_soc_write ( codec , SGTL5000_CHIP_LINREG_CTRL ,
cache [ SGTL5000_CHIP_LINREG_CTRL > > 1 ] ) ;
snd_soc_write ( codec , SGTL5000_CHIP_ANA_POWER ,
cache [ SGTL5000_CHIP_ANA_POWER > > 1 ] ) ;
snd_soc_write ( codec , SGTL5000_CHIP_CLK_CTRL ,
cache [ SGTL5000_CHIP_CLK_CTRL > > 1 ] ) ;
snd_soc_write ( codec , SGTL5000_CHIP_REF_CTRL ,
cache [ SGTL5000_CHIP_REF_CTRL > > 1 ] ) ;
snd_soc_write ( codec , SGTL5000_CHIP_LINE_OUT_CTRL ,
cache [ SGTL5000_CHIP_LINE_OUT_CTRL > > 1 ] ) ;
return 0 ;
}
static int sgtl5000_resume ( struct snd_soc_codec * codec )
{
/* Bring the codec back up to standby to enable regulators */
sgtl5000_set_bias_level ( codec , SND_SOC_BIAS_STANDBY ) ;
/* Restore registers by cached in memory */
sgtl5000_restore_regs ( codec ) ;
return 0 ;
}
# else
# define sgtl5000_suspend NULL
# define sgtl5000_resume NULL
# endif /* CONFIG_SUSPEND */
/*
* sgtl5000 has 3 internal power supplies :
* 1. VAG , normally set to vdda / 2
* 2. chargepump , set to different value
* according to voltage of vdda and vddio
* 3. line out VAG , normally set to vddio / 2
*
* and should be set according to :
* 1. vddd provided by external or not
* 2. vdda and vddio voltage value . > 3.1 v or not
* 3. chip revision > = 0x11 or not . If > = 0x11 , not use external vddd .
*/
static int sgtl5000_set_power_regs ( struct snd_soc_codec * codec )
{
int vddd ;
int vdda ;
int vddio ;
u16 ana_pwr ;
u16 lreg_ctrl ;
int vag ;
struct sgtl5000_priv * sgtl5000 = snd_soc_codec_get_drvdata ( codec ) ;
vdda = regulator_get_voltage ( sgtl5000 - > supplies [ VDDA ] . consumer ) ;
vddio = regulator_get_voltage ( sgtl5000 - > supplies [ VDDIO ] . consumer ) ;
vddd = regulator_get_voltage ( sgtl5000 - > supplies [ VDDD ] . consumer ) ;
vdda = vdda / 1000 ;
vddio = vddio / 1000 ;
vddd = vddd / 1000 ;
if ( vdda < = 0 | | vddio < = 0 | | vddd < 0 ) {
dev_err ( codec - > dev , " regulator voltage not set correctly \n " ) ;
return - EINVAL ;
}
/* according to datasheet, maximum voltage of supplies */
if ( vdda > 3600 | | vddio > 3600 | | vddd > 1980 ) {
dev_err ( codec - > dev ,
" exceed max voltage vdda %dmv vddio %dma vddd %dma \n " ,
vdda , vddio , vddd ) ;
return - EINVAL ;
}
/* reset value */
ana_pwr = snd_soc_read ( codec , SGTL5000_CHIP_ANA_POWER ) ;
ana_pwr | = SGTL5000_DAC_STEREO |
SGTL5000_ADC_STEREO |
SGTL5000_REFTOP_POWERUP ;
lreg_ctrl = snd_soc_read ( codec , SGTL5000_CHIP_LINREG_CTRL ) ;
if ( vddio < 3100 & & vdda < 3100 ) {
/* enable internal oscillator used for charge pump */
snd_soc_update_bits ( codec , SGTL5000_CHIP_CLK_TOP_CTRL ,
SGTL5000_INT_OSC_EN ,
SGTL5000_INT_OSC_EN ) ;
/* Enable VDDC charge pump */
ana_pwr | = SGTL5000_VDDC_CHRGPMP_POWERUP ;
} else if ( vddio > = 3100 & & vdda > = 3100 ) {
/*
* if vddio and vddd > 3.1 v ,
* charge pump should be clean before set ana_pwr
*/
snd_soc_update_bits ( codec , SGTL5000_CHIP_ANA_POWER ,
SGTL5000_VDDC_CHRGPMP_POWERUP , 0 ) ;
/* VDDC use VDDIO rail */
lreg_ctrl | = SGTL5000_VDDC_ASSN_OVRD ;
lreg_ctrl | = SGTL5000_VDDC_MAN_ASSN_VDDIO < <
SGTL5000_VDDC_MAN_ASSN_SHIFT ;
}
snd_soc_write ( codec , SGTL5000_CHIP_LINREG_CTRL , lreg_ctrl ) ;
snd_soc_write ( codec , SGTL5000_CHIP_ANA_POWER , ana_pwr ) ;
/* set voltage to register */
snd_soc_update_bits ( codec , SGTL5000_CHIP_LINREG_CTRL ,
( 0x1 < < 4 ) - 1 , 0x8 ) ;
/*
* if vddd linear reg has been enabled ,
* simple digital supply should be clear to get
* proper VDDD voltage .
*/
if ( ana_pwr & SGTL5000_LINEREG_D_POWERUP )
snd_soc_update_bits ( codec , SGTL5000_CHIP_ANA_POWER ,
SGTL5000_LINREG_SIMPLE_POWERUP ,
0 ) ;
else
snd_soc_update_bits ( codec , SGTL5000_CHIP_ANA_POWER ,
SGTL5000_LINREG_SIMPLE_POWERUP |
SGTL5000_STARTUP_POWERUP ,
0 ) ;
/*
* set ADC / DAC VAG to vdda / 2 ,
* should stay in range ( 0.8 v , 1.575 v )
*/
vag = vdda / 2 ;
if ( vag < = SGTL5000_ANA_GND_BASE )
vag = 0 ;
else if ( vag > = SGTL5000_ANA_GND_BASE + SGTL5000_ANA_GND_STP *
( SGTL5000_ANA_GND_MASK > > SGTL5000_ANA_GND_SHIFT ) )
vag = SGTL5000_ANA_GND_MASK > > SGTL5000_ANA_GND_SHIFT ;
else
vag = ( vag - SGTL5000_ANA_GND_BASE ) / SGTL5000_ANA_GND_STP ;
snd_soc_update_bits ( codec , SGTL5000_CHIP_REF_CTRL ,
vag < < SGTL5000_ANA_GND_SHIFT ,
vag < < SGTL5000_ANA_GND_SHIFT ) ;
/* set line out VAG to vddio / 2, in range (0.8v, 1.675v) */
vag = vddio / 2 ;
if ( vag < = SGTL5000_LINE_OUT_GND_BASE )
vag = 0 ;
else if ( vag > = SGTL5000_LINE_OUT_GND_BASE +
SGTL5000_LINE_OUT_GND_STP * SGTL5000_LINE_OUT_GND_MAX )
vag = SGTL5000_LINE_OUT_GND_MAX ;
else
vag = ( vag - SGTL5000_LINE_OUT_GND_BASE ) /
SGTL5000_LINE_OUT_GND_STP ;
snd_soc_update_bits ( codec , SGTL5000_CHIP_LINE_OUT_CTRL ,
vag < < SGTL5000_LINE_OUT_GND_SHIFT |
SGTL5000_LINE_OUT_CURRENT_360u < <
SGTL5000_LINE_OUT_CURRENT_SHIFT ,
vag < < SGTL5000_LINE_OUT_GND_SHIFT |
SGTL5000_LINE_OUT_CURRENT_360u < <
SGTL5000_LINE_OUT_CURRENT_SHIFT ) ;
return 0 ;
}
static int sgtl5000_enable_regulators ( struct snd_soc_codec * codec )
{
u16 reg ;
int ret ;
int rev ;
int i ;
int external_vddd = 0 ;
struct sgtl5000_priv * sgtl5000 = snd_soc_codec_get_drvdata ( codec ) ;
for ( i = 0 ; i < ARRAY_SIZE ( sgtl5000 - > supplies ) ; i + + )
sgtl5000 - > supplies [ i ] . supply = supply_names [ i ] ;
ret = regulator_bulk_get ( codec - > dev , ARRAY_SIZE ( sgtl5000 - > supplies ) ,
sgtl5000 - > supplies ) ;
if ( ! ret )
external_vddd = 1 ;
else {
/* set internal ldo to 1.2v */
int voltage = LDO_VOLTAGE ;
ret = ldo_regulator_register ( codec , & ldo_init_data , voltage ) ;
if ( ret ) {
dev_err ( codec - > dev ,
" Failed to register vddd internal supplies: %d \n " ,
ret ) ;
return ret ;
}
sgtl5000 - > supplies [ VDDD ] . supply = LDO_CONSUMER_NAME ;
ret = regulator_bulk_get ( codec - > dev ,
ARRAY_SIZE ( sgtl5000 - > supplies ) ,
sgtl5000 - > supplies ) ;
if ( ret ) {
ldo_regulator_remove ( codec ) ;
dev_err ( codec - > dev ,
" Failed to request supplies: %d \n " , ret ) ;
return ret ;
}
}
ret = regulator_bulk_enable ( ARRAY_SIZE ( sgtl5000 - > supplies ) ,
sgtl5000 - > supplies ) ;
if ( ret )
goto err_regulator_free ;
/* wait for all power rails bring up */
udelay ( 10 ) ;
/* read chip information */
reg = snd_soc_read ( codec , SGTL5000_CHIP_ID ) ;
if ( ( ( reg & SGTL5000_PARTID_MASK ) > > SGTL5000_PARTID_SHIFT ) ! =
SGTL5000_PARTID_PART_ID ) {
dev_err ( codec - > dev ,
" Device with ID register %x is not a sgtl5000 \n " , reg ) ;
ret = - ENODEV ;
goto err_regulator_disable ;
}
rev = ( reg & SGTL5000_REVID_MASK ) > > SGTL5000_REVID_SHIFT ;
dev_info ( codec - > dev , " sgtl5000 revision %d \n " , rev ) ;
/*
* workaround for revision 0x11 and later ,
* roll back to use internal LDO
*/
if ( external_vddd & & rev > = 0x11 ) {
int voltage = LDO_VOLTAGE ;
/* disable all regulator first */
regulator_bulk_disable ( ARRAY_SIZE ( sgtl5000 - > supplies ) ,
sgtl5000 - > supplies ) ;
/* free VDDD regulator */
regulator_bulk_free ( ARRAY_SIZE ( sgtl5000 - > supplies ) ,
sgtl5000 - > supplies ) ;
ret = ldo_regulator_register ( codec , & ldo_init_data , voltage ) ;
if ( ret )
return ret ;
sgtl5000 - > supplies [ VDDD ] . supply = LDO_CONSUMER_NAME ;
ret = regulator_bulk_get ( codec - > dev ,
ARRAY_SIZE ( sgtl5000 - > supplies ) ,
sgtl5000 - > supplies ) ;
if ( ret ) {
ldo_regulator_remove ( codec ) ;
dev_err ( codec - > dev ,
" Failed to request supplies: %d \n " , ret ) ;
return ret ;
}
ret = regulator_bulk_enable ( ARRAY_SIZE ( sgtl5000 - > supplies ) ,
sgtl5000 - > supplies ) ;
if ( ret )
goto err_regulator_free ;
/* wait for all power rails bring up */
udelay ( 10 ) ;
}
return 0 ;
err_regulator_disable :
regulator_bulk_disable ( ARRAY_SIZE ( sgtl5000 - > supplies ) ,
sgtl5000 - > supplies ) ;
err_regulator_free :
regulator_bulk_free ( ARRAY_SIZE ( sgtl5000 - > supplies ) ,
sgtl5000 - > supplies ) ;
if ( external_vddd )
ldo_regulator_remove ( codec ) ;
return ret ;
}
static int sgtl5000_probe ( struct snd_soc_codec * codec )
{
int ret ;
struct sgtl5000_priv * sgtl5000 = snd_soc_codec_get_drvdata ( codec ) ;
/* setup i2c data ops */
ret = snd_soc_codec_set_cache_io ( codec , 16 , 16 , SND_SOC_I2C ) ;
if ( ret < 0 ) {
dev_err ( codec - > dev , " Failed to set cache I/O: %d \n " , ret ) ;
return ret ;
}
ret = sgtl5000_enable_regulators ( codec ) ;
if ( ret )
return ret ;
/* power up sgtl5000 */
ret = sgtl5000_set_power_regs ( codec ) ;
if ( ret )
goto err ;
/* enable small pop, introduce 400ms delay in turning off */
snd_soc_update_bits ( codec , SGTL5000_CHIP_REF_CTRL ,
SGTL5000_SMALL_POP ,
SGTL5000_SMALL_POP ) ;
/* disable short cut detector */
snd_soc_write ( codec , SGTL5000_CHIP_SHORT_CTRL , 0 ) ;
/*
* set i2s as default input of sound switch
* TODO : add sound switch to control and dapm widge .
*/
snd_soc_write ( codec , SGTL5000_CHIP_SSS_CTRL ,
SGTL5000_DAC_SEL_I2S_IN < < SGTL5000_DAC_SEL_SHIFT ) ;
snd_soc_write ( codec , SGTL5000_CHIP_DIG_POWER ,
SGTL5000_ADC_EN | SGTL5000_DAC_EN ) ;
/* enable dac volume ramp by default */
snd_soc_write ( codec , SGTL5000_CHIP_ADCDAC_CTRL ,
SGTL5000_DAC_VOL_RAMP_EN |
SGTL5000_DAC_MUTE_RIGHT |
SGTL5000_DAC_MUTE_LEFT ) ;
snd_soc_write ( codec , SGTL5000_CHIP_PAD_STRENGTH , 0x015f ) ;
snd_soc_write ( codec , SGTL5000_CHIP_ANA_CTRL ,
SGTL5000_HP_ZCD_EN |
SGTL5000_ADC_ZCD_EN ) ;
snd_soc_write ( codec , SGTL5000_CHIP_MIC_CTRL , 0 ) ;
/*
* disable DAP
* TODO :
* Enable DAP in kcontrol and dapm .
*/
snd_soc_write ( codec , SGTL5000_DAP_CTRL , 0 ) ;
/* leading to standby state */
ret = sgtl5000_set_bias_level ( codec , SND_SOC_BIAS_STANDBY ) ;
if ( ret )
goto err ;
snd_soc_add_controls ( codec , sgtl5000_snd_controls ,
ARRAY_SIZE ( sgtl5000_snd_controls ) ) ;
snd_soc_dapm_new_controls ( & codec - > dapm , sgtl5000_dapm_widgets ,
ARRAY_SIZE ( sgtl5000_dapm_widgets ) ) ;
snd_soc_dapm_add_routes ( & codec - > dapm , audio_map ,
ARRAY_SIZE ( audio_map ) ) ;
snd_soc_dapm_new_widgets ( & codec - > dapm ) ;
return 0 ;
err :
regulator_bulk_disable ( ARRAY_SIZE ( sgtl5000 - > supplies ) ,
sgtl5000 - > supplies ) ;
regulator_bulk_free ( ARRAY_SIZE ( sgtl5000 - > supplies ) ,
sgtl5000 - > supplies ) ;
ldo_regulator_remove ( codec ) ;
return ret ;
}
static int sgtl5000_remove ( struct snd_soc_codec * codec )
{
struct sgtl5000_priv * sgtl5000 = snd_soc_codec_get_drvdata ( codec ) ;
sgtl5000_set_bias_level ( codec , SND_SOC_BIAS_OFF ) ;
regulator_bulk_disable ( ARRAY_SIZE ( sgtl5000 - > supplies ) ,
sgtl5000 - > supplies ) ;
regulator_bulk_free ( ARRAY_SIZE ( sgtl5000 - > supplies ) ,
sgtl5000 - > supplies ) ;
ldo_regulator_remove ( codec ) ;
return 0 ;
}
2011-02-28 17:33:01 +03:00
static struct snd_soc_codec_driver sgtl5000_driver = {
2011-02-23 21:08:21 +03:00
. probe = sgtl5000_probe ,
. remove = sgtl5000_remove ,
. suspend = sgtl5000_suspend ,
. resume = sgtl5000_resume ,
. set_bias_level = sgtl5000_set_bias_level ,
. reg_cache_size = ARRAY_SIZE ( sgtl5000_regs ) ,
. reg_word_size = sizeof ( u16 ) ,
. reg_cache_step = 2 ,
. reg_cache_default = sgtl5000_regs ,
. volatile_register = sgtl5000_volatile_register ,
} ;
static __devinit int sgtl5000_i2c_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct sgtl5000_priv * sgtl5000 ;
int ret ;
sgtl5000 = kzalloc ( sizeof ( struct sgtl5000_priv ) , GFP_KERNEL ) ;
if ( ! sgtl5000 )
return - ENOMEM ;
/*
* copy DAP default values to default value array .
* sgtl5000 register space has a big hole , merge it
* at init phase makes life easy .
* FIXME : should we drop ' const ' of sgtl5000_regs ?
*/
memcpy ( ( void * ) ( & sgtl5000_regs [ 0 ] + ( SGTL5000_DAP_REG_OFFSET > > 1 ) ) ,
sgtl5000_dap_regs ,
SGTL5000_MAX_REG_OFFSET - SGTL5000_DAP_REG_OFFSET ) ;
i2c_set_clientdata ( client , sgtl5000 ) ;
ret = snd_soc_register_codec ( & client - > dev ,
& sgtl5000_driver , & sgtl5000_dai , 1 ) ;
if ( ret ) {
dev_err ( & client - > dev , " Failed to register codec: %d \n " , ret ) ;
kfree ( sgtl5000 ) ;
return ret ;
}
return 0 ;
}
static __devexit int sgtl5000_i2c_remove ( struct i2c_client * client )
{
struct sgtl5000_priv * sgtl5000 = i2c_get_clientdata ( client ) ;
snd_soc_unregister_codec ( & client - > dev ) ;
kfree ( sgtl5000 ) ;
return 0 ;
}
static const struct i2c_device_id sgtl5000_id [ ] = {
{ " sgtl5000 " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( i2c , sgtl5000_id ) ;
static struct i2c_driver sgtl5000_i2c_driver = {
. driver = {
. name = " sgtl5000 " ,
. owner = THIS_MODULE ,
} ,
. probe = sgtl5000_i2c_probe ,
. remove = __devexit_p ( sgtl5000_i2c_remove ) ,
. id_table = sgtl5000_id ,
} ;
static int __init sgtl5000_modinit ( void )
{
return i2c_add_driver ( & sgtl5000_i2c_driver ) ;
}
module_init ( sgtl5000_modinit ) ;
static void __exit sgtl5000_exit ( void )
{
i2c_del_driver ( & sgtl5000_i2c_driver ) ;
}
module_exit ( sgtl5000_exit ) ;
MODULE_DESCRIPTION ( " Freescale SGTL5000 ALSA SoC Codec Driver " ) ;
MODULE_AUTHOR ( " Zeng Zhaoming <zhaoming.zeng@freescale.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;