From 1d3ee43b13f6a8d43a33c2f36c401fdbf829894f Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E9=99=88=E8=BE=89?= Date: Tue, 19 Apr 2011 09:38:09 +0800 Subject: [PATCH] add alc5621 and alc5631 codec support --- arch/arm/configs/rk29_sdk_defconfig | 2 + arch/arm/mach-rk29/board-rk29sdk.c | 14 + sound/soc/codecs/Kconfig | 16 +- sound/soc/codecs/Makefile | 6 + sound/soc/codecs/alc5621.c | 1932 +++++++++++++++++++++++++++ sound/soc/codecs/alc5621.h | 516 +++++++ sound/soc/codecs/rt5631.c | 1556 +++++++++++++++++++++ sound/soc/codecs/rt5631.h | 578 ++++++++ sound/soc/rk29/Kconfig | 19 +- sound/soc/rk29/Makefile | 4 + sound/soc/rk29/rk29_alc5621.c | 249 ++++ sound/soc/rk29/rk29_i2s.c | 161 ++- sound/soc/rk29/rk29_rt5631.c | 258 ++++ 13 files changed, 5221 insertions(+), 90 deletions(-) create mode 100644 sound/soc/codecs/alc5621.c create mode 100644 sound/soc/codecs/alc5621.h create mode 100644 sound/soc/codecs/rt5631.c create mode 100644 sound/soc/codecs/rt5631.h mode change 100755 => 100644 sound/soc/rk29/Kconfig mode change 100755 => 100644 sound/soc/rk29/Makefile create mode 100644 sound/soc/rk29/rk29_alc5621.c mode change 100755 => 100644 sound/soc/rk29/rk29_i2s.c create mode 100644 sound/soc/rk29/rk29_rt5631.c diff --git a/arch/arm/configs/rk29_sdk_defconfig b/arch/arm/configs/rk29_sdk_defconfig index cf905e663252..9919260e32b3 100755 --- a/arch/arm/configs/rk29_sdk_defconfig +++ b/arch/arm/configs/rk29_sdk_defconfig @@ -1309,6 +1309,8 @@ CONFIG_SND_RK29_SOC_I2S=y CONFIG_SND_RK29_SOC_I2S_8CH=y # CONFIG_SND_RK29_SOC_WM8988 is not set CONFIG_SND_RK29_SOC_WM8900=y +# CONFIG_SND_RK29_SOC_alc5621 is not set +# CONFIG_SND_RK29_SOC_alc5631 is not set # CONFIG_SND_RK29_SOC_WM8994 is not set # CONFIG_SND_RK29_CODEC_SOC_MASTER is not set CONFIG_SND_RK29_CODEC_SOC_SLAVE=y diff --git a/arch/arm/mach-rk29/board-rk29sdk.c b/arch/arm/mach-rk29/board-rk29sdk.c index c70363ffc421..22442001d422 100755 --- a/arch/arm/mach-rk29/board-rk29sdk.c +++ b/arch/arm/mach-rk29/board-rk29sdk.c @@ -512,6 +512,20 @@ static struct i2c_board_info __initdata board_i2c0_devices[] = { .flags = 0, }, #endif +#if defined (CONFIG_SND_SOC_alc5621) + { + .type = "ALC5621", + .addr = 0x1a, + .flags = 0, + }, +#endif +#if defined (CONFIG_SND_SOC_alc5631) + { + .type = "rt5631", + .addr = 0x1a, + .flags = 0, + }, +#endif #if defined (CONFIG_SND_SOC_RK1000) { .type = "rk1000_i2c_codec", diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 1d0c6bf24363..4ba65a696c2d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -42,6 +42,8 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8900 if I2C + select SND_SOC_alc5621 if I2C + select SND_SOC_alc5631 if I2C select SND_SOC_WM8903 if I2C select SND_SOC_WM8940 if I2C select SND_SOC_WM8960 if I2C @@ -66,11 +68,13 @@ config SND_SOC_ALL_CODECS be selected separately. If unsure select "N". - +select SND_SOC_ALC5623_RT if I2C +config SND_SOC_ALC5623 + tristate config SND_SOC_WM_HUBS tristate - default y if SND_SOC_WM8993=y || SND_SOC_WM8994=y - default m if SND_SOC_WM8993=m || SND_SOC_WM8994=m + default y if SND_SOC_WM8993=y + default m if SND_SOC_WM8993=m config SND_SOC_AC97_CODEC tristate @@ -179,6 +183,12 @@ config SND_SOC_WM8776 config SND_SOC_WM8900 tristate +config SND_SOC_alc5621 + tristate + +config SND_SOC_alc5631 + tristate + config SND_SOC_WM8903 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 36af1ce465df..49b4439c6a35 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -30,6 +30,8 @@ snd-soc-wm8750-objs := wm8750.o snd-soc-wm8753-objs := wm8753.o snd-soc-wm8776-objs := wm8776.o snd-soc-wm8900-objs := wm8900.o +snd-soc-alc5621-objs := alc5621.o +snd-soc-alc5631-objs := rt5631.o snd-soc-wm8903-objs := wm8903.o snd-soc-wm8940-objs := wm8940.o snd-soc-wm8960-objs := wm8960.o @@ -49,6 +51,8 @@ snd-soc-rk1000-objs := rk1000_codec.o # Amp snd-soc-max9877-objs := max9877.o +snd-soc-alc5623-objs := alc5623_tuning.o +obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o obj-$(CONFIG_SND_SOC_AD1938) += snd-soc-ad1938.o @@ -81,6 +85,8 @@ obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o +obj-$(CONFIG_SND_SOC_alc5621) += snd-soc-alc5621.o +obj-$(CONFIG_SND_SOC_alc5631) += snd-soc-alc5631.o obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o obj-$(CONFIG_SND_SOC_WM8974) += snd-soc-wm8974.o diff --git a/sound/soc/codecs/alc5621.c b/sound/soc/codecs/alc5621.c new file mode 100644 index 000000000000..ab04ab7dedf9 --- /dev/null +++ b/sound/soc/codecs/alc5621.c @@ -0,0 +1,1932 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "alc5621.h" + +#if REALTEK_HWDEP + +#include +#include + +#endif + +#define AUDIO_NAME "rt5621" +#define RT5621_VERSION "alsa 1.0.21 0.05" + +#ifdef RT5621_DEBUG +#define dbg(format, arg...) \ + printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif +#define err(format, arg...) \ + printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg) +#define info(format, arg...) \ + printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg) +#define warn(format, arg...) \ + printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg) + +static int caps_charge = 500; +module_param(caps_charge, int, 0); +MODULE_PARM_DESC(caps_charge, "RT5621 cap charge time (msecs)"); + +/* codec private data */ +struct rt5621_priv { + unsigned int sysclk; +}; + +static struct snd_soc_device *rt5621_socdev; + +struct rt5621_reg{ + + u8 reg_index; + u16 reg_value; +}; + +static struct rt5621_reg init_data[] = { + {RT5621_AUDIO_INTERFACE, 0x8000}, //set I2S codec to slave mode + {RT5621_STEREO_DAC_VOL, 0x0808}, //default stereo DAC volume to 0db + {RT5621_OUTPUT_MIXER_CTRL, 0x0740}, //default output mixer control + {RT5621_ADC_REC_MIXER, 0x3f3f}, //set record source is Mic1 by default + {RT5621_MIC_CTRL, 0x0500}, //set Mic1,Mic2 boost 20db + {RT5621_SPK_OUT_VOL, 0x8080}, //default speaker volume to 0db + {RT5621_HP_OUT_VOL, 0x8888}, //default HP volume to -12db + {RT5621_ADD_CTRL_REG, 0x5f00}, //Class AB/D speaker ratio is 1VDD + {RT5621_STEREO_AD_DA_CLK_CTRL, 0x066d}, //set Dac filter to 256fs + {RT5621_HID_CTRL_INDEX, 0x46}, //Class D setting + {RT5621_HID_CTRL_DATA, 0xFFFF}, //power on Class D Internal register +}; + +#define RT5621_INIT_REG_NUM ARRAY_SIZE(init_data) + +/* + * rt5621 register cache + * We can't read the RT5621 register space when we + * are using 2 wire for device control, so we cache them instead. + */ +static const u16 rt5621_reg[0x80/2]; + + +/* virtual HP mixers regs */ +#define HPL_MIXER 0x80 +#define HPR_MIXER 0x82 +/*reg84*/ +/*bit0,1:for hp pga power control + *bit2,3:for aux pga power control + */ +#define MISC_FUNC_REG 0x84 +static u16 reg80=0,reg82=0, reg84 = 0; + + +/* + * read rt5621 register cache + */ +static inline unsigned int rt5621_read_reg_cache(struct snd_soc_codec *codec, + unsigned int reg) +{ + u16 *cache = codec->reg_cache; + if (reg < 1 || reg > (ARRAY_SIZE(rt5621_reg) + 1)) + return -1; + return cache[reg/2]; +} + + +/* + * write rt5621 register cache + */ + +static inline void rt5621_write_reg_cache(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + u16 *cache = codec->reg_cache; + if (reg < 0 || reg > 0x7e) + return; + cache[reg/2] = value; +} + + + +static int rt5621_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u8 data[3]; + + if(reg>0x7E) + { + if(reg==HPL_MIXER) + reg80=value; + else if(reg==HPR_MIXER) + reg82=value; + else if (reg == MISC_FUNC_REG) + reg84 = value; + else + return -EIO; + + return 0; + } + + + printk("rt5621 write reg=%x,value=%x\n",reg,value); + data[0] = reg; + data[1] = (0xFF00 & value) >> 8; + data[2] = 0x00FF & value; + + if (codec->hw_write(codec->control_data, data, 3) == 3) + { + rt5621_write_reg_cache (codec, reg, value); + printk(KERN_INFO "rt5621 write reg=%x,value=%x\n",reg,value); + return 0; + } + else + { + printk(KERN_ERR "rt5621 write faile\n"); + return -EIO; + } +} + + +static unsigned int rt5621_read(struct snd_soc_codec *codec, unsigned int reg) +{ + u8 data[2]={0}; + unsigned int value=0x0; + + if(reg>0x7E) + { + if(reg==HPL_MIXER) + return reg80; + else if(reg==HPR_MIXER) + return reg82; + else if (reg == MISC_FUNC_REG) + return reg84; + else + return -EIO; + + return -EIO; + } + + + data[0] = reg; +//flove031811_S +#if 0 + i2c_master_recv(codec->control_data, data, 2); + + value = (data[0]<<8) | data[1]; + printk("rt5621_read reg%x=%x\n",reg,value); +#elif 1 + + i2c_master_reg8_recv(codec->control_data,reg,data, 2,100 * 1000); + + value = (data[0]<<8) | data[1]; + printk("rt5621_read reg%x=%x\n",reg,value); + return value; + +#else + if(codec->hw_write(codec->control_data, data, 1) ==1) + { + i2c_master_recv(codec->control_data, data, 2); + + value = (data[0]<<8) | data[1]; + printk(KERN_DEBUG "rt5621 read reg%x=%x\n",reg,value); + + return value; + } + else + { + printk(KERN_ERR "rt5621 read faile\n"); + return -EIO; + } +#endif +//flove031811_E +} + +#define rt5621_write_mask(c, reg, value, mask) snd_soc_update_bits(c, reg, mask, value) + + +#define rt5621_reset(c) rt5621_write(c, 0x0, 0) + +static unsigned int rt5621_read_index(struct snd_soc_codec *codec, unsigned int index) +{ + unsigned int value; + + rt5621_write(codec, 0x6a, index); + mdelay(1); + value = rt5621_read(codec, 0x6c); + return value; +} + +static int rt5621_init_reg(struct snd_soc_codec *codec) +{ + int i; + + for (i = 0; i < RT5621_INIT_REG_NUM; i++) + { + rt5621_write(codec, init_data[i].reg_index, init_data[i].reg_value); + } + + return 0; +} + + +#if !USE_DAPM_CONTROL +//***************************************************************************** +// +//function:Change audio codec power status +// +//***************************************************************************** +static int rt5621_ChangeCodecPowerStatus(struct snd_soc_codec *codec,int power_state) +{ + unsigned short int PowerDownState=0; + + switch(power_state) + { + case POWER_STATE_D0: //FULL ON-----power on all power + + rt5621_write(codec,RT5621_PWR_MANAG_ADD1,~PowerDownState); + rt5621_write(codec,RT5621_PWR_MANAG_ADD2,~PowerDownState); + rt5621_write(codec,RT5621_PWR_MANAG_ADD3,~PowerDownState); + + break; + + case POWER_STATE_D1: //LOW ON----- + + + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD2 ,PWR_VREF |PWR_DAC_REF_CIR |PWR_L_DAC_CLK |PWR_R_DAC_CLK |PWR_L_HP_MIXER |PWR_R_HP_MIXER| + PWR_L_ADC_CLK_GAIN |PWR_R_ADC_CLK_GAIN |PWR_L_ADC_REC_MIXER |PWR_R_ADC_REC_MIXER|PWR_CLASS_AB + ,PWR_VREF |PWR_DAC_REF_CIR |PWR_L_DAC_CLK |PWR_R_DAC_CLK |PWR_L_HP_MIXER |PWR_R_HP_MIXER| + PWR_L_ADC_CLK_GAIN |PWR_R_ADC_CLK_GAIN |PWR_L_ADC_REC_MIXER |PWR_R_ADC_REC_MIXER|PWR_CLASS_AB); + + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD3 ,PWR_MAIN_BIAS|PWR_HP_R_OUT_VOL|PWR_HP_L_OUT_VOL|PWR_SPK_OUT| + PWR_MIC1_FUN_CTRL|PWR_MIC1_BOOST_MIXER + ,PWR_MAIN_BIAS|PWR_HP_R_OUT_VOL|PWR_HP_L_OUT_VOL|PWR_SPK_OUT| + PWR_MIC1_FUN_CTRL|PWR_MIC1_BOOST_MIXER); + + + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD1 ,PWR_MAIN_I2S_EN|PWR_HP_OUT_ENH_AMP|PWR_HP_OUT_AMP|PWR_MIC1_BIAS_EN + ,PWR_MAIN_I2S_EN|PWR_HP_OUT_ENH_AMP|PWR_HP_OUT_AMP|PWR_MIC1_BIAS_EN); + + break; + + case POWER_STATE_D1_PLAYBACK: //Low on of Playback + + + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD2,PWR_VREF|PWR_DAC_REF_CIR|PWR_L_DAC_CLK|PWR_R_DAC_CLK|PWR_L_HP_MIXER|PWR_R_HP_MIXER|PWR_CLASS_AB|PWR_CLASS_D + ,PWR_VREF|PWR_DAC_REF_CIR|PWR_L_DAC_CLK|PWR_R_DAC_CLK|PWR_L_HP_MIXER|PWR_R_HP_MIXER|PWR_CLASS_AB|PWR_CLASS_D); + + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD3,PWR_MAIN_BIAS|PWR_HP_R_OUT_VOL|PWR_HP_L_OUT_VOL|PWR_SPK_OUT + ,PWR_MAIN_BIAS|PWR_HP_R_OUT_VOL|PWR_HP_L_OUT_VOL|PWR_SPK_OUT); + + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD1,PWR_MAIN_I2S_EN|PWR_HP_OUT_ENH_AMP|PWR_HP_OUT_AMP + ,PWR_MAIN_I2S_EN|PWR_HP_OUT_ENH_AMP|PWR_HP_OUT_AMP); + + + break; + + case POWER_STATE_D1_RECORD: //Low on of Record + + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD1 ,PWR_MAIN_I2S_EN|PWR_MIC1_BIAS_EN + ,PWR_MAIN_I2S_EN|PWR_MIC1_BIAS_EN); + + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD2 ,PWR_VREF|PWR_L_ADC_CLK_GAIN|PWR_R_ADC_CLK_GAIN|PWR_L_ADC_REC_MIXER|PWR_R_ADC_REC_MIXER + ,PWR_VREF|PWR_L_ADC_CLK_GAIN|PWR_R_ADC_CLK_GAIN|PWR_L_ADC_REC_MIXER|PWR_R_ADC_REC_MIXER); + + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD3 ,PWR_MAIN_BIAS|PWR_MIC2_BOOST_MIXER|PWR_MIC1_BOOST_MIXER + ,PWR_MAIN_BIAS|PWR_MIC2_BOOST_MIXER|PWR_MIC1_BOOST_MIXER); + + break; + + case POWER_STATE_D2: //STANDBY---- + + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD1 ,0,PWR_MAIN_I2S_EN|PWR_HP_OUT_ENH_AMP|PWR_HP_OUT_AMP|PWR_MIC1_BIAS_EN); + + + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD3 ,0,PWR_HP_R_OUT_VOL|PWR_HP_L_OUT_VOL|PWR_SPK_OUT|PWR_MIC1_FUN_CTRL|PWR_MIC1_BOOST_MIXER); + + + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD2 ,0,PWR_DAC_REF_CIR |PWR_L_DAC_CLK |PWR_R_DAC_CLK |PWR_L_HP_MIXER |PWR_R_HP_MIXER| + PWR_L_ADC_CLK_GAIN |PWR_R_ADC_CLK_GAIN |PWR_L_ADC_REC_MIXER |PWR_R_ADC_REC_MIXER|PWR_CLASS_AB|PWR_CLASS_D); + + + break; + + case POWER_STATE_D2_PLAYBACK: //STANDBY of playback + + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD3 ,0,/*PWR_HP_R_OUT_VOL|PWR_HP_L_OUT_VOL|*/PWR_SPK_OUT); + + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD1 ,0,PWR_HP_OUT_ENH_AMP|PWR_HP_OUT_AMP); + + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD2 ,0,PWR_DAC_REF_CIR|PWR_L_DAC_CLK|PWR_R_DAC_CLK|PWR_L_HP_MIXER|PWR_R_HP_MIXER|PWR_CLASS_AB|PWR_CLASS_D); + + break; + + case POWER_STATE_D2_RECORD: //STANDBY of record + + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD1 ,0,PWR_MIC1_BIAS_EN); + + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD2 ,0,PWR_L_ADC_CLK_GAIN|PWR_R_ADC_CLK_GAIN|PWR_L_ADC_REC_MIXER|PWR_R_ADC_REC_MIXER); + + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD3 ,0,PWR_MIC2_BOOST_MIXER|PWR_MIC1_BOOST_MIXER); + + break; + + case POWER_STATE_D3: //SLEEP + case POWER_STATE_D4: //OFF----power off all power + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD1 ,0,PWR_HP_OUT_ENH_AMP|PWR_HP_OUT_AMP); + rt5621_write(codec,RT5621_PWR_MANAG_ADD3,0); + rt5621_write(codec,RT5621_PWR_MANAG_ADD1,0); + rt5621_write(codec,RT5621_PWR_MANAG_ADD2,0); + + break; + + default: + + break; + } + + return 0; +} + + +//***************************************************************************** +// +//function AudioOutEnable:Mute/Unmute audio out channel +// WavOutPath:output channel +// Mute :Mute/Unmute output channel +// +//***************************************************************************** +static int rt5621_AudioOutEnable(struct snd_soc_codec *codec,unsigned short int WavOutPath,int Mute) +{ + int RetVal=0; + + if(Mute) + { + switch(WavOutPath) + { + case RT_WAVOUT_ALL_ON: + + RetVal=rt5621_write_mask(codec,RT5621_SPK_OUT_VOL,RT_L_MUTE|RT_R_MUTE,RT_L_MUTE|RT_R_MUTE); //Mute Speaker right/left channel + RetVal=rt5621_write_mask(codec,RT5621_HP_OUT_VOL,RT_L_MUTE|RT_R_MUTE,RT_L_MUTE|RT_R_MUTE); //Mute headphone right/left channel + RetVal=rt5621_write_mask(codec,RT5621_MONO_AUX_OUT_VOL,RT_L_MUTE|RT_R_MUTE,RT_L_MUTE|RT_R_MUTE); //Mute Aux/Mono right/left channel + RetVal=rt5621_write_mask(codec,RT5621_STEREO_DAC_VOL,RT_M_HP_MIXER|RT_M_SPK_MIXER|RT_M_MONO_MIXER + ,RT_M_HP_MIXER|RT_M_SPK_MIXER|RT_M_MONO_MIXER); //Mute DAC to HP,Speaker,Mono Mixer + + break; + + case RT_WAVOUT_HP: + + RetVal=rt5621_write_mask(codec,RT5621_HP_OUT_VOL,RT_L_MUTE|RT_R_MUTE,RT_L_MUTE|RT_R_MUTE); //Mute headphone right/left channel + + break; + + case RT_WAVOUT_SPK: + + RetVal=rt5621_write_mask(codec,RT5621_SPK_OUT_VOL,RT_L_MUTE|RT_R_MUTE,RT_L_MUTE|RT_R_MUTE); //Mute Speaker right/left channel + + break; + + case RT_WAVOUT_AUXOUT: + + RetVal=rt5621_write_mask(codec,RT5621_MONO_AUX_OUT_VOL,RT_L_MUTE|RT_R_MUTE,RT_L_MUTE|RT_R_MUTE); //Mute AuxOut right/left channel + + break; + + case RT_WAVOUT_MONO: + + RetVal=rt5621_write_mask(codec,RT5621_MONO_AUX_OUT_VOL,RT_L_MUTE,RT_L_MUTE); //Mute MonoOut channel + + break; + + case RT_WAVOUT_DAC: + + RetVal=rt5621_write_mask(codec,RT5621_STEREO_DAC_VOL,RT_M_HP_MIXER|RT_M_SPK_MIXER|RT_M_MONO_MIXER + ,RT_M_HP_MIXER|RT_M_SPK_MIXER|RT_M_MONO_MIXER); //Mute DAC to HP,Speaker,Mono Mixer + break; + + default: + + return 0; + + } + } + else + { + switch(WavOutPath) + { + + case RT_WAVOUT_ALL_ON: + + RetVal=rt5621_write_mask(codec,RT5621_SPK_OUT_VOL ,0,RT_L_MUTE|RT_R_MUTE); //Mute Speaker right/left channel + RetVal=rt5621_write_mask(codec,RT5621_HP_OUT_VOL ,0,RT_L_MUTE|RT_R_MUTE); //Mute headphone right/left channel + RetVal=rt5621_write_mask(codec,RT5621_MONO_AUX_OUT_VOL ,0,RT_L_MUTE|RT_R_MUTE); //Mute Aux/Mono right/left channel + RetVal=rt5621_write_mask(codec,RT5621_STEREO_DAC_VOL ,0,RT_M_HP_MIXER|RT_M_SPK_MIXER|RT_M_MONO_MIXER); //Mute DAC to HP,Speaker,Mono Mixer + + break; + + case RT_WAVOUT_HP: + + RetVal=rt5621_write_mask(codec,RT5621_HP_OUT_VOL,0,RT_L_MUTE|RT_R_MUTE); //UnMute headphone right/left channel + + break; + + case RT_WAVOUT_SPK: + + RetVal=rt5621_write_mask(codec,RT5621_SPK_OUT_VOL,0,RT_L_MUTE|RT_R_MUTE); //unMute Speaker right/left channel + + break; + + case RT_WAVOUT_AUXOUT: + + RetVal=rt5621_write_mask(codec,RT5621_MONO_AUX_OUT_VOL,0,RT_L_MUTE|RT_R_MUTE);//unMute AuxOut right/left channel + + break; + + case RT_WAVOUT_MONO: + + RetVal=rt5621_write_mask(codec,RT5621_MONO_AUX_OUT_VOL,0,RT_L_MUTE); //unMute MonoOut channel + + break; + + case RT_WAVOUT_DAC: + + RetVal=rt5621_write_mask(codec,RT5621_STEREO_DAC_VOL,0,RT_M_HP_MIXER|RT_M_SPK_MIXER|RT_M_MONO_MIXER); //unMute DAC to HP,Speaker,Mono Mixer + + default: + return 0; + } + + } + + return RetVal; +} + + +//***************************************************************************** +// +//function:Enable/Disable ADC input source control +// +//***************************************************************************** +static int Enable_ADC_Input_Source(struct snd_soc_codec *codec,unsigned short int ADC_Input_Sour,int Enable) +{ + int bRetVal=0; + + if(Enable) + { + //Enable ADC source + bRetVal=rt5621_write_mask(codec,RT5621_ADC_REC_MIXER,0,ADC_Input_Sour); + } + else + { + //Disable ADC source + bRetVal=rt5621_write_mask(codec,RT5621_ADC_REC_MIXER,ADC_Input_Sour,ADC_Input_Sour); + } + + return bRetVal; +} +#endif + + +//static const char *rt5621_spkl_pga[] = {"Vmid","HPL mixer","SPK mixer","Mono Mixer"}; +static const char *rt5621_spkn_source_sel[] = {"RN", "RP", "LN"}; +static const char *rt5621_spk_pga[] = {"Vmid","HP mixer","SPK mixer","Mono Mixer"}; +static const char *rt5621_hpl_pga[] = {"Vmid","HPL mixer"}; +static const char *rt5621_hpr_pga[] = {"Vmid","HPR mixer"}; +static const char *rt5621_mono_pga[] = {"Vmid","HP mixer","SPK mixer","Mono Mixer"}; +static const char *rt5621_amp_type_sel[] = {"Class AB","Class D"}; +static const char *rt5621_mic_boost_sel[] = {"Bypass","20db","30db","40db"}; + +static const struct soc_enum rt5621_enum[] = { +SOC_ENUM_SINGLE(RT5621_OUTPUT_MIXER_CTRL, 14, 3, rt5621_spkn_source_sel), /* spkn source from hp mixer */ +SOC_ENUM_SINGLE(RT5621_OUTPUT_MIXER_CTRL, 10, 4, rt5621_spk_pga), /* spk input sel 1 */ +SOC_ENUM_SINGLE(RT5621_OUTPUT_MIXER_CTRL, 9, 2, rt5621_hpl_pga), /* hp left input sel 2 */ +SOC_ENUM_SINGLE(RT5621_OUTPUT_MIXER_CTRL, 8, 2, rt5621_hpr_pga), /* hp right input sel 3 */ +SOC_ENUM_SINGLE(RT5621_OUTPUT_MIXER_CTRL, 6, 4, rt5621_mono_pga), /* mono input sel 4 */ +SOC_ENUM_SINGLE(RT5621_MIC_CTRL , 10,4, rt5621_mic_boost_sel), /*Mic1 boost sel 5 */ +SOC_ENUM_SINGLE(RT5621_MIC_CTRL , 8,4,rt5621_mic_boost_sel), /*Mic2 boost sel 6 */ +SOC_ENUM_SINGLE(RT5621_OUTPUT_MIXER_CTRL,13,2,rt5621_amp_type_sel), /*Speaker AMP sel 7 */ +}; + +static int rt5621_amp_sel_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned short val; + unsigned short mask, bitmask; + + for (bitmask = 1; bitmask < e->max; bitmask <<= 1) + ; + if (ucontrol->value.enumerated.item[0] > e->max - 1) + return -EINVAL; + val = ucontrol->value.enumerated.item[0] << e->shift_l; + mask = (bitmask - 1) << e->shift_l; + if (e->shift_l != e->shift_r) { + if (ucontrol->value.enumerated.item[1] > e->max - 1) + return -EINVAL; + val |= ucontrol->value.enumerated.item[1] << e->shift_r; + mask |= (bitmask - 1) << e->shift_r; + } + + snd_soc_update_bits(codec, e->reg, mask, val); + val &= (0x1 << 13); + if (val == 0) + { + snd_soc_update_bits(codec, 0x3c, 0x0000, 0x4000); /*power off classd*/ + snd_soc_update_bits(codec, 0x3c, 0x8000, 0x8000); /*power on classab*/ + } + else + { + snd_soc_update_bits(codec, 0x3c, 0x0000, 0x8000); /*power off classab*/ + snd_soc_update_bits(codec, 0x3c, 0x4000, 0x4000); /*power on classd*/ + } + return 0; +} + + + +static const struct snd_kcontrol_new rt5621_snd_controls[] = { +SOC_DOUBLE("Speaker Playback Volume", RT5621_SPK_OUT_VOL, 8, 0, 31, 1), +SOC_DOUBLE("Speaker Playback Switch", RT5621_SPK_OUT_VOL, 15, 7, 1, 1), +SOC_DOUBLE("Headphone Playback Volume", RT5621_HP_OUT_VOL, 8, 0, 31, 1), +SOC_DOUBLE("Headphone Playback Switch", RT5621_HP_OUT_VOL,15, 7, 1, 1), +SOC_DOUBLE("AUX Playback Volume", RT5621_MONO_AUX_OUT_VOL, 8, 0, 31, 1), +SOC_DOUBLE("AUX Playback Switch", RT5621_MONO_AUX_OUT_VOL, 15, 7, 1, 1), +SOC_DOUBLE("PCM Playback Volume", RT5621_STEREO_DAC_VOL, 8, 0, 31, 1), +SOC_DOUBLE("Line In Volume", RT5621_LINE_IN_VOL, 8, 0, 31, 1), +SOC_SINGLE("Mic 1 Volume", RT5621_MIC_VOL, 8, 31, 1), +SOC_SINGLE("Mic 2 Volume", RT5621_MIC_VOL, 0, 31, 1), +SOC_ENUM("Mic 1 Boost", rt5621_enum[5]), +SOC_ENUM("Mic 2 Boost", rt5621_enum[6]), +SOC_ENUM_EXT("Speaker Amp Type", rt5621_enum[7], snd_soc_get_enum_double, rt5621_amp_sel_put), +SOC_DOUBLE("AUX In Volume", RT5621_AUXIN_VOL, 8, 0, 31, 1), +SOC_DOUBLE("Capture Volume", RT5621_ADC_REC_GAIN, 7, 0, 31, 0), +}; + + + +/* add non dapm controls */ +static int rt5621_add_controls(struct snd_soc_codec *codec) +{ + int err, i; + + for (i = 0; i < ARRAY_SIZE(rt5621_snd_controls); i++) { + err = snd_ctl_add(codec->card, + snd_soc_cnew(&rt5621_snd_controls[i],codec, NULL)); + if (err < 0) + return err; + } + return 0; +} + +void hp_depop_mode2(struct snd_soc_codec *codec) +{ + rt5621_write_mask(codec, 0x3e, 0x8000, 0x8000); + rt5621_write_mask(codec, 0x04, 0x8080, 0x8080); + rt5621_write_mask(codec, 0x3a, 0x0100, 0x0100); + rt5621_write_mask(codec, 0x3c, 0x2000, 0x2000); + rt5621_write_mask(codec, 0x3e, 0x0600, 0x0600); + rt5621_write_mask(codec, 0x5e, 0x0200, 0x0200); + schedule_timeout_uninterruptible(msecs_to_jiffies(300)); +} + +void aux_depop_mode2(struct snd_soc_codec *codec) +{ + rt5621_write_mask(codec, 0x3e, 0x8000, 0x8000); + rt5621_write_mask(codec, 0x06, 0x8080, 0x8080); + rt5621_write_mask(codec, 0x3a, 0x0100, 0x0100); + rt5621_write_mask(codec, 0x3c, 0x2000, 0x2000); + rt5621_write_mask(codec, 0x3e, 0x6000, 0x6000); + rt5621_write_mask(codec, 0x5e, 0x0020, 0x0200); + schedule_timeout_uninterruptible(msecs_to_jiffies(300)); + rt5621_write_mask(codec, 0x3a, 0x0002, 0x0002); + rt5621_write_mask(codec, 0x3a, 0x0001, 0x0001); +} +#if USE_DAPM_CONTROL + +/* + * _DAPM_ Controls + */ + +/* We have to create a fake left and right HP mixers because + * the codec only has a single control that is shared by both channels. + * This makes it impossible to determine the audio path using the current + * register map, thus we add a new (virtual) register to help determine the + * audio route within the device. + */ + static int mixer_event (struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + + u16 l, r, lineIn,mic1,mic2, aux, pcm; + + l = rt5621_read(w->codec, HPL_MIXER); + r = rt5621_read(w->codec, HPR_MIXER); + lineIn = rt5621_read(w->codec, RT5621_LINE_IN_VOL); + mic2 = rt5621_read(w->codec, RT5621_MIC_ROUTING_CTRL); + aux = rt5621_read(w->codec,RT5621_AUXIN_VOL); + pcm = rt5621_read(w->codec, RT5621_STEREO_DAC_VOL); + + + if (event & SND_SOC_DAPM_PRE_REG) + return 0; + + if (l & 0x1 || r & 0x1) + rt5621_write(w->codec, RT5621_STEREO_DAC_VOL, pcm & 0x7fff); + else + rt5621_write(w->codec, RT5621_STEREO_DAC_VOL, pcm | 0x8000); + + if (l & 0x2 || r & 0x2) + rt5621_write(w->codec, RT5621_MIC_ROUTING_CTRL, mic2 & 0xff7f); + else + rt5621_write(w->codec, RT5621_MIC_ROUTING_CTRL, mic2 | 0x0080); + + mic1 = rt5621_read(w->codec, RT5621_MIC_ROUTING_CTRL); + if (l & 0x4 || r & 0x4) + rt5621_write(w->codec, RT5621_MIC_ROUTING_CTRL, mic1 & 0x7fff); + else + rt5621_write(w->codec, RT5621_MIC_ROUTING_CTRL, mic1 | 0x8000); + + if (l & 0x8 || r & 0x8) + rt5621_write(w->codec, RT5621_AUXIN_VOL, aux & 0x7fff); + else + rt5621_write(w->codec, RT5621_AUXIN_VOL, aux | 0x8000); + + if (l & 0x10 || r & 0x10) + rt5621_write(w->codec, RT5621_LINE_IN_VOL, lineIn & 0x7fff); + else + rt5621_write(w->codec, RT5621_LINE_IN_VOL, lineIn | 0x8000); + + return 0; +} + + +static int hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + unsigned int reg = rt5621_read(codec, MISC_FUNC_REG); + + if (((reg & 0x03) != 0x00) && ((reg & 0x03) != 0x03)) + return 0; + + switch (event) + { + case SND_SOC_DAPM_POST_PMU: + hp_depop_mode2(codec); + rt5621_write_mask(codec, 0x04, 0x0000, 0x8080); + rt5621_write_mask(codec, 0x3a, 0x0020, 0x0020); + break; + case SND_SOC_DAPM_POST_PMD: + rt5621_write_mask(codec, 0x04, 0x8080, 0x8080); + rt5621_write_mask(codec, 0x3a, 0x0000, 0x0010); + rt5621_write_mask(codec, 0x3a, 0x0000, 0x0020); + rt5621_write_mask(codec, 0x3e, 0x0000, 0x0600); + break; + } + return 0; +} + +static int aux_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + unsigned int reg = rt5621_read(codec, MISC_FUNC_REG); + + if (((reg & 0x0c) != 0x00) && ((reg & 0x0c) != 0x0c)) + return 0; + + switch (event) + { + case SND_SOC_DAPM_POST_PMU: + aux_depop_mode2(codec); + rt5621_write_mask(codec, 0x06, 0x0000, 0x8080); + break; + case SND_SOC_DAPM_POST_PMD: + rt5621_write_mask(codec, 0x06, 0x8080, 0x8080); + rt5621_write_mask(codec, 0x3a, 0x0000, 0x0001); + rt5621_write_mask(codec, 0x3a, 0x0000, 0x0002); + rt5621_write_mask(codec, 0x3e, 0x0000, 0x6000); + break; + } + return 0; +} + +/* Left Headphone Mixers */ +static const struct snd_kcontrol_new rt5621_hpl_mixer_controls[] = { +SOC_DAPM_SINGLE("LineIn Playback Switch", HPL_MIXER, 4, 1, 0), +SOC_DAPM_SINGLE("AUXIN Playback Switch", HPL_MIXER, 3, 1, 0), +SOC_DAPM_SINGLE("Mic1 Playback Switch", HPL_MIXER, 2, 1, 0), +SOC_DAPM_SINGLE("Mic2 Playback Switch", HPL_MIXER, 1, 1, 0), +SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 0, 1, 0), +SOC_DAPM_SINGLE("RecordL Playback Switch", RT5621_ADC_REC_GAIN, 15, 1,1), +}; + +/* Right Headphone Mixers */ +static const struct snd_kcontrol_new rt5621_hpr_mixer_controls[] = { +SOC_DAPM_SINGLE("LineIn Playback Switch", HPR_MIXER, 4, 1, 0), +SOC_DAPM_SINGLE("AUXIN Playback Switch", HPR_MIXER, 3, 1, 0), +SOC_DAPM_SINGLE("Mic1 Playback Switch", HPR_MIXER, 2, 1, 0), +SOC_DAPM_SINGLE("Mic2 Playback Switch", HPR_MIXER, 1, 1, 0), +SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 0, 1, 0), +SOC_DAPM_SINGLE("RecordR Playback Switch", RT5621_ADC_REC_GAIN, 14, 1,1), +}; + +//Left Record Mixer +static const struct snd_kcontrol_new rt5621_captureL_mixer_controls[] = { +SOC_DAPM_SINGLE("Mic1 Capture Switch", RT5621_ADC_REC_MIXER, 14, 1, 1), +SOC_DAPM_SINGLE("Mic2 Capture Switch", RT5621_ADC_REC_MIXER, 13, 1, 1), +SOC_DAPM_SINGLE("LineInL Capture Switch",RT5621_ADC_REC_MIXER,12, 1, 1), +SOC_DAPM_SINGLE("AUXIN Capture Switch", RT5621_ADC_REC_MIXER, 11, 1, 1), +SOC_DAPM_SINGLE("HPMixerL Capture Switch", RT5621_ADC_REC_MIXER,10, 1, 1), +SOC_DAPM_SINGLE("SPKMixer Capture Switch",RT5621_ADC_REC_MIXER,9, 1, 1), +SOC_DAPM_SINGLE("MonoMixer Capture Switch",RT5621_ADC_REC_MIXER,8, 1, 1), +}; + + +//Right Record Mixer +static const struct snd_kcontrol_new rt5621_captureR_mixer_controls[] = { +SOC_DAPM_SINGLE("Mic1 Capture Switch", RT5621_ADC_REC_MIXER, 6, 1, 1), +SOC_DAPM_SINGLE("Mic2 Capture Switch", RT5621_ADC_REC_MIXER, 5, 1, 1), +SOC_DAPM_SINGLE("LineInR Capture Switch",RT5621_ADC_REC_MIXER,4, 1, 1), +SOC_DAPM_SINGLE("AUXIN Capture Switch", RT5621_ADC_REC_MIXER, 3, 1, 1), +SOC_DAPM_SINGLE("HPMixerR Capture Switch", RT5621_ADC_REC_MIXER,2, 1, 1), +SOC_DAPM_SINGLE("SPKMixer Capture Switch",RT5621_ADC_REC_MIXER,1, 1, 1), +SOC_DAPM_SINGLE("MonoMixer Capture Switch",RT5621_ADC_REC_MIXER,0, 1, 1), +}; + +/* Speaker Mixer */ +static const struct snd_kcontrol_new rt5621_speaker_mixer_controls[] = { +SOC_DAPM_SINGLE("LineIn Playback Switch", RT5621_LINE_IN_VOL, 14, 1, 1), +SOC_DAPM_SINGLE("AUXIN Playback Switch", RT5621_AUXIN_VOL, 14, 1, 1), +SOC_DAPM_SINGLE("Mic1 Playback Switch", RT5621_MIC_ROUTING_CTRL, 14, 1, 1), +SOC_DAPM_SINGLE("Mic2 Playback Switch", RT5621_MIC_ROUTING_CTRL, 6, 1, 1), +SOC_DAPM_SINGLE("PCM Playback Switch", RT5621_STEREO_DAC_VOL, 14, 1, 1), +}; + + +/* Mono Mixer */ +static const struct snd_kcontrol_new rt5621_mono_mixer_controls[] = { +SOC_DAPM_SINGLE("LineIn Playback Switch", RT5621_LINE_IN_VOL, 13, 1, 1), +SOC_DAPM_SINGLE("Mic1 Playback Switch", RT5621_MIC_ROUTING_CTRL, 13, 1, 1), +SOC_DAPM_SINGLE("Mic2 Playback Switch", RT5621_MIC_ROUTING_CTRL, 5, 1, 1), +SOC_DAPM_SINGLE("AUXIN Playback Switch", RT5621_AUXIN_VOL, 13, 1, 1), +SOC_DAPM_SINGLE("PCM Playback Switch", RT5621_STEREO_DAC_VOL, 13, 1, 1), +SOC_DAPM_SINGLE("RecordL Playback Switch", RT5621_ADC_REC_GAIN, 13, 1,1), +SOC_DAPM_SINGLE("RecordR Playback Switch", RT5621_ADC_REC_GAIN, 12, 1,1), +}; + +/* mono output mux */ +static const struct snd_kcontrol_new rt5621_mono_mux_controls = +SOC_DAPM_ENUM("Route", rt5621_enum[4]); + +/* speaker left output mux */ +static const struct snd_kcontrol_new rt5621_hp_spk_mux_controls = +SOC_DAPM_ENUM("Route", rt5621_enum[1]); + + +/* headphone left output mux */ +static const struct snd_kcontrol_new rt5621_hpl_out_mux_controls = +SOC_DAPM_ENUM("Route", rt5621_enum[2]); + +/* headphone right output mux */ +static const struct snd_kcontrol_new rt5621_hpr_out_mux_controls = +SOC_DAPM_ENUM("Route", rt5621_enum[3]); + +static const struct snd_soc_dapm_widget rt5621_dapm_widgets[] = { +SND_SOC_DAPM_MUX("Mono Out Mux", SND_SOC_NOPM, 0, 0, + &rt5621_mono_mux_controls), +SND_SOC_DAPM_MUX("Speaker Out Mux", SND_SOC_NOPM, 0, 0, + &rt5621_hp_spk_mux_controls), +SND_SOC_DAPM_MUX("Left Headphone Out Mux", SND_SOC_NOPM, 0, 0, + &rt5621_hpl_out_mux_controls), +SND_SOC_DAPM_MUX("Right Headphone Out Mux", SND_SOC_NOPM, 0, 0, + &rt5621_hpr_out_mux_controls), + +SND_SOC_DAPM_MIXER_E("Left HP Mixer",RT5621_PWR_MANAG_ADD2, 5, 0, + &rt5621_hpl_mixer_controls[0], ARRAY_SIZE(rt5621_hpl_mixer_controls), + mixer_event, SND_SOC_DAPM_POST_REG), +SND_SOC_DAPM_MIXER_E("Right HP Mixer",RT5621_PWR_MANAG_ADD2, 4, 0, + &rt5621_hpr_mixer_controls[0], ARRAY_SIZE(rt5621_hpr_mixer_controls), + mixer_event, SND_SOC_DAPM_POST_REG), +SND_SOC_DAPM_MIXER("Mono Mixer", RT5621_PWR_MANAG_ADD2, 2, 0, + &rt5621_mono_mixer_controls[0], ARRAY_SIZE(rt5621_mono_mixer_controls)), + +SND_SOC_DAPM_MIXER("Speaker Mixer", RT5621_PWR_MANAG_ADD2,3,0, + &rt5621_speaker_mixer_controls[0], ARRAY_SIZE(rt5621_speaker_mixer_controls)), + +SND_SOC_DAPM_MIXER("Left Record Mixer", RT5621_PWR_MANAG_ADD2,1,0, + &rt5621_captureL_mixer_controls[0], ARRAY_SIZE(rt5621_captureL_mixer_controls)), +SND_SOC_DAPM_MIXER("Right Record Mixer", RT5621_PWR_MANAG_ADD2,0,0, + &rt5621_captureR_mixer_controls[0], ARRAY_SIZE(rt5621_captureR_mixer_controls)), + +SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", RT5621_PWR_MANAG_ADD2,9, 0), +SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", RT5621_PWR_MANAG_ADD2, 8, 0), + +SND_SOC_DAPM_MIXER("IIS Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_MIXER("Line Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("AUXIN Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", RT5621_PWR_MANAG_ADD2, 7, 0), +SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", RT5621_PWR_MANAG_ADD2, 6, 0), + +SND_SOC_DAPM_PGA_E("Left Headphone", MISC_FUNC_REG, 0, 0, NULL, 0, + hp_event, SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("Right Headphone", MISC_FUNC_REG, 1, 0, NULL, 0, + hp_event, SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_PGA("Speaker PGA", RT5621_PWR_MANAG_ADD3, 12, 0, NULL, 0), + +SND_SOC_DAPM_PGA_E("AUXL Out", MISC_FUNC_REG, 2, 0, NULL, 0, + aux_event, SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("AUXR Out", MISC_FUNC_REG, 3, 0, NULL, 0, + aux_event, SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_PGA("Left Line In", RT5621_PWR_MANAG_ADD3, 7, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right Line In", RT5621_PWR_MANAG_ADD3, 6, 0, NULL, 0), +SND_SOC_DAPM_PGA("Left AUX In", RT5621_PWR_MANAG_ADD3, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right AUX In", RT5621_PWR_MANAG_ADD3, 4, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Mic 1 PGA", RT5621_PWR_MANAG_ADD3, 3, 0, NULL, 0), +SND_SOC_DAPM_PGA("Mic 2 PGA", RT5621_PWR_MANAG_ADD3, 2, 0, NULL, 0), +SND_SOC_DAPM_PGA("Mic 1 Pre Amp", RT5621_PWR_MANAG_ADD3, 1, 0, NULL, 0), +SND_SOC_DAPM_PGA("Mic 2 Pre Amp", RT5621_PWR_MANAG_ADD3, 0, 0, NULL, 0), + +SND_SOC_DAPM_MICBIAS("Mic Bias1", RT5621_PWR_MANAG_ADD1, 11, 0), + +SND_SOC_DAPM_OUTPUT("AUXL"), +SND_SOC_DAPM_OUTPUT("AUXR"), +SND_SOC_DAPM_OUTPUT("HPL"), +SND_SOC_DAPM_OUTPUT("HPR"), +SND_SOC_DAPM_OUTPUT("SPK"), + +SND_SOC_DAPM_INPUT("LINEL"), +SND_SOC_DAPM_INPUT("LINER"), +SND_SOC_DAPM_INPUT("AUXINL"), +SND_SOC_DAPM_INPUT("AUXINR"), +SND_SOC_DAPM_INPUT("MIC1"), +SND_SOC_DAPM_INPUT("MIC2"), +SND_SOC_DAPM_VMID("VMID"), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* left HP mixer */ + {"Left HP Mixer" , "LineIn Playback Switch" , "Left Line In"}, + {"Left HP Mixer" , "AUXIN Playback Switch" , "Left AUX In"}, + {"Left HP Mixer" , "Mic1 Playback Switch" , "Mic 1 PGA"}, + {"Left HP Mixer" , "Mic2 Playback Switch" , "Mic 2 PGA"}, + {"Left HP Mixer" , "PCM Playback Switch" , "Left DAC"}, + {"Left HP Mixer" , "RecordL Playback Switch" , "Left Record Mixer"}, + + /* right HP mixer */ + {"Right HP Mixer" , "LineIn Playback Switch" , "Right Line In"}, + {"Right HP Mixer" , "AUXIN Playback Switch" , "Right AUX In"}, + {"Right HP Mixer" , "Mic1 Playback Switch" , "Mic 1 PGA"}, + {"Right HP Mixer" , "Mic2 Playback Switch" , "Mic 2 PGA"}, + {"Right HP Mixer" , "PCM Playback Switch" , "Right DAC"}, + {"Right HP Mixer" , "RecordR Playback Switch" , "Right Record Mixer"}, + + /* virtual mixer - mixes left & right channels for spk and mono */ + {"IIS Mixer" , NULL , "Left DAC"}, + {"IIS Mixer" , NULL , "Right DAC"}, + {"Line Mixer" , NULL , "Right Line In"}, + {"Line Mixer" , NULL , "Left Line In"}, + {"AUXIN Mixer" , NULL , "Left AUX In"}, + {"AUXIN Mixer" , NULL , "Right AUX In"}, + {"HP Mixer" , NULL , "Left HP Mixer"}, + {"HP Mixer" , NULL , "Right HP Mixer"}, + + /* speaker mixer */ + {"Speaker Mixer" , "LineIn Playback Switch" , "Line Mixer"}, + {"Speaker Mixer" , "AUXIN Playback Switch" , "AUXIN Mixer"}, + {"Speaker Mixer" , "Mic1 Playback Switch" , "Mic 1 PGA"}, + {"Speaker Mixer" , "Mic2 Playback Switch" , "Mic 2 PGA"}, + {"Speaker Mixer" , "PCM Playback Switch" , "IIS Mixer"}, + + + /* mono mixer */ + {"Mono Mixer" , "LineIn Playback Switch" , "Line Mixer"}, + {"Mono Mixer" , "Mic1 Playback Switch" , "Mic 1 PGA"}, + {"Mono Mixer" , "Mic2 Playback Switch" , "Mic 2 PGA"}, + {"Mono Mixer" , "PCM Playback Switch" , "IIS Mixer"}, + {"Mono Mixer" , "AUXIN Playback Switch" , "AUXIN Mixer"}, + {"Mono Mixer" , "RecordL Playback Switch" , "Left Record Mixer"}, + {"Mono Mixer" , "RecordR Playback Switch" , "Right Record Mixer"}, + + /*Left record mixer */ + {"Left Record Mixer" , "Mic1 Capture Switch" , "Mic 1 Pre Amp"}, + {"Left Record Mixer" , "Mic2 Capture Switch" , "Mic 2 Pre Amp"}, + {"Left Record Mixer" , "LineInL Capture Switch" , "LINEL"}, + {"Left Record Mixer" , "AUXIN Capture Switch" , "AUXINL"}, + {"Left Record Mixer" , "HPMixerL Capture Switch" , "Left HP Mixer"}, + {"Left Record Mixer" , "SPKMixer Capture Switch" , "Speaker Mixer"}, + {"Left Record Mixer" , "MonoMixer Capture Switch" , "Mono Mixer"}, + + /*Right record mixer */ + {"Right Record Mixer" , "Mic1 Capture Switch" , "Mic 1 Pre Amp"}, + {"Right Record Mixer" , "Mic2 Capture Switch" , "Mic 2 Pre Amp"}, + {"Right Record Mixer" , "LineInR Capture Switch" , "LINER"}, + {"Right Record Mixer" , "AUXIN Capture Switch" , "AUXINR"}, + {"Right Record Mixer" , "HPMixerR Capture Switch" , "Right HP Mixer"}, + {"Right Record Mixer" , "SPKMixer Capture Switch" , "Speaker Mixer"}, + {"Right Record Mixer" , "MonoMixer Capture Switch" , "Mono Mixer"}, + + /* headphone left mux */ + {"Left Headphone Out Mux" , "HPL mixer" , "Left HP Mixer"}, + + /* headphone right mux */ + {"Right Headphone Out Mux", "HPR mixer", "Right HP Mixer"}, + + /* speaker mux */ + {"Speaker Out Mux", "HP mixer", "HP Mixer"}, + {"Speaker Out Mux", "SPK mixer", "Speaker Mixer"}, + {"Speaker Out Mux", "Mono Mixer", "Mono Mixer"}, + + /* mono mux */ + {"Mono Out Mux", "HP mixer", "HP Mixer"}, + {"Mono Out Mux", "SPK mixer", "Speaker Mixer"}, + {"Mono Out Mux", "Mono Mixer", "Mono Mixer"}, + + /* output pga */ + {"HPL", NULL, "Left Headphone"}, + {"Left Headphone", NULL, "Left Headphone Out Mux"}, + + {"HPR", NULL, "Right Headphone"}, + {"Right Headphone", NULL, "Right Headphone Out Mux"}, + + {"SPK", NULL, "Speaker PGA"}, + {"Speaker PGA", NULL, "Speaker Out Mux"}, + + {"AUXL", NULL, "AUXL Out"}, + {"AUXL Out", NULL, "Mono Out Mux"}, + + {"AUXR", NULL, "AUXR Out"}, + {"AUXR Out", NULL, "Mono Out Mux"}, + + /* input pga */ + {"Left Line In", NULL, "LINEL"}, + {"Right Line In", NULL, "LINER"}, + + {"Left AUX In", NULL, "AUXINL"}, + {"Right AUX In", NULL, "AUXINR"}, + + {"Mic 1 Pre Amp", NULL, "MIC1"}, + {"Mic 2 Pre Amp", NULL, "MIC2"}, + {"Mic 1 PGA", NULL, "Mic 1 Pre Amp"}, + {"Mic 2 PGA", NULL, "Mic 2 Pre Amp"}, + + /* left ADC */ + {"Left ADC", NULL, "Left Record Mixer"}, + + /* right ADC */ + {"Right ADC", NULL, "Right Record Mixer"}, + +}; + +static int rt5621_add_widgets(struct snd_soc_codec *codec) +{ + snd_soc_dapm_new_controls(codec, rt5621_dapm_widgets, + ARRAY_SIZE(rt5621_dapm_widgets)); + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_new_widgets(codec); + + return 0; +} + +#else + +static int rt5621_pcm_hw_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *codec_dai) +{ + + struct snd_soc_codec *codec = codec_dai->codec; + int stream = substream->stream; + + switch (stream) + { + case SNDRV_PCM_STREAM_PLAYBACK: + + rt5621_ChangeCodecPowerStatus(codec,POWER_STATE_D1_PLAYBACK); //power on dac to hp and speaker out + + rt5621_AudioOutEnable(codec,RT_WAVOUT_SPK,0); //unmute speaker out + + rt5621_AudioOutEnable(codec,RT_WAVOUT_HP,0); //unmute hp out + + break; + case SNDRV_PCM_STREAM_CAPTURE: + + rt5621_ChangeCodecPowerStatus(codec,POWER_STATE_D1_RECORD); //power on input to adc + + Enable_ADC_Input_Source(codec,RT_WAVIN_L_MIC1|RT_WAVIN_R_MIC1,1); //enable record source from mic1 + + break; + } + + return 0; +} + +#endif +/* PLL divisors */ +struct _pll_div { + u32 pll_in; + u32 pll_out; + u16 regvalue; +}; + +static const struct _pll_div codec_pll_div[] = { + + { 2048000, 8192000, 0x0ea0}, + { 3686400, 8192000, 0x4e27}, + { 12000000, 8192000, 0x456b}, + { 13000000, 8192000, 0x495f}, + { 13100000, 8192000, 0x0320}, + { 2048000, 11289600, 0xf637}, + { 3686400, 11289600, 0x2f22}, + { 12000000, 11289600, 0x3e2f}, + { 13000000, 11289600, 0x4d5b}, + { 13100000, 11289600, 0x363b}, + { 2048000, 16384000, 0x1ea0}, + { 3686400, 16384000, 0x9e27}, + { 12000000, 16384000, 0x452b}, + { 13000000, 16384000, 0x542f}, + { 13100000, 16384000, 0x03a0}, + { 2048000, 16934400, 0xe625}, + { 3686400, 16934400, 0x9126}, + { 12000000, 16934400, 0x4d2c}, + { 13000000, 16934400, 0x742f}, + { 13100000, 16934400, 0x3c27}, + { 2048000, 22579200, 0x2aa0}, + { 3686400, 22579200, 0x2f20}, + { 12000000, 22579200, 0x7e2f}, + { 13000000, 22579200, 0x742f}, + { 13100000, 22579200, 0x3c27}, + { 2048000, 24576000, 0x2ea0}, + { 3686400, 24576000, 0xee27}, + { 12000000, 24576000, 0x2915}, + { 13000000, 24576000, 0x772e}, + { 13100000, 24576000, 0x0d20}, +}; + +static const struct _pll_div codec_bclk_pll_div[] = { + + { 1536000, 24576000, 0x3ea0}, + { 3072000, 24576000, 0x1ea0}, + { 512000, 24576000, 0x8e90}, + { 256000, 24576000, 0xbe80}, + { 2822400, 11289600, 0x1ee0}, //flove040711 + { 3072000, 12288000, 0x1ee0}, //flove040711 +}; + + +static int rt5621_set_dai_pll(struct snd_soc_dai *codec_dai, + int pll_id, unsigned int freq_in, unsigned int freq_out) +{ + int i; + int ret = -EINVAL; + struct snd_soc_codec *codec = codec_dai->codec; + + if (pll_id < RT5621_PLL_FR_MCLK || pll_id > RT5621_PLL_FR_BCLK) + return -EINVAL; + + //rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD2, 0x0000,0x1000); //disable PLL power + + if (!freq_in || !freq_out) { + + return 0; + } + + if (RT5621_PLL_FR_MCLK == pll_id) { + for (i = 0; i < ARRAY_SIZE(codec_pll_div); i++) { + + if (codec_pll_div[i].pll_in == freq_in && codec_pll_div[i].pll_out == freq_out) + { + rt5621_write_mask(codec, RT5621_GLOBAL_CLK_CTRL_REG, 0x0000, 0x4000); + rt5621_write(codec,RT5621_PLL_CTRL,codec_pll_div[i].regvalue);//set PLL parameter + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD2, 0x1000,0x1000); //enable PLL power + ret = 0; + } + } + } + else if (RT5621_PLL_FR_BCLK == pll_id) + { + for (i = 0; i < ARRAY_SIZE(codec_bclk_pll_div); i++) + { + if ((freq_in == codec_bclk_pll_div[i].pll_in) && (freq_out == codec_bclk_pll_div[i].pll_out)) + { + rt5621_write_mask(codec, RT5621_GLOBAL_CLK_CTRL_REG, 0x4000, 0x4000); + rt5621_write(codec,RT5621_PLL_CTRL,codec_bclk_pll_div[i].regvalue);//set PLL parameter + rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD2, 0x1000,0x1000); //enable PLL power + ret = 0; + } + } + } + + rt5621_write_mask(codec,RT5621_GLOBAL_CLK_CTRL_REG,0x8000,0x8000);//Codec sys-clock from PLL + return ret; +} + + +struct _coeff_div { + u32 mclk; + u32 rate; + u16 fs; + u16 regvalue; +}; + +/* codec hifi mclk (after PLL) clock divider coefficients */ +static const struct _coeff_div coeff_div[] = { + /* 8k */ + { 8192000, 8000, 256*4, 0x2a2d}, + {12288000, 8000, 384*4, 0x2c2f}, + + /* 11.025k */ + {11289600, 11025, 256*4, 0x2a2d}, + {16934400, 11025, 384*4, 0x2c2f}, + + /* 16k */ + {12288000, 16000, 384*2, 0x1c2f}, + {16384000, 16000, 256*4, 0x2a2d}, + {24576000, 16000, 384*4, 0x2c2f}, + + /* 22.05k */ + {11289600, 22050, 256*2, 0x1a2d}, + {16934400, 22050, 384*2, 0x1c2f}, + + /* 32k */ + {12288000, 32000, 384 , 0x0c2f}, + {16384000, 32000, 256*2, 0x1a2d}, + {24576000, 32000, 384*2, 0x1c2f}, + + /* 44.1k */ + {11289600, 44100, 256*1, 0x0a2d}, + {22579200, 44100, 256*2, 0x1a2d}, + {45158400, 44100, 256*4, 0x2a2d}, + + /* 48k */ + {12288000, 48000, 256*1, 0x0a2d}, + {24576000, 48000, 256*2, 0x1a2d}, + {49152000, 48000, 256*4, 0x2a2d}, + +}; + + + +static int get_coeff(int mclk, int rate) +{ + int i; + + printk("get_coeff mclk=%d,rate=%d\n",mclk,rate); + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) + return i; + } + return -EINVAL; +} + + + + +/* + * Clock after PLL and dividers + */ + /*in this driver, you have to set sysclk to be 24576000, + * but you don't need to give a clk to be 24576000, our + * internal pll will generate this clock! so it won't make + * you any difficult. + */ +static int rt5621_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 rt5621_priv *rt5621 = codec->private_data; + + switch (freq) { + case 24576000: + rt5621->sysclk = freq; + return 0; + } + return 0; +} + + +static int rt5621_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 iface = 0; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface = 0x0000; + break; + case SND_SOC_DAIFMT_CBS_CFS: + iface = 0x8000; + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x0000; + break; + case SND_SOC_DAIFMT_RIGHT_J: + iface |= 0x0001; + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0002; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= 0x4003; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + iface |= 0x0000; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x0100; + break; + default: + return -EINVAL; + } + + rt5621_write(codec,RT5621_AUDIO_INTERFACE,iface); + return 0; +} + + +static int rt5621_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev ->card->codec; + struct rt5621_priv *rt5621 = codec->private_data; + u16 iface=rt5621_read(codec,RT5621_AUDIO_INTERFACE)&0xfff3; + int coeff = get_coeff(rt5621->sysclk, params_rate(params)); + + printk("rt5621_pcm_hw_params\n"); + if (coeff < 0) + coeff = get_coeff(24576000, params_rate(params)); /*if not set sysclk, default to be 24.576MHz*/ + + /* bit size */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + iface |= 0x0000; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + iface |= 0x0004; + break; + case SNDRV_PCM_FORMAT_S24_LE: + iface |= 0x0008; + break; + case SNDRV_PCM_FORMAT_S32_LE: + iface |= 0x000c; + break; + } + + /* set iface & srate */ + rt5621_write(codec, RT5621_AUDIO_INTERFACE, iface); + + if (coeff >= 0) + rt5621_write(codec, RT5621_STEREO_AD_DA_CLK_CTRL, coeff_div[coeff].regvalue); +// else +// { +// printk(KERN_ERR "cant find matched sysclk and rate config\n"); +// return -EINVAL; + +// } + return 0; +} + +#if !USE_DAPM_CONTROL +static int rt5621_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + break; + case SND_SOC_BIAS_OFF: + + rt5621_write_mask(codec, 0x02, 0x8080, 0x8080); + rt5621_write_mask(codec, 0x04, 0x8080, 0x8080); + rt5621_write(codec, 0x3e, 0x0000); + rt5621_write(codec, 0x3c, 0x0000); + rt5621_write(codec, 0x3a, 0x0000); + break; + } + codec->bias_level = level; + return 0; +} +#else +static int rt5621_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + + break; + case SND_SOC_BIAS_OFF: + + rt5621_write_mask(codec, 0x02, 0x8080, 0x8080); + rt5621_write_mask(codec, 0x04, 0x8080, 0x8080); + rt5621_write(codec, 0x3e, 0x0000); + rt5621_write(codec, 0x3c, 0x0000); + rt5621_write(codec, 0x3a, 0x0000); + break; + } + codec->bias_level = level; + return 0; +} +#endif + + + + + +#if !USE_DAPM_CONTROL + +static void rt5621_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->card->codec; + int stream = substream->stream; + + switch (stream) + { + case SNDRV_PCM_STREAM_PLAYBACK: + + rt5621_AudioOutEnable(codec,RT_WAVOUT_SPK,1); //mute speaker out + + rt5621_AudioOutEnable(codec,RT_WAVOUT_HP,1); //mute hp out + + rt5621_ChangeCodecPowerStatus(codec,POWER_STATE_D2_PLAYBACK); //power off dac to hp and speaker out + + + + break; + case SNDRV_PCM_STREAM_CAPTURE: + + Enable_ADC_Input_Source(codec,RT_WAVIN_L_MIC1|RT_WAVIN_R_MIC1,0); //disable record source from mic1 + + rt5621_ChangeCodecPowerStatus(codec,POWER_STATE_D2_RECORD); + + + break; + } +} + +#endif + + +#define RT5621_HIFI_RATES SNDRV_PCM_RATE_8000_48000 + +#define RT5621_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +struct snd_soc_dai_ops rt5621_hifi_ops = { + .hw_params = rt5621_pcm_hw_params, + .set_fmt = rt5621_set_dai_fmt, + .set_sysclk = rt5621_set_dai_sysclk, + .set_pll = rt5621_set_dai_pll, +#if !USE_DAPM_CONTROL + .prepare = rt5621_pcm_hw_prepare, + .shutdown = rt5621_shutdown, +#endif + +}; + +struct snd_soc_dai rt5621_dai = { + + .name = "RT5621", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5621_HIFI_RATES, + .formats = RT5621_FORMATS,}, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5621_HIFI_RATES, + .formats = RT5621_FORMATS,}, + + .ops = &rt5621_hifi_ops, +}; + + +EXPORT_SYMBOL_GPL(rt5621_dai); + +static ssize_t rt5621_index_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct snd_soc_device *socdev = dev_get_drvdata(dev); + struct snd_soc_codec *codec = socdev ->card->codec; + int count = 0; + int value; + int i; + + count += sprintf(buf, "%s index register\n", codec->name); + + for (i = 0; i < 0x60; i++) { + count += sprintf(buf + count, "index-%2x ", i); + if (count >= PAGE_SIZE - 1) + break; + value = rt5621_read_index(codec, i); + count += snprintf(buf + count, PAGE_SIZE - count, "0x%4x", value); + + if (count >= PAGE_SIZE - 1) + break; + + count += snprintf(buf + count, PAGE_SIZE - count, "\n"); + if (count >= PAGE_SIZE - 1) + break; + } + + if (count >= PAGE_SIZE) + count = PAGE_SIZE - 1; + + return count; + +} + +static DEVICE_ATTR(index_reg, 0444, rt5621_index_reg_show, NULL); + +#if defined(CONFIG_SND_HWDEP) +#if REALTEK_HWDEP + +#define RT_CE_CODEC_HWDEP_NAME "rt56xx hwdep " + +static int rt56xx_hwdep_open(struct snd_hwdep *hw, struct file *file) +{ + printk("enter %s\n", __func__); + return 0; +} + +static int rt56xx_hwdep_release(struct snd_hwdep *hw, struct file *file) +{ + printk("enter %s\n", __func__); + return 0; +} + + +static int rt56xx_hwdep_ioctl_common(struct snd_hwdep *hw, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct rt56xx_cmd rt56xx; + struct rt56xx_cmd __user *_rt56xx = arg; + struct rt56xx_reg_state *buf; + struct rt56xx_reg_state *p; + struct snd_soc_codec *codec = hw->private_data; + + if (copy_from_user(&rt56xx, _rt56xx, sizeof(rt56xx))) + return -EFAULT; + buf = kmalloc(sizeof(*buf) * rt56xx.number, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + if (copy_from_user(buf, rt56xx.buf, sizeof(*buf) * rt56xx.number)) { + goto err; + } + switch (cmd) { + case RT_READ_CODEC_REG_IOCTL: + for (p = buf; p < buf + rt56xx.number; p++) + { + p->reg_value = codec->read(codec, p->reg_index); + } + if (copy_to_user(rt56xx.buf, buf, sizeof(*buf) * rt56xx.number)) + goto err; + + break; + case RT_WRITE_CODEC_REG_IOCTL: + for (p = buf; p < buf + rt56xx.number; p++) + codec->write(codec, p->reg_index, p->reg_value); + break; + } + + kfree(buf); + return 0; + +err: + kfree(buf); + return -EFAULT; + +} + +static int rt56xx_codec_dump_reg(struct snd_hwdep *hw, struct file *file, unsigned long arg) +{ + struct rt56xx_cmd rt56xx; + struct rt56xx_cmd __user *_rt56xx = arg; + struct rt56xx_reg_state *buf; + struct snd_soc_codec *codec = hw->private_data; + int number = codec->reg_cache_size; + int i; + + printk(KERN_DEBUG "enter %s, number = %d\n", __func__, number); + if (copy_from_user(&rt56xx, _rt56xx, sizeof(rt56xx))) + return -EFAULT; + + buf = kmalloc(sizeof(*buf) * number, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + for (i = 0; i < number; i++) + { + buf[i].reg_index = i << 1; + buf[i].reg_value = codec->read(codec, buf[i].reg_index); + } + if (copy_to_user(rt56xx.buf, buf, sizeof(*buf) * i)) + goto err; + rt56xx.number = number; + if (copy_to_user(_rt56xx, &rt56xx, sizeof(rt56xx))) + goto err; + kfree(buf); + return 0; +err: + kfree(buf); + return -EFAULT; + +} + +static int rt56xx_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigned int cmd, unsigned long arg) +{ + if (cmd == RT_READ_ALL_CODEC_REG_IOCTL) + { + return rt56xx_codec_dump_reg(hw, file, arg); + } + else + { + return rt56xx_hwdep_ioctl_common(hw, file, cmd, arg); + } +} + +static int realtek_ce_init_hwdep(struct snd_soc_codec *codec) +{ + struct snd_hwdep *hw; + struct snd_card *card = codec->card; + int err; + + if ((err = snd_hwdep_new(card, RT_CE_CODEC_HWDEP_NAME, 0, &hw)) < 0) + return err; + + strcpy(hw->name, RT_CE_CODEC_HWDEP_NAME); + hw->private_data = codec; + hw->ops.open = rt56xx_hwdep_open; + hw->ops.release = rt56xx_hwdep_release; + hw->ops.ioctl = rt56xx_hwdep_ioctl; + return 0; +} + +#endif +#endif + +static void rt5621_work(struct work_struct *work) +{ + struct snd_soc_codec *codec = + container_of(work, struct snd_soc_codec, delayed_work.work); + + rt5621_set_bias_level(codec, codec->bias_level); +} + + +static int rt5621_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev ->card->codec; + + /* we only need to suspend if we are a valid card */ + if(!codec->card) + return 0; + + rt5621_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; +} + +static int rt5621_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev ->card->codec; + int i; + u8 data[3]; + u16 *cache = codec->reg_cache; + + /* we only need to resume if we are a valid card */ + if(!codec->card) + return 0; + + /* Sync reg_cache with the hardware */ + + for (i = 0; i < ARRAY_SIZE(rt5621_reg); i++) { + if (i == RT5621_RESET) + continue; + data[0] =i << 1; + data[1] = (0xFF00 & cache[i]) >> 8; + data[2] = 0x00FF & cache[i]; + codec->hw_write(codec->control_data, data, 3); + } + + rt5621_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + /* charge rt5621 caps */ + + if (codec->suspend_bias_level == SND_SOC_BIAS_ON) { + rt5621_set_bias_level(codec, SND_SOC_BIAS_PREPARE); + codec->bias_level = SND_SOC_BIAS_ON; + schedule_delayed_work(&codec->delayed_work, + msecs_to_jiffies(caps_charge)); + } + return 0; +} + + +/* + * initialise the RT5621 driver + * register the mixer and dsp interfaces with the kernel + */ +static int rt5621_init(struct snd_soc_device *socdev) +{ + struct snd_soc_codec *codec = socdev ->card->codec; + int ret = 0; + + printk(KERN_INFO "alsa version is 1.0.21, codec driver version is 0.04\n"); + codec->name = "RT5621"; + codec->owner = THIS_MODULE; + codec->read = rt5621_read; + codec->write = rt5621_write; + codec->set_bias_level = rt5621_set_bias_level; + codec->dai = &rt5621_dai; + codec->num_dai = 1; + codec->reg_cache_step = 2; + codec->reg_cache_size = ARRAY_SIZE(rt5621_reg) * 2; + codec->reg_cache = kmemdup(rt5621_reg, sizeof(rt5621_reg), GFP_KERNEL); + + if (codec->reg_cache == NULL) + return -ENOMEM; + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + printk(KERN_ERR "rt5621: failed to create pcms\n"); + goto pcm_err; + } + + + rt5621_reset(codec); + rt5621_write(codec, RT5621_PWR_MANAG_ADD3, 0x8000);//enable Main bias + rt5621_write(codec, RT5621_PWR_MANAG_ADD2, 0x2000);//enable Vref + + hp_depop_mode2(codec); + + rt5621_init_reg(codec); + rt5621_set_bias_level(codec, SND_SOC_BIAS_PREPARE); + codec->bias_level = SND_SOC_BIAS_STANDBY; + schedule_delayed_work(&codec->delayed_work, + msecs_to_jiffies(caps_charge)); + + rt5621_add_controls(codec); + + #if USE_DAPM_CONTROL + + rt5621_add_widgets(codec); + + #endif + + #if defined(CONFIG_SND_HWDEP) + #if REALTEK_HWDEP + + realtek_ce_init_hwdep(codec); + + #endif + #endif + + ret = snd_soc_init_card(socdev); + + if (ret < 0) { + printk(KERN_ERR "rt5621: failed to register card\n"); + goto card_err; + } + return ret; + +card_err: + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +pcm_err: + kfree(codec->reg_cache); + return ret; +} + +static int rt5621_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + struct snd_soc_device *socdev = rt5621_socdev; + struct snd_soc_codec *codec = socdev ->card->codec; + int ret; + + i2c_set_clientdata(i2c, codec); + codec->control_data = i2c; + + ret = rt5621_init(socdev); + if (ret < 0) + pr_err("failed to initialise rt5621\n"); + + return ret; +} + + +static int rt5621_i2c_remove(struct i2c_client *client) +{ + struct snd_soc_codec *codec = i2c_get_clientdata(client); + kfree(codec->reg_cache); + return 0; +} + +static const struct i2c_device_id rt5621_i2c_id[] = { + {"ALC5621", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rt5621_i2c_id); +static struct i2c_driver rt5621_i2c_driver = { + .driver = { + .name = "RT5621 I2C Codec", + .owner = THIS_MODULE, + }, + .probe = rt5621_i2c_probe, + .remove = rt5621_i2c_remove, + .id_table = rt5621_i2c_id, +}; + +static int rt5621_add_i2c_device(struct platform_device *pdev, + const struct rt5621_setup_data *setup) +{ +#if 0 + struct i2c_board_info info; + struct i2c_adapter *adapter; + struct i2c_client *client; +#endif + int ret; + ret = i2c_add_driver(&rt5621_i2c_driver); + if (ret != 0) { + dev_err(&pdev->dev, "can't add i2c driver\n"); + return ret; + } +#if 0 + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = setup->i2c_address; + strlcpy(info.type, "rt5621", I2C_NAME_SIZE); + + adapter = i2c_get_adapter(setup->i2c_bus); + if (!adapter) { + dev_err(&pdev->dev, "can't get i2c adapter %d\n", + setup->i2c_bus); + goto err_driver; + } + + client = i2c_new_device(adapter, &info); + i2c_put_adapter(adapter); + if (!client) { + dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", + (unsigned int)info.addr); + goto err_driver; + } +#endif + return 0; + +#if 0 +err_driver: + i2c_del_driver(&rt5621_i2c_driver); + return -ENODEV; +#endif +} + + +static int rt5621_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct rt5621_setup_data *setup = socdev->codec_data; + struct snd_soc_codec *codec; + struct rt5621_priv *rt5621; + int ret; + + pr_info("RT5621 Audio Codec %s\n", RT5621_VERSION); + + codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (codec == NULL) + return -ENOMEM; + + rt5621 = kzalloc(sizeof(struct rt5621_priv), GFP_KERNEL); + if (rt5621 == NULL) { + kfree(codec); + return -ENOMEM; + } + codec->private_data = rt5621; + socdev ->card->codec = codec; + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + rt5621_socdev = socdev; + INIT_DELAYED_WORK(&codec->delayed_work, rt5621_work); + ret = device_create_file(&pdev->dev, &dev_attr_index_reg); + if (ret < 0) + printk(KERN_WARNING "asoc: failed to add index_reg sysfs files\n"); + + ret = -ENODEV; +// if (setup->i2c_address) { + codec->hw_write = (hw_write_t)i2c_master_send; + ret = rt5621_add_i2c_device(pdev, setup); +// } + if (ret != 0) { + kfree(codec->private_data); + kfree(codec); + } + return ret; +} + +static int run_delayed_work(struct delayed_work *dwork) +{ + int ret; + + /* cancel any work waiting to be queued. */ + ret = cancel_delayed_work(dwork); + + /* if there was any work waiting then we run it now and + * wait for it's completion */ + if (ret) { + schedule_delayed_work(dwork, 0); + flush_scheduled_work(); + } + return ret; +} + +static int rt5621_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev ->card->codec; + + if (codec->control_data) + rt5621_set_bias_level(codec, SND_SOC_BIAS_OFF); + run_delayed_work(&codec->delayed_work); + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); + device_remove_file(&pdev->dev, &dev_attr_index_reg); + i2c_unregister_device(codec->control_data); + i2c_del_driver(&rt5621_i2c_driver); + kfree(codec->private_data); + kfree(codec); + + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_rt5621 = { + .probe = rt5621_probe, + .remove = rt5621_remove, + .suspend = rt5621_suspend, + .resume = rt5621_resume, +}; + +EXPORT_SYMBOL_GPL(soc_codec_dev_rt5621); + +static int __init rt5621_modinit(void) +{ + return snd_soc_register_dai(&rt5621_dai); +} + +static void __exit rt5621_exit(void) +{ + snd_soc_unregister_dai(&rt5621_dai); +} + +module_init(rt5621_modinit); +module_exit(rt5621_exit); +MODULE_LICENSE("GPL"); + diff --git a/sound/soc/codecs/alc5621.h b/sound/soc/codecs/alc5621.h new file mode 100644 index 000000000000..bdcebbdbe114 --- /dev/null +++ b/sound/soc/codecs/alc5621.h @@ -0,0 +1,516 @@ +#ifndef _RT5621_H +#define _RT5621_H + + +#define RT5621_RESET 0X00 //RESET CODEC TO DEFAULT +#define RT5621_SPK_OUT_VOL 0X02 //SPEAKER OUT VOLUME +#define RT5621_HP_OUT_VOL 0X04 //HEADPHONE OUTPUT VOLUME +#define RT5621_MONO_AUX_OUT_VOL 0X06 //MONO OUTPUT/AUXOUT VOLUME +#define RT5621_AUXIN_VOL 0X08 //AUXIN VOLUME +#define RT5621_LINE_IN_VOL 0X0A //LINE IN VOLUME +#define RT5621_STEREO_DAC_VOL 0X0C //STEREO DAC VOLUME +#define RT5621_MIC_VOL 0X0E //MICROPHONE VOLUME +#define RT5621_MIC_ROUTING_CTRL 0X10 //MIC ROUTING CONTROL +#define RT5621_ADC_REC_GAIN 0X12 //ADC RECORD GAIN +#define RT5621_ADC_REC_MIXER 0X14 //ADC RECORD MIXER CONTROL +#define RT5621_SOFT_VOL_CTRL_TIME 0X16 //SOFT VOLUME CONTROL TIME +#define RT5621_OUTPUT_MIXER_CTRL 0X1C //OUTPUT MIXER CONTROL +#define RT5621_MIC_CTRL 0X22 //MICROPHONE CONTROL +#define RT5621_AUDIO_INTERFACE 0X34 //AUDIO INTERFACE +#define RT5621_STEREO_AD_DA_CLK_CTRL 0X36 //STEREO AD/DA CLOCK CONTROL +#define RT5621_COMPANDING_CTRL 0X38 //COMPANDING CONTROL +#define RT5621_PWR_MANAG_ADD1 0X3A //POWER MANAGMENT ADDITION 1 +#define RT5621_PWR_MANAG_ADD2 0X3C //POWER MANAGMENT ADDITION 2 +#define RT5621_PWR_MANAG_ADD3 0X3E //POWER MANAGMENT ADDITION 3 +#define RT5621_ADD_CTRL_REG 0X40 //ADDITIONAL CONTROL REGISTER +#define RT5621_GLOBAL_CLK_CTRL_REG 0X42 //GLOBAL CLOCK CONTROL REGISTER +#define RT5621_PLL_CTRL 0X44 //PLL CONTROL +#define RT5621_GPIO_OUTPUT_PIN_CTRL 0X4A //GPIO OUTPUT PIN CONTROL +#define RT5621_GPIO_PIN_CONFIG 0X4C //GPIO PIN CONFIGURATION +#define RT5621_GPIO_PIN_POLARITY 0X4E //GPIO PIN POLARITY/TYPE +#define RT5621_GPIO_PIN_STICKY 0X50 //GPIO PIN STICKY +#define RT5621_GPIO_PIN_WAKEUP 0X52 //GPIO PIN WAKE UP +#define RT5621_GPIO_PIN_STATUS 0X54 //GPIO PIN STATUS +#define RT5621_GPIO_PIN_SHARING 0X56 //GPIO PIN SHARING +#define RT5621_OVER_TEMP_CURR_STATUS 0X58 //OVER TEMPERATURE AND CURRENT STATUS +#define RT5621_JACK_DET_CTRL 0X5A //JACK DETECT CONTROL REGISTER +#define RT5621_MISC_CTRL 0X5E //MISC CONTROL +#define RT5621_PSEDUEO_SPATIAL_CTRL 0X60 //PSEDUEO STEREO & SPATIAL EFFECT BLOCK CONTROL +#define RT5621_EQ_CTRL 0X62 //EQ CONTROL +#define RT5621_EQ_MODE_ENABLE 0X66 //EQ MODE CHANGE ENABLE +#define RT5621_AVC_CTRL 0X68 //AVC CONTROL +#define RT5621_HID_CTRL_INDEX 0X6A //HIDDEN CONTROL INDEX PORT +#define RT5621_HID_CTRL_DATA 0X6C //HIDDEN CONTROL DATA PORT +#define RT5621_VENDOR_ID1 0x7C //VENDOR ID1 +#define RT5621_VENDOR_ID2 0x7E //VENDOR ID2 + + +//global definition +#define RT_L_MUTE (0x1<<15) //MUTE LEFT CONTROL BIT +#define RT_L_ZC (0x1<<14) //LEFT ZERO CROSS CONTROL BIT +#define RT_L_SM (0x1<<13) //LEFT SOFTMUTE CONTROL BIT +#define RT_R_MUTE (0x1<<7) //MUTE RIGHT CONTROL BIT +#define RT_R_ZC (0x1<<6) //RIGHT ZERO CROSS CONTROL BIT +#define RT_R_SM (0x1<<5) //RIGHT SOFTMUTE CONTROL BIT +#define RT_M_HP_MIXER (0x1<<15) //Mute source to HP Mixer +#define RT_M_SPK_MIXER (0x1<<14) //Mute source to Speaker Mixer +#define RT_M_MONO_MIXER (0x1<<13) //Mute source to Mono Mixer +#define SPK_CLASS_AB 0 +#define SPK_CLASS_D 1 + +//Mic Routing Control(0x10) +#define M_MIC1_TO_HP_MIXER (0x1<<15) //Mute MIC1 to HP mixer +#define M_MIC1_TO_SPK_MIXER (0x1<<14) //Mute MiC1 to SPK mixer +#define M_MIC1_TO_MONO_MIXER (0x1<<13) //Mute MIC1 to MONO mixer +#define MIC1_DIFF_INPUT_CTRL (0x1<<12) //MIC1 different input control +#define M_MIC2_TO_HP_MIXER (0x1<<7) //Mute MIC2 to HP mixer +#define M_MIC2_TO_SPK_MIXER (0x1<<6) //Mute MiC2 to SPK mixer +#define M_MIC2_TO_MONO_MIXER (0x1<<5) //Mute MIC2 to MONO mixer +#define MIC2_DIFF_INPUT_CTRL (0x1<<4) //MIC2 different input control + +//ADC Record Gain(0x12) +#define M_ADC_L_TO_HP_MIXER (0x1<<15) //Mute left of ADC to HP Mixer +#define M_ADC_R_TO_HP_MIXER (0x1<<14) //Mute right of ADC to HP Mixer +#define M_ADC_L_TO_MONO_MIXER (0x1<<13) //Mute left of ADC to MONO Mixer +#define M_ADC_R_TO_MONO_MIXER (0x1<<12) //Mute right of ADC to MONO Mixer +#define ADC_L_GAIN_MASK (0x1f<<7) //ADC Record Gain Left channel Mask +#define ADC_L_ZC_DET (0x1<<6) //ADC Zero-Cross Detector Control +#define ADC_R_ZC_DET (0x1<<5) //ADC Zero-Cross Detector Control +#define ADC_R_GAIN_MASK (0x1f<<0) //ADC Record Gain Right channel Mask + +//ADC Input Mixer Control(0x14) +#define M_MIC1_TO_ADC_L_MIXER (0x1<<14) //Mute mic1 to left channel of ADC mixer +#define M_MIC2_TO_ADC_L_MIXER (0x1<<13) //Mute mic2 to left channel of ADC mixer +#define M_LINEIN_L_TO_ADC_L_MIXER (0x1<<12) //Mute line In left channel to left channel of ADC mixer +#define M_AUXIN_L_TO_ADC_L_MIXER (0x1<<11) //Mute aux In left channel to left channel of ADC mixer +#define M_HPMIXER_L_TO_ADC_L_MIXER (0x1<<10) //Mute HP mixer left channel to left channel of ADC mixer +#define M_SPKMIXER_L_TO_ADC_L_MIXER (0x1<<9) //Mute SPK mixer left channel to left channel of ADC mixer +#define M_MONOMIXER_L_TO_ADC_L_MIXER (0x1<<8) //Mute MONO mixer left channel to left channel of ADC mixer +#define M_MIC1_TO_ADC_R_MIXER (0x1<<6) //Mute mic1 to right channel of ADC mixer +#define M_MIC2_TO_ADC_R_MIXER (0x1<<5) //Mute mic2 to right channel of ADC mixer +#define M_LINEIN_R_TO_ADC_R_MIXER (0x1<<4) //Mute lineIn right channel to right channel of ADC mixer +#define M_AUXIN_R_TO_ADC_R_MIXER (0x1<<3) //Mute aux In right channel to right channel of ADC mixer +#define M_HPMIXER_R_TO_ADC_R_MIXER (0x1<<2) //Mute HP mixer right channel to right channel of ADC mixer +#define M_SPKMIXER_R_TO_ADC_R_MIXER (0x1<<1) //Mute SPK mixer right channel to right channel of ADC mixer +#define M_MONOMIXER_R_TO_ADC_R_MIXER (0x1<<0) //Mute MONO mixer right channel to right channel of ADC mixer + +//Output Mixer Control(0x1C) +#define SPKOUT_N_SOUR_MASK (0x3<<14) +#define SPKOUT_N_SOUR_LN (0x2<<14) +#define SPKOUT_N_SOUR_RP (0x1<<14) +#define SPKOUT_N_SOUR_RN (0x0<<14) +#define SPK_OUTPUT_CLASS_AB (0x0<<13) +#define SPK_OUTPUT_CLASS_D (0x1<<13) +#define SPK_CLASS_AB_S_AMP (0x0<<12) +#define SPK_CALSS_AB_W_AMP (0x1<<12) +#define SPKOUT_INPUT_SEL_MASK (0x3<<10) +#define SPKOUT_INPUT_SEL_MONOMIXER (0x3<<10) +#define SPKOUT_INPUT_SEL_SPKMIXER (0x2<<10) +#define SPKOUT_INPUT_SEL_HPMIXER (0x1<<10) +#define SPKOUT_INPUT_SEL_VMID (0x0<<10) +#define HPL_INPUT_SEL_HPLMIXER (0x1<<9) +#define HPR_INPUT_SEL_HPRMIXER (0x1<<8) +#define MONO_AUX_INPUT_SEL_MASK (0x3<<6) +#define MONO_AUX_INPUT_SEL_MONO (0x3<<6) +#define MONO_AUX_INPUT_SEL_SPK (0x2<<6) +#define MONO_AUX_INPUT_SEL_HP (0x1<<6) +#define MONO_AUX_INPUT_SEL_VMID (0x0<<6) + +//Micphone Control define(0x22) +#define MIC1 1 +#define MIC2 2 +#define MIC_BIAS_90_PRECNET_AVDD 1 +#define MIC_BIAS_75_PRECNET_AVDD 2 + +#define MIC1_BOOST_CTRL_MASK (0x3<<10) +#define MIC1_BOOST_CTRL_BYPASS (0x0<<10) +#define MIC1_BOOST_CTRL_20DB (0x1<<10) +#define MIC1_BOOST_CTRL_30DB (0x2<<10) +#define MIC1_BOOST_CTRL_40DB (0x3<<10) + +#define MIC2_BOOST_CTRL_MASK (0x3<<8) +#define MIC2_BOOST_CTRL_BYPASS (0x0<<8) +#define MIC2_BOOST_CTRL_20DB (0x1<<8) +#define MIC2_BOOST_CTRL_30DB (0x2<<8) +#define MIC2_BOOST_CTRL_40DB (0x3<<8) + +#define MICBIAS_VOLT_CTRL_MASK (0x1<<5) +#define MICBIAS_VOLT_CTRL_90P (0x0<<5) +#define MICBIAS_VOLT_CTRL_75P (0x1<<5) + +#define MICBIAS_SHORT_CURR_DET_MASK (0x3) +#define MICBIAS_SHORT_CURR_DET_600UA (0x0) +#define MICBIAS_SHORT_CURR_DET_1200UA (0x1) +#define MICBIAS_SHORT_CURR_DET_1800UA (0x2) + +//Audio Interface(0x34) +#define SDP_MASTER_MODE (0x0<<15) //Main I2S interface select Master mode +#define SDP_SLAVE_MODE (0x1<<15) //Main I2S interface select Slave mode +#define I2S_PCM_MODE (0x1<<14) //PCM 0:mode A ,1:mode B +#define MAIN_I2S_BCLK_POL_CTRL (0x1<<7) //0:Normal 1:Invert +#define ADC_DATA_L_R_SWAP (0x1<<5) //0:ADC data appear at left phase of LRCK + //1:ADC data appear at right phase of LRCK +#define DAC_DATA_L_R_SWAP (0x1<<4) //0:DAC data appear at left phase of LRCK + //1:DAC data appear at right phase of LRCK +//Data Length Slection +#define I2S_DL_MASK (0x3<<2) //main i2s Data Length mask +#define I2S_DL_16 (0x0<<2) //16 bits +#define I2S_DL_20 (0x1<<2) //20 bits +#define I2S_DL_24 (0x2<<2) //24 bits +#define I2S_DL_32 (0x3<<2) //32 bits + +//PCM Data Format Selection +#define I2S_DF_MASK (0x3) //main i2s Data Format mask +#define I2S_DF_I2S (0x0) //I2S FORMAT +#define I2S_DF_RIGHT (0x1) //RIGHT JUSTIFIED format +#define I2S_DF_LEFT (0x2) //LEFT JUSTIFIED format +#define I2S_DF_PCM (0x3) //PCM format + +//Stereo AD/DA Clock Control(0x36h) +#define I2S_PRE_DIV_MASK (0x7<<12) +#define I2S_PRE_DIV_1 (0x0<<12) //DIV 1 +#define I2S_PRE_DIV_2 (0x1<<12) //DIV 2 +#define I2S_PRE_DIV_4 (0x2<<12) //DIV 4 +#define I2S_PRE_DIV_8 (0x3<<12) //DIV 8 +#define I2S_PRE_DIV_16 (0x4<<12) //DIV 16 +#define I2S_PRE_DIV_32 (0x5<<12) //DIV 32 + +#define I2S_SCLK_DIV_MASK (0x7<<9) +#define I2S_SCLK_DIV_1 (0x0<<9) //DIV 1 +#define I2S_SCLK_DIV_2 (0x1<<9) //DIV 2 +#define I2S_SCLK_DIV_3 (0x2<<9) //DIV 3 +#define I2S_SCLK_DIV_4 (0x3<<9) //DIV 4 +#define I2S_SCLK_DIV_6 (0x4<<9) //DIV 6 +#define I2S_SCLK_DIV_8 (0x5<<9) //DIV 8 +#define I2S_SCLK_DIV_12 (0x6<<9) //DIV 12 +#define I2S_SCLK_DIV_16 (0x7<<9) //DIV 16 + +#define I2S_WCLK_DIV_PRE_MASK (0xF<<5) +#define I2S_WCLK_PRE_DIV_1 (0x0<<5) //DIV 1 +#define I2S_WCLK_PRE_DIV_2 (0x1<<5) //DIV 2 +#define I2S_WCLK_PRE_DIV_3 (0x2<<5) //DIV 3 +#define I2S_WCLK_PRE_DIV_4 (0x3<<5) //DIV 4 +#define I2S_WCLK_PRE_DIV_5 (0x4<<5) //DIV 5 +#define I2S_WCLK_PRE_DIV_6 (0x5<<5) //DIV 6 +#define I2S_WCLK_PRE_DIV_7 (0x6<<5) //DIV 7 +#define I2S_WCLK_PRE_DIV_8 (0x7<<5) //DIV 8 +//........................ + +#define I2S_WCLK_DIV_MASK (0x7<<2) +#define I2S_WCLK_DIV_2 (0x0<<2) //DIV 2 +#define I2S_WCLK_DIV_4 (0x1<<2) //DIV 4 +#define I2S_WCLK_DIV_8 (0x2<<2) //DIV 8 +#define I2S_WCLK_DIV_16 (0x3<<2) //DIV 16 +#define I2S_WCLK_DIV_32 (0x4<<2) //DIV 32 + +#define ADDA_FILTER_CLK_SEL_256FS (0<<1) //256FS +#define ADDA_FILTER_CLK_SEL_384FS (1<<1) //384FS + +#define ADDA_OSR_SEL_64FS (0) //64FS +#define ADDA_OSR_SEL_128FS (1) //128FS + +//Power managment addition 1 (0x3A),0:Disable,1:Enable +#define PWR_MAIN_I2S_EN (0x1<<15) +#define PWR_ZC_DET_PD_EN (0x1<<14) +#define PWR_MIC1_BIAS_EN (0x1<<11) +#define PWR_SHORT_CURR_DET_EN (0x1<<10) +#define PWR_SOFTGEN_EN (0x1<<8) +#define PWR_DEPOP_BUF_HP (0x1<<6) +#define PWR_HP_OUT_AMP (0x1<<5) +#define PWR_HP_OUT_ENH_AMP (0x1<<4) +#define PWR_DEPOP_BUF_AUX (0x1<<2) +#define PWR_AUX_OUT_AMP (0x1<<1) +#define PWR_AUX_OUT_ENH_AMP (0x1) + + +//Power managment addition 2(0x3C),0:Disable,1:Enable +#define PWR_CLASS_AB (0x1<<15) +#define PWR_CLASS_D (0x1<<14) +#define PWR_VREF (0x1<<13) +#define PWR_PLL (0x1<<12) +#define PWR_DAC_REF_CIR (0x1<<10) +#define PWR_L_DAC_CLK (0x1<<9) +#define PWR_R_DAC_CLK (0x1<<8) +#define PWR_L_ADC_CLK_GAIN (0x1<<7) +#define PWR_R_ADC_CLK_GAIN (0x1<<6) +#define PWR_L_HP_MIXER (0x1<<5) +#define PWR_R_HP_MIXER (0x1<<4) +#define PWR_SPK_MIXER (0x1<<3) +#define PWR_MONO_MIXER (0x1<<2) +#define PWR_L_ADC_REC_MIXER (0x1<<1) +#define PWR_R_ADC_REC_MIXER (0x1) + +//Power managment addition 3(0x3E),0:Disable,1:Enable +#define PWR_MAIN_BIAS (0x1<<15) +#define PWR_AUXOUT_L_VOL_AMP (0x1<<14) +#define PWR_AUXOUT_R_VOL_AMP (0x1<<13) +#define PWR_SPK_OUT (0x1<<12) +#define PWR_HP_L_OUT_VOL (0x1<<10) +#define PWR_HP_R_OUT_VOL (0x1<<9) +#define PWR_LINEIN_L_VOL (0x1<<7) +#define PWR_LINEIN_R_VOL (0x1<<6) +#define PWR_AUXIN_L_VOL (0x1<<5) +#define PWR_AUXIN_R_VOL (0x1<<4) +#define PWR_MIC1_FUN_CTRL (0x1<<3) +#define PWR_MIC2_FUN_CTRL (0x1<<2) +#define PWR_MIC1_BOOST_MIXER (0x1<<1) +#define PWR_MIC2_BOOST_MIXER (0x1) + + +//Additional Control Register(0x40) +#define AUXOUT_SEL_DIFF (0x1<<15) //Differential Mode +#define AUXOUT_SEL_SE (0x1<<15) //Single-End Mode + +#define SPK_AB_AMP_CTRL_MASK (0x7<<12) +#define SPK_AB_AMP_CTRL_RATIO_225 (0x0<<12) //2.25 Vdd +#define SPK_AB_AMP_CTRL_RATIO_200 (0x1<<12) //2.00 Vdd +#define SPK_AB_AMP_CTRL_RATIO_175 (0x2<<12) //1.75 Vdd +#define SPK_AB_AMP_CTRL_RATIO_150 (0x3<<12) //1.50 Vdd +#define SPK_AB_AMP_CTRL_RATIO_125 (0x4<<12) //1.25 Vdd +#define SPK_AB_AMP_CTRL_RATIO_100 (0x5<<12) //1.00 Vdd + +#define SPK_D_AMP_CTRL_MASK (0x3<<10) +#define SPK_D_AMP_CTRL_RATIO_175 (0x0<<10) //1.75 Vdd +#define SPK_D_AMP_CTRL_RATIO_150 (0x1<<10) //1.50 Vdd +#define SPK_D_AMP_CTRL_RATIO_125 (0x2<<10) //1.25 Vdd +#define SPK_D_AMP_CTRL_RATIO_100 (0x3<<10) //1.00 Vdd + +#define STEREO_DAC_HI_PASS_FILTER_EN (0x1<<9) //Stereo DAC high pass filter enable +#define STEREO_ADC_HI_PASS_FILTER_EN (0x1<<8) //Stereo ADC high pass filter enable + +#define DIG_VOL_BOOST_MASK (0x3<<4) //Digital volume Boost mask +#define DIG_VOL_BOOST_0DB (0x0<<4) //Digital volume Boost 0DB +#define DIG_VOL_BOOST_6DB (0x1<<4) //Digital volume Boost 6DB +#define DIG_VOL_BOOST_12DB (0x2<<4) //Digital volume Boost 12DB +#define DIG_VOL_BOOST_18DB (0x3<<4) //Digital volume Boost 18DB + + +//Global Clock Control Register(0x42) +#define SYSCLK_SOUR_SEL_MASK (0x1<<15) +#define SYSCLK_SOUR_SEL_MCLK (0x0<<15) //system Clock source from MCLK +#define SYSCLK_SOUR_SEL_PLL (0x1<<15) //system Clock source from PLL +#define PLLCLK_SOUR_SEL_MCLK (0x0<<14) //PLL clock source from MCLK +#define PLLCLK_SOUR_SEL_BITCLK (0x1<<14) //PLL clock source from BITCLK + +#define PLLCLK_DIV_RATIO_MASK (0x3<<1) +#define PLLCLK_DIV_RATIO_DIV1 (0x0<<1) //DIV 1 +#define PLLCLK_DIV_RATIO_DIV2 (0x1<<1) //DIV 2 +#define PLLCLK_DIV_RATIO_DIV4 (0x2<<1) //DIV 4 +#define PLLCLK_DIV_RATIO_DIV8 (0x3<<1) //DIV 8 + +#define PLLCLK_PRE_DIV1 (0x0) //DIV 1 +#define PLLCLK_PRE_DIV2 (0x1) //DIV 2 + +//PLL Control(0x44) + +#define PLL_CTRL_M_VAL(m) ((m)&0xf) +#define PLL_CTRL_K_VAL(k) (((k)&0x7)<<4) +#define PLL_CTRL_N_VAL(n) (((n)&0xff)<<8) + +//GPIO Pin Configuration(0x4C) +#define GPIO_PIN_MASK (0x1<<1) +#define GPIO_PIN_SET_INPUT (0x1<<1) +#define GPIO_PIN_SET_OUTPUT (0x0<<1) + +//Pin Sharing(0x56) +#define LINEIN_L_PIN_SHARING (0x1<<15) +#define LINEIN_L_PIN_AS_LINEIN_L (0x0<<15) +#define LINEIN_L_PIN_AS_JD1 (0x1<<15) + +#define LINEIN_R_PIN_SHARING (0x1<<14) +#define LINEIN_R_PIN_AS_LINEIN_R (0x0<<14) +#define LINEIN_R_PIN_AS_JD2 (0x1<<14) + +#define GPIO_PIN_SHARING (0x3) +#define GPIO_PIN_AS_GPIO (0x0) +#define GPIO_PIN_AS_IRQOUT (0x1) +#define GPIO_PIN_AS_PLLOUT (0x3) + +//Jack Detect Control Register(0x5A) +#define JACK_DETECT_MASK (0x3<<14) +#define JACK_DETECT_USE_JD2 (0x3<<14) +#define JACK_DETECT_USE_JD1 (0x2<<14) +#define JACK_DETECT_USE_GPIO (0x1<<14) +#define JACK_DETECT_OFF (0x0<<14) + +#define SPK_EN_IN_HI (0x1<<11) +#define AUX_R_EN_IN_HI (0x1<<10) +#define AUX_L_EN_IN_HI (0x1<<9) +#define HP_EN_IN_HI (0x1<<8) +#define SPK_EN_IN_LO (0x1<<7) +#define AUX_R_EN_IN_LO (0x1<<6) +#define AUX_L_EN_IN_LO (0x1<<5) +#define HP_EN_IN_LO (0x1<<4) + +////MISC CONTROL(0x5E) +#define DISABLE_FAST_VREG (0x1<<15) +#define SPK_CLASS_AB_OC_PD (0x1<<13) +#define SPK_CLASS_AB_OC_DET (0x1<<12) +#define HP_DEPOP_MODE3_EN (0x1<<10) +#define HP_DEPOP_MODE2_EN (0x1<<9) +#define HP_DEPOP_MODE1_EN (0x1<<8) +#define AUXOUT_DEPOP_MODE3_EN (0x1<<6) +#define AUXOUT_DEPOP_MODE2_EN (0x1<<5) +#define AUXOUT_DEPOP_MODE1_EN (0x1<<4) +#define M_DAC_L_INPUT (0x1<<3) +#define M_DAC_R_INPUT (0x1<<2) +#define IRQOUT_INV_CTRL (0x1<<0) + +//Psedueo Stereo & Spatial Effect Block Control(0x60) +#define SPATIAL_CTRL_EN (0x1<<15) +#define ALL_PASS_FILTER_EN (0x1<<14) +#define PSEUDO_STEREO_EN (0x1<<13) +#define STEREO_EXPENSION_EN (0x1<<12) + +#define GAIN_3D_PARA_L_MASK (0x7<<9) +#define GAIN_3D_PARA_L_1_00 (0x0<<9) +#define GAIN_3D_PARA_L_1_25 (0x1<<9) +#define GAIN_3D_PARA_L_1_50 (0x2<<9) +#define GAIN_3D_PARA_L_1_75 (0x3<<9) +#define GAIN_3D_PARA_L_2_00 (0x4<<9) + +#define GAIN_3D_PARA_R_MASK (0x7<<6) +#define GAIN_3D_PARA_R_1_00 (0x0<<6) +#define GAIN_3D_PARA_R_1_25 (0x1<<6) +#define GAIN_3D_PARA_R_1_50 (0x2<<6) +#define GAIN_3D_PARA_R_1_75 (0x3<<6) +#define GAIN_3D_PARA_R_2_00 (0x4<<6) + +#define RATIO_3D_L_MASK (0x3<<4) +#define RATIO_3D_L_0_0 (0x0<<4) +#define RATIO_3D_L_0_66 (0x1<<4) +#define RATIO_3D_L_1_0 (0x2<<4) + +#define RATIO_3D_R_MASK (0x3<<2) +#define RATIO_3D_R_0_0 (0x0<<2) +#define RATIO_3D_R_0_66 (0x1<<2) +#define RATIO_3D_R_1_0 (0x2<<2) + +#define APF_MASK (0x3) +#define APF_FOR_48K (0x3) +#define APF_FOR_44_1K (0x2) +#define APF_FOR_32K (0x1) + +//EQ CONTROL(0x62) + +#define EN_HW_EQ_BLK (0x1<<15) //HW EQ block control +#define EN_HW_EQ_HPF_MODE (0x1<<14) //High Frequency shelving filter mode +#define EN_HW_EQ_SOUR (0x1<<11) //0:DAC PATH,1:ADC PATH +#define EN_HW_EQ_HPF (0x1<<4) //EQ High Pass Filter Control +#define EN_HW_EQ_BP3 (0x1<<3) //EQ Band-3 Control +#define EN_HW_EQ_BP2 (0x1<<2) //EQ Band-2 Control +#define EN_HW_EQ_BP1 (0x1<<1) //EQ Band-1 Control +#define EN_HW_EQ_LPF (0x1<<0) //EQ Low Pass Filter Control + +//EQ Mode Change Enable(0x66) +#define EQ_HPF_CHANGE_EN (0x1<<4) //EQ High Pass Filter Mode Change Enable +#define EQ_BP3_CHANGE_EN (0x1<<3) //EQ Band-3 Pass Filter Mode Change Enable +#define EQ_BP2_CHANGE_EN (0x1<<2) //EQ Band-2 Pass Filter Mode Change Enable +#define EQ_BP1_CHANGE_EN (0x1<<1) //EQ Band-1 Pass Filter Mode Change Enable +#define EQ_LPF_CHANGE_EN (0x1<<0) //EQ Low Pass Filter Mode Change Enable + + +//AVC Control(0x68) +#define AVC_ENABLE (0x1<<15) +#define AVC_TARTGET_SEL_MASK (0x1<<14) +#define AVC_TARTGET_SEL_R (0x1<<14) +#define AVC_TARTGET_SEL_L (0x0<<14) + + +struct rt5621_setup_data { + unsigned short i2c_address; + unsigned short i2c_bus; +}; + + + +#define RT5621_PLL_FR_MCLK 0 +#define RT5621_PLL_FR_BCLK 1 + + +#define USE_DAPM_CONTROL 0 +#define REALTEK_HWDEP 0 + +//WaveOut channel for realtek codec +enum +{ + RT_WAVOUT_SPK =(0x1<<0), + RT_WAVOUT_SPK_R =(0x1<<1), + RT_WAVOUT_SPK_L =(0x1<<2), + RT_WAVOUT_HP =(0x1<<3), + RT_WAVOUT_HP_R =(0x1<<4), + RT_WAVOUT_HP_L =(0x1<<5), + RT_WAVOUT_MONO =(0x1<<6), + RT_WAVOUT_AUXOUT =(0x1<<7), + RT_WAVOUT_AUXOUT_R =(0x1<<8), + RT_WAVOUT_AUXOUT_L =(0x1<<9), + RT_WAVOUT_LINEOUT =(0x1<<10), + RT_WAVOUT_LINEOUT_R =(0x1<<11), + RT_WAVOUT_LINEOUT_L =(0x1<<12), + RT_WAVOUT_DAC =(0x1<<13), + RT_WAVOUT_ALL_ON =(0x1<<14), +}; + +//WaveIn channel for realtek codec +enum +{ + RT_WAVIN_R_MONO_MIXER =(0x1<<0), + RT_WAVIN_R_SPK_MIXER =(0x1<<1), + RT_WAVIN_R_HP_MIXER =(0x1<<2), + RT_WAVIN_R_PHONE =(0x1<<3), + RT_WAVIN_R_AUXIN =(0x1<<3), + RT_WAVIN_R_LINE_IN =(0x1<<4), + RT_WAVIN_R_MIC2 =(0x1<<5), + RT_WAVIN_R_MIC1 =(0x1<<6), + + RT_WAVIN_L_MONO_MIXER =(0x1<<8), + RT_WAVIN_L_SPK_MIXER =(0x1<<9), + RT_WAVIN_L_HP_MIXER =(0x1<<10), + RT_WAVIN_L_PHONE =(0x1<<11), + RT_WAVIN_L_AUXIN =(0x1<<11), + RT_WAVIN_L_LINE_IN =(0x1<<12), + RT_WAVIN_L_MIC2 =(0x1<<13), + RT_WAVIN_L_MIC1 =(0x1<<14), +}; + +enum +{ + POWER_STATE_D0=0, + POWER_STATE_D1, + POWER_STATE_D1_PLAYBACK, + POWER_STATE_D1_RECORD, + POWER_STATE_D2, + POWER_STATE_D2_PLAYBACK, + POWER_STATE_D2_RECORD, + POWER_STATE_D3, + POWER_STATE_D4 + +}; + +#if REALTEK_HWDEP + +struct rt56xx_reg_state +{ + unsigned int reg_index; + unsigned int reg_value; +}; + +struct rt56xx_cmd +{ + size_t number; + struct rt56xx_reg_state __user *buf; +}; + +enum +{ + RT_READ_CODEC_REG_IOCTL = _IOR('R', 0x01, struct rt56xx_cmd), + RT_READ_ALL_CODEC_REG_IOCTL = _IOR('R', 0x02, struct rt56xx_cmd), + RT_WRITE_CODEC_REG_IOCTL = _IOW('R', 0x03, struct rt56xx_cmd), +}; + +#endif + +extern struct snd_soc_dai rt5621_dai; +extern struct snd_soc_codec_device soc_codec_dev_rt5621; + +#endif diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c new file mode 100644 index 000000000000..00e6b860fa8d --- /dev/null +++ b/sound/soc/codecs/rt5631.c @@ -0,0 +1,1556 @@ +/* + * rt5631.c -- RT5631 ALSA Soc Audio driver + * + * Copyright 2009 Realtek Microelectronics + * + * Author: flove + * + * Based on WM8753.c + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt5631.h" + +#define RT5631_VERSION "0.01 alsa 1.0.21" +#define ALSA_SOC_VERSION "1.0.21" +static const u16 rt5631_reg[0x80]; +static int timesofbclk = 32; +module_param(timesofbclk, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(timeofbclk, "relationship between bclk and fs"); + +#define VIRTUAL_POWER_CONTROL 0x90 +/* + * bit0: spkl amp power + * bit1: spkr amp power + * bit2: dmic flag + * +*/ +struct rt5631_priv { + int master; + int sysclk; + int dmic_used_flag; +}; + +static unsigned int reg90; +static int rt5631_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val); +static unsigned int rt5631_read(struct snd_soc_codec *codec, unsigned int reg); +#define rt5631_reset(c) rt5631_write(c, RT5631_RESET, 0) +#define rt5631_write_mask(c, reg, val, mask) snd_soc_update_bits(c, reg, mask, val) +static int rt5631_reg_init(struct snd_soc_codec *codec); + +static struct snd_soc_device *rt5631_socdev; +/* + * read rt5631 register cache + */ +static inline unsigned int rt5631_read_reg_cache(struct snd_soc_codec *codec, + unsigned int reg) +{ + u16 *cache = codec->reg_cache; + if (reg < 1 || reg > (ARRAY_SIZE(rt5631_reg) + 1)) + return -1; + return cache[reg]; +} + + +/* + * write rt5631 register cache + */ + +static inline void rt5631_write_reg_cache(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + u16 *cache = codec->reg_cache; + if (reg < 0 || reg > 0x7e) + return; + cache[reg] = value; +} + +static int rt5631_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) +{ + u8 data[3]; + + if (reg > 0x7e) { + if (reg == 0x90) + reg90 = val; + } + + data[0] = reg; + data[1] = (0xff00 & val) >> 8; + data[2] = (0xff & val); + + if (codec->hw_write(codec->control_data, data, 3) == 3) { + rt5631_write_reg_cache(codec, reg, val); + printk(KERN_INFO "%s reg=0x%x, val=0x%x\n", __func__, reg, val); + return 0; + } else { + printk(KERN_ERR "%s failed\n", __func__); + return -EIO; + } +} + +static unsigned int rt5631_read(struct snd_soc_codec *codec, unsigned int reg) +{ + u8 data[2] = {0}; + unsigned int value = 0x0; + + if (reg > 0x7e) { + if (reg == 0x90) + return reg90; + } + + data[0] = reg; + + i2c_master_reg8_recv(codec->control_data,reg,data,2,100 * 1000); + + value = (data[0]<<8) | data[1]; + + printk("rt5631_read reg%x=%x\n",reg,value); + + return value; +} + +#define rt5631_write_index_reg(c, addr, data) \ +{ \ + rt5631_write(c, 0x6a, addr); \ + rt5631_write(c, 0x6c, data); \ +} + + +struct rt5631_init_reg{ + u8 reg; + u16 val; +}; + +static struct rt5631_init_reg init_list[] = { + + {RT5631_SPK_OUT_VOL , 0xc8c8}, //speaker channel volume select SPKMIXER,0DB by default + {RT5631_HP_OUT_VOL , 0xc0c0}, //Headphone channel volume select OUTMIXER,0DB by default + {RT5631_MONO_AXO_1_2_VOL , 0xf0c0}, //AXO1/AXO2 channel volume select OUTMIXER,0DB by default + {RT5631_ADC_REC_MIXER , 0xb0f0}, //Record Mixer source from Mic1 by default + {RT5631_MIC_CTRL_2 , 0x5500}, //Mic1/Mic2 boost 40DB by default + {RT5631_OUTMIXER_L_CTRL , 0xdfC0}, //DAC_L-->OutMixer_L by default + {RT5631_OUTMIXER_R_CTRL , 0xdfC0}, //DAC_R-->OutMixer_R by default + {RT5631_AXO1MIXER_CTRL , 0x8840}, //OutMixer_L-->AXO1Mixer by default + {RT5631_AXO2MIXER_CTRL , 0x8880}, //OutMixer_R-->AXO2Mixer by default + {RT5631_SPK_MIXER_CTRL , 0xd8d8}, //DAC-->SpeakerMixer + {RT5631_SPK_MONO_OUT_CTRL , 0x6c00}, //Speaker volume-->SPOMixer(L-->L,R-->R) + {RT5631_GEN_PUR_CTRL_REG , 0x4e00}, //Speaker AMP ratio gain is 1.44X + {RT5631_SPK_MONO_HP_OUT_CTRL, 0x0000}, //HP from OutMixer,speaker out from SpeakerOut Mixer + {RT5631_DEPOP_FUN_CTRL_2 , 0x8000}, //HP depop by register control + {RT5631_INT_ST_IRQ_CTRL_2 , 0x0f18}, //enable HP zero cross + {RT5631_MIC_CTRL_1 , 0x8000}, //set mic 1 to differnetial mode +// {RT5631_GPIO_CTRL , 0x0000}, //set GPIO to input pin +// {RT5631_JACK_DET_CTRL , 0x4e80}, //Jack detect for GPIO,high is HP,low is speaker +// {RT5631_JACK_DET_CTRL , 0x4bc0}, //Jack detect for GPIO,high is speaker,low is hp +}; + +#define RT5631_INIT_REG_LEN ARRAY_SIZE(init_list) + +static int rt5631_reg_init(struct snd_soc_codec *codec) +{ + int i; + + for (i = 0; i < RT5631_INIT_REG_LEN; i ++) { + rt5631_write(codec, init_list[i].reg, init_list[i].val); + } + + return 0; +} + +static const char *rt5631_spol_source_sel[] = {"SPOLMIX", "MONOIN_RX", "VDAC", "DACL"}; +static const char *rt5631_spor_source_sel[] = {"SPORMIX", "MONOIN_RX", "VDAC", "DACR"}; +static const char *rt5631_mono_source_sel[] = {"MONOMIX", "MONOIN_RX", "VDAC"}; +static const char *rt5631_input_mode_source_sel[] = {"Single-end", "Differential"}; +static const char *rt5631_mic_boost[] = {"Bypass", "+20db", "+24db", "+30db", + "+35db", "+40db", "+44db", "+50db", "+52db"}; + +static const struct soc_enum rt5631_enum[] = { +SOC_ENUM_SINGLE(RT5631_SPK_MONO_HP_OUT_CTRL, 14, 4, rt5631_spol_source_sel), /*0*/ +SOC_ENUM_SINGLE(RT5631_SPK_MONO_HP_OUT_CTRL, 10, 4, rt5631_spor_source_sel), /*1*/ +SOC_ENUM_SINGLE(RT5631_SPK_MONO_HP_OUT_CTRL, 6, 3, rt5631_mono_source_sel), /*2*/ +SOC_ENUM_SINGLE(RT5631_MIC_CTRL_1, 15, 2, rt5631_input_mode_source_sel), /*3*/ +SOC_ENUM_SINGLE(RT5631_MIC_CTRL_1, 7, 2, rt5631_input_mode_source_sel), /*4*/ +SOC_ENUM_SINGLE(RT5631_MONO_INPUT_VOL, 15, 2, rt5631_input_mode_source_sel), /*5*/ +SOC_ENUM_SINGLE(RT5631_MIC_CTRL_2, 12, 9, rt5631_mic_boost), /*6*/ +SOC_ENUM_SINGLE(RT5631_MIC_CTRL_2, 8, 9, rt5631_mic_boost), /*7*/ +}; + +static int rt5631_dmic_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int val; + + val = rt5631_read(codec, VIRTUAL_POWER_CONTROL) & 0x0004; + val >>= 2; + ucontrol->value.integer.value[0] = val; + return 0; +} + +static void rt5631_close_dmic(struct snd_soc_codec *codec) //disable DMic to ADC filter +{ + rt5631_write_mask(codec, RT5631_DIG_MIC_CTRL, DMIC_L_CH_MUTE | DMIC_R_CH_MUTE, DMIC_L_CH_MUTE_MASK | DMIC_R_CH_MUTE_MASK); + + rt5631_write_mask(codec, RT5631_DIG_MIC_CTRL, DMIC_DIS, DMIC_ENA_MASK); +} + +static int rt5631_dmic_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int old, new; + struct rt5631_priv *rt5631 = codec->private_data; + + old = rt5631_read(codec, VIRTUAL_POWER_CONTROL) & 0x0004; + new = ucontrol->value.integer.value[0] << 2; + + if (old == new) + return 0; + + rt5631_write_mask(codec, VIRTUAL_POWER_CONTROL, new, 0x0004); + + if (new) + rt5631->dmic_used_flag = 1; + else + { + rt5631_close_dmic(codec); + rt5631->dmic_used_flag=0; + } + + return 0; +} + +static const struct snd_kcontrol_new rt5631_snd_controls[] = { +SOC_ENUM("MIC1 Mode Control", rt5631_enum[3]), +SOC_ENUM("MIC1 Boost", rt5631_enum[6]), + +SOC_ENUM("MIC2 Mode Control", rt5631_enum[4]), +SOC_ENUM("MIC2 Boost", rt5631_enum[7]), +SOC_ENUM("MONOIN Mode Control", rt5631_enum[5]), + +SOC_DOUBLE("PCM Playback Volume", RT5631_STEREO_DAC_VOL_2, 8, 0, 255, 1), +SOC_DOUBLE("PCM Playback Switch", RT5631_STEREO_DAC_VOL_1,15, 7, 1, 1), + +SOC_DOUBLE("MONOIN_RX Capture Volume", RT5631_MONO_INPUT_VOL, 8, 0, 31, 1), + +SOC_DOUBLE("AXI Capture Volume", RT5631_AUX_IN_VOL, 8, 0, 31, 1), + +SOC_SINGLE("AXO1 Playback Switch", RT5631_MONO_AXO_1_2_VOL, 15, 1, 1), +SOC_SINGLE("AXO2 Playback Switch", RT5631_MONO_AXO_1_2_VOL, 7, 1, 1), +SOC_DOUBLE("OUTVOL Playback Volume", RT5631_MONO_AXO_1_2_VOL, 8, 0, 31, 1), + +SOC_DOUBLE("Speaker Playback Switch", RT5631_SPK_OUT_VOL,15, 7, 1, 1), +SOC_DOUBLE("Speaker Playback Volume", RT5631_SPK_OUT_VOL, 8, 0, 63, 1), + +SOC_SINGLE("MONO Playback Switch", RT5631_MONO_AXO_1_2_VOL, 13, 1, 1), + +SOC_DOUBLE("HP Playback Switch", RT5631_HP_OUT_VOL,15, 7, 1, 1), +SOC_DOUBLE("HP Playback Volume", RT5631_HP_OUT_VOL, 8, 0, 63, 1), + +//SOC_SINGLE_EXT("HIFI Loopback", ),/*not finished*/ +//SOC_SINGLE_EXT("Voice Loopback", ), /*not finished*/ +SOC_SINGLE_EXT("DMIC Capture Switch", VIRTUAL_POWER_CONTROL, 2, 1, 0, + rt5631_dmic_get, rt5631_dmic_put), +}; + +static int rt5631_add_controls(struct snd_soc_codec *codec) +{ + int err, i; + + for (i = 0; i < ARRAY_SIZE(rt5631_snd_controls); i ++) { + + err = snd_ctl_add(codec->card, + snd_soc_cnew(&rt5631_snd_controls[i], + codec, NULL)); + if (err < 0) + return err; + } + + return 0; +} + +static const struct snd_kcontrol_new rt5631_recmixl_mixer_controls[] = { +SOC_DAPM_SINGLE("OUTMIXL Capture Switch", RT5631_ADC_REC_MIXER, 15, 1, 1), +SOC_DAPM_SINGLE("MIC1_BST1 Capture Switch", RT5631_ADC_REC_MIXER, 14, 1, 1), +SOC_DAPM_SINGLE("AXILVOL Capture Switch", RT5631_ADC_REC_MIXER, 13, 1, 1), +SOC_DAPM_SINGLE("MONOIN_RX Capture Switch", RT5631_ADC_REC_MIXER, 12, 1, 1), +}; + +static const struct snd_kcontrol_new rt5631_recmixr_mixer_controls[] = { +SOC_DAPM_SINGLE("MONOIN_RX Capture Switch", RT5631_ADC_REC_MIXER, 4, 1, 1), +SOC_DAPM_SINGLE("AXIRVOL Capture Switch", RT5631_ADC_REC_MIXER, 5, 1, 1), +SOC_DAPM_SINGLE("MIC2_BST2 Capture Switch", RT5631_ADC_REC_MIXER, 6, 1, 1), +SOC_DAPM_SINGLE("OUTMIXR Capture Switch", RT5631_ADC_REC_MIXER, 7, 1, 1), +}; + + +static const struct snd_kcontrol_new rt5631_spkmixl_mixer_controls[] = { +SOC_DAPM_SINGLE("RECMIXL Playback Switch", RT5631_SPK_MIXER_CTRL, 15, 1, 1), +SOC_DAPM_SINGLE("MIC1_P Playback Switch", RT5631_SPK_MIXER_CTRL, 14, 1, 1), +SOC_DAPM_SINGLE("DACL Playback Switch", RT5631_SPK_MIXER_CTRL, 13, 1, 1), +SOC_DAPM_SINGLE("OUTMIXL Playback Switch", RT5631_SPK_MIXER_CTRL, 12, 1, 1) +}; + +static const struct snd_kcontrol_new rt5631_spkmixr_mixer_controls[] = { +SOC_DAPM_SINGLE("OUTMIXR Playback Switch", RT5631_SPK_MIXER_CTRL, 4, 1, 1), +SOC_DAPM_SINGLE("DACR Playback Switch", RT5631_SPK_MIXER_CTRL, 5, 1, 1), +SOC_DAPM_SINGLE("MIC2_P Playback Switch", RT5631_SPK_MIXER_CTRL, 6, 1, 1), +SOC_DAPM_SINGLE("RECMIXR Playback Switch", RT5631_SPK_MIXER_CTRL, 7, 1, 1), +}; + +static const struct snd_kcontrol_new rt5631_outmixl_mixer_controls[] = { +SOC_DAPM_SINGLE("RECMIXL Playback Switch", RT5631_OUTMIXER_L_CTRL, 15, 1, 1), +SOC_DAPM_SINGLE("RECMIXR Playback Switch", RT5631_OUTMIXER_L_CTRL, 14, 1, 1), +SOC_DAPM_SINGLE("DACL Playback Switch", RT5631_OUTMIXER_L_CTRL, 13, 1, 1), +SOC_DAPM_SINGLE("MIC1_BST1 Playback Switch", RT5631_OUTMIXER_L_CTRL, 12, 1, 1), +SOC_DAPM_SINGLE("MIC2_BST2 Playback Switch", RT5631_OUTMIXER_L_CTRL, 11, 1, 1), +SOC_DAPM_SINGLE("MONOIN_RXP Playback Switch", RT5631_OUTMIXER_L_CTRL, 10, 1, 1), +SOC_DAPM_SINGLE("AXILVOL Playback Switch", RT5631_OUTMIXER_L_CTRL, 9, 1, 1), +SOC_DAPM_SINGLE("AXIRVOL Playback Switch", RT5631_OUTMIXER_L_CTRL, 8, 1, 1), +SOC_DAPM_SINGLE("VDAC Playback Switch", RT5631_OUTMIXER_L_CTRL, 7, 1, 1) +}; + +static const struct snd_kcontrol_new rt5631_outmixr_mixer_controls[] = { +SOC_DAPM_SINGLE("VDAC Playback Switch", RT5631_OUTMIXER_R_CTRL, 7, 1, 1), +SOC_DAPM_SINGLE("AXIRVOL Playback Switch", RT5631_OUTMIXER_R_CTRL, 8, 1, 1), +SOC_DAPM_SINGLE("AXILVOL Playback Switch", RT5631_OUTMIXER_R_CTRL, 9, 1, 1), +SOC_DAPM_SINGLE("MONOIN_RXN Playback Switch", RT5631_OUTMIXER_R_CTRL, 10, 1, 1), +SOC_DAPM_SINGLE("MIC2_BST2 Playback Switch", RT5631_OUTMIXER_R_CTRL, 11, 1, 1), +SOC_DAPM_SINGLE("MIC1_BST1 Playback Switch", RT5631_OUTMIXER_R_CTRL, 12, 1, 1), +SOC_DAPM_SINGLE("DACR Playback Switch", RT5631_OUTMIXER_R_CTRL, 13, 1, 1), +SOC_DAPM_SINGLE("RECMIXR Playback Switch", RT5631_OUTMIXER_R_CTRL, 14, 1, 1), +SOC_DAPM_SINGLE("RECMIXL Playback Switch", RT5631_OUTMIXER_R_CTRL, 15, 1, 1), +}; + + +static const struct snd_kcontrol_new rt5631_AXO1MIX_mixer_controls[] = { +SOC_DAPM_SINGLE("MIC1_BST1 Playback Switch", RT5631_AXO1MIXER_CTRL, 15 , 1, 1), +SOC_DAPM_SINGLE("MIC2_BST2 Playback Switch", RT5631_AXO1MIXER_CTRL, 11, 1, 1), +SOC_DAPM_SINGLE("OUTVOLL Playback Switch", RT5631_AXO1MIXER_CTRL, 7 ,1 ,1), +SOC_DAPM_SINGLE("OUTVOLR Playback Switch", RT5631_AXO1MIXER_CTRL, 6, 1, 1), +}; + + +static const struct snd_kcontrol_new rt5631_AXO2MIX_mixer_controls[] = { +SOC_DAPM_SINGLE("MIC1_BST1 Playback Switch", RT5631_AXO2MIXER_CTRL, 15, 1, 1), +SOC_DAPM_SINGLE("MIC2_BST2 Playback Switch", RT5631_AXO2MIXER_CTRL, 11, 1, 1), +SOC_DAPM_SINGLE("OUTVOLL Playback Switch", RT5631_AXO2MIXER_CTRL, 7, 1, 1), +SOC_DAPM_SINGLE("OUTVOLR Playback Switch", RT5631_AXO2MIXER_CTRL, 6, 1 ,1), +}; + +static const struct snd_kcontrol_new rt5631_spolmix_mixer_controls[] = { +SOC_DAPM_SINGLE("SPKVOLL Playback Switch", RT5631_SPK_MONO_OUT_CTRL, 15, 1, 1), +SOC_DAPM_SINGLE("SPKVOLR Playback Switch", RT5631_SPK_MONO_OUT_CTRL, 14, 1, 1), +}; + + +static const struct snd_kcontrol_new rt5631_spormix_mixer_controls[] = { +SOC_DAPM_SINGLE("SPKVOLL Playback Switch", RT5631_SPK_MONO_OUT_CTRL, 13, 1, 1), +SOC_DAPM_SINGLE("SPKVOLR Playback Switch", RT5631_SPK_MONO_OUT_CTRL, 12, 1, 1), +}; + + +static const struct snd_kcontrol_new rt5631_monomix_mixer_controls[] = { +SOC_DAPM_SINGLE("OUTVOLL Playback Switch", RT5631_SPK_MONO_OUT_CTRL, 11, 1, 1), +SOC_DAPM_SINGLE("OUTVOLR Playback Switch", RT5631_SPK_MONO_OUT_CTRL, 10, 1, 1), +}; + + +static const struct snd_kcontrol_new rt5631_spol_mux_control = +SOC_DAPM_ENUM("Route", rt5631_enum[0]); +static const struct snd_kcontrol_new rt5631_spor_mux_control = +SOC_DAPM_ENUM("Route", rt5631_enum[1]); +static const struct snd_kcontrol_new rt5631_mono_mux_control = +SOC_DAPM_ENUM("Route", rt5631_enum[2]); + + + +static int spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + unsigned int l, r; + + l = rt5631_read(codec, VIRTUAL_POWER_CONTROL) & 0x0001; + r = (rt5631_read(codec, VIRTUAL_POWER_CONTROL) & 0x0002) >> 1; + + switch (event) { + case SND_SOC_DAPM_POST_PMD: + printk("spk_event --SND_SOC_DAPM_POST_PMD\n"); + rt5631_write_mask(codec, RT5631_SPK_OUT_VOL, 0x8080, 0x8080); + + if ((l == 0) && (r == 0)) + rt5631_write_mask(codec, RT5631_PWR_MANAG_ADD1, 0x0000, 0x1000); + + rt5631_write_mask(codec, RT5631_PWR_MANAG_ADD4, 0x0000, 0xC000); + break; + case SND_SOC_DAPM_POST_PMU: + printk("spk_event --SND_SOC_DAPM_POST_PMU\n"); + if ((l != 0) || (r != 0)) + rt5631_write_mask(codec, RT5631_PWR_MANAG_ADD1, 0x1000, 0x1000); + rt5631_write_mask(codec, RT5631_PWR_MANAG_ADD4, 0xC000, 0xC000); + rt5631_write_mask(codec, RT5631_SPK_OUT_VOL, 0x0000, 0x8080); + break; + default: + return -EINVAL; + } + + return 0; +} + + +static void hp_depop2(struct snd_soc_codec *codec,unsigned int EnableHPOut) +{ + + unsigned int SoftVol,HPZeroCross; + + SoftVol = rt5631_read(codec, RT5631_SOFT_VOL_CTRL); + rt5631_write(codec,RT5631_SOFT_VOL_CTRL,0); + HPZeroCross=rt5631_read(codec, RT5631_INT_ST_IRQ_CTRL_2); + rt5631_write(codec,RT5631_INT_ST_IRQ_CTRL_2,HPZeroCross&0xf7ff); //disable Zero Cross of HP + //enable HP out + if(EnableHPOut) + { + + rt5631_write_index_reg(codec,0x56,0x303e); + + rt5631_write_mask(codec,RT5631_PWR_MANAG_ADD3,PWR_CHARGE_PUMP|PWR_HP_L_AMP|PWR_HP_R_AMP + ,PWR_CHARGE_PUMP|PWR_HP_L_AMP|PWR_HP_R_AMP); + rt5631_write(codec,RT5631_DEPOP_FUN_CTRL_1,POW_ON_SOFT_GEN|EN_DEPOP2_FOR_HP); + schedule_timeout_uninterruptible(msecs_to_jiffies(100)); + rt5631_write_mask(codec,RT5631_PWR_MANAG_ADD3,PWR_HP_DEPOP_DIS|PWR_HP_AMP_DRIVING,PWR_HP_DEPOP_DIS|PWR_HP_AMP_DRIVING); + } + else //disable HP out + { + rt5631_write_index_reg(codec,0x56,0x303F); + rt5631_write(codec,RT5631_DEPOP_FUN_CTRL_1,POW_ON_SOFT_GEN|EN_MUTE_UNMUTE_DEPOP|PD_HPAMP_L_ST_UP|PD_HPAMP_R_ST_UP); + schedule_timeout_uninterruptible(msecs_to_jiffies(75)); + rt5631_write(codec,RT5631_DEPOP_FUN_CTRL_1,POW_ON_SOFT_GEN|PD_HPAMP_L_ST_UP|PD_HPAMP_R_ST_UP); + rt5631_write_mask(codec,RT5631_PWR_MANAG_ADD3,0,PWR_HP_DEPOP_DIS|PWR_HP_AMP_DRIVING); + rt5631_write(codec,RT5631_DEPOP_FUN_CTRL_1,POW_ON_SOFT_GEN|EN_DEPOP2_FOR_HP|PD_HPAMP_L_ST_UP|PD_HPAMP_R_ST_UP); + schedule_timeout_uninterruptible(msecs_to_jiffies(80)); + rt5631_write(codec,RT5631_DEPOP_FUN_CTRL_1,POW_ON_SOFT_GEN); + rt5631_write_mask(codec,RT5631_PWR_MANAG_ADD3,0,PWR_CHARGE_PUMP|PWR_HP_L_AMP|PWR_HP_R_AMP); + } + + rt5631_write(codec,RT5631_SOFT_VOL_CTRL,SoftVol); + rt5631_write(codec,RT5631_INT_ST_IRQ_CTRL_2,HPZeroCross); + +} + +static void HP_Mute_Unmute_Depop(struct snd_soc_codec *codec,unsigned int EnableHPOut) +{ + + unsigned int SoftVol,HPZeroCross; + + SoftVol = rt5631_read(codec, RT5631_SOFT_VOL_CTRL); + rt5631_write(codec,RT5631_SOFT_VOL_CTRL,0); + HPZeroCross=rt5631_read(codec, RT5631_INT_ST_IRQ_CTRL_2); + rt5631_write(codec,RT5631_INT_ST_IRQ_CTRL_2,HPZeroCross&0xf7ff); //disable Zero Cross of HP + + if(EnableHPOut) //unmute HP out + { + schedule_timeout_uninterruptible(msecs_to_jiffies(10)); + rt5631_write_index_reg(codec,0x56,0x302f); + rt5631_write(codec,RT5631_DEPOP_FUN_CTRL_1,POW_ON_SOFT_GEN|EN_MUTE_UNMUTE_DEPOP|EN_HP_R_M_UN_MUTE_DEPOP|EN_HP_L_M_UN_MUTE_DEPOP); + rt5631_write_mask(codec,RT5631_HP_OUT_VOL,0x0000,0x8080); + schedule_timeout_uninterruptible(msecs_to_jiffies(160)); + + } + else //mute HP out + { + rt5631_write_index_reg(codec,0x56,0x302f); + rt5631_write(codec,RT5631_DEPOP_FUN_CTRL_1,POW_ON_SOFT_GEN|EN_MUTE_UNMUTE_DEPOP|EN_HP_R_M_UN_MUTE_DEPOP|EN_HP_L_M_UN_MUTE_DEPOP); + rt5631_write_mask(codec,RT5631_HP_OUT_VOL,0x8080,0x8080); + schedule_timeout_uninterruptible(msecs_to_jiffies(150)); + } + + rt5631_write(codec,RT5631_SOFT_VOL_CTRL,SoftVol); + rt5631_write(codec,RT5631_INT_ST_IRQ_CTRL_2,HPZeroCross); + +} + +static int open_hp_end_widgets(struct snd_soc_codec *codec) +{ + /*need to be fixed*/ + /* + * + *open hp last widget, e.g. power, switch + */ + + HP_Mute_Unmute_Depop(codec,1); + + return 0; +} + +static int close_hp_end_widgets(struct snd_soc_codec *codec) +{ + /*need to be fixed*/ + /* + * + *close hp last widget, e.g. power, switch + */ + + HP_Mute_Unmute_Depop(codec,0); + + return 0; +} +static int hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + unsigned int l, r; + static unsigned int hp_out_enable=0; + + l = (rt5631_read(codec, RT5631_PWR_MANAG_ADD4) & (0x01 << 11)) >> 11; + r = (rt5631_read(codec, RT5631_PWR_MANAG_ADD4) & (0x01 << 10)) >> 10; + + switch (event) { + case SND_SOC_DAPM_PRE_PMD: + printk("hp_event --SND_SOC_DAPM_PRE_PMD\n"); + if ((l && r)&&(hp_out_enable)) + { + close_hp_end_widgets(codec); + hp_depop2(codec,0); + hp_out_enable=0; + } + break; + case SND_SOC_DAPM_POST_PMU: + printk("hp_event --SND_SOC_DAPM_POST_PMU\n"); + if ((l && r)&&(!hp_out_enable)) + { + hp_depop2(codec,1); + open_hp_end_widgets(codec); + hp_out_enable=1; + } + break; + default: + return -EINVAL; + } + + return 0; +} + + +static const struct snd_soc_dapm_widget rt5631_dapm_widgets[] = { + +SND_SOC_DAPM_INPUT("MIC1"), +SND_SOC_DAPM_INPUT("MIC2"), +SND_SOC_DAPM_INPUT("AXIL"), +SND_SOC_DAPM_INPUT("AXIR"), +SND_SOC_DAPM_INPUT("MONOIN_RXN"), +SND_SOC_DAPM_INPUT("MONOIN_RXP"), + + +SND_SOC_DAPM_PGA("Mic1 Boost", RT5631_PWR_MANAG_ADD2, 5, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Mic2 Boost", RT5631_PWR_MANAG_ADD2, 4, 0, NULL, 0), + +SND_SOC_DAPM_PGA("MONOIN_RXP Boost", RT5631_PWR_MANAG_ADD4, 7, 0, NULL, 0), +SND_SOC_DAPM_PGA("MONOIN_RXN Boost", RT5631_PWR_MANAG_ADD4, 6, 0, NULL, 0), + +SND_SOC_DAPM_PGA("AXIL Boost", RT5631_PWR_MANAG_ADD4, 9, 0, NULL, 0), +SND_SOC_DAPM_PGA("AXIR Boost", RT5631_PWR_MANAG_ADD4, 8, 0, NULL, 0), +SND_SOC_DAPM_MIXER("MONO_IN", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_MIXER("RECMIXL Mixer", RT5631_PWR_MANAG_ADD2, 11, 0, + &rt5631_recmixl_mixer_controls[0], ARRAY_SIZE(rt5631_recmixl_mixer_controls)), +SND_SOC_DAPM_MIXER("RECMIXR Mixer", RT5631_PWR_MANAG_ADD2, 10, 0, + &rt5631_recmixr_mixer_controls[0], ARRAY_SIZE(rt5631_recmixr_mixer_controls)), + +SND_SOC_DAPM_MIXER("ADC Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_ADC("Left ADC", "Left ADC HIFI Capture", RT5631_PWR_MANAG_ADD1, 11, 0), +SND_SOC_DAPM_ADC("Right ADC", "Right ADC HIFI Capture", RT5631_PWR_MANAG_ADD1, 10, 0), +SND_SOC_DAPM_DAC("Left DAC", "Left DAC HIFI Playback", RT5631_PWR_MANAG_ADD1, 9, 0), +SND_SOC_DAPM_DAC("Right DAC", "Right DAC HIFI Playback", RT5631_PWR_MANAG_ADD1, 8, 0), +SND_SOC_DAPM_DAC("Voice DAC", "Voice DAC Mono Playback", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_PGA("Voice DAC Boost", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_MIXER("SPKMIXL Mixer", RT5631_PWR_MANAG_ADD2, 13, 0, + &rt5631_spkmixl_mixer_controls[0], ARRAY_SIZE(rt5631_spkmixl_mixer_controls)), +SND_SOC_DAPM_MIXER("OUTMIXL Mixer", RT5631_PWR_MANAG_ADD2, 15, 0, + &rt5631_outmixl_mixer_controls[0], ARRAY_SIZE(rt5631_outmixl_mixer_controls)), +SND_SOC_DAPM_MIXER("OUTMIXR Mixer", RT5631_PWR_MANAG_ADD2, 14, 0, + &rt5631_outmixr_mixer_controls[0], ARRAY_SIZE(rt5631_outmixr_mixer_controls)), +SND_SOC_DAPM_MIXER("SPKMIXR Mixer", RT5631_PWR_MANAG_ADD2, 12, 0, + &rt5631_spkmixr_mixer_controls[0], ARRAY_SIZE(rt5631_spkmixr_mixer_controls)), +SND_SOC_DAPM_PGA("Left SPK Vol", RT5631_PWR_MANAG_ADD4, 15, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right SPK Vol", RT5631_PWR_MANAG_ADD4, 14, 0, NULL, 0), +SND_SOC_DAPM_PGA_E("Left HP Vol", RT5631_PWR_MANAG_ADD4, 11, 0, NULL, 0, + hp_event, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("Right HP Vol", RT5631_PWR_MANAG_ADD4, 10, 0, NULL, 0, + hp_event, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA("Left Out Vol", RT5631_PWR_MANAG_ADD4, 13, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right Out Vol", RT5631_PWR_MANAG_ADD4, 12, 0, NULL, 0), + +SND_SOC_DAPM_MIXER("AXO1MIX Mixer", RT5631_PWR_MANAG_ADD3, 11, 0, + &rt5631_AXO1MIX_mixer_controls[0], ARRAY_SIZE(rt5631_AXO1MIX_mixer_controls)), +SND_SOC_DAPM_MIXER("SPOLMIX Mixer", SND_SOC_NOPM, 0, 0, + &rt5631_spolmix_mixer_controls[0], ARRAY_SIZE(rt5631_spolmix_mixer_controls)), +SND_SOC_DAPM_MIXER("MONOMIX Mixer", RT5631_PWR_MANAG_ADD3, 9, 0, + &rt5631_monomix_mixer_controls[0], ARRAY_SIZE(rt5631_monomix_mixer_controls)), +SND_SOC_DAPM_MIXER("SPORMIX Mixer", SND_SOC_NOPM, 0, 0, + &rt5631_spormix_mixer_controls[0], ARRAY_SIZE(rt5631_spormix_mixer_controls)), +SND_SOC_DAPM_MIXER("AXO2MIX Mixer", RT5631_PWR_MANAG_ADD3, 10, 0, + &rt5631_AXO2MIX_mixer_controls[0], ARRAY_SIZE(rt5631_AXO2MIX_mixer_controls)), + + +SND_SOC_DAPM_MUX("SPOL Mux", SND_SOC_NOPM, 0, 0, &rt5631_spol_mux_control), +SND_SOC_DAPM_MUX("SPOR Mux", SND_SOC_NOPM, 0, 0, &rt5631_spor_mux_control), +SND_SOC_DAPM_MUX("Mono Mux", SND_SOC_NOPM, 0, 0, &rt5631_mono_mux_control), + +SND_SOC_DAPM_PGA("Mono Amp", RT5631_PWR_MANAG_ADD3, 7, 0, NULL, 0), + + +SND_SOC_DAPM_PGA_E("SPKL Amp", VIRTUAL_POWER_CONTROL, 0, 0, NULL, 0, + spk_event, SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("SPKR Amp", VIRTUAL_POWER_CONTROL, 1, 0, NULL, 0, + spk_event, SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_MICBIAS("Mic Bias1", RT5631_PWR_MANAG_ADD2, 3, 0), +SND_SOC_DAPM_MICBIAS("Mic Bias2", RT5631_PWR_MANAG_ADD2, 2, 0), + +SND_SOC_DAPM_OUTPUT("LOUT"), +SND_SOC_DAPM_OUTPUT("ROUT"), +SND_SOC_DAPM_OUTPUT("SPOL"), +SND_SOC_DAPM_OUTPUT("SPOR"), +SND_SOC_DAPM_OUTPUT("HPOL"), +SND_SOC_DAPM_OUTPUT("HPOR"), +SND_SOC_DAPM_OUTPUT("MONO"), + +}; + + +static const struct snd_soc_dapm_route audio_map[] = { + + {"Mic1 Boost", NULL, "MIC1"}, + {"Mic2 Boost", NULL, "MIC2"}, + {"MONOIN_RXP Boost", NULL, "MONOIN_RXP"}, + {"MONOIN_RXN Boost", NULL, "MONOIN_RXN"}, + {"AXIL Boost", NULL, "AXIL"}, + {"AXIR Boost", NULL, "AXIR"}, + + {"MONO_IN", NULL, "MONOIN_RXP Boost"}, + {"MONO_IN", NULL, "MONOIN_RXN Boost"}, + + {"RECMIXL Mixer", "OUTMIXL Capture Switch", "OUTMIXL Mixer"}, + {"RECMIXL Mixer", "MIC1_BST1 Capture Switch", "Mic1 Boost"}, + {"RECMIXL Mixer", "AXILVOL Capture Switch", "AXIL Boost"}, + {"RECMIXL Mixer", "MONOIN_RX Capture Switch", "MONO_IN"}, + + {"RECMIXR Mixer", "OUTMIXR Capture Switch", "OUTMIXR Mixer"}, + {"RECMIXR Mixer", "MIC2_BST2 Capture Switch", "Mic2 Boost"}, + {"RECMIXR Mixer", "AXIRVOL Capture Switch", "AXIR Boost"}, + {"RECMIXR Mixer", "MONOIN_RX Capture Switch", "MONO_IN"}, + + {"ADC Mixer", NULL, "RECMIXL Mixer"}, + {"ADC Mixer", NULL, "RECMIXR Mixer"}, + {"Left ADC", NULL, "ADC Mixer"}, + {"Right ADC", NULL,"ADC Mixer"}, + + {"Voice DAC Boost", NULL, "Voice DAC"}, + + {"SPKMIXL Mixer", "RECMIXL Playback Switch", "RECMIXL Mixer"}, + {"SPKMIXL Mixer", "MIC1_P Playback Switch", "MIC1"}, + {"SPKMIXL Mixer", "DACL Playback Switch", "Left DAC"}, + {"SPKMIXL Mixer", "OUTMIXL Playback Switch", "OUTMIXL Mixer"}, + + {"SPKMIXR Mixer", "OUTMIXR Playback Switch", "OUTMIXR Mixer"}, + {"SPKMIXR Mixer", "DACR Playback Switch", "Right DAC"}, + {"SPKMIXR Mixer", "MIC2_P Playback Switch", "MIC2"}, + {"SPKMIXR Mixer", "RECMIXR Playback Switch", "RECMIXR Mixer"}, + + + {"OUTMIXL Mixer", "RECMIXL Playback Switch", "RECMIXL Mixer"}, + {"OUTMIXL Mixer", "RECMIXR Playback Switch", "RECMIXR Mixer"}, + {"OUTMIXL Mixer", "DACL Playback Switch", "Left DAC"}, + {"OUTMIXL Mixer", "MIC1_BST1 Playback Switch", "Mic1 Boost"}, + {"OUTMIXL Mixer", "MIC2_BST2 Playback Switch", "Mic2 Boost"}, + {"OUTMIXL Mixer", "MONOIN_RXP Playback Switch", "MONOIN_RXP Boost"}, + {"OUTMIXL Mixer", "AXILVOL Playback Switch", "AXIL Boost"}, + {"OUTMIXL Mixer", "AXIRVOL Playback Switch", "AXIR Boost"}, + {"OUTMIXL Mixer", "VDAC Playback Switch", "Voice DAC Boost"}, + + {"OUTMIXR Mixer", "RECMIXL Playback Switch", "RECMIXL Mixer"}, + {"OUTMIXR Mixer", "RECMIXR Playback Switch", "RECMIXR Mixer"}, + {"OUTMIXR Mixer", "DACR Playback Switch", "Right DAC"}, + {"OUTMIXR Mixer", "MIC1_BST1 Playback Switch", "Mic1 Boost"}, + {"OUTMIXR Mixer", "MIC2_BST2 Playback Switch", "Mic2 Boost"}, + {"OUTMIXR Mixer", "MONOIN_RXN Playback Switch", "MONOIN_RXN Boost"}, + {"OUTMIXR Mixer", "AXILVOL Playback Switch", "AXIL Boost"}, + {"OUTMIXR Mixer", "AXIRVOL Playback Switch", "AXIR Boost"}, + {"OUTMIXR Mixer", "VDAC Playback Switch", "Voice DAC Boost"}, + + {"Left SPK Vol", NULL, "SPKMIXL Mixer"}, + {"Right SPK Vol", NULL, "SPKMIXR Mixer"}, + {"Left HP Vol", NULL, "OUTMIXL Mixer"}, + {"Left Out Vol", NULL, "OUTMIXL Mixer"}, + {"Right Out Vol", NULL, "OUTMIXR Mixer"}, + {"Right HP Vol", NULL, "OUTMIXR Mixer"}, + + + {"AXO1MIX Mixer", "MIC1_BST1 Playback Switch", "Mic1 Boost"}, + {"AXO1MIX Mixer", "OUTVOLL Playback Switch", "Left Out Vol"}, + {"AXO1MIX Mixer", "OUTVOLR Playback Switch", "Right Out Vol"}, + {"AXO1MIX Mixer", "MIC2_BST2 Playback Switch", "Mic2 Boost"}, + + + {"AXO2MIX Mixer", "MIC1_BST1 Playback Switch", "Mic1 Boost"}, + {"AXO2MIX Mixer", "OUTVOLL Playback Switch", "Left Out Vol"}, + {"AXO2MIX Mixer", "OUTVOLR Playback Switch", "Right Out Vol"}, + {"AXO2MIX Mixer", "MIC2_BST2 Playback Switch", "Mic2 Boost"}, + + + {"SPOLMIX Mixer", "SPKVOLL Playback Switch", "Left SPK Vol"}, + {"SPOLMIX Mixer", "SPKVOLR Playback Switch", "Right SPK Vol"}, + + {"SPORMIX Mixer", "SPKVOLL Playback Switch", "Left SPK Vol"}, + {"SPORMIX Mixer", "SPKVOLR Playback Switch", "Right SPK Vol"}, + + + {"MONOMIX Mixer", "OUTVOLL Playback Switch", "Left Out Vol"}, + {"MONOMIX Mixer", "OUTVOLR Playback Switch", "Right Out Vol"}, + + + + {"SPOL Mux", "SPOLMIX", "SPOLMIX Mixer"}, + {"SPOL Mux", "MONOIN_RX", "MONO_IN"}, + {"SPOL Mux", "VDAC", "Voice DAC Boost"}, + {"SPOL Mux", "DACL", "Left DAC"}, + + {"SPOR Mux", "SPORMIX", "SPORMIX Mixer"}, + {"SPOR Mux", "MONOIN_RX", "MONO_IN"}, + {"SPOR Mux", "VDAC", "Voice DAC Boost"}, + {"SPOR Mux", "DACR", "Right DAC"}, + + {"Mono Mux", "MONOMIX", "MONOMIX Mixer"}, + {"Mono Mux", "MONOIN_RX", "MONO_IN"}, + {"Mono Mux", "VDAC", "Voice DAC Boost"}, + + {"SPKL Amp", NULL, "SPOL Mux"}, + {"SPKR Amp", NULL, "SPOR Mux"}, + {"Mono Amp", NULL, "Mono Mux"}, + + {"LOUT", NULL, "AXO1MIX Mixer"}, + {"ROUT", NULL, "AXO2MIX Mixer"}, + {"SPOL", NULL, "SPKL Amp"}, + {"SPOR", NULL, "SPKR Amp"}, + {"HPOL", NULL, "Left HP Vol"}, + {"HPOR", NULL, "Right HP Vol"}, + {"MONO", NULL, "Mono Amp"} + +}; + +static int rt5631_add_widgets(struct snd_soc_codec *codec) +{ + snd_soc_dapm_new_controls(codec, rt5631_dapm_widgets, + ARRAY_SIZE(rt5631_dapm_widgets)); + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_new_widgets(codec); + + return 0; +} + +struct _coeff_div{ + unsigned int mclk; //pllout or MCLK + unsigned int bclk; //master mode + unsigned int rate; + unsigned int reg_val; +}; +/*PLL divisors*/ +struct _pll_div { + u32 pll_in; + u32 pll_out; + u16 regvalue; +}; + +static const struct _pll_div codec_master_pll_div[] = { + + { 2048000, 8192000, 0x0ea0}, + { 3686400, 8192000, 0x4e27}, + { 12000000, 8192000, 0x456b}, + { 13000000, 8192000, 0x495f}, + { 13100000, 8192000, 0x0320}, + { 2048000, 11289600, 0xf637}, + { 3686400, 11289600, 0x2f22}, + { 12000000, 11289600, 0x3e2f}, + { 13000000, 11289600, 0x4d5b}, + { 13100000, 11289600, 0x363b}, + { 2048000, 16384000, 0x1ea0}, + { 3686400, 16384000, 0x9e27}, + { 12000000, 16384000, 0x452b}, + { 13000000, 16384000, 0x542f}, + { 13100000, 16384000, 0x03a0}, + { 2048000, 16934400, 0xe625}, + { 3686400, 16934400, 0x9126}, + { 12000000, 16934400, 0x4d2c}, + { 13000000, 16934400, 0x742f}, + { 13100000, 16934400, 0x3c27}, + { 2048000, 22579200, 0x2aa0}, + { 3686400, 22579200, 0x2f20}, + { 12000000, 22579200, 0x7e2f}, + { 13000000, 22579200, 0x742f}, + { 13100000, 22579200, 0x3c27}, + { 2048000, 24576000, 0x2ea0}, + { 3686400, 24576000, 0xee27}, + { 12000000, 24576000, 0x2915}, + { 13000000, 24576000, 0x772e}, + { 13100000, 24576000, 0x0d20}, + { 26000000, 24576000, 0x2027}, + { 26000000, 22579200, 0x392f}, + { 24576000, 22579200, 0x0921}, + { 24576000, 24576000, 0x02a0}, +}; + +static const struct _pll_div codec_slave_pll_div[] = { + { 256000, 4096000, 0x3ea0}, + { 352800, 5644800, 0x3ea0}, + { 512000, 8192000, 0x3ea0}, + { 705600, 11289600, 0x3ea0}, + { 1024000, 16384000, 0x3ea0}, + { 1411200, 22579200, 0x3ea0}, + { 1536000, 24576000, 0x3ea0}, + { 2048000, 16384000, 0x1ea0}, + { 2822400, 22579200, 0x1ea0}, + { 3072000, 24576000, 0x1ea0}, + { 705600, 11289600, 0x3ea0}, + { 705600, 8467200, 0x3ab0}, + +}; + +struct _coeff_div coeff_div[] = { + //sysclk is 256fs + { 2048000, 8000 * 32, 8000, 0x1000}, + { 2048000, 8000 * 64, 8000, 0x0000}, + { 2822400, 11025 * 32, 11025, 0x1000}, + { 2822400, 11025 * 64, 11025, 0x0000}, + { 4096000, 16000 * 32, 16000, 0x1000}, + { 4096000, 16000 * 64, 16000, 0x0000}, + { 5644800, 22050 * 32, 22050, 0x1000}, + { 5644800, 22050 * 64, 22050, 0x0000}, + { 8192000, 32000 * 32, 32000, 0x1000}, + { 8192000, 32000 * 64, 32000, 0x0000}, + {11289600, 44100 * 32, 44100, 0x1000}, + {11289600, 44100 * 64, 44100, 0x0000}, + {12288000, 48000 * 32, 48000, 0x1000}, + {12288000, 48000 * 64, 48000, 0x0000}, + //sysclk is 512fs + { 4096000, 8000 * 32, 8000, 0x3000}, + { 4096000, 8000 * 64, 8000, 0x2000}, + { 5644800, 11025 * 32, 11025, 0x3000}, + { 5644800, 11025 * 64, 11025, 0x2000}, + { 8192000, 16000 * 32, 16000, 0x3000}, + { 8192000, 16000 * 64, 16000, 0x2000}, + {11289600, 22050 * 32, 22050, 0x3000}, + {11289600, 22050 * 64, 22050, 0x2000}, + {16384000, 32000 * 32, 32000, 0x3000}, + {16384000, 32000 * 64, 32000, 0x2000}, + {22579200, 44100 * 32, 44100, 0x3000}, + {22579200, 44100 * 64, 44100, 0x2000}, + {24576000, 48000 * 32, 48000, 0x3000}, + {24576000, 48000 * 64, 48000, 0x2000}, + //sysclk is 24.576Mhz or 22.579200Mhz + {24576000, 8000 * 32, 8000, 0x7080}, + {24576000, 8000 * 64, 8000, 0x6080}, + {24576000, 16000 * 32, 16000, 0x5080}, + {24576000, 16000 * 64, 16000, 0x4080}, + {24576000, 24000 * 32, 24000, 0x5000}, + {24576000, 24000 * 64, 24000, 0x4000}, + {24576000, 32000 * 32, 32000, 0x3080}, + {24576000, 32000 * 64, 32000, 0x2080}, + + {22579200, 11025 * 32, 11025, 0x7000}, + {22579200, 11025 * 64, 11025, 0x6000}, + {22579200, 22050 * 32, 22050, 0x5000}, + {22579200, 22050 * 64, 22050, 0x4000}, + +}; + + + + +static int get_coeff(int mclk, int rate, int timesofbclk) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if ((coeff_div[i].mclk == mclk) + && (coeff_div[i].rate == rate) + && ((coeff_div[i].bclk / coeff_div[i].rate) == timesofbclk)) + return i; + } + + return -1; +} + +static int get_coeff_in_slave_mode(int mclk, int rate) +{ + return get_coeff(mclk, rate, timesofbclk); +} + +static int get_coeff_in_master_mode(int mclk, int rate, int bclk) +{ + return get_coeff(mclk, rate, (bclk / rate)); +} +static void rt5631_set_dmic_params(struct snd_soc_codec *codec, struct snd_pcm_hw_params *params) +{ +// struct rt5631_priv *rt5631 = codec->private_data; + + unsigned int rate; + + printk(KERN_DEBUG "enter %s\n", __func__); + rt5631_write_mask(codec, RT5631_GPIO_CTRL, GPIO_PIN_FUN_SEL_GPIO_DIMC|GPIO_DMIC_FUN_SEL_DIMC + , GPIO_PIN_FUN_SEL_MASK|GPIO_DMIC_FUN_SEL_MASK); + rt5631_write_mask(codec, RT5631_DIG_MIC_CTRL, DMIC_ENA, DMIC_ENA_MASK); + rt5631_write_mask(codec, RT5631_DIG_MIC_CTRL,DMIC_L_CH_LATCH_FALLING|DMIC_R_CH_LATCH_RISING + ,DMIC_L_CH_LATCH_MASK|DMIC_R_CH_LATCH_MASK); + + rate = params_rate(params); + switch (rate) { + case 44100: + case 48000: + rt5631_write_mask(codec, RT5631_DIG_MIC_CTRL, DMIC_CLK_CTRL_TO_32FS, DMIC_CLK_CTRL_MASK); + break; + case 32000: + case 22050: + rt5631_write_mask(codec, RT5631_DIG_MIC_CTRL, DMIC_CLK_CTRL_TO_64FS, DMIC_CLK_CTRL_MASK); + break; + case 16000: + case 11025: + case 8000: + rt5631_write_mask(codec, RT5631_DIG_MIC_CTRL, DMIC_CLK_CTRL_TO_128FS, DMIC_CLK_CTRL_MASK); + break; + } + + rt5631_write_mask(codec, RT5631_DIG_MIC_CTRL, + DMIC_L_CH_UNMUTE | DMIC_R_CH_UNMUTE, DMIC_L_CH_MUTE_MASK | DMIC_R_CH_MUTE_MASK); + +} + +static int rt5631_hifi_pcm_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ +// struct snd_soc_codec *codec = dai->codec; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->card->codec; + struct rt5631_priv *rt5631 = codec->private_data; + int stream = substream->stream; + unsigned int iface = 0; + int rate = params_rate(params); + int coeff = 0; + unsigned int CodecRegData; + + printk(KERN_DEBUG "enter %s\n", __func__); + + if (!rt5631->master) + coeff = get_coeff_in_slave_mode(rt5631->sysclk, rate); + else + coeff = get_coeff_in_master_mode(rt5631->sysclk, rate, rate * timesofbclk); + + if (coeff < 0) { + printk(KERN_ERR "%s get_coeff err!\n", __func__); + // return -EINVAL; + } + + switch (params_format(params)) + { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + iface |= 0x0004; + break; + case SNDRV_PCM_FORMAT_S24_LE: + iface |= 0x0008; + break; + case SNDRV_PCM_FORMAT_S8: + iface |= 0x000c; + break; + default: + return -EINVAL; + } + + if (stream == SNDRV_PCM_STREAM_CAPTURE) { + if (rt5631->dmic_used_flag) //use Digital Mic + rt5631_set_dmic_params(codec, params); + else //use Analog Mic + { + CodecRegData=rt5631_read(codec,RT5631_ADC_REC_MIXER); + if((CodecRegData&0x4040)==0) //use stereo mic from mic1&mic2,no copy ADC channel + { + rt5631_write_mask(codec, RT5631_INT_ST_IRQ_CTRL_2, 0x0000, 0xc000); + } + else if((CodecRegData&0x4000)==0) //use mic1,copy ADC left to right + { + rt5631_write_mask(codec, RT5631_INT_ST_IRQ_CTRL_2, 0x4000, 0xc000); + } + else if((CodecRegData&0x0040)==0)//use mic2,copy ADC right to left + { + rt5631_write_mask(codec, RT5631_INT_ST_IRQ_CTRL_2, 0x8000, 0xc000); + } + else + { + rt5631_write_mask(codec, RT5631_INT_ST_IRQ_CTRL_2, 0x0000, 0xc000); + } + + } + } + + rt5631_write_mask(codec, RT5631_SDP_CTRL, iface, SDP_I2S_DL_MASK); + + if(coeff>=0) + rt5631_write(codec, RT5631_STEREO_AD_DA_CLK_CTRL, coeff_div[coeff].reg_val); + + + rt5631_write_mask(codec, RT5631_PWR_MANAG_ADD1, 0x80e0, 0x80e0); + +return 0; +} + +static int rt5631_hifi_codec_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct rt5631_priv *rt5631 = codec->private_data; + u16 iface = 0; + + printk(KERN_DEBUG "enter %s\n", __func__); + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5631->master = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + iface |= (0x0001 << 15); + rt5631->master = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= (0x0001); + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= (0x0002); + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= (0x0003); + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= (0x0001 << 7); + break; + default: + return -EINVAL; + } + + rt5631_write(codec, RT5631_SDP_CTRL, iface); + + return 0; +} +static int rt5631_hifi_codec_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 rt5631_priv *rt5631 = codec->private_data; + + printk(KERN_DEBUG "enter %s\n", __func__); + if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) { + rt5631->sysclk = freq; + return 0; + } + + printk("unsupported sysclk freq %u for audio i2s\n", freq); + printk("Set sysclk to 24.576Mhz by default\n"); + + rt5631->sysclk = 24576000; + return 0; + //return -EINVAL; +} + +static int rt5631_codec_set_dai_pll(struct snd_soc_dai *codec_dai, + int pll_id, unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct rt5631_priv *rt5631 = codec->private_data; + int i; + int ret = -EINVAL; + + + printk(KERN_DEBUG "enter %s\n", __func__); +// rt5631_write_mask(codec, RT5631_PWR_MANAG_ADD2, 0, PWR_PLL); + + if (!freq_in || !freq_out) + return 0; + + if (rt5631->master) { + for (i = 0; i < ARRAY_SIZE(codec_master_pll_div); i ++) { + + if ((freq_in == codec_master_pll_div[i].pll_in) && (freq_out == codec_master_pll_div[i].pll_out)) { + + rt5631_write(codec, RT5631_PLL_CTRL, codec_master_pll_div[i].regvalue); + rt5631_write_mask(codec, RT5631_PWR_MANAG_ADD2, PWR_PLL, PWR_PLL); + schedule_timeout_uninterruptible(msecs_to_jiffies(20)); + rt5631_write(codec, RT5631_GLOBAL_CLK_CTRL, 0x4000); + ret = 0; + + } + + } + } else { + + for (i = 0; i < ARRAY_SIZE(codec_slave_pll_div); i ++) { + + if ((freq_in == codec_slave_pll_div[i].pll_in) && (freq_out == codec_slave_pll_div[i].pll_out)) { + + rt5631_write(codec, RT5631_PLL_CTRL, codec_slave_pll_div[i].regvalue); + rt5631_write_mask(codec, RT5631_PWR_MANAG_ADD2, PWR_PLL, PWR_PLL); + schedule_timeout_uninterruptible(msecs_to_jiffies(20)); + rt5631_write(codec, RT5631_GLOBAL_CLK_CTRL, 0x5000); + ret = 0; + + } + + } + } + + return 0; +} + +static void rt5631_hifi_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai) +{ + +} + +#define RT5631_STEREO_RATES (SNDRV_PCM_RATE_8000_48000) +#define RT5631_FORMAT (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S8) + +struct snd_soc_dai_ops rt5631_ops = { + .hw_params = rt5631_hifi_pcm_params, + .set_fmt = rt5631_hifi_codec_set_dai_fmt, + .set_sysclk = rt5631_hifi_codec_set_dai_sysclk, + .set_pll = rt5631_codec_set_dai_pll, + .shutdown = rt5631_hifi_shutdown, +}; + +struct snd_soc_dai rt5631_dai[] = { + { + .name = "RT5631 HIFI", + .id = 1, + .playback = { + .stream_name = "HIFI Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5631_STEREO_RATES, + .formats = RT5631_FORMAT, + } , + .capture = { + .stream_name = "HIFI Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5631_STEREO_RATES, + .formats = RT5631_FORMAT, + }, + .ops =&rt5631_ops, + }, + + { + .name = "RT5631 Reserved", + .id = 2, + } +}; + +EXPORT_SYMBOL_GPL(rt5631_dai); + +static int rt5631_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) +{ + printk(KERN_DEBUG "enter %s\n", __func__); + + printk("rt5631_set_bias_level=%d\n",level); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + rt5631_write_mask(codec, RT5631_PWR_MANAG_ADD3,PWR_VREF|PWR_MAIN_BIAS, PWR_VREF|PWR_MAIN_BIAS); + rt5631_write_mask(codec, RT5631_PWR_MANAG_ADD2,0x000c, 0x000c); + break; + case SND_SOC_BIAS_STANDBY: + + break; + case SND_SOC_BIAS_OFF: + rt5631_write_mask(codec, RT5631_SPK_OUT_VOL ,0x8080, 0x8080); + rt5631_write_mask(codec, RT5631_HP_OUT_VOL ,0x8080, 0x8080); + rt5631_write(codec, RT5631_PWR_MANAG_ADD1, 0x0000); + rt5631_write(codec, RT5631_PWR_MANAG_ADD2, 0x0000); + rt5631_write(codec, RT5631_PWR_MANAG_ADD3, 0x0000); + rt5631_write(codec, RT5631_PWR_MANAG_ADD4, 0x0000); + break; + } + + codec->bias_level = level; + return 0; +} + +static int rt5631_init(struct snd_soc_device *socdev) +{ + struct snd_soc_codec *codec = socdev->card->codec; + int ret; + + printk(KERN_DEBUG "enter %s\n", __func__); + codec->name = "RT5631"; + codec->owner = THIS_MODULE; + codec->read = rt5631_read; + codec->write = rt5631_write; + codec->set_bias_level = rt5631_set_bias_level; + codec->dai = rt5631_dai; + codec->num_dai = 2; + codec->reg_cache_size = ARRAY_SIZE(rt5631_reg); + codec->reg_cache_step = 1; + codec->reg_cache = kmemdup(rt5631_reg, sizeof(rt5631_reg), GFP_KERNEL); + + if (codec->reg_cache == NULL) + return -ENOMEM; + + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + printk(KERN_ERR "rt5631: failed to create pcms\n"); + goto pcm_err; + } + + rt5631_reset(codec); + rt5631_write_mask(codec, RT5631_PWR_MANAG_ADD3,PWR_VREF|PWR_MAIN_BIAS, PWR_VREF|PWR_MAIN_BIAS); + schedule_timeout_uninterruptible(msecs_to_jiffies(110)); + rt5631_write_mask(codec, RT5631_PWR_MANAG_ADD3, PWR_FAST_VREF_CTRL, PWR_FAST_VREF_CTRL); + codec->bias_level = SND_SOC_BIAS_STANDBY; + schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(100)); + + rt5631_reg_init(codec); + rt5631_add_controls(codec); + rt5631_add_widgets(codec); + ret = snd_soc_init_card(socdev); + if (ret < 0) { + printk(KERN_ERR "rt5631: failed to register card!\n"); + goto card_err; + } + printk(KERN_INFO "rt5631 initial ok!\n"); + return 0; + + +card_err: + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +pcm_err: + kfree(codec->reg_cache); + codec->reg_cache = NULL; + return ret; +} + + +static const struct i2c_device_id rt5631_i2c_id[] = { + {"rt5631", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id); + +static int rt5631_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct snd_soc_device *socdev = rt5631_socdev; + struct snd_soc_codec *codec = socdev->card->codec; + int ret; + + printk(KERN_DEBUG "enter %s\n", __func__); + i2c_set_clientdata(i2c, codec); + codec->control_data = i2c; + + ret = rt5631_init(socdev); + if (ret < 0) + pr_err("failed to initialise rt5631!\n"); + + return ret; + + +} + +static int rt5631_i2c_remove(struct i2c_client *client) +{ + struct snd_soc_codec *codec = i2c_get_clientdata(client); + + printk(KERN_DEBUG "enter %s\n", __func__); + kfree(codec->reg_cache); + return 0; +} + +struct i2c_driver rt5631_i2c_driver = { + .driver = { + .name = "RT5631 I2C Codec", + .owner = THIS_MODULE, + }, + .probe = rt5631_i2c_probe, + .remove = rt5631_i2c_remove, + .id_table = rt5631_i2c_id, +}; + + +static int rt5631_add_i2c_device(struct platform_device *pdev, + const struct rt5631_setup_data *setup) +{ +#if 0 + struct i2c_board_info info; + struct i2c_adapter *adapter; + struct i2c_client *client; +#endif + int ret; + + printk(KERN_DEBUG "enter %s\n", __func__); + ret = i2c_add_driver(&rt5631_i2c_driver); + if (ret != 0) { + dev_err(&pdev->dev, "can't add i2c driver\n"); + return ret; + } + +#if 0 + memset(&info, '\0', sizeof(struct i2c_board_info)); + info.addr = setup->i2c_address; + info.platform_data = pdev; + strlcpy(info.type, "rt5631", I2C_NAME_SIZE); + + adapter = i2c_get_adapter(setup->i2c_bus); + if (!adapter) { + dev_err(&pdev->dev, "can't get i2c adapter %d\n", + setup->i2c_bus); + goto err_driver; + } + + client = i2c_new_device(adapter, &info); + i2c_put_adapter(adapter); + if (!client) { + dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", + (unsigned int)info.addr); + goto err_driver; + } +#endif + + return 0; + +#if 0 +err_driver: + i2c_del_driver(&rt5631_i2c_driver); + return -ENODEV; +#endif + +} + +static void rt5631_work(struct work_struct *work) +{ + struct snd_soc_codec *codec = container_of(work, struct snd_soc_codec, delayed_work.work); + + rt5631_set_bias_level(codec, codec->bias_level); +} + +static int rt5631_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct rt5631_setup_data *setup; + struct snd_soc_codec *codec; + struct rt5631_priv *rt5631; + int ret = 0; + + printk(KERN_DEBUG "enter %s\n", __func__); + + pr_info("RT5631 Audio Codec %s", RT5631_VERSION); + + if(socdev->codec_data) + { + setup = socdev->codec_data; + } + + codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (codec == NULL) { + return -ENOMEM; + } + + + rt5631 = kzalloc(sizeof(struct rt5631_priv), GFP_KERNEL); + if (rt5631 == NULL) { + ret = -ENOMEM; + goto priv_err; + } + codec->private_data = rt5631; + socdev->card->codec = codec; + mutex_init(&codec->mutex); + rt5631_socdev = socdev; + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + INIT_DELAYED_WORK(&codec->delayed_work, rt5631_work); + +#if 0 + if (setup->i2c_address) { + codec->hw_write = (hw_write_t)i2c_master_send; + + ret = rt5631_add_i2c_device(pdev, setup); + } +#else + //probe i2c device driver + { + codec->hw_write = (hw_write_t)i2c_master_send; + // codec->hw_read = (hw_read_t)i2c_master_recv; + ret = rt5631_add_i2c_device(pdev, setup); + } +#endif + + if (ret != 0) { + goto i2c_device_err; + } + return 0; + +i2c_device_err: + kfree(rt5631); + socdev->card->codec->private_data = NULL; + +priv_err: + kfree(codec); + socdev->card->codec = NULL; + + return ret; +} + +static int run_delayed_work(struct delayed_work *dwork) +{ + int ret; + + ret = cancel_delayed_work(dwork); + + if (ret) { + schedule_delayed_work(dwork, 0); + flush_scheduled_work(); + } + + return ret; +} + +static int rt5631_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + + struct snd_soc_codec *codec =socdev->card->codec; + + if (codec->control_data) + rt5631_set_bias_level(codec, SND_SOC_BIAS_OFF); + run_delayed_work(&codec->delayed_work); + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); + i2c_unregister_device(codec->control_data); + i2c_del_driver(&rt5631_i2c_driver); + kfree(codec->private_data); + kfree(codec); + + return 0; + +} + +static int rt5631_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + + struct snd_soc_codec *codec =socdev->card->codec; + + rt5631_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int rt5631_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + +// int i; +// u8 data[3]; +// u16 *cache = codec->reg_cache; + +#if 1 + rt5631_write_mask(codec, RT5631_PWR_MANAG_ADD3,PWR_VREF|PWR_MAIN_BIAS, PWR_VREF|PWR_MAIN_BIAS); + schedule_timeout_uninterruptible(msecs_to_jiffies(110)); + rt5631_write_mask(codec, RT5631_PWR_MANAG_ADD3, PWR_FAST_VREF_CTRL, PWR_FAST_VREF_CTRL); + rt5631_reg_init(codec); + rt5631_write_mask(codec, RT5631_PWR_MANAG_ADD1, 0x80e0, 0x80e0); +#else + printk(KERN_DEBUG "enter %s\n", __func__); + for (i = 0; i < ARRAY_SIZE(rt5631_reg); i++) { + if (i == RT5631_RESET) + continue; + data[0] = i << 1; + data[1] = (0xff00 & cache[i]) >> 8; + data[2] = 0x00ff & cache[i]; + codec->hw_write(codec->control_data, data, 2); + } + + rt5631_set_bias_level(codec, SND_SOC_BIAS_STANDBY); +#endif + + if (codec->suspend_bias_level == SND_SOC_BIAS_ON) { + rt5631_set_bias_level(codec, SND_SOC_BIAS_PREPARE); + codec->bias_level = SND_SOC_BIAS_ON; + schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(100)); + } + + return 0; + + +} + +struct snd_soc_codec_device soc_codec_dev_rt5631 = +{ + .probe = rt5631_probe, + .remove = rt5631_remove, + .suspend = rt5631_suspend, + .resume = rt5631_resume, +}; + +EXPORT_SYMBOL_GPL(soc_codec_dev_rt5631); + +static int __init rt5631_modinit(void) +{ + return snd_soc_register_dais(rt5631_dai, ARRAY_SIZE(rt5631_dai)); +} + +static void __exit rt5631_modexit(void) +{ + snd_soc_unregister_dais(rt5631_dai, ARRAY_SIZE(rt5631_dai)); +} + +module_init(rt5631_modinit); +module_exit(rt5631_modexit); +MODULE_DESCRIPTION("ASoC RT5631 driver"); +MODULE_AUTHOR("flove"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt5631.h b/sound/soc/codecs/rt5631.h new file mode 100644 index 000000000000..b2e316f08d25 --- /dev/null +++ b/sound/soc/codecs/rt5631.h @@ -0,0 +1,578 @@ +#ifndef __RTCODEC5631_H__ +#define __RTCODEC5631_H__ + + +#define RT5631_RESET 0X00 //RESET CODEC TO DEFAULT +#define RT5631_SPK_OUT_VOL 0X02 //SPEAKER OUT VOLUME +#define RT5631_HP_OUT_VOL 0X04 //HEADPHONE OUTPUT VOLUME +#define RT5631_MONO_AXO_1_2_VOL 0X06 //MONO/AUXOUT OUTPUT VOLUME +#define RT5631_AUX_IN_VOL 0X0A //AUX IN VOLUME +#define RT5631_STEREO_DAC_VOL_1 0X0C //STEREO DAC VOLUME 1 +#define RT5631_MIC_CTRL_1 0X0E //MICROPHONE CONTROL 1 +#define RT5631_STEREO_DAC_VOL_2 0X10 //STEREO DAC VOLUME 2 +#define RT5631_ADC_CTRL_1 0X12 //STEREO ADC CONTROL 1 +#define RT5631_ADC_REC_MIXER 0X14 //ADC RECORD MIXER CONTROL +#define RT5631_ADC_CTRL_2 0X16 //STEREO ADC CONTROL 2 +#define RT5631_OUTMIXER_L_CTRL 0X1A //LEFT OUTPUT MIXER CONTROL +#define RT5631_OUTMIXER_R_CTRL 0X1C //RIGHT OUTPUT MIXER CONTROL +#define RT5631_AXO1MIXER_CTRL 0X1E //LOUT MIXER CONTROL +#define RT5631_AXO2MIXER_CTRL 0X20 //LOUT MIXER CONTROL +#define RT5631_MIC_CTRL_2 0X22 //MICROPHONE CONTROL 2 +#define RT5631_DIG_MIC_CTRL 0X24 //DIGITAL MICROPHONE CONTROL +#define RT5631_MONO_INPUT_VOL 0X26 //MONO INPUT VOLUME +#define RT5631_SPK_MIXER_CTRL 0X28 //SPEAKER MIXER CONTROL +#define RT5631_SPK_MONO_OUT_CTRL 0X2A //SPEAKER/MONO OUTPUT CONTROL +#define RT5631_SPK_MONO_HP_OUT_CTRL 0X2C //SPEAKER/MONO/HP OUTPUT CONTROL +#define RT5631_SDP_CTRL 0X34 //STEREO I2S SERIAL DATA PORT CONTROL +#define RT5631_STEREO_AD_DA_CLK_CTRL 0X38 //STEREO AD/DA CLOCK CONTROL +#define RT5631_PWR_MANAG_ADD1 0X3A //POWER MANAGMENT ADDITION 1 +#define RT5631_PWR_MANAG_ADD2 0X3B //POWER MANAGMENT ADDITION 2 +#define RT5631_PWR_MANAG_ADD3 0X3C //POWER MANAGMENT ADDITION 3 +#define RT5631_PWR_MANAG_ADD4 0X3E //POWER MANAGMENT ADDITION 4 +#define RT5631_GEN_PUR_CTRL_REG 0X40 //GENERAL PURPOSE CONTROL REGISTER +#define RT5631_GLOBAL_CLK_CTRL 0X42 //GLOBAL CLOCK CONTROL +#define RT5631_PLL_CTRL 0X44 //PLL CONTROL +#define RT5631_INT_ST_IRQ_CTRL_1 0X48 //INTERNAL STATUS AND IRQ CONTROL 1 +#define RT5631_INT_ST_IRQ_CTRL_2 0X4A //INTERNAL STATUS AND IRQ CONTROL 2 +#define RT5631_GPIO_CTRL 0X4C //GPIO OUTPUT CONTROL +#define RT5631_MISC_CTRL 0X52 //MISC CONTROL +#define RT5631_DEPOP_FUN_CTRL_1 0X54 //DE-POP FUNCTION CONTROL 1 +#define RT5631_DEPOP_FUN_CTRL_2 0X56 //DE-POP FUNCTION CONTROL 2 +#define RT5631_JACK_DET_CTRL 0X5A //JACK DETECT CONTROL REGISTER +#define RT5631_SOFT_VOL_CTRL 0X5C //SOFT VOLUME CONTROL +#define RT5631_ALC_CTRL_1 0X64 //ALC CONTROL 1 +#define RT5631_ALC_CTRL_2 0X65 //ALC CONTROL 2 +#define RT5631_ALC_CTRL_3 0X66 //ALC CONTROL 3 +#define RT5631_PSEUDO_SPATL_CTRL 0X68 //PSEUDO STEREO AND SPATIAL EFFECT CONTROL +#define RT5631_INDEX_ADD 0X6A //INDEX ADDRESS +#define RT5631_INDEX_DATA 0X6C //INDEX DATA +#define RT5631_EQ_CTRL 0X6E //EQ CONTROL +#define RT5631_VENDOR_ID1 0x7C //VENDOR ID1 +#define RT5631_VENDOR_ID2 0x7E //VENDOR ID2 + + +//global definition +#define RT_L_MUTE (0x1<<15) //MUTE LEFT CONTROL BIT +#define RT_R_MUTE (0x1<<7) //MUTE RIGHT CONTROL BIT + +//Speaker Output Control(0x02) +#define SPK_L_VOL_SEL_MASK (0x1<<14) //Speaker left channel volume input select MASK +#define SPK_L_VOL_SEL_VMID (0x0<<14) //Speaker left channel volume input select VMID +#define SPK_L_VOL_SEL_SPKMIX_L (0x1<<14) //Speaker left channel volume input select SPKMIXER LEFT +#define SPK_R_VOL_SEL_MASK (0x1<< 6) //Speaker right channel volume input select MASK +#define SPK_R_VOL_SEL_VMID (0x0<< 6) //Speaker right channel volume input select VMID +#define SPK_R_VOL_SEL_SPKMIX_R (0x1<< 6) //Speaker right channel volume input select SPKMIXER RIGHT + +//Headphone Output Control(0x04) +#define HP_L_VOL_SEL_MASK (0x1<<14) //HP left channel volume input select MASK +#define HP_L_VOL_SEL_VMID (0x0<<14) //HP left channel volume input select VMID +#define HP_L_VOL_SEL_OUTMIX_L (0x1<<14) //HP left channel volume input select OUTMIXER LEFT +#define HP_R_VOL_SEL_MASK (0x1<< 6) //HP right channel volume input select MASK +#define HP_R_VOL_SEL_VMID (0x0<< 6) //HP right channel volume input select VMID +#define HP_R_VOL_SEL_OUTMIX_R (0x1<< 6) //HP right channel volume input select OUTMIXER RIGHT + + +//Output Control for AUXOUT/MONO(0x06) +#define AUXOUT_1_VOL_SEL_MASK (0x1<<14) //LOUT channel volume input select MASK +#define AUXOUT_1_VOL_SEL_VMID (0x0<<14) //LOUT channel volume input select VMID +#define AUXOUT_1_VOL_SEL_OUTMIX_L (0x1<<14) //LOUT channel volume input select OUTMIXER LEFT +#define MUTE_MONO (0x1<<13) //Mute Mono control +#define AUXOUT_2_VOL_SEL_MASK (0x1<< 6) //ROUT channel volume input select MASK +#define AUXOUT_2_VOL_SEL_VMID (0x0<< 6) //ROUT channel volume input select VMID +#define AUXOUT_2_VOL_SEL_OUTMIX_R (0x1<< 6) //ROUT channel volume input select OUTMIXER RIGHT + + +//Microphone Input Control 1(0x0E) +#define MIC1_DIFF_INPUT_CTRL (0x1<<15) //MIC1 different input control +#define MIC2_DIFF_INPUT_CTRL (0x1<< 7) //MIC2 different input control + + +//ADC Recording Mixer Control(0x14) +#define M_OUTMIXER_L_TO_RECMIXER_L (0x1<<15) //Mute left OUTMIXER to left RECMIXER +#define M_MIC1_TO_RECMIXER_L (0x1<<14) //Mute mic1 to left RECMIXER +#define M_AXIL_TO_RECMIXER_L (0x1<<13) //Mute AXIL to left RECMIXER +#define M_MONO_IN_TO_RECMIXER_L (0x1<<12) //Mute BB_RX to left RECMIXER +#define M_OUTMIXER_R_TO_RECMIXER_R (0x1<< 7) //Mute right OUTMIXER to right RECMIXER +#define M_MIC2_TO_RECMIXER_R (0x1<< 6) //Mute mic2 to right RECMIXER +#define M_AXIR_TO_RECMIXER_R (0x1<< 5) //Mute AXIR to right RECMIXER +#define M_MONO_IN_TO_RECMIXER_R (0x1<< 4) //Mute BB_RX to right RECMIXER + +//Left Output Mixer Control(0x1A) +#define M_RECMIXER_L_TO_OUTMIXER_L (0x1<<15) //Mute Left RecMixer to Left OutMixer +#define M_RECMIXER_R_TO_OUTMIXER_L (0x1<<14) //Mute Right RecMixer to Left OutMixer +#define M_DAC_L_TO_OUTMIXER_L (0x1<<13) //Mute Left Dac to Left OutMixer +#define M_MIC1_TO_OUTMIXER_L (0x1<<12) //Mute Mic1 to Left OutMixer +#define M_MIC2_TO_OUTMIXER_L (0x1<<11) //Mute Mic2 to Left OutMixer +#define M_MONO_IN_P_TO_OUTMIXER_L (0x1<<10) //Mute MONO IN positive to Left OutMixer +#define M_AXIL_TO_OUTMIXER_L (0x1<< 9) //Mute AXIL to Left OutMixer +#define M_AXIR_TO_OUTMIXER_L (0x1<< 8) //Mute AXIR to Left OutMixer + + +//Right Output Mixer Control(0x1C) +#define M_RECMIXER_L_TO_OUTMIXER_R (0x1<<15) //Mute Left RecMixer to Right OutMixer +#define M_RECMIXER_R_TO_OUTMIXER_R (0x1<<14) //Mute Right RecMixer to Right OutMixer +#define M_DAC_R_TO_OUTMIXER_R (0x1<<13) //Mute Left Dac to Right OutMixer +#define M_MIC1_TO_OUTMIXER_R (0x1<<12) //Mute Mic1 to Right OutMixer +#define M_MIC2_TO_OUTMIXER_R (0x1<<11) //Mute Mic2 to Right OutMixer +#define M_MONO_IN_N_TO_OUTMIXER_R (0x1<<10) //Mute MONO IN Negative to Right OutMixer +#define M_AXIL_TO_OUTMIXER_R (0x1<< 9) //Mute AXIL to Right OutMixer +#define M_AXIR_TO_OUTMIXER_R (0x1<< 8) //Mute AXIR to Right OutMixer + + +//Lout Mixer Control(0x1E) +#define M_MIC1_TO_AXO1MIXER (0x1<<15) //Mute MIC1 to LOUT Mixer +#define M_MIC2_TO_AXO1MIXER (0x1<<11) //Mute MIC2 to LOUT Mixer +#define M_OUTMIXER_L_TO_AXO1MIXER (0x1<< 7) //Mute Left Output mixer to LOUT Mixer +#define M_OUTMIXER_R_TO_AXO1MIXER (0x1<< 6) //Mute Right Output mixer to LOUT Mixer + + +//Rout Mixer Control(0x20) +#define M_MIC1_TO_AXO2MIXER (0x1<<15) //Mute MIC1 to ROUT Mixer +#define M_MIC2_TO_AXO2MIXER (0x1<<11) //Mute MIC2 to ROUT Mixer +#define M_OUTMIXER_L_TO_AXO2MIXER (0x1<< 7) //Mute Left Output mixer to ROUT Mixer +#define M_OUTMIXER_R_TO_AXO2MIXER (0x1<< 6) //Mute Right Output mixer to ROUT Mixer + +//Micphone Input Control 2(0x22) +#define MIC_BIAS_90_PRECNET_AVDD 1 +#define MIC_BIAS_75_PRECNET_AVDD 2 + +#define MIC1_BOOST_CTRL_MASK (0xf<<12) +#define MIC1_BOOST_CTRL_BYPASS (0x0<<12) +#define MIC1_BOOST_CTRL_20DB (0x1<<12) +#define MIC1_BOOST_CTRL_24DB (0x2<<12) +#define MIC1_BOOST_CTRL_30DB (0x3<<12) +#define MIC1_BOOST_CTRL_35DB (0x4<<12) +#define MIC1_BOOST_CTRL_40DB (0x5<<12) +#define MIC1_BOOST_CTRL_34DB (0x6<<12) +#define MIC1_BOOST_CTRL_50DB (0x7<<12) +#define MIC1_BOOST_CTRL_52DB (0x8<<12) + +#define MIC2_BOOST_CTRL_MASK (0xf<< 8) +#define MIC2_BOOST_CTRL_BYPASS (0x0<< 8) +#define MIC2_BOOST_CTRL_20DB (0x1<< 8) +#define MIC2_BOOST_CTRL_24DB (0x2<< 8) +#define MIC2_BOOST_CTRL_30DB (0x3<< 8) +#define MIC2_BOOST_CTRL_35DB (0x4<< 8) +#define MIC2_BOOST_CTRL_40DB (0x5<< 8) +#define MIC2_BOOST_CTRL_34DB (0x6<< 8) +#define MIC2_BOOST_CTRL_50DB (0x7<< 8) +#define MIC2_BOOST_CTRL_52DB (0x8<< 8) + +#define MICBIAS1_VOLT_CTRL_MASK (0x1<< 7) +#define MICBIAS1_VOLT_CTRL_90P (0x0<< 7) +#define MICBIAS1_VOLT_CTRL_75P (0x1<< 7) + +#define MICBIAS1_S_C_DET_MASK (0x1<< 6) +#define MICBIAS1_S_C_DET_DIS (0x0<< 6) +#define MICBIAS1_S_C_DET_ENA (0x1<< 6) + +#define MICBIAS1_SHORT_CURR_DET_MASK (0x3<< 4) +#define MICBIAS1_SHORT_CURR_DET_600UA (0x0<< 4) +#define MICBIAS1_SHORT_CURR_DET_1500UA (0x1<< 4) +#define MICBIAS1_SHORT_CURR_DET_2000UA (0x2<< 4) + +#define MICBIAS2_VOLT_CTRL_MASK (0x1<< 3) +#define MICBIAS2_VOLT_CTRL_90P (0x0<< 3) +#define MICBIAS2_VOLT_CTRL_75P (0x1<< 3) + +#define MICBIAS2_S_C_DET_MASK (0x1<< 2) +#define MICBIAS2_S_C_DET_DIS (0x0<< 2) +#define MICBIAS2_S_C_DET_ENA (0x1<< 2) + +#define MICBIAS2_SHORT_CURR_DET_MASK (0x3) +#define MICBIAS2_SHORT_CURR_DET_600UA (0x0) +#define MICBIAS2_SHORT_CURR_DET_1500UA (0x1) +#define MICBIAS2_SHORT_CURR_DET_2000UA (0x2) + + +//Digital Microphone Control(0x24) +#define DMIC_ENA_MASK (0x1<<15) +#define DMIC_ENA (0x1<<15) //use DMIC to ADC Digital filter +#define DMIC_DIS (0x0<<15) //use ADC mixer to ADC Digital filter + +#define DMIC_L_CH_MUTE_MASK (0x1<<13) +#define DMIC_L_CH_UNMUTE (0x0<<13) +#define DMIC_L_CH_MUTE (0x1<<13) + +#define DMIC_R_CH_MUTE_MASK (0x1<<12) +#define DMIC_R_CH_UNMUTE (0x0<<12) +#define DMIC_R_CH_MUTE (0x1<<12) + +#define DMIC_L_CH_LATCH_MASK (0x1<< 9) +#define DMIC_L_CH_LATCH_RISING (0x1<< 9) +#define DMIC_L_CH_LATCH_FALLING (0x0<< 9) + +#define DMIC_R_CH_LATCH_MASK (0x1<< 8) +#define DMIC_R_CH_LATCH_RISING (0x1<< 8) +#define DMIC_R_CH_LATCH_FALLING (0x0<< 8) + +#define DMIC_CLK_CTRL_MASK (0x3<<4) +#define DMIC_CLK_CTRL_TO_128FS (0x0<<4) +#define DMIC_CLK_CTRL_TO_64FS (0x1<<4) +#define DMIC_CLK_CTRL_TO_32FS (0x2<<4) + + +//Speaker Mixer Control(0x28) +#define M_RECMIXER_L_TO_SPKMIXER_L (0x1<<15) //Mute Left RecMixer to Left Speaker Mixer +#define M_MIC1_P_TO_SPKMIXER_L (0x1<<14) //Mute MIC1 Positive to Left Speaker Mixer +#define M_DAC_L_TO_SPKMIXER_L (0x1<<13) //Mute Left Dac to Left Speaker Mixer +#define M_OUTMIXER_L_TO_SPKMIXER_L (0x1<<12) //Mute Left OutMixer to Left Speaker Mixer + +#define M_RECMIXER_R_TO_SPKMIXER_R (0x1<< 7) //Mute Right RecMixer to Right Speaker Mixer +#define M_MIC2_P_TO_SPKMIXER_R (0x1<< 6) //Mute MIC1 Positive to Right Speaker Mixer +#define M_DAC_R_TO_SPKMIXER_R (0x1<< 5) //Mute Right Dac to Right Speaker Mixer +#define M_OUTMIXER_R_TO_SPKMIXER_R (0x1<< 4) //Mute Right OutMixer to Right Speaker Mixer + + + +//Speaker/Mono Output Control(0x2A) +#define M_SPKVOL_L_TO_SPOL_MIXER (0x1<<15) //Mute Left Speaker Volume to SPOL Mixer +#define M_SPKVOL_R_TO_SPOL_MIXER (0x1<<14) //Mute Right Speaker Volume to SPOL Mixer +#define M_SPKVOL_L_TO_SPOR_MIXER (0x1<<13) //Mute Left Speaker Volume to SPOR Mixer +#define M_SPKVOL_R_TO_SPOR_MIXER (0x1<<12) //Mute Right Speaker Volume to SPOR Mixer +#define M_OUTVOL_L_TO_MONOMIXER (0x1<<11) //Mute Left Output Volume to Mono Mixer +#define M_OUTVOL_R_TO_MONOMIXER (0x1<<10) //Mute Right Output Volume to Mono Mixer + + +//Speaker/Mono/HP Output Control(0x2C) +#define SPK_L_MUX_SEL_MASK (0x3<<14) //Left Speaker mux select Mask +#define SPK_L_MUX_SEL_SPKMIXER_L (0x0<<14) //Left Speaker mux select Speaker mixer left +#define SPK_L_MUX_SEL_MONO_IN (0x1<<14) //Left Speaker mux select MONO input +#define SPK_L_MUX_SEL_DAC_L (0x3<<14) //Left Speaker mux select Dac left + +#define SPK_R_MUX_SEL_MASK (0x3<<10) //Right Speaker mux select Mask +#define SPK_R_MUX_SEL_SPKMIXER_R (0x0<<10) //Right Speaker mux select Speaker mixer right +#define SPK_R_MUX_SEL_MONO_IN (0x1<<10) //Right Speaker mux select MONO input +#define SPK_R_MUX_SEL_DAC_R (0x3<<10) //Right Speaker mux select Dac right + +#define MONO_MUX_SEL_MASK (0x3<< 6) //Monoout mux select Mask +#define MONO_MUX_SEL_MONOMIXER (0x0<< 6) //Monoout mux select Mono Mixer +#define MONO_MUX_SEL_MONO_IN (0x1<< 6) //Monoout mux select MONO input + +#define HP_L_MUX_SEL_MASK (0x1<< 3) //HP left mux select Mask +#define HP_L_MUX_SEL_HPVOL_L (0x0<< 3) //HP left mux select left HP output volume +#define HP_L_MUX_SEL_DAC_L (0x1<< 3) //HP left mux select Dac left channel + +#define HP_R_MUX_SEL_MASK (0x1<< 2) //HP left mux select Mask +#define HP_R_MUX_SEL_HPVOL_R (0x0<< 2) //HP left mux select left HP output volume +#define HP_R_MUX_SEL_DAC_R (0x1<< 2) //HP left mux select Dac left channel + + +//Stereo I2S Serial Data Port Control(0x34) +#define SDP_MODE_SEL_MASK (0x1<<15) //Main I2S interface select MASK +#define SDP_MODE_SEL_MASTER (0x0<<15) //Main I2S interface select MASTER MODE +#define SDP_MODE_SEL_SLAVE (0x1<<15) //Main I2S interface select SLAVE MODE + +#define SDP_ADC_CPS_SEL_MASK (0x3<<10) //ADC Compress select Mask +#define SDP_ADC_CPS_SEL_OFF (0x0<<10) //ADC Compress select OFF +#define SDP_ADC_CPS_SEL_U_LAW (0x1<<10) //ADC Compress select u_law +#define SDP_ADC_CPS_SEL_A_LAW (0x2<<10) //ADC Compress select a_law + +#define SDP_DAC_CPS_SEL_MASK (0x3<< 8) //DAC Compress select Mask +#define SDP_DAC_CPS_SEL_OFF (0x0<< 8) //DAC Compress select OFF +#define SDP_DAC_CPS_SEL_U_LAW (0x1<< 8) //DAC Compress select u_law +#define SDP_DAC_CPS_SEL_A_LAW (0x2<< 8) //DAC Compress select a_law + +#define SDP_I2S_BCLK_POL_CTRL (0x1<<7) //0:Normal 1:Invert + +#define SDP_DAC_R_INV (0x1<<6) //0:Normal 1:Invert + +#define SDP_ADC_DATA_L_R_SWAP (0x1<<5) //0:ADC data appear at left phase of LRCK + //1:ADC data appear at right phase of LRCK +#define SDP_DAC_DATA_L_R_SWAP (0x1<<4) //0:DAC data appear at left phase of LRCK + //1:DAC data appear at right phase of LRCK +//Data Length Slection +#define SDP_I2S_DL_MASK (0x3<<2) //Stereo Serial Data Length mask +#define SDP_I2S_DL_16 (0x0<<2) //16 bits +#define SDP_I2S_DL_20 (0x1<<2) //20 bits +#define SDP_I2S_DL_24 (0x2<<2) //24 bits +#define SDP_I2S_DL_8 (0x3<<2) //8 bits + +//PCM Data Format Selection +#define SDP_I2S_DF_MASK (0x3) //main i2s Data Format mask +#define SDP_I2S_DF_I2S (0x0) //I2S FORMAT +#define SDP_I2S_DF_LEFT (0x1) //Left JUSTIFIED +#define SDP_I2S_DF_PCM_A (0x2) //PCM format A +#define SDP_I2S_DF_PCM_B (0x3) //PCM format B + +//Stereo AD/DA Clock Control(0x38h) +#define I2S_PRE_DIV_MASK (0x7<<13) +#define I2S_PRE_DIV_1 (0x0<<13) //DIV 1 +#define I2S_PRE_DIV_2 (0x1<<13) //DIV 2 +#define I2S_PRE_DIV_4 (0x2<<13) //DIV 4 +#define I2S_PRE_DIV_8 (0x3<<13) //DIV 8 +#define I2S_PRE_DIV_16 (0x4<<13) //DIV 16 +#define I2S_PRE_DIV_32 (0x5<<13) //DIV 32 + +#define I2S_LRCK_SEL_N_BCLK_MASK (0x1<<12) //CLOCK RELATIVE OF BCLK AND LCRK +#define I2S_LRCK_SEL_64_BCLK (0x0<<12) //64FS +#define I2S_LRCK_SEL_32_BCLK (0x1<<12) //32FS + +#define DAC_OSR_SEL_MASK (0x3<<10) +#define DAC_OSR_SEL_128FS (0x3<<10) +#define DAC_OSR_SEL_64FS (0x3<<10) +#define DAC_OSR_SEL_32FS (0x3<<10) +#define DAC_OSR_SEL_16FS (0x3<<10) + +#define ADC_OSR_SEL_MASK (0x3<< 8) +#define ADC_OSR_SEL_128FS (0x3<< 8) +#define ADC_OSR_SEL_64FS (0x3<< 8) +#define ADC_OSR_SEL_32FS (0x3<< 8) +#define ADC_OSR_SEL_16FS (0x3<< 8) + +#define ADDA_FILTER_CLK_SEL_256FS (0<<7) //256FS +#define ADDA_FILTER_CLK_SEL_384FS (1<<7) //384FS + + + +//Power managment addition 1 (0x3A),0:Disable,1:Enable +#define PWR_MAIN_I2S_EN (0x1<<15) +#define PWR_CLASS_D (0x1<<12) +#define PWR_ADC_L_CLK (0x1<<11) +#define PWR_ADC_R_CLK (0x1<<10) +#define PWR_DAC_L_CLK (0x1<< 9) +#define PWR_DAC_R_CLK (0x1<< 8) +#define PWR_DAC_REF (0x1<< 7) +#define PWR_DAC_L_TO_MIXER (0x1<< 6) +#define PWR_DAC_R_TO_MIXER (0x1<<5) + + +//Power managment addition 2 (0x3B),0:Disable,1:Enable +#define PWR_OUTMIXER_L (0x1<<15) +#define PWR_OUTMIXER_R (0x1<<14) +#define PWR_SPKMIXER_L (0x1<<13) +#define PWR_SPKMIXER_R (0x1<<12) +#define PWR_RECMIXER_L (0x1<<11) +#define PWR_RECMIXER_R (0x1<<10) +#define PWR_MIC1_BOOT_GAIN (0x1<< 5) +#define PWR_MIC2_BOOT_GAIN (0x1<< 4) +#define PWR_MICBIAS1_VOL (0x1<< 3) +#define PWR_MICBIAS2_VOL (0x1<< 2) +#define PWR_PLL (0x1<< 1) + + +//Power managment addition 3(0x3C),0:Disable,1:Enable +#define PWR_VREF (0x1<<15) +#define PWR_FAST_VREF_CTRL (0x1<<14) +#define PWR_MAIN_BIAS (0x1<<13) +#define PWR_AXO1MIXER (0x1<<11) +#define PWR_AXO2MIXER (0x1<<10) +#define PWR_MONOMIXER (0x1<< 9) +#define PWR_MONO_DEPOP_DIS (0x1<< 8) +#define PWR_MONO_AMP_EN (0x1<< 7) +#define PWR_CHARGE_PUMP (0x1<<4) +#define PWR_HP_L_AMP (0x1<<3) +#define PWR_HP_R_AMP (0x1<<2) +#define PWR_HP_DEPOP_DIS (0x1<<1) +#define PWR_HP_AMP_DRIVING (0x1) + + +//Power managment addition 4(0x3E),0:Disable,1:Enable +#define PWR_SPK_L_VOL (0x1<<15) +#define PWR_SPK_R_VOL (0x1<<14) +#define PWR_LOUT_VOL (0x1<<13) +#define PWR_ROUT_VOL (0x1<<12) +#define PWR_HP_L_OUT_VOL (0x1<<11) +#define PWR_HP_R_OUT_VOL (0x1<<10) +#define PWR_AXIL_IN_VOL (0x1<< 9) +#define PWR_AXIR_IN_VOL (0x1<< 8) +#define PWR_MONO_IN_P_VOL (0x1<< 7) +#define PWR_MONO_IN_N_VOL (0x1<< 6) + + +//General Purpose Control Register(0x40) +#define SPK_AMP_AUTO_RATIO_EN (0x1<<15) //Speaker Amplifier Auto Ratio Gain Control + +#define SPK_AMP_RATIO_CTRL_MASK (0x7<<12) +#define SPK_AMP_RATIO_CTRL_2_34 (0x0<<12) //7.40DB +#define SPK_AMP_RATIO_CTRL_1_99 (0x1<<12) //5.99DB +#define SPK_AMP_RATIO_CTRL_1_68 (0x2<<12) //4.50DB +#define SPK_AMP_RATIO_CTRL_1_56 (0x3<<12) //3.86DB +#define SPK_AMP_RATIO_CTRL_1_44 (0x4<<12) //3.16DB +#define SPK_AMP_RATIO_CTRL_1_27 (0x5<<12) //2.10DB +#define SPK_AMP_RATIO_CTRL_1_09 (0x6<<12) //0.80DB +#define SPK_AMP_RATIO_CTRL_1_00 (0x7<<12) //0.00DB + +#define STEREO_DAC_HI_PASS_FILT_EN (0x1<<11) //Stereo DAC high pass filter enable +#define STEREO_ADC_HI_PASS_FILT_EN (0x1<<10) //Stereo ADC high pass filter enable + +#define ADC_WIND_FILT_MASK (0x3<<4) //Select ADC Wind Filter Clock type +#define ADC_WIND_FILT_8_16_32K (0x0<<4) //8/16/32k +#define ADC_WIND_FILT_11_22_44K (0x1<<4) //11/22/44k +#define ADC_WIND_FILT_12_24_48K (0x2<<4) //12/24/48k + +#define ADC_WIND_FILT_EN (0x1<<3) //Enable ADC Wind Filter + +#define ADC_WIND_CNR_FREQ_MASK (0x7<<0) //SelectADC Wind Filter Corner Frequency +#define ADC_WIND_CNR_FREQ_82_113_122 (0x0<<0) //82/113/122 Hz +#define ADC_WIND_CNR_FREQ_102_141_153 (0x1<<0) //102/141/153 Hz +#define ADC_WIND_CNR_FREQ_131_180_156 (0x2<<0) //131/180/156 Hz +#define ADC_WIND_CNR_FREQ_163_225_245 (0x3<<0) //163/225/245 Hz +#define ADC_WIND_CNR_FREQ_204_281_306 (0x4<<0) //204/281/306 Hz +#define ADC_WIND_CNR_FREQ_261_360_392 (0x5<<0) //261/360/392 Hz +#define ADC_WIND_CNR_FREQ_327_450_490 (0x6<<0) //327/450/490 Hz +#define ADC_WIND_CNR_FREQ_408_563_612 (0x7<<0) //408/563/612 Hz + + +//Global Clock Control Register(0x42) +#define SYSCLK_SOUR_SEL_MASK (0x1<<14) +#define SYSCLK_SOUR_SEL_MCLK (0x0<<14) //system Clock source from MCLK +#define SYSCLK_SOUR_SEL_PLL (0x1<<14) //system Clock source from PLL +#define SYSCLK_SOUR_SEL_PLL_TCK (0x2<<14) //system Clock source from PLL track + +#define PLLCLK_SOUR_SEL_MCLK (0x0<<12) //PLL clock source from MCLK +#define PLLCLK_SOUR_SEL_BITCLK (0x1<<12) //PLL clock source from BITCLK + +#define PLLCLK_PRE_DIV1 (0x0<<11) //DIV 1 +#define PLLCLK_PRE_DIV2 (0x1<<11) //DIV 2 + +//PLL Control(0x44) + +#define PLL_CTRL_M_VAL(m) ((m)&0xf) //M code for analog PLL +#define PLL_CTRL_K_VAL(k) (((k)&0x7)<<4) //K code for analog PLL +#define PLL_CTRL_N_VAL(n) (((n)&0xff)<<8) //N code for analog PLL + + +//GPIO Pin Configuration(0x4C) +#define GPIO_PIN_FUN_SEL_MASK (0x1<<15) +#define GPIO_PIN_FUN_SEL_IRQ (0x1<<15) //GPIO pin SELECT IRQ +#define GPIO_PIN_FUN_SEL_GPIO_DIMC (0x0<<15) //GPIO PIN SELECT GPIO_DMIC + + +#define GPIO_DMIC_FUN_SEL_MASK (0x1<<3) +#define GPIO_DMIC_FUN_SEL_DIMC (0x1<<3) //GPIO pin SELECT DMIC +#define GPIO_DMIC_FUN_SEL_GPIO (0x0<<3) //GPIO PIN SELECT GPIO + +#define GPIO_PIN_CON_MASK (0x1<<2) +#define GPIO_PIN_SET_INPUT (0x0<<2) //GPIO pin select input +#define GPIO_PIN_SET_OUTPUT (0x1<<2) //GPIO pin select output + +//De-POP function Control 1(0x54) +#define POW_ON_SOFT_GEN (0x1<<15) //POWER ON SOFT GENERATOR +#define EN_MUTE_UNMUTE_DEPOP (0x1<<14) //Enable mute/unmute depop +#define EN_DEPOP2_FOR_HP (0x1<<7) //Enable depop 2 for HP +#define PD_HPAMP_L_ST_UP (0x1<<5) //Power Down HPAMP_L Starts Up Signal +#define PD_HPAMP_R_ST_UP (0x1<<4) //Power Down HPAMP_R Starts Up Signal +#define EN_HP_L_M_UN_MUTE_DEPOP (0x1<<1) //Enable left HP mute/unmute depop +#define EN_HP_R_M_UN_MUTE_DEPOP (0x1<<0) //Enable right HP mute/unmute depop + +//De-POP Fnction Control(0x56) +#define ENA_CAP_FREE_DEPOP (0x1<<14) //enable depop for Capfree block + + +//Jack Detect Control Register(0x5A) +#define JD_USE_MASK (0x3<<14) //JD Pin select +#define JD_USE_JD2 (0x3<<14) //select JD2 +#define JD_USE_JD1 (0x2<<14) //select JD1 +#define JD_USE_GPIO (0x1<<14) //select GPIO +#define JD_OFF (0x0<<14) //off + +#define JD_HP_EN (0x1<<11) //JD trigger enable for HP +#define JD_HP_TRI_MASK (0x1<<10) //Trigger mask +#define JD_HP_TRI_HI (0x1<<10) //high trigger +#define JD_HP_TRI_LO (0x1<<10) //low trigger + +#define JD_SPK_L_EN (0x1<<9) //JD trigger enable for speaker LP/LN +#define JD_SPK_L_TRI_MASK (0x1<<8) //Trigger mask +#define JD_SPK_L_TRI_HI (0x1<<8) //high trigger +#define JD_SPK_L_TRI_LO (0x0<<8) //low trigger + +#define JD_SPK_R_EN (0x1<<7) //JD trigger enable for speaker RP/RN +#define JD_SPK_R_TRI_MASK (0x1<<6) //Trigger mask +#define JD_SPK_R_TRI_HI (0x1<<6) //high trigger +#define JD_SPK_R_TRI_LO (0x0<<6) //low trigger + +#define JD_MONO_EN (0x1<<5) //JD trigger enable for monoout +#define JD_MONO_TRI_MASK (0x1<<4) //Trigger mask +#define JD_MONO_TRI_HI (0x1<<4) //high trigger +#define JD_MONO_TRI_LO (0x0<<4) //low trigger + +#define JD_AUX_1_EN (0x1<<3) //JD trigger enable for Lout +#define JD_AUX_1_MASK (0x1<<2) //Trigger mask +#define JD_AUX_1_TRI_HI (0x1<<2) //high trigger +#define JD_AUX_1_TRI_LO (0x0<<2) //low trigger + +#define JD_AUX_2_EN (0x1<<1) //JD trigger enable for Rout + +#define JD_AUX_2_MASK (0x1<<0) //Trigger mask +#define JD_AUX_2_TRI_HI (0x1<<0) //high trigger +#define JD_AUX_2_TRI_LO (0x0<<0) //low trigger + + +////ALC CONTROL 1(0x64) +#define ALC_ATTACK_RATE_MASK (0x1F<<8) //select ALC attack rate +#define ALC_RECOVERY_RATE_MASK (0x1F<<0) //select ALC Recovery rate + + +////ALC CONTROL 2(0x65) +#define ALC_COM_NOISE_GATE_MASK (0xF<<0) //select Compensation gain for Noise gate function + + +////ALC CONTROL 3(0x66) +#define ALC_FUN_MASK (0x3<<14) //select ALC path +#define ALC_FUN_DIS (0x0<<14) //disable +#define ALC_ENA_DAC_PATH (0x1<<14) //DAC path +#define ALC_ENA_ADC_PATH (0x3<<14) //ADC path + +#define ALC_PARA_UPDATE (0x1<<13) //update ALC parameter + +#define ALC_LIMIT_LEVEL_MASK (0x1F<<8) //ALC limit level + +#define ALC_NOISE_GATE_FUN_MASK (0x1<<7) //ALC noise gate function +#define ALC_NOISE_GATE_FUN_DIS (0x0<<7) //disable +#define ALC_NOISE_GATE_FUN_ENA (0x1<<7) //enable + +#define ALC_NOISE_GATE_H_D_MASK (0x1<<6) //ALC noise gate hold data function +#define ALC_NOISE_GATE_H_D_DIS (0x0<<6) //disable +#define ALC_NOISE_GATE_H_D_ENA (0x1<<6) //enable + +//Psedueo Stereo & Spatial Effect Block Control(0x68) +#define SPATIAL_CTRL_EN (0x1<<15) //enable Spatial effect +#define ALL_PASS_FILTER_EN (0x1<<14) //enable all pass filter +#define PSEUDO_STEREO_EN (0x1<<13) //enable pseudo stereo block +#define STEREO_EXPENSION_EN (0x1<<12) //enable stereo expansion block + +#define GAIN_3D_PARA_MASK (0x3<<6) //3D gain parameter +#define GAIN_3D_PARA_1_00 (0x0<<6) //3D gain 1.0 +#define GAIN_3D_PARA_1_50 (0x1<<6) //3D gain 1.5 +#define GAIN_3D_PARA_2_00 (0x2<<6) //3D gain 2.0 + +#define RATIO_3D_MASK (0x3<<4) //3D ratio parameter +#define RATIO_3D_0_0 (0x0<<4) //3D ratio 0.0 +#define RATIO_3D_0_66 (0x1<<4) //3D ratio 0.66 +#define RATIO_3D_1_0 (0x2<<4) //3D ratio 1.0 + +#define APF_FUN_SLE_MASK (0x3<<0) //select samplerate for all pass filter +#define APF_FUN_SEL_48K (0x3<<0) //select 48k +#define APF_FUN_SEL_44_1K (0x2<<0) //select 44.1k +#define APF_FUN_SEL_32K (0x1<<0) //select 32k +#define APF_FUN_DIS (0x0<<0) //disable + + +//EQ CONTROL 1(0x6E) + +#define HW_EQ_PATH_SEL_MASK (0x1<<15) //HW EQ FUN SEL +#define HW_EQ_PATH_SEL_DAC (0x0<<15) //HW EQ FOR DAC PATH +#define HW_EQ_PATH_SEL_ADC (0x1<<15) //HW EQ FOR ADC PATH + +#define HW_EQ_UPDATE_CTRL (0x1<<14) //HW EQ Update CTRL + +#define EN_HW_EQ_HPF2 (0x1<<5) //EQ High Pass Filter 2 Control +#define EN_HW_EQ_HPF1 (0x1<<4) //EQ High Pass Filter 1 Control +#define EN_HW_EQ_BP3 (0x1<<3) //EQ Band-3 Control +#define EN_HW_EQ_BP2 (0x1<<2) //EQ Band-2 Control +#define EN_HW_EQ_BP1 (0x1<<1) //EQ Band-1 Control +#define EN_HW_EQ_LPF (0x1<<0) //EQ Low Pass Filter Control + +#define REALTEK_HWDEP 0 +struct rt5631_setup_data { + int i2c_address; + int i2c_bus; +}; + + +extern struct snd_soc_dai rt5631_dai[]; +extern struct snd_soc_codec_device soc_codec_dev_rt5631; + + +#endif //__RTCODEC5631_H__ diff --git a/sound/soc/rk29/Kconfig b/sound/soc/rk29/Kconfig old mode 100755 new mode 100644 index 84e566608218..2eeac425d9b5 --- a/sound/soc/rk29/Kconfig +++ b/sound/soc/rk29/Kconfig @@ -40,7 +40,22 @@ config SND_RK29_SOC_WM8900 help Say Y if you want to add support for SoC audio on rockchip with the WM8900. - +config SND_RK29_SOC_alc5621 + tristate "SoC I2S Audio support for rockchip - alc5621" + depends on SND_RK29_SOC && I2C_RK29 + select SND_RK29_SOC_I2S + select SND_SOC_alc5621 + help + Say Y if you want to add support for SoC audio on rockchip + with the alc5621. +config SND_RK29_SOC_alc5631 + tristate "SoC I2S Audio support for rockchip - alc5631" + depends on SND_RK29_SOC && I2C_RK29 + select SND_RK29_SOC_I2S + select SND_SOC_alc5631 + help + Say Y if you want to add support for SoC audio on rockchip + with the alc5631. config SND_RK29_SOC_WM8994 tristate "SoC I2S Audio support for rockchip - WM8994" depends on SND_RK29_SOC && I2C_RK29 @@ -59,7 +74,7 @@ config SND_RK29_SOC_RK1000 Say Y if you want to add support for SoC audio on rockchip with the RK1000. -if SND_RK29_SOC_WM8988 || SND_RK29_SOC_RK1000 || SND_RK29_SOC_WM8994 || SND_RK29_SOC_WM8900 +if SND_RK29_SOC_WM8988 || SND_RK29_SOC_RK1000 || SND_RK29_SOC_WM8994 || SND_RK29_SOC_WM8900 || SND_RK29_SOC_alc5621 || SND_RK29_SOC_alc5631 choice prompt "Set i2s type" config SND_RK29_CODEC_SOC_MASTER diff --git a/sound/soc/rk29/Makefile b/sound/soc/rk29/Makefile old mode 100755 new mode 100644 index 3e00a89cdbb6..17e5b941e799 --- a/sound/soc/rk29/Makefile +++ b/sound/soc/rk29/Makefile @@ -7,6 +7,8 @@ obj-$(CONFIG_SND_RK29_SOC_I2S) += snd-soc-rockchip-i2s.o # ROCKCHIP Machine Support snd-soc-wm8900-objs := rk29_wm8900.o +snd-soc-alc5621-objs := rk29_alc5621.o +snd-soc-alc5631-objs := rk29_rt5631.o snd-soc-wm8988-objs := rk29_wm8988.o snd-soc-rk1000-objs := rk29_rk1000codec.o snd-soc-wm8994-objs := rk29_wm8994.o @@ -14,4 +16,6 @@ snd-soc-wm8994-objs := rk29_wm8994.o obj-$(CONFIG_SND_RK29_SOC_WM8994) += snd-soc-wm8994.o obj-$(CONFIG_SND_RK29_SOC_WM8988) += snd-soc-wm8988.o obj-$(CONFIG_SND_RK29_SOC_WM8900) += snd-soc-wm8900.o +obj-$(CONFIG_SND_RK29_SOC_alc5621) += snd-soc-alc5621.o +obj-$(CONFIG_SND_RK29_SOC_alc5631) += snd-soc-alc5631.o obj-$(CONFIG_SND_RK29_SOC_RK1000) += snd-soc-rk1000.o diff --git a/sound/soc/rk29/rk29_alc5621.c b/sound/soc/rk29/rk29_alc5621.c new file mode 100644 index 000000000000..62f2ed545d05 --- /dev/null +++ b/sound/soc/rk29/rk29_alc5621.c @@ -0,0 +1,249 @@ +/* + * rk29_wm8900.c -- SoC audio for rockchip + * + * Driver for rockchip alc5623 audio + * + * 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. + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../codecs/alc5621.h" +#include "rk29_pcm.h" +#include "rk29_i2s.h" + +#if 1 +#define DBG(x...) printk(KERN_INFO x) +#else +#define DBG(x...) +#endif + + + +static int rk29_hw_params_alc5623(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + unsigned int pll_out = 0i,sys_clk; + int ret; + + DBG("rk29_hw_params for rk29_alc5623\n"); + /*by Vincent Hsiung for EQ Vol Change*/ + #define HW_PARAMS_FLAG_EQVOL_ON 0x21 + #define HW_PARAMS_FLAG_EQVOL_OFF 0x22 + if ((params->flags == HW_PARAMS_FLAG_EQVOL_ON)||(params->flags == HW_PARAMS_FLAG_EQVOL_OFF)) + { + ret = codec_dai->ops->hw_params(substream, params, codec_dai); //by Vincent + DBG("rk29_hw_params set EQ vol for rk29_alc5623\n"); + } + else + { + + /* set codec DAI configuration for codec side */ + #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) + DBG("rk29_hw_params for rk29_alc5623 codec as slave\n"); + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + #endif + + #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM ); + #endif + if (ret < 0)return ret; + + /* set cpu DAI configuration */ + #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) + DBG("rk29_hw_params for rk29_alc5623 cpu as master\n"); + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + #endif + + #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + #endif + if (ret < 0)return ret; + + } + + + switch(params_rate(params)) { + case 8000: + sys_clk= 12288000; + pll_out = 12288000; + break; + case 16000: + sys_clk= 11289600; + pll_out = 12288000; + break; + case 24000: + sys_clk = 24576000; + pll_out = 12288000; + break; + case 32000: + sys_clk= 12288000; + pll_out = 12288000; + case 48000: + sys_clk = 12288000; + pll_out = 12288000; + break; + /*------------------------------*/ + case 11025: + sys_clk = 11289600; + pll_out = 11289600; + break; + case 22050: + sys_clk = 11289600; + pll_out = 11289600; + break; + + case 44100: + sys_clk = 11289600; + pll_out = 11289600; + break; + default: + DBG("rk29_hw_params for rk29_alc5623,invalid sapmleRate:%d\n",params_rate(params)); + return -EINVAL; + break; + } + DBG("rk29_hw_params for rk29_alc5623, sapmleRate:%d\n",params_rate(params)); + + + /*Set the system clk for codec*/ + ret=snd_soc_dai_set_sysclk(codec_dai, 0,sys_clk,SND_SOC_CLOCK_IN);//ALC5621 system clk from MCLK or PLL + if (ret < 0) + { + DBG("rk29_hw_params_alc5623:failed to set the sysclk for codec side\n"); + return ret; + } + + /*Set the pll of alc5621,the Pll source from MCLK no matter slave or master mode*/ + ret=snd_soc_dai_set_pll(codec_dai,RT5621_PLL_FR_BCLK,params_rate(params)*64,sys_clk); + if (ret < 0) + { + DBG("rk29_hw_params_alc5623:failed to set the pll for codec side\n"); + return ret; + } + + + #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) + snd_soc_dai_set_clkdiv(codec_dai, ALC5623_BCLK_DIV, ALC5623_BCLK_DIV_4); + snd_soc_dai_set_clkdiv(codec_dai, ALC5623_DAC_LRCLK,(pll_out/4)/params_rate(params)); + snd_soc_dai_set_clkdiv(codec_dai, ALC5623_ADC_LRCLK,(pll_out/4)/params_rate(params)); + #endif + + #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) + snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0); + snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK, (pll_out/4)/params_rate(params)-1); + snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, 3); + #endif + DBG("rk29_hw_params_alc5623:,LRCK=%d\n",(pll_out/4)/params_rate(params)); + return 0; +} + +static const struct snd_soc_dapm_widget alc5623_dapm_widgets[] = { + SND_SOC_DAPM_LINE("Audio Out", NULL), + SND_SOC_DAPM_LINE("Line in", NULL), + SND_SOC_DAPM_MIC("Micn", NULL), + SND_SOC_DAPM_MIC("Micp", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[]= { + + {"Audio Out", NULL, "HP_L"}, + {"Audio Out", NULL, "HP_R"}, + {"Line in", NULL, "RINPUT1"}, + {"Line in", NULL, "LINPUT1"}, + {"Micn", NULL, "RINPUT2"}, + {"Micp", NULL, "LINPUT2"}, +}; + +/* + * Logic for a wm8900 as connected on a rockchip board. + */ +static int rk29_alc5623_init(struct snd_soc_codec *codec) +{ + + DBG("rk29_alc5623_init\n"); + + /* Add specific widgets */ + snd_soc_dapm_new_controls(codec, alc5623_dapm_widgets, ARRAY_SIZE(alc5623_dapm_widgets)); + + /* Set up specific audio path audio_mapnects */ + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_nc_pin(codec, "HP_L"); + snd_soc_dapm_nc_pin(codec, "HP_R"); + snd_soc_dapm_sync(codec); + DBG("rk29_alc5623_init end\n"); + return 0; +} + +static struct snd_soc_ops rk29_ops = { + .hw_params = rk29_hw_params_alc5623, +}; + +static struct snd_soc_dai_link rk29_dai_alc5623 = { + .name = "ALC5623", + .stream_name = "ALC5623 PCM", + .cpu_dai = &rk29_i2s_dai[0], + .codec_dai = &rt5621_dai, + .init = rk29_alc5623_init, + .ops = &rk29_ops, +}; + +static struct snd_soc_card snd_soc_card_rk29_alc5623 = { + .name = "RK29_ALC5623", + .platform = &rk29_soc_platform, + .dai_link = &rk29_dai_alc5623, + .num_links = 1, +}; + + +static struct snd_soc_device rk29_snd_devdata_alc5623 = { + .card = &snd_soc_card_rk29_alc5623, + .codec_dev = &soc_codec_dev_rt5621, +}; + +static struct platform_device *rk29_snd_device_alc5623; + +static int __init audio_card_init_alc5623(void) +{ + int ret =0; + DBG("audio_card_init_alc5623\n"); + rk29_snd_device_alc5623 = platform_device_alloc("soc-audio", -1); + if (!rk29_snd_device_alc5623) { + DBG("audio_card_init_alc5623:platform device allocation failed\n"); + ret = -ENOMEM; + return ret; + } + platform_set_drvdata(rk29_snd_device_alc5623, &rk29_snd_devdata_alc5623); + rk29_snd_devdata_alc5623.dev = &rk29_snd_device_alc5623->dev; + ret = platform_device_add(rk29_snd_device_alc5623); + if (ret) { + DBG("audio_card_init_alc5623:platform device add failed\n"); + platform_device_put(rk29_snd_device_alc5623); + } + return ret; +} + +static void __exit audio_card_exit_alc5623(void) +{ + platform_device_unregister(rk29_snd_device_alc5623); +} + +module_init(audio_card_init_alc5623); +module_exit(audio_card_exit_alc5623); +/* Module information */ +MODULE_AUTHOR("rockchip"); +MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/rk29/rk29_i2s.c b/sound/soc/rk29/rk29_i2s.c old mode 100755 new mode 100644 index cdf68ecf2775..bba344a121b8 --- a/sound/soc/rk29/rk29_i2s.c +++ b/sound/soc/rk29/rk29_i2s.c @@ -123,86 +123,93 @@ static struct rockchip_pcm_dma_params rockchip_i2s_pcm_stereo_in[MAX_I2S] = { /* *Turn on or off the transmission path. */ + +static int flag_i2s_tx = 0; +static int flag_i2s_rx = 0; static void rockchip_snd_txctrl(struct rk29_i2s_info *i2s, int on) { - u32 opr,xfer,fifosts; - int xor=0; - - opr = readl(&(pheadi2s->I2S_DMACR)); - xfer = readl(&(pheadi2s->I2S_XFER)); + u32 opr,xfer,fifosts; - if(xfer & I2S_RX_TRAN_START) { - xor = 1; - } + opr = readl(&(pheadi2s->I2S_DMACR)); + xfer = readl(&(pheadi2s->I2S_XFER)); + + opr &= ~I2S_TRAN_DMA_ENABLE; + xfer &= ~I2S_RX_TRAN_START; + xfer &= ~I2S_TX_TRAN_START; + + if (on) + { + I2S_DBG("rockchip_snd_txctrl: on\n"); - opr &= ~I2S_TRAN_DMA_ENABLE; - xfer &= ~I2S_TX_TRAN_START; + //stop tx + if ((flag_i2s_rx == 0) && (flag_i2s_tx == 0)) + writel(xfer, &(pheadi2s->I2S_XFER)); + writel(opr, &(pheadi2s->I2S_DMACR)); + + //start tx + opr |= I2S_TRAN_DMA_ENABLE; + xfer |= I2S_TX_TRAN_START; + xfer |= I2S_RX_TRAN_START; + writel(opr, &(pheadi2s->I2S_DMACR)); + writel(xfer, &(pheadi2s->I2S_XFER)); + + flag_i2s_tx = 1; + } + else + { + //stop tx + flag_i2s_tx = 0; - if (on) - { - if(xor) { - xfer &= ~I2S_RX_TRAN_START; - } - writel(0, &(pheadi2s->I2S_XFER)); - writel(opr, &(pheadi2s->I2S_DMACR)); - - opr |= I2S_TRAN_DMA_ENABLE; - xfer |= I2S_TX_TRAN_START; - if(xor) { - xfer |= I2S_RX_TRAN_START; - } - - writel(opr, &(pheadi2s->I2S_DMACR)); - writel(xfer, &(pheadi2s->I2S_XFER)); - } - else - { - writel(xfer, &(pheadi2s->I2S_XFER)); - udelay(5); - writel(opr, &(pheadi2s->I2S_DMACR)); + if ((flag_i2s_rx == 0) && (flag_i2s_tx == 0)) + writel(xfer, &(pheadi2s->I2S_XFER)); + udelay(5); + writel(opr, &(pheadi2s->I2S_DMACR)); - } - I2S_DBG("Enter %s, %d, opr=0x%08X, xfer=0x%08X\n", __func__, __LINE__, opr, xfer); + I2S_DBG("rockchip_snd_txctrl: off\n"); + } } static void rockchip_snd_rxctrl(struct rk29_i2s_info *i2s, int on) { - u32 opr,xfer,fifosts; - int xor=0; - - opr = readl(&(pheadi2s->I2S_DMACR)); - xfer = readl(&(pheadi2s->I2S_XFER)); + u32 opr,xfer,fifosts; + - if(xfer & I2S_TX_TRAN_START){ - xor = 1; - } - - opr &= ~I2S_RECE_DMA_ENABLE; - xfer &= ~I2S_RX_TRAN_START; + opr = readl(&(pheadi2s->I2S_DMACR)); + xfer = readl(&(pheadi2s->I2S_XFER)); + + opr &= ~I2S_RECE_DMA_ENABLE; + xfer &= ~I2S_RX_TRAN_START; + xfer &= ~I2S_TX_TRAN_START; + if (on) + { + I2S_DBG("rockchip_snd_rxctrl: on\n"); + + //stop rx + if ((flag_i2s_rx == 0) && (flag_i2s_tx == 0)) + writel(xfer, &(pheadi2s->I2S_XFER)); + writel(opr, &(pheadi2s->I2S_DMACR)); + + //start rx + opr |= I2S_RECE_DMA_ENABLE; + xfer |= I2S_TX_TRAN_START; + xfer |= I2S_RX_TRAN_START; + writel(opr, &(pheadi2s->I2S_DMACR)); + writel(xfer, &(pheadi2s->I2S_XFER)); + + flag_i2s_rx = 1; + } + else + { + //stop rx + flag_i2s_rx = 0; - if (on) - { - if(xor) { - xfer &= ~I2S_TX_TRAN_START; - } - writel(0, &(pheadi2s->I2S_XFER)); - writel(opr, &(pheadi2s->I2S_DMACR)); - - opr |= I2S_RECE_DMA_ENABLE; - xfer |= I2S_RX_TRAN_START; - if(xor) { - xfer |= I2S_TX_TRAN_START; - } - writel(opr, &(pheadi2s->I2S_DMACR)); - writel(xfer, &(pheadi2s->I2S_XFER)); - } - else - { - writel(xfer, &(pheadi2s->I2S_XFER)); - udelay(5); - writel(opr, &(pheadi2s->I2S_DMACR)); - } - I2S_DBG("Enter %s, %d, opr=0x%08X, xfer=0x%08X\n", __func__, __LINE__, opr, xfer); + if ((flag_i2s_rx == 0) && (flag_i2s_tx == 0)) + writel(xfer, &(pheadi2s->I2S_XFER)); + udelay(5); + writel(opr, &(pheadi2s->I2S_DMACR)); + + I2S_DBG("rockchip_snd_rxctrl: off\n"); + } } /* @@ -569,8 +576,6 @@ static int __devinit rockchip_i2s_probe(struct platform_device *pdev) { struct rk29_i2s_info *i2s; struct snd_soc_dai *dai; - struct clk *general_pll; - unsigned long i2smclk; int ret; I2S_DBG("Enter %s, %d pdev->id = %d >>>>>>>>>>>\n", __func__, __LINE__, pdev->id); @@ -633,21 +638,7 @@ static int __devinit rockchip_i2s_probe(struct platform_device *pdev) } clk_enable(i2s->iis_clk); - general_pll=clk_get(NULL, "general_pll"); - if(clk_get_rate(general_pll)>260000000) - { - i2smclk=11289600; - } - else if(clk_get_rate(general_pll)>130000000) - { - i2smclk=11289600/2; - } - else - { - i2smclk=11289600/4; - } - I2S_DBG("func is %s,general pll=%ld,mclk=%ld\n",__FUNCTION__,clk_get_rate(general_pll),i2smclk); - clk_set_rate(i2s->iis_clk, i2smclk); + clk_set_rate(i2s->iis_clk, 11289600); ret = rk29_i2s_probe(pdev, dai, i2s, 0); if (ret) goto err_clk; diff --git a/sound/soc/rk29/rk29_rt5631.c b/sound/soc/rk29/rk29_rt5631.c new file mode 100644 index 000000000000..690c00aacb6e --- /dev/null +++ b/sound/soc/rk29/rk29_rt5631.c @@ -0,0 +1,258 @@ +/* + * rk29_rt5631.c -- SoC audio for rockchip + * + * Driver for rockchip rt5631 audio + * + * 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. + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../codecs/rt5631.h" +#include "rk29_pcm.h" +#include "rk29_i2s.h" + +#if 0 +#define DBG(x...) printk(KERN_INFO x) +#else +#define DBG(x...) +#endif + +static int rk29_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + unsigned int pll_out = 0; + unsigned int lrclk = 0; + int ret; + + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + /*by Vincent Hsiung for EQ Vol Change*/ + #define HW_PARAMS_FLAG_EQVOL_ON 0x21 + #define HW_PARAMS_FLAG_EQVOL_OFF 0x22 + if ((params->flags == HW_PARAMS_FLAG_EQVOL_ON)||(params->flags == HW_PARAMS_FLAG_EQVOL_OFF)) + { + ret = codec_dai->ops->hw_params(substream, params, codec_dai); //by Vincent + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + } + else + { + + /* set codec DAI configuration */ + #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + #endif + #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM ); + #endif + if (ret < 0) + return ret; + + /* set cpu DAI configuration */ + #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + #endif + #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + #endif + if (ret < 0) + return ret; + + } + + + switch(params_rate(params)) { + case 8000: + case 16000: + case 24000: + case 32000: + case 48000: + pll_out = 12288000; + break; + case 11025: + case 22050: + case 44100: + pll_out = 11289600; + break; + default: + DBG("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params)); + return -EINVAL; + break; + } + DBG("Enter:%s, %d, rate=%d\n",__FUNCTION__,__LINE__,params_rate(params)); + + #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) +#if 0 //use pll from blck + /*Set the pll of rt5631,the Pll source from BITCLK on CPU is master mode*/ + //bitclk is 64fs + ret=snd_soc_dai_set_pll(codec_dai,0,params_rate(params)*64,pll_out); + if (ret < 0) + { + DBG("rk29_hw_params_rt5631:failed to set the pll for codec side\n"); + return ret; + } +#endif + /*Set the system clk for codec*/ + ret=snd_soc_dai_set_sysclk(codec_dai, 0,pll_out,SND_SOC_CLOCK_IN); + if (ret < 0) + { + DBG("rk29_hw_params_rt5631:failed to set the sysclk for codec side\n"); + return ret; + } + #endif + +/* + #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) + + if((24576000%params_rate(params))==0) //for 8k,16k,32k,48k + { + snd_soc_dai_set_pll(codec_dai,0,pll_out, 24576000); + snd_soc_dai_set_sysclk(codec_dai,0, 24576000, SND_SOC_CLOCK_IN); + } + else if((22579200%params_rate(params))==0) //for 11k,22k,44k + { + snd_soc_dai_set_pll(codec_dai,0,pll_out, 22579200); + snd_soc_dai_set_sysclk(codec_dai,0, 22579200, SND_SOC_CLOCK_IN); + } + + #endif +*/ + + #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) + snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0); + snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK, (pll_out/4)/params_rate(params)-1); + snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, 3); + #endif + + DBG("Enter:%s, %d, LRCK=%d\n",__FUNCTION__,__LINE__,(pll_out/4)/params_rate(params)); + + return 0; +} + +static const struct snd_soc_dapm_widget rt5631_dapm_widgets[] = { + + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), + SND_SOC_DAPM_HP("Headphone Jack", NULL), + +}; + +static const struct snd_soc_dapm_route audio_map[]={ + + /* Mic Jack --> MIC_IN*/ + {"Mic Bias1", NULL, "Mic Jack"}, + {"MIC1", NULL, "Mic Bias1"}, + /* HP_OUT --> Headphone Jack */ + {"Headphone Jack", NULL, "HPOL"}, + {"Headphone Jack", NULL, "HPOR"}, + /* LINE_OUT --> Ext Speaker */ + {"Ext Spk", NULL, "SPOL"}, + {"Ext Spk", NULL, "SPOR"}, + +} ; + +/* + * Logic for a rt5631 as connected on a rockchip board. + */ +static int rk29_rt5631_init(struct snd_soc_codec *codec) +{ + struct snd_soc_dai *codec_dai = &codec->dai[0]; + int ret; + + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + + /* Add specific widgets */ + snd_soc_dapm_new_controls(codec, rt5631_dapm_widgets, + ARRAY_SIZE(rt5631_dapm_widgets)); + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + /* Set up specific audio path audio_mapnects */ + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); +// snd_soc_dapm_nc_pin(codec, "HP_L"); + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); +// snd_soc_dapm_nc_pin(codec, "HP_R"); + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + snd_soc_dapm_sync(codec); + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + + return 0; +} + +static struct snd_soc_ops rk29_ops = { + .hw_params = rk29_hw_params, +}; + +static struct snd_soc_dai_link rk29_dai = { + .name = "RT5631", + .stream_name = "RT5631 PCM", + .cpu_dai = &rk29_i2s_dai[0], + .codec_dai = &rt5631_dai, + .init = rk29_rt5631_init, + .ops = &rk29_ops, +}; + +static struct snd_soc_card snd_soc_card_rk29 = { + .name = "RK29_RT5631", + .platform = &rk29_soc_platform, + .dai_link = &rk29_dai, + .num_links = 1, +}; + + +static struct snd_soc_device rk29_snd_devdata = { + .card = &snd_soc_card_rk29, + .codec_dev = &soc_codec_dev_rt5631, +}; + +static struct platform_device *rk29_snd_device; + +static int __init audio_card_init(void) +{ + int ret =0; + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + rk29_snd_device = platform_device_alloc("soc-audio", -1); + if (!rk29_snd_device) { + DBG("platform device allocation failed\n"); + ret = -ENOMEM; + return ret; + } + platform_set_drvdata(rk29_snd_device, &rk29_snd_devdata); + rk29_snd_devdata.dev = &rk29_snd_device->dev; + ret = platform_device_add(rk29_snd_device); + if (ret) { + DBG("platform device add failed\n"); + platform_device_put(rk29_snd_device); + } + return ret; +} + +static void __exit audio_card_exit(void) +{ + platform_device_unregister(rk29_snd_device); +} + +module_init(audio_card_init); +module_exit(audio_card_exit); +/* Module information */ +MODULE_AUTHOR("rockchip"); +MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface"); +MODULE_LICENSE("GPL"); + -- 2.34.1