From 3e88e0abc3ab0edaeeb9ae20207914c2fc7b9f50 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E9=99=88=E9=87=91=E6=B3=89?= Date: Wed, 18 May 2011 16:39:05 +0800 Subject: [PATCH] add rt5625 driver --- arch/arm/mach-rk29/board-rk29phonepadsdk.c | 9 + sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rt5625.c | 2451 ++++++++++++++++++++ sound/soc/codecs/rt5625.h | 742 ++++++ sound/soc/rk29/Kconfig | 12 +- sound/soc/rk29/Makefile | 2 + sound/soc/rk29/rk29_rt5625.c | 253 ++ 8 files changed, 3474 insertions(+), 1 deletion(-) create mode 100644 sound/soc/codecs/rt5625.c create mode 100644 sound/soc/codecs/rt5625.h create mode 100644 sound/soc/rk29/rk29_rt5625.c diff --git a/arch/arm/mach-rk29/board-rk29phonepadsdk.c b/arch/arm/mach-rk29/board-rk29phonepadsdk.c index 0be0d09cea0e..d5336961b7fb 100755 --- a/arch/arm/mach-rk29/board-rk29phonepadsdk.c +++ b/arch/arm/mach-rk29/board-rk29phonepadsdk.c @@ -567,6 +567,15 @@ static struct i2c_board_info __initdata board_i2c0_devices[] = { .flags = 0, }, #endif + +#if defined (CONFIG_SND_SOC_RT5625) + { + .type = "rt5625", + .addr = 0x1e, //need check A1 pin,A1 pin is low,addr=0x1e,A1 pin is high,addr=0x1f. + .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 4ba65a696c2d..ec6669f9ed72 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -44,6 +44,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8900 if I2C select SND_SOC_alc5621 if I2C select SND_SOC_alc5631 if I2C + select SND_SOC_RT5625 if I2C select SND_SOC_WM8903 if I2C select SND_SOC_WM8940 if I2C select SND_SOC_WM8960 if I2C @@ -188,6 +189,9 @@ config SND_SOC_alc5621 config SND_SOC_alc5631 tristate + +config SND_SOC_RT5625 + tristate config SND_SOC_WM8903 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 49b4439c6a35..cef035f1cb3e 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -32,6 +32,7 @@ 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-rt5625-objs := rt5625.o snd-soc-wm8903-objs := wm8903.o snd-soc-wm8940-objs := wm8940.o snd-soc-wm8960-objs := wm8960.o @@ -87,6 +88,7 @@ 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_RT5625) += snd-soc-rt5625.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/rt5625.c b/sound/soc/codecs/rt5625.c new file mode 100644 index 000000000000..960f944b4ee6 --- /dev/null +++ b/sound/soc/codecs/rt5625.c @@ -0,0 +1,2451 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt5625.h" + +#if REALTEK_HWDEP + +#include +#include + +#endif + +#define AUDIO_NAME "rt5625" +#define RT5625_VERSION "0.03 alsa 1.0.21" +#define ALSA_SOC_VERSION "1.0.21" + +#define RT5625_EQ_FUNC_ENA 0 + +static void hp_depop_mode2(struct snd_soc_codec *codec); +static void hp_mute_unmute_depop(struct snd_soc_codec *codec,int mute); + +struct rt5625_priv { + unsigned int stereo_sysclk; + unsigned int voice_sysclk; +}; + +struct rt5625_init_reg { + u8 reg_index; + u16 reg_value; +}; + +static struct rt5625_init_reg rt5625_init_list[] = { + + {RT5625_HP_OUT_VOL , 0x8888}, //default is -12db + {RT5625_SPK_OUT_VOL , 0x8080}, //default is 0db + {RT5625_DAC_AND_MIC_CTRL , 0xee03}, //DAC to hpmixer + {RT5625_OUTPUT_MIXER_CTRL , 0x0748}, //all output from hpmixer + {RT5625_MIC_CTRL , 0x0500}, //mic boost 20db + {RT5625_ADC_REC_MIXER , 0x3f3f}, //record source from mic1 + {RT5625_GEN_CTRL_REG1 , 0x0c0a}, //speaker vdd ratio is 1 + {RT5625_ADC_REC_GAIN , 0xd5d5}, //gain 15db of ADC by default + +}; + +#define RT5625_INIT_REG_NUM ARRAY_SIZE(rt5625_init_list) + +#if (RT5625_EQ_FUNC_ENA==1) +//************************************************************************************************* +//eq table +//************************************************************************************************* +enum +{ + NORMAL=0, + CLUB, + DANCE, + LIVE, + POP, + ROCK, + OPPO, + TREBLE, + BASS +}; + +typedef struct _HW_EQ_PRESET +{ + u16 HwEqType; + u16 EqValue[14]; + u16 HwEQCtrl; + +}HW_EQ_PRESET; + + +HW_EQ_PRESET HwEq_Preset[]={ +/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0x6e*/ + {NORMAL,{0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},0x0000}, + {CLUB ,{0x1C10,0x0000,0xC1CC,0x1E5D,0x0699,0xCD48,0x188D,0x0699,0xC3B6,0x1CD0,0x0699,0x0436,0x0000},0x800E}, + {DANCE ,{0x1F2C,0x095B,0xC071,0x1F95,0x0616,0xC96E,0x1B11,0xFC91,0xDCF2,0x1194,0xFAF2,0x0436,0x0000},0x800F}, + {LIVE ,{0x1EB5,0xFCB6,0xC24A,0x1DF8,0x0E7C,0xC883,0x1C10,0x0699,0xDA41,0x1561,0x0295,0x0436,0x0000},0x800F}, + {POP ,{0x1EB5,0xFCB6,0xC1D4,0x1E5D,0x0E23,0xD92E,0x16E6,0xFCB6,0x0000,0x0969,0xF988,0x0436,0x0000},0x800F}, + {ROCK ,{0x1EB5,0xFCB6,0xC071,0x1F95,0x0424,0xC30A,0x1D27,0xF900,0x0C5D,0x0FC7,0x0E23,0x0436,0x0000},0x800F}, + {OPPO ,{0x0000,0x0000,0xCA4A,0x17F8,0x0FEC,0xCA4A,0x17F8,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},0x800F}, + {TREBLE,{0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x188D,0x1699},0x8010}, + {BASS ,{0x1A43,0x0C00,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},0x8001}, + +}; + +#endif +//************************************************************************************************* +//************************************************************************************************* + +/* + * bit[0] for linein playback switch + * bit[1] phone + * bit[2] mic1 + * bit[3] mic2 + * bit[4] vopcm + * + */ +#define HPL_MIXER 0x80 +#define HPR_MIXER 0x82 +static unsigned int reg80 = 0, reg82 = 0; + +/* + * bit[0][1][2] use for aec control + * bit[3] for none + * bit[4] for SPKL pga + * bit[5] for SPKR pga + * bit[6] for hpl pga + * bit[7] for hpr pga + * bit[8] for dump dsp + * bit[12~15] for eq function + */ + #define VIRTUAL_REG_FOR_MISC_FUNC 0x84 +static unsigned int reg84 = 0; + + +static const u16 rt5625_reg[] = { + 0x59b4, 0x8080, 0x8080, 0x8080, /*reg00-reg06*/ + 0xc800, 0xe808, 0x1010, 0x0808, /*reg08-reg0e*/ + 0xe0ef, 0xcbcb, 0x7f7f, 0x0000, /*reg10-reg16*/ + 0xe010, 0x0000, 0x8008, 0x2007, /*reg18-reg1e*/ + 0x0000, 0x0000, 0x00c0, 0xef00, /*reg20-reg26*/ + 0x0000, 0x0000, 0x0000, 0x0000, /*reg28-reg2e*/ + 0x0000, 0x0000, 0x0000, 0x0000, /*reg30-reg36*/ + 0x0000, 0x0000, 0x0000, 0x0000, /*reg38-reg3e*/ + 0x0c0a, 0x0000, 0x0000, 0x0000, /*reg40-reg46*/ + 0x0029, 0x0000, 0xbe3e, 0x3e3e, /*reg48-reg4e*/ + 0x0000, 0x0000, 0x803a, 0x0000, /*reg50-reg56*/ + 0x0000, 0x0009, 0x0000, 0x3000, /*reg58-reg5e*/ + 0x3075, 0x1010, 0x3110, 0x0000, /*reg60-reg66*/ + 0x0553, 0x0000, 0x0000, 0x0000, /*reg68-reg6e*/ + 0x0000, 0x0000, 0x0000, 0x0000, /*reg70-reg76*/ + 0x0000, 0x0000, 0x0000, 0x0000, /*reg78-reg7e*/ +}; + + +Voice_DSP_Reg VODSP_AEC_Init_Value[]= +{ + {0x232C, 0x0025}, + {0x230B, 0x0001}, + {0x2308, 0x007F}, + {0x23F8, 0x4003}, + {0x2301, 0x0002}, + {0x2328, 0x0001}, + {0x2304, 0x00FA}, + {0x2305, 0x0500}, + {0x2306, 0x4000}, + {0x230D, 0x0900}, + {0x230E, 0x0280}, + {0x2312, 0x00B1}, + {0x2314, 0xC000}, + {0x2316, 0x0041}, + {0x2317, 0x2200}, + {0x2318, 0x0C00}, + {0x231D, 0x0050}, + {0x231F, 0x4000}, + {0x2330, 0x0008}, + {0x2335, 0x000A}, + {0x2336, 0x0004}, + {0x2337, 0x5000}, + {0x233A, 0x0300}, + {0x233B, 0x0030}, + {0x2341, 0x0008}, + {0x2343, 0x0800}, + {0x2352, 0x7FFF}, + {0x237F, 0x0400}, + {0x23A7, 0x2800}, + {0x22CE, 0x0400}, + {0x22D3, 0x1500}, + {0x22D4, 0x2800}, + {0x22D5, 0x3000}, + {0x2399, 0x2800}, + {0x230C, 0x0000}, //to enable VODSP AEC function +}; + + +#define SET_VODSP_REG_INIT_NUM ARRAY_SIZE(VODSP_AEC_Init_Value) +static struct snd_soc_device *rt5625_socdev; + +static inline unsigned int rt5625_read_reg_cache(struct snd_soc_codec *codec, + unsigned int reg) +{ + u16 *cache = codec->reg_cache; + + if (reg > 0x7e) + return 0; + return cache[reg / 2]; +} + + +static unsigned int rt5625_read_hw_reg(struct snd_soc_codec *codec, unsigned int reg) +{ + u8 data[2] = {0}; + unsigned int value = 0x0; + + data[0] = reg; + + i2c_master_reg8_recv(codec->control_data,reg,data,2,100 * 1000); + + value = (data[0]<<8) | data[1]; + + printk("rt5625_read reg%x=%x\n",reg,value); + + return value; +} + + +static unsigned int rt5625_read(struct snd_soc_codec *codec, unsigned int reg) +{ + if ((reg == 0x80) + || (reg == 0x82) + || (reg == 0x84)) + return (reg == 0x80) ? reg80 : ((reg == 0x82) ? reg82 : reg84); + + return rt5625_read_hw_reg(codec, reg); +} + + +static inline void rt5625_write_reg_cache(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + u16 *cache = codec->reg_cache; + if (reg > 0x7E) + return; + cache[reg / 2] = value; +} + +static int rt5625_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u8 data[3]; + unsigned int *regvalue = NULL; + + data[0] = reg; + data[1] = (value & 0xff00) >> 8; + data[2] = value & 0x00ff; + + if ((reg == 0x80) + || (reg == 0x82) + || (reg == 0x84)) + { + regvalue = ((reg == 0x80) ? ®80 : ((reg == 0x82) ? ®82 : ®84)); + *regvalue = value; + printk(KERN_INFO "rt5625_write ok, reg = %x, value = %x\n", reg, value); + return 0; + } + rt5625_write_reg_cache(codec, reg, value); + + if (codec->hw_write(codec->control_data, data, 3) == 3) + { + printk(KERN_INFO "rt5625_write ok, reg = %x, value = %x\n", reg, value); + return 0; + } + else + { + printk(KERN_ERR "rt5625_write fail\n"); + return -EIO; + } +} + +int rt5625_write_mask(struct snd_soc_codec *codec, unsigned int reg,unsigned int value,unsigned int mask) +{ + + unsigned char RetVal=0; + unsigned int CodecData; + +// printk(KERN_INFO "rt5625_write_mask ok, reg = %x, value = %x ,mask= %x\n", reg, value,mask); + + if(!mask) + return 0; + + if(mask!=0xffff) + { + CodecData=rt5625_read(codec,reg); + CodecData&=~mask; + CodecData|=(value&mask); + RetVal=rt5625_write(codec,reg,CodecData); + } + else + { + RetVal=rt5625_write(codec,reg,value); + } + + return RetVal; +} + + +void rt5625_write_index(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + + rt5625_write(codec,0x6a,reg); + rt5625_write(codec,0x6c,value); +} + +unsigned int rt5625_read_index(struct snd_soc_codec *codec, unsigned int reg) +{ + unsigned int value = 0x0; + rt5625_write(codec,0x6a,reg); + value=rt5625_read(codec,0x6c); + + return value; +} + +void rt5625_write_index_mask(struct snd_soc_codec *codec, unsigned int reg,unsigned int value,unsigned int mask) +{ + +// unsigned char RetVal=0; + unsigned int CodecData; + +// printk(KERN_INFO "rt5625_write_index_mask ok, reg = %x, value = %x ,mask= %x\n", reg, value,mask); + + if(!mask) + return; + + if(mask!=0xffff) + { + CodecData=rt5625_read_index(codec,reg); + CodecData&=~mask; + CodecData|=(value&mask); + rt5625_write_index(codec,reg,CodecData); + } + else + { + rt5625_write_index(codec,reg,value); + } +} + +//#define rt5625_write_mask(c, reg, value, mask) snd_soc_update_bits(c, reg, mask, value) + +#define rt5625_reset(c) rt5625_write(c, RT5625_RESET, 0) + +/*read/write dsp reg*/ +static int rt5625_wait_vodsp_i2c_done(struct snd_soc_codec *codec) +{ + unsigned int checkcount = 0, vodsp_data; + + vodsp_data = rt5625_read(codec, RT5625_VODSP_REG_CMD); + while(vodsp_data & VODSP_BUSY) + { + if(checkcount > 10) + return -EBUSY; + vodsp_data = rt5625_read(codec, RT5625_VODSP_REG_CMD); + checkcount ++; + } + return 0; +} + +static int rt5625_write_vodsp_reg(struct snd_soc_codec *codec, unsigned int vodspreg, unsigned int value) +{ + int ret = 0; + + if(ret != rt5625_wait_vodsp_i2c_done(codec)) + return -EBUSY; + + rt5625_write(codec, RT5625_VODSP_REG_ADDR, vodspreg); + rt5625_write(codec, RT5625_VODSP_REG_DATA, value); + rt5625_write(codec, RT5625_VODSP_REG_CMD, VODSP_WRITE_ENABLE | VODSP_CMD_MW); + mdelay(10); + return ret; + +} + +static unsigned int rt5625_read_vodsp_reg(struct snd_soc_codec *codec, unsigned int vodspreg) +{ + int ret = 0; + unsigned int nDataH, nDataL; + unsigned int value; + + if(ret != rt5625_wait_vodsp_i2c_done(codec)) + return -EBUSY; + + rt5625_write(codec, RT5625_VODSP_REG_ADDR, vodspreg); + rt5625_write(codec, RT5625_VODSP_REG_CMD, VODSP_READ_ENABLE | VODSP_CMD_MR); + + if (ret != rt5625_wait_vodsp_i2c_done(codec)) + return -EBUSY; + rt5625_write(codec, RT5625_VODSP_REG_ADDR, 0x26); + rt5625_write(codec, RT5625_VODSP_REG_CMD, VODSP_READ_ENABLE | VODSP_CMD_RR); + + if(ret != rt5625_wait_vodsp_i2c_done(codec)) + return -EBUSY; + nDataH = rt5625_read(codec, RT5625_VODSP_REG_DATA); + rt5625_write(codec, RT5625_VODSP_REG_ADDR, 0x25); + rt5625_write(codec, RT5625_VODSP_REG_CMD, VODSP_READ_ENABLE | VODSP_CMD_RR); + + if(ret != rt5625_wait_vodsp_i2c_done(codec)) + return -EBUSY; + nDataL = rt5625_read(codec, RT5625_VODSP_REG_DATA); + value = ((nDataH & 0xff) << 8) |(nDataL & 0xff); + printk(KERN_INFO "%s vodspreg=0x%x, value=0x%x\n", __func__, vodspreg, value); + return value; +} + +static int rt5625_reg_init(struct snd_soc_codec *codec) +{ + int i; + + for (i = 0; i < RT5625_INIT_REG_NUM; i++) + rt5625_write(codec, rt5625_init_list[i].reg_index, rt5625_init_list[i].reg_value); + + return 0; +} + +//************************************************************************************************* +//************************************************************************************************* +#if (RT5625_EQ_FUNC_ENA==1) +//eq function +static void rt5625_update_eqmode(struct snd_soc_codec *codec, int mode) +{ + u16 HwEqIndex=0; + + if(mode==NORMAL) + { + /*clear EQ parameter*/ + for(HwEqIndex=0;HwEqIndex<=0x0C;HwEqIndex++) + { + rt5625_write_index(codec, HwEqIndex, HwEq_Preset[mode].EqValue[HwEqIndex]); + } + + rt5625_write_mask(codec, 0x6e,0x0,EN_HW_EQ_BLK | EN_HW_EQ_HPF | EN_HW_EQ_BP3 | EN_HW_EQ_BP2 | EN_HW_EQ_BP1 | EN_HW_EQ_LPF); /*disable EQ block*/ + } + else + { + /*Fill EQ parameter*/ + for(HwEqIndex=0;HwEqIndex<=0x0C;HwEqIndex++) + { + rt5625_write_index(codec, HwEqIndex, HwEq_Preset[mode].EqValue[HwEqIndex]); + } + + //enable EQ block + rt5625_write_mask(codec, 0x6e,HwEq_Preset[mode].HwEQCtrl,EN_HW_EQ_BLK | EN_HW_EQ_HPF | EN_HW_EQ_BP3 | EN_HW_EQ_BP2 | EN_HW_EQ_BP1 | EN_HW_EQ_LPF); + + //update EQ parameter + rt5625_write_mask(codec, 0x6e,0x0080,0x0080); + } +} + + +static int rt5625_eq_sel_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + u16 Virtual_reg = rt5625_read(codec, VIRTUAL_REG_FOR_MISC_FUNC); + int rt5625_mode=((Virtual_reg)&0xf000)>>12; + + if ( rt5625_mode == ucontrol->value.integer.value[0]) + return 0; + + rt5625_update_eqmode(codec, ucontrol->value.enumerated.item[0]); + + Virtual_reg &= 0x0fff; + Virtual_reg |= (ucontrol->value.integer.value[0])<<12; + rt5625_write(codec, VIRTUAL_REG_FOR_MISC_FUNC, Virtual_reg); + + return 0; +} +#endif +//************************************************************************************************* +//************************************************************************************************* +static const char *rt5625_aec_path_sel[] = {"aec func disable","aec func for pcm in/out", + "aec func for iis in/out","aec func for analog in/out"}; /*0*/ +static const char *rt5625_spk_out_sel[] = {"Class AB", "Class D"}; /*1*/ +static const char *rt5625_spk_l_source_sel[] = {"LPRN", "LPRP", "LPLN", "MM"}; /*2*/ +static const char *rt5625_spkmux_source_sel[] = {"VMID", "HP Mixer", + "SPK Mixer", "Mono Mixer"}; /*3*/ +static const char *rt5625_hplmux_source_sel[] = {"VMID","HPL Mixer"}; /*4*/ +static const char *rt5625_hprmux_source_sel[] = {"VMID","HPR Mixer"}; /*5*/ +static const char *rt5625_auxmux_source_sel[] = {"VMID", "HP Mixer", + "SPK Mixer", "Mono Mixer"}; /*6*/ +static const char *rt5625_spkamp_ratio_sel[] = {"2.25 Vdd", "2.00 Vdd", + "1.75 Vdd", "1.50 Vdd", "1.25 Vdd", "1.00 Vdd"}; /*7*/ +static const char *rt5625_mic1_boost_sel[] = {"Bypass", "+20db", "+30db", "+40db"}; /*8*/ +static const char *rt5625_mic2_boost_sel[] = {"Bypass", "+20db", "+30db", "+40db"}; /*9*/ +static const char *rt5625_dmic_boost_sel[] = {"Bypass", "+6db", "+12db", "+18db", + "+24db", "+30db", "+36db", "+42db"}; /*10*/ +static const char *rt5625_adcr_func_sel[] = {"Stereo ADC", "Voice ADC", + "VoDSP Interface", "PDM Slave Interface"}; /*11*/ +#if (RT5625_EQ_FUNC_ENA==1) +static const char *rt5625_eq_sel[] = {"NORMAL", "CLUB","DANCE", "LIVE","POP", /*12*/ + "ROCK", "OPPO", "TREBLE", "BASS"}; +#endif + +static const struct soc_enum rt5625_enum[] = { + +SOC_ENUM_SINGLE(VIRTUAL_REG_FOR_MISC_FUNC, 0, 4, rt5625_aec_path_sel), /*0*/ +SOC_ENUM_SINGLE(RT5625_OUTPUT_MIXER_CTRL, 13, 2, rt5625_spk_out_sel), /*1*/ +SOC_ENUM_SINGLE(RT5625_OUTPUT_MIXER_CTRL, 14, 4, rt5625_spk_l_source_sel), /*2*/ +SOC_ENUM_SINGLE(RT5625_OUTPUT_MIXER_CTRL, 10, 4, rt5625_spkmux_source_sel),/*3*/ +SOC_ENUM_SINGLE(RT5625_OUTPUT_MIXER_CTRL, 9, 2, rt5625_hplmux_source_sel), /*4*/ +SOC_ENUM_SINGLE(RT5625_OUTPUT_MIXER_CTRL, 8, 2, rt5625_hprmux_source_sel),/*5*/ +SOC_ENUM_SINGLE(RT5625_OUTPUT_MIXER_CTRL, 6, 4, rt5625_auxmux_source_sel),/*6*/ +SOC_ENUM_SINGLE(RT5625_GEN_CTRL_REG1, 1, 6, rt5625_spkamp_ratio_sel), /*7*/ +SOC_ENUM_SINGLE(RT5625_MIC_CTRL, 10, 4, rt5625_mic1_boost_sel), /*8*/ +SOC_ENUM_SINGLE(RT5625_MIC_CTRL, 8, 4, rt5625_mic2_boost_sel), /*9*/ +SOC_ENUM_SINGLE(RT5625_DMIC_CTRL, 0, 8, rt5625_dmic_boost_sel), /*10*/ +SOC_ENUM_SINGLE(RT5625_DAC_ADC_VODAC_FUN_SEL, 4, 4, rt5625_adcr_func_sel), /*11*/ +#if (RT5625_EQ_FUNC_ENA==1) +SOC_ENUM_SINGLE(VIRTUAL_REG_FOR_MISC_FUNC, 12, 9, rt5625_eq_sel), /*EQ mode select mode 12*/ +#endif +}; + + + +//***************************************************************************** +//function:Enable the Voice PCM interface Path +//***************************************************************************** +static int ConfigPcmVoicePath(struct snd_soc_codec *codec,unsigned int bEnableVoicePath,unsigned int mode) +{ + + if(bEnableVoicePath) + { + //Power on DAC reference + rt5625_write_mask(codec,RT5625_PWR_MANAG_ADD1,PWR_DAC_REF|PWR_VOICE_DF2SE,PWR_DAC_REF|PWR_VOICE_DF2SE); + //Power on Voice DAC/ADC + rt5625_write_mask(codec,RT5625_PWR_MANAG_ADD2,PWR_VOICE_CLOCK,PWR_VOICE_CLOCK); + //routing voice to HPMixer + rt5625_write_mask(codec,RT5625_VOICE_DAC_OUT_VOL,0,M_V_DAC_TO_HP_MIXER); + + switch(mode) + { + case PCM_SLAVE_MODE_B: //8kHz sampling rate,16 bits PCM mode and Slave mode,PCM mode is B,MCLK=24.576MHz from Oscillator. + //CSR PSKEY_PCM_CONFIG32 (HEX) = 0x08C00000,PSKEY_FORMAT=0x0060 + + //Enable GPIO 1,3,4,5 to voice interface + //Set I2S to Slave mode + //Voice I2S SYSCLK Source select Main SYSCLK + //Set voice I2S VBCLK Polarity to Invert + //Set Data length to 16 bit + //set Data Fomrat to PCM mode B + //the register 0x36 value's should is 0xC083 + rt5625_write(codec,RT5625_EXTEND_SDP_CTRL,0xC083); + + //Set LRCK voice select divide 32 + //set voice blck select divide 6 and 8 + //voice filter clock divide 3 and 16 + //the register 0x64 value's should is 0x5524 + rt5625_write(codec,RT5625_VOICE_DAC_PCMCLK_CTRL1,0x5524); + + break; + + case PCM_SLAVE_MODE_A: //8kHz sampling rate,16 bits PCM and Slave mode,PCM mode is A,MCLK=24.576MHz from Oscillator. + //CSR PSKEY_PCM_CONFIG32 (HEX) = 0x08C00004,PSKEY_FORMAT=0x0060 + + //Enable GPIO 1,3,4,5 to voice interface + //Set I2S to Slave mode + //Voice I2S SYSCLK Source select Main SYSCLK + //Set voice i2s VBCLK Polarity to Invert + //Set Data length to 16 bit + //set Data Fomrat to PCM mode A + //the register 0x36 value's should is 0xC082 + rt5625_write(codec,RT5625_EXTEND_SDP_CTRL,0xC082); + + //Set LRCK voice select divide 64 + //set voice blck select divide 6 and 8 + //voice filter clock divide 3 and 16 + //the register 0x64 value's should is 0x5524 + rt5625_write(codec,RT5625_VOICE_DAC_PCMCLK_CTRL1,0x5524); + + break; + + case PCM_MASTER_MODE_B: //8kHz sampling rate,16 bits PCM and Master mode,PCM mode is B,Clock from PLL OUT + //CSR PSKEY_PCM_CONFIG32 (HEX) = 0x08000002,PSKEY_FORMAT=0x0060 + + //Enable GPIO 1,3,4,5 to voice interface + //Set I2S to master mode + //Set voice i2s VBCLK Polarity to Invert + //Set Data length to 16 bit + //set Data Fomrat to PCM mode B + //the register 0x36 value's should is 0x8083 + rt5625_write(codec,RT5625_EXTEND_SDP_CTRL,0x8083); + + //Set LRCK voice select divide 64 + //set voice blck select divide 6 and 8 + //voice filter clock divide 3 and 16 + //the register 0x64 value's should is 0x5524 + rt5625_write(codec,RT5625_VOICE_DAC_PCMCLK_CTRL1,0x5524); + + break; + + default: + //do nothing + break; + + } + } + else + { + //Power down Voice Different to sing-end power + rt5625_write_mask(codec,RT5625_PWR_MANAG_ADD1,0,PWR_VOICE_DF2SE); + //Power down Voice DAC/ADC + rt5625_write_mask(codec,RT5625_PWR_MANAG_ADD2,0,PWR_VOICE_CLOCK); + //Disable Voice PCM interface + rt5625_write_mask(codec,RT5625_EXTEND_SDP_CTRL,0,EXT_I2S_FUNC_ENABLE); + } + + return 0; +} + +static int init_vodsp_aec(struct snd_soc_codec *codec) +{ + int i; + int ret = 0; + + /*disable LDO power*/ + rt5625_write_mask(codec, RT5625_LDO_CTRL,0,LDO_ENABLE); + mdelay(20); + rt5625_write_mask(codec, RT5625_VODSP_CTL,VODSP_NO_PD_MODE_ENA,VODSP_NO_PD_MODE_ENA); + /*enable LDO power and set output voltage to 1.2V*/ + rt5625_write_mask(codec, RT5625_LDO_CTRL,LDO_ENABLE|LDO_OUT_VOL_CTRL_1_20V,LDO_ENABLE|LDO_OUT_VOL_CTRL_MASK); + mdelay(20); + /*enable power of VODSP I2C interface*/ + rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD3,PWR_VODSP_INTERFACE|PWR_I2C_FOR_VODSP,PWR_VODSP_INTERFACE|PWR_I2C_FOR_VODSP); + mdelay(1); + rt5625_write_mask(codec, RT5625_VODSP_CTL,0,VODSP_NO_RST_MODE_ENA); /*Reset VODSP*/ + mdelay(1); + rt5625_write_mask(codec, RT5625_VODSP_CTL,VODSP_NO_RST_MODE_ENA,VODSP_NO_RST_MODE_ENA); /*set VODSP to non-reset status*/ + mdelay(20); + + /*initize AEC paramter*/ + for(i = 0; i < SET_VODSP_REG_INIT_NUM; i++) + { + ret = rt5625_write_vodsp_reg(codec, VODSP_AEC_Init_Value[i].VoiceDSPIndex,VODSP_AEC_Init_Value[i].VoiceDSPValue); + if(ret) + return -EIO; + } + + schedule_timeout_uninterruptible(msecs_to_jiffies(10)); + + return 0; +} + +//*********************************************************************************************** +//function:Enable/Disable the vodsp interface Path +//For system clock only suport specific clock,realtek suggest customer to use 24.576Mhz or 22.5792Mhz +//clock fro MCLK(MCLK=48k*512 or 44.1k*512Mhz) +//*********************************************************************************************** +static int set_vodsp_aec_path(struct snd_soc_codec *codec, unsigned int mode) +{ + + switch(mode) + { + + case PCM_IN_PCM_OUT: + //set PCM format + ConfigPcmVoicePath(codec,1,PCM_MASTER_MODE_B); + //set AEC path + rt5625_write_mask(codec, 0x26,0x0300,0x0300); + rt5625_write_mask(codec, RT5625_VODSP_PDM_CTL,VODSP_RXDP_PWR|VODSP_RXDP_S_SEL_VOICE|VOICE_PCM_S_SEL_AEC_TXDP + ,VODSP_RXDP_PWR|VODSP_RXDP_S_SEL_MASK|VOICE_PCM_S_SEL_MASK); + rt5625_write_mask(codec, RT5625_DAC_ADC_VODAC_FUN_SEL,ADCR_FUNC_SEL_PDM|VODAC_SOUR_SEL_VODSP_TXDC + ,ADCR_FUNC_SEL_MASK|VODAC_SOUR_SEL_MASK); + rt5625_write_mask(codec, RT5625_VODSP_CTL,VODSP_LRCK_SEL_8K,VODSP_LRCK_SEL_MASK); + rt5625_write_mask(codec, 0x26,0x0000,0x0300); + //set input&output path and power + rt5625_write_mask(codec, 0x3a,0x0c8f,0x0c8f);//power on related bit + rt5625_write_mask(codec, 0x3c,0xa4cb,0xa4cb);//power on related bit + rt5625_write_mask(codec, 0x3e,0x3302,0xf302);//power on related bit + + rt5625_write(codec, 0x10, 0xee0f);//mute DAC to hpmixer + rt5625_write(codec, 0x0e, 0x8808);//set Mic1 to differential mode + rt5625_write(codec, 0x22, 0x0000);//Mic boost 0db + rt5625_write(codec, 0x12, 0xCBD3);//ADC_Mixer_R boost 10.5 db + rt5625_write(codec, 0x14, 0x7f3f);//Mic1->ADCMixer_R + rt5625_write(codec, 0x18, 0xa010);//VoDAC to speakerMixer,0db + rt5625_write(codec, 0x1c, 0x8808);//speaker source from speakermixer + + rt5625_write_mask(codec, 0x02,0x0000,0x8080); //unmute speaker + + break; + + + case ANALOG_IN_ANALOG_OUT: + rt5625_write_mask(codec, 0x26,0x0300,0x0300); + rt5625_write_mask(codec, RT5625_VODSP_PDM_CTL,VODSP_RXDP_PWR|VODSP_RXDP_S_SEL_ADCL|VOICE_PCM_S_SEL_AEC_TXDP + ,VODSP_RXDP_PWR|VODSP_RXDP_S_SEL_MASK|VOICE_PCM_S_SEL_MASK); + rt5625_write_mask(codec, RT5625_DAC_ADC_VODAC_FUN_SEL,ADCR_FUNC_SEL_PDM|VODAC_SOUR_SEL_VODSP_TXDC|DAC_FUNC_SEL_VODSP_TXDP|ADCL_FUNC_SEL_VODSP + ,ADCR_FUNC_SEL_MASK|VODAC_SOUR_SEL_MASK|DAC_FUNC_SEL_MASK|ADCL_FUNC_SEL_MASK); + rt5625_write_mask(codec, RT5625_VODSP_CTL,VODSP_LRCK_SEL_16K,VODSP_LRCK_SEL_MASK); + rt5625_write_mask(codec, 0x26,0x0000,0x0300); + //set input&output path and power + rt5625_write_mask(codec, 0x3a,0xcc8f,0xcc8f);//power on related bit + rt5625_write_mask(codec, 0x3c,0xa7cf,0xa7cf);//power on related bit + rt5625_write_mask(codec, 0x3e,0xf312,0xf312);//power on related bit + + rt5625_write(codec, 0x0e, 0x8808);//set Mic1 to differential mode + rt5625_write(codec, 0x08, 0xe800);//set phone in to differential mode + rt5625_write(codec, 0x22, 0x0000);//Mic boost 0db + rt5625_write(codec, 0x14, 0x773f);//Mic1->ADCMixer_R,phone in-->ADCMixer_L + rt5625_write(codec, 0x12, 0xCBD3);//ADC_Mixer_R boost 10.5 db + rt5625_write(codec, 0x1c, 0x88c8);//speaker from spkmixer,monoOut from monoMixer + rt5625_write(codec, 0x18, 0xA010);//unmute VoDAC to spkmixer + rt5625_write(codec, 0x10, 0xee0e);//unmute DAC to monoMixer + rt5625_write(codec, 0x62, 0x2222); + rt5625_write(codec, 0x64, 0x3122); + rt5625_write_mask(codec, 0x02,0x0000,0x8080); //unmute speaker + rt5625_write_mask(codec, 0x06,0x0000,0x8080); //unmute auxout + break; + + case DAC_IN_ADC_OUT: + rt5625_write_mask(codec, 0x26,0x0300,0x0300); + rt5625_write_mask(codec,RT5625_DAC_ADC_VODAC_FUN_SEL,ADCR_FUNC_SEL_PDM|DAC_FUNC_SEL_VODSP_TXDC + ,ADCR_FUNC_SEL_MASK|DAC_FUNC_SEL_MASK); + rt5625_write_mask(codec,RT5625_VODSP_PDM_CTL,VODSP_SRC1_PWR|VODSP_SRC2_PWR|VODSP_RXDP_PWR|VODSP_RXDP_S_SEL_SRC1|REC_S_SEL_SRC2, + VODSP_SRC1_PWR|VODSP_SRC2_PWR|VODSP_RXDP_PWR|VODSP_RXDP_S_SEL_MASK|REC_S_SEL_MASK); + rt5625_write_mask(codec,RT5625_VODSP_CTL,VODSP_LRCK_SEL_16K,VODSP_LRCK_SEL_MASK); + rt5625_write_mask(codec, 0x26,0x0000,0x0300); + //set input&output path and power + rt5625_write_mask(codec, 0x3a,0xcc0f,0xcc0f);//power on related bit + rt5625_write_mask(codec, 0x3c,0xa7cb,0xa7cb);//power on related bit + rt5625_write_mask(codec, 0x3e,0x3302,0x3302);//power on related bit + + rt5625_write(codec, 0x0e, 0x8808);//set Mic1 to differential mode + rt5625_write(codec, 0x22, 0x0000);//Mic boost 0db + rt5625_write(codec, 0x14, 0x7f3f);//Mic1->ADCMixer_R + rt5625_write(codec, 0x12, 0xCBD3);//ADC_Mixer_R boost 10.5 db + rt5625_write(codec, 0x1c, 0x8808);//speaker out from spkMixer + rt5625_write(codec, 0x10, 0xee0d);//unmute DAC to spkMixer + rt5625_write(codec, 0x60, 0x3075); + rt5625_write(codec, 0x62, 0x1010); + rt5625_write_mask(codec, 0x02,0x0000,0x8080); //unmute speaker + + break; + + case VODSP_AEC_DISABLE: + default: + rt5625_write_mask(codec, 0x02,0x8080,0x8080);//mute speaker out + rt5625_write_mask(codec, 0x06,0x8080,0x8080);//mute auxout + rt5625_write(codec, 0x22, 0x0500);//Mic boost 20db by default + rt5625_write(codec, 0x14, 0x3f3f);//record from Mic1 by default + rt5625_write(codec, 0x12, 0xD5D5);//ADC_Mixer_R boost 15 db by default + rt5625_write(codec, 0x1c, 0x0748);//all output from HPmixer by default + rt5625_write(codec, 0x10, 0xee03);//DAC to HPmixer by default + rt5625_write(codec, 0x18, 0xe010);//mute VoDAC to mixer by default + rt5625_write_mask(codec, 0x26,0x0300,0x0300); + /*set stereo DAC&Voice DAC&Stereo ADC function select to default*/ + rt5625_write(codec, RT5625_DAC_ADC_VODAC_FUN_SEL,0); + /*set VODSP&PDM Control to default*/ + rt5625_write(codec, RT5625_VODSP_PDM_CTL,0); + rt5625_write_mask(codec, 0x26,0x0000,0x0300); + rt5625_write_mask(codec, 0x3e,0x0000,0xf312);//power down related bit + rt5625_write_mask(codec, 0x3a,0x0000,0xcc8d);//power down related bit + rt5625_write_mask(codec, 0x3c,0x0000,0x07cf);//power down related bit + + + break; + } + + return 0; +} + +static int enable_vodsp_aec(struct snd_soc_codec *codec, unsigned int VodspAEC_En, unsigned int AEC_mode) +{ + int ret = 0; + + + if (VodspAEC_En != 0) + { + + //enable power of VODSP I2C interface & VODSP interface + rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD3,PWR_VODSP_INTERFACE|PWR_I2C_FOR_VODSP,PWR_VODSP_INTERFACE|PWR_I2C_FOR_VODSP); + //enable power of VODSP I2S interface + rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD1,PWR_I2S_INTERFACE,PWR_I2S_INTERFACE); + //select input/output of VODSP AEC + set_vodsp_aec_path(codec, AEC_mode); + + } + else + { + //disable VODSP AEC path + set_vodsp_aec_path(codec, VODSP_AEC_DISABLE); + //set VODSP AEC to power down mode + rt5625_write_mask(codec, RT5625_VODSP_CTL,0,VODSP_NO_PD_MODE_ENA); + //disable power of VODSP I2C interface & VODSP interface + rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD3,0,PWR_VODSP_INTERFACE|PWR_I2C_FOR_VODSP); + } + + return ret; +} + +static void rt5625_aec_config(struct snd_soc_codec *codec, unsigned int mode) +{ + printk("rt5625_aec_config %d\n",mode); + + if (mode == VODSP_AEC_DISABLE) + { + enable_vodsp_aec(codec,0, mode); + /*disable LDO power*/ + rt5625_write_mask(codec, RT5625_LDO_CTRL,0,LDO_ENABLE); + } + else + { + init_vodsp_aec(codec); + + enable_vodsp_aec(codec,1, mode); + } +} +//**************************************************************************************************************** +//* +//*function:disable rt5625's function. +//* +//* +//**************************************************************************************************************** +static int rt5625_func_aec_disable(struct snd_soc_codec *codec,int mode) +{ + + switch(mode) + { + case RT5625_AEC_PCM_IN_OUT: + case RT5625_AEC_IIS_IN_OUT: + case RT5625_AEC_ANALOG_IN_OUT: + + rt5625_aec_config(codec,VODSP_AEC_DISABLE); //disable AEC function and path + + break; + + default: + + break; + } + + return 0; +} + + +static int rt5625_get_dsp_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + /*cause we choose bit[0][1] to store the mode type*/ + int mode = (rt5625_read(codec, VIRTUAL_REG_FOR_MISC_FUNC)) & 0x03; + + ucontrol->value.integer.value[0] = mode; + return 0; +} + + +static int rt5625_set_dsp_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + u16 Virtual_reg = rt5625_read(codec, VIRTUAL_REG_FOR_MISC_FUNC); + int rt5625_mode=(Virtual_reg)&0x03; + + printk("1rt5625_mode=%d,value[0]=%ld,Virtual_reg=%x\n",rt5625_mode,ucontrol->value.integer.value[0],Virtual_reg); + + if ( rt5625_mode == ucontrol->value.integer.value[0]) + return 0; + + switch(ucontrol->value.integer.value[0]) + { + case RT5625_AEC_PCM_IN_OUT: + + rt5625_aec_config(codec,PCM_IN_PCM_OUT);//enable AEC PCM in/out function and path + + break; + + case RT5625_AEC_IIS_IN_OUT: + + rt5625_aec_config(codec,DAC_IN_ADC_OUT);//enable AEC IIS in/out function and path + + break; + + case RT5625_AEC_ANALOG_IN_OUT: + + rt5625_aec_config(codec,ANALOG_IN_ANALOG_OUT);//enable AEC analog in/out function and path + + break; + + case RT5625_AEC_DISABLE: + + rt5625_func_aec_disable(codec,rt5625_mode); //disable previous select function. + + break; + + default: + + break; + } + + Virtual_reg &= 0xfffc; + Virtual_reg |= (ucontrol->value.integer.value[0]); + rt5625_write(codec, VIRTUAL_REG_FOR_MISC_FUNC, Virtual_reg); + + printk("2rt5625_mode=%d,value[0]=%ld,Virtual_reg=%x\n",rt5625_mode,ucontrol->value.integer.value[0],Virtual_reg); + return 0; +} + +static int rt5625_dump_dsp_reg(struct snd_soc_codec *codec) +{ + int i; + + rt5625_write_mask(codec, RT5625_VODSP_CTL, VODSP_NO_PD_MODE_ENA,VODSP_NO_PD_MODE_ENA); + for (i = 0; i < SET_VODSP_REG_INIT_NUM; i++) { + rt5625_read_vodsp_reg(codec, VODSP_AEC_Init_Value[i].VoiceDSPIndex); + } + return 0; +} + + +static int rt5625_dump_dsp_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int mode = rt5625_read(codec, VIRTUAL_REG_FOR_MISC_FUNC); + + mode &= ~(0x01 << 8); + mode |= (ucontrol->value.integer.value[0] << 8); + rt5625_write(codec, VIRTUAL_REG_FOR_MISC_FUNC, mode); + rt5625_dump_dsp_reg(codec); + + return 0; +} + +static const struct snd_kcontrol_new rt5625_snd_controls[] = { +SOC_ENUM_EXT("rt5625 aec mode sel", rt5625_enum[0], rt5625_get_dsp_mode, rt5625_set_dsp_mode), +SOC_ENUM("SPK Amp Type", rt5625_enum[1]), +SOC_ENUM("Left SPK Source", rt5625_enum[2]), +SOC_ENUM("SPK Amp Ratio", rt5625_enum[7]), +SOC_ENUM("Mic1 Boost", rt5625_enum[8]), +SOC_ENUM("Mic2 Boost", rt5625_enum[9]), +SOC_ENUM("Dmic Boost", rt5625_enum[10]), +SOC_ENUM("ADCR Func", rt5625_enum[11]), +SOC_DOUBLE("PCM Playback Volume", RT5625_STEREO_DAC_VOL, 8, 0, 63, 1), +SOC_DOUBLE("LineIn Playback Volume", RT5625_LINE_IN_VOL, 8, 0, 31, 1), +SOC_SINGLE("Phone Playback Volume", RT5625_PHONEIN_VOL, 8, 31, 1), +SOC_SINGLE("Mic1 Playback Volume", RT5625_MIC_VOL, 8, 31, 1), +SOC_SINGLE("Mic2 Playback Volume", RT5625_MIC_VOL, 0, 31, 1), +SOC_DOUBLE("PCM Capture Volume", RT5625_ADC_REC_GAIN, 8, 0, 31, 1), +SOC_DOUBLE("SPKOUT Playback Volume", RT5625_SPK_OUT_VOL, 8, 0, 31, 1), +SOC_DOUBLE("SPKOUT Playback Switch", RT5625_SPK_OUT_VOL, 15, 7, 1, 1), +SOC_DOUBLE("HPOUT Playback Volume", RT5625_HP_OUT_VOL, 8, 0, 31, 1), +SOC_DOUBLE("HPOUT Playback Switch", RT5625_HP_OUT_VOL, 15, 7, 1, 1), +SOC_DOUBLE("AUXOUT Playback Volume", RT5625_AUX_OUT_VOL, 8, 0, 31, 1), +SOC_DOUBLE("AUXOUT Playback Switch", RT5625_AUX_OUT_VOL, 15, 7, 1, 1), +SOC_DOUBLE("ADC Record Gain", RT5625_ADC_REC_GAIN, 8, 0, 31, 0), +SOC_SINGLE_EXT("VoDSP Dump", VIRTUAL_REG_FOR_MISC_FUNC, 8, 1, 0, + snd_soc_get_volsw, rt5625_dump_dsp_put), +#if (RT5625_EQ_FUNC_ENA==1) +SOC_ENUM_EXT("EQ Mode", rt5625_enum[12], snd_soc_get_enum_double, rt5625_eq_sel_put), +#endif +}; + +static int rt5625_add_controls(struct snd_soc_codec *codec) +{ + int err, i; + + for (i = 0; i < ARRAY_SIZE(rt5625_snd_controls); i++){ + err = snd_ctl_add(codec->card, + snd_soc_cnew(&rt5625_snd_controls[i], + codec, NULL)); + if (err < 0) + return err; + } + return 0; +} + +static void hp_depop_mode2(struct snd_soc_codec *codec) +{ + rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD1, PWR_SOFTGEN_EN, PWR_SOFTGEN_EN); + rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD3, PWR_HP_R_OUT_VOL|PWR_HP_L_OUT_VOL, + PWR_HP_R_OUT_VOL|PWR_HP_L_OUT_VOL); + rt5625_write(codec, RT5625_MISC_CTRL,HP_DEPOP_MODE2_EN); + printk(KERN_INFO "delay 500 msec\n"); + schedule_timeout_uninterruptible(msecs_to_jiffies(500)); + + + rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD1, PWR_HP_OUT_AMP|PWR_HP_OUT_ENH_AMP, + PWR_HP_OUT_AMP|PWR_HP_OUT_ENH_AMP); +// rt5625_write_mask(codec, RT5625_MISC_CTRL, 0, HP_DEPOP_MODE2_EN); + +} + +//enable depop function for mute/unmute +static void hp_mute_unmute_depop(struct snd_soc_codec *codec,int mute) +{ + if(mute) + { + rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD1, PWR_SOFTGEN_EN, PWR_SOFTGEN_EN); + rt5625_write(codec, RT5625_MISC_CTRL,M_UM_DEPOP_EN|HP_R_M_UM_DEPOP_EN|HP_L_M_UM_DEPOP_EN); + //Mute headphone right/left channel + rt5625_write_mask(codec,RT5625_HP_OUT_VOL,RT_L_MUTE|RT_R_MUTE,RT_L_MUTE|RT_R_MUTE); + mdelay(50); + } + else + { + rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD1, PWR_SOFTGEN_EN, PWR_SOFTGEN_EN); + rt5625_write(codec, RT5625_MISC_CTRL, M_UM_DEPOP_EN|HP_R_M_UM_DEPOP_EN|HP_L_M_UM_DEPOP_EN); + //unMute headphone right/left channel + rt5625_write_mask(codec,RT5625_HP_OUT_VOL,0,RT_L_MUTE|RT_R_MUTE); + mdelay(50); + } + +} + + +/* + * _DAPM_ Controls + */ + /*Left ADC Rec mixer*/ + /*Left ADC Rec mixer*/ +static const struct snd_kcontrol_new rt5625_left_adc_rec_mixer_controls[] = { +SOC_DAPM_SINGLE("Mic1 Capture Switch", RT5625_ADC_REC_MIXER, 14, 1, 1), +SOC_DAPM_SINGLE("Mic2 Capture Switch", RT5625_ADC_REC_MIXER, 13, 1, 1), +SOC_DAPM_SINGLE("LineIn Capture Switch", RT5625_ADC_REC_MIXER, 12, 1, 1), +SOC_DAPM_SINGLE("Phone Capture Switch", RT5625_ADC_REC_MIXER, 11, 1, 1), +SOC_DAPM_SINGLE("HP Mixer Capture Switch", RT5625_ADC_REC_MIXER, 10, 1, 1), +SOC_DAPM_SINGLE("SPK Mixer Capture Switch", RT5625_ADC_REC_MIXER, 9, 1, 1), +SOC_DAPM_SINGLE("MoNo Mixer Capture Switch", RT5625_ADC_REC_MIXER, 8, 1, 1), + +}; + +/*Left ADC Rec mixer*/ +static const struct snd_kcontrol_new rt5625_right_adc_rec_mixer_controls[] = { +SOC_DAPM_SINGLE("Mic1 Capture Switch", RT5625_ADC_REC_MIXER, 6, 1, 1), +SOC_DAPM_SINGLE("Mic2 Capture Switch", RT5625_ADC_REC_MIXER, 5, 1, 1), +SOC_DAPM_SINGLE("LineIn Capture Switch", RT5625_ADC_REC_MIXER, 4, 1, 1), +SOC_DAPM_SINGLE("Phone Capture Switch", RT5625_ADC_REC_MIXER, 3, 1, 1), +SOC_DAPM_SINGLE("HP Mixer Capture Switch", RT5625_ADC_REC_MIXER, 2, 1, 1), +SOC_DAPM_SINGLE("SPK Mixer Capture Switch", RT5625_ADC_REC_MIXER, 1, 1, 1), +SOC_DAPM_SINGLE("MoNo Mixer Capture Switch", RT5625_ADC_REC_MIXER, 0, 1, 1), +}; + +/*Left hpmixer mixer*/ +static const struct snd_kcontrol_new rt5625_left_hp_mixer_controls[] = { +SOC_DAPM_SINGLE("ADC Playback Switch", RT5625_ADC_REC_GAIN, 15, 1, 1), +SOC_DAPM_SINGLE("LineIn Playback Switch", HPL_MIXER, 0, 1, 0), +SOC_DAPM_SINGLE("Phone Playback Switch", HPL_MIXER, 1, 1, 0), +SOC_DAPM_SINGLE("Mic1 Playback Switch", HPL_MIXER, 2, 1, 0), +SOC_DAPM_SINGLE("Mic2 Playback Switch", HPL_MIXER, 3, 1, 0), +SOC_DAPM_SINGLE("Voice DAC Playback Switch", HPL_MIXER, 4, 1, 0), +SOC_DAPM_SINGLE("HIFI DAC Playback Switch", RT5625_DAC_AND_MIC_CTRL, 3, 1, 1), + +}; + +/*Right hpmixer mixer*/ +static const struct snd_kcontrol_new rt5625_right_hp_mixer_controls[] = { +SOC_DAPM_SINGLE("ADC Playback Switch", RT5625_ADC_REC_GAIN, 7, 1, 1), +SOC_DAPM_SINGLE("LineIn Playback Switch", HPR_MIXER, 0, 1, 0), +SOC_DAPM_SINGLE("Phone Playback Switch", HPR_MIXER, 1, 1, 0), +SOC_DAPM_SINGLE("Mic1 Playback Switch", HPR_MIXER, 2, 1, 0), +SOC_DAPM_SINGLE("Mic2 Playback Switch", HPR_MIXER, 3, 1, 0), +SOC_DAPM_SINGLE("Voice DAC Playback Switch", HPR_MIXER, 4, 1, 0), +SOC_DAPM_SINGLE("HIFI DAC Playback Switch", RT5625_DAC_AND_MIC_CTRL, 2, 1, 1), + +}; + +/*mono mixer*/ +static const struct snd_kcontrol_new rt5625_mono_mixer_controls[] = { +SOC_DAPM_SINGLE("ADCL Playback Switch", RT5625_ADC_REC_GAIN, 14, 1, 1), +SOC_DAPM_SINGLE("ADCR Playback Switch", RT5625_ADC_REC_GAIN, 6, 1, 1), +SOC_DAPM_SINGLE("Line Mixer Playback Switch", RT5625_LINE_IN_VOL, 13, 1, 1), +SOC_DAPM_SINGLE("Mic1 Playback Switch", RT5625_DAC_AND_MIC_CTRL, 13, 1, 1), +SOC_DAPM_SINGLE("Mic2 Playback Switch", RT5625_DAC_AND_MIC_CTRL, 9, 1, 1), +SOC_DAPM_SINGLE("DAC Mixer Playback Switch", RT5625_DAC_AND_MIC_CTRL, 0, 1, 1), +SOC_DAPM_SINGLE("Voice DAC Playback Switch", RT5625_VOICE_DAC_OUT_VOL, 13, 1, 1), +}; + +/*speaker mixer*/ +static const struct snd_kcontrol_new rt5625_spk_mixer_controls[] = { +SOC_DAPM_SINGLE("Line Mixer Playback Switch", RT5625_LINE_IN_VOL, 14, 1, 1), +SOC_DAPM_SINGLE("Phone Playback Switch", RT5625_PHONEIN_VOL, 14, 1, 1), +SOC_DAPM_SINGLE("Mic1 Playback Switch", RT5625_DAC_AND_MIC_CTRL, 14, 1, 1), +SOC_DAPM_SINGLE("Mic2 Playback Switch", RT5625_DAC_AND_MIC_CTRL, 10, 1, 1), +SOC_DAPM_SINGLE("DAC Mixer Playback Switch", RT5625_DAC_AND_MIC_CTRL, 1, 1, 1), +SOC_DAPM_SINGLE("Voice DAC Playback Switch", RT5625_VOICE_DAC_OUT_VOL, 14, 1, 1), +}; + +static int mixer_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = w->codec; + unsigned int l, r; + + printk(KERN_INFO "enter %s\n", __func__); + + l= rt5625_read(codec, HPL_MIXER); + r = rt5625_read(codec, HPR_MIXER); + + if ((l & 0x1) || (r & 0x1)) + rt5625_write_mask(codec, 0x0a, 0x0000, 0x8000); + else + rt5625_write_mask(codec, 0x0a, 0x8000, 0x8000); + + if ((l & 0x2) || (r & 0x2)) + rt5625_write_mask(codec, 0x08, 0x0000, 0x8000); + else + rt5625_write_mask(codec, 0x08, 0x8000, 0x8000); + + if ((l & 0x4) || (r & 0x4)) + rt5625_write_mask(codec, 0x10, 0x0000, 0x8000); + else + rt5625_write_mask(codec, 0x10, 0x8000, 0x8000); + + if ((l & 0x8) || (r & 0x8)) + rt5625_write_mask(codec, 0x10, 0x0000, 0x0800); + else + rt5625_write_mask(codec, 0x10, 0x0800, 0x0800); + + if ((l & 0x10) || (r & 0x10)) + rt5625_write_mask(codec, 0x18, 0x0000, 0x8000); + else + rt5625_write_mask(codec, 0x18, 0x8000, 0x8000); + + return 0; +} + + +/* + * bit[0][1] use for aec control + * bit[2][3] for ADCR func + * bit[4] for SPKL pga + * bit[5] for SPKR pga + * bit[6] for hpl pga + * bit[7] for hpr pga + */ +static int spk_pga_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) + { + struct snd_soc_codec *codec = w->codec; + int reg; + + printk(KERN_INFO "enter %s\n", __func__); + reg = rt5625_read(codec, VIRTUAL_REG_FOR_MISC_FUNC) & (0x3 << 4); + if ((reg >> 4) != 0x3 && reg != 0) + return 0; + + switch (event) + { + case SND_SOC_DAPM_POST_PMU: + printk(KERN_INFO "after virtual spk power up!\n"); + rt5625_write_mask(codec, 0x3e, 0x3000, 0x3000); + rt5625_write_mask(codec, 0x02, 0x0000, 0x8080); + rt5625_write_mask(codec, 0x3a, 0x0400, 0x0400);//power on spk amp + break; + case SND_SOC_DAPM_POST_PMD: + printk(KERN_INFO "aftet virtual spk power down!\n"); + rt5625_write_mask(codec, 0x3a, 0x0000, 0x0400);//power off spk amp + rt5625_write_mask(codec, 0x02, 0x8080, 0x8080); + rt5625_write_mask(codec, 0x3e, 0x0000, 0x3000); + break; + default: + return 0; + } + return 0; +} + + + + +static int hp_pga_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = w->codec; + int reg; + + printk(KERN_INFO "enter %s\n", __func__); + + reg = rt5625_read(codec, VIRTUAL_REG_FOR_MISC_FUNC) & (0x3 << 6); + if ((reg >> 6) != 0x3 && reg != 0) + return 0; + + switch (event) + { + case SND_SOC_DAPM_POST_PMD: + + printk(KERN_INFO "aftet virtual hp power down!\n"); + + hp_mute_unmute_depop(codec,1);//mute hp + rt5625_write_mask(codec, 0x3a, 0x0000, 0x0300); + rt5625_write_mask(codec, 0x3e, 0x0000, 0x0c00); + break; + + case SND_SOC_DAPM_POST_PMU: + + printk(KERN_INFO "after virtual hp power up!\n"); + hp_depop_mode2(codec); + hp_mute_unmute_depop(codec,0);//unmute hp + break; + + default: + return 0; + } + + return 0; +} + + + +static int aux_pga_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) +{ + return 0; +} + +/*SPKOUT Mux*/ +static const struct snd_kcontrol_new rt5625_spkout_mux_out_controls = +SOC_DAPM_ENUM("Route", rt5625_enum[3]); + +/*HPLOUT MUX*/ +static const struct snd_kcontrol_new rt5625_hplout_mux_out_controls = +SOC_DAPM_ENUM("Route", rt5625_enum[4]); + +/*HPROUT MUX*/ +static const struct snd_kcontrol_new rt5625_hprout_mux_out_controls = +SOC_DAPM_ENUM("Route", rt5625_enum[5]); +/*AUXOUT MUX*/ +static const struct snd_kcontrol_new rt5625_auxout_mux_out_controls = +SOC_DAPM_ENUM("Route", rt5625_enum[6]); + +static const struct snd_soc_dapm_widget rt5625_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("Left LineIn"), +SND_SOC_DAPM_INPUT("Right LineIn"), +SND_SOC_DAPM_INPUT("Phone"), +SND_SOC_DAPM_INPUT("Mic1"), +SND_SOC_DAPM_INPUT("Mic2"), + +SND_SOC_DAPM_PGA("Mic1 Boost", RT5625_PWR_MANAG_ADD3, 1, 0, NULL, 0), +SND_SOC_DAPM_PGA("Mic2 Boost", RT5625_PWR_MANAG_ADD3, 0, 0, NULL, 0), + +SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback DAC", RT5625_PWR_MANAG_ADD2, 9, 0), +SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback DAC", RT5625_PWR_MANAG_ADD2, 8, 0), +SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback DAC", RT5625_PWR_MANAG_ADD2, 10, 0), + +SND_SOC_DAPM_PGA("Left LineIn PGA", RT5625_PWR_MANAG_ADD3, 7, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right LineIn PGA", RT5625_PWR_MANAG_ADD3, 6, 0, NULL, 0), +SND_SOC_DAPM_PGA("Phone PGA", RT5625_PWR_MANAG_ADD3, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA("Mic1 PGA", RT5625_PWR_MANAG_ADD3, 3, 0, NULL, 0), +SND_SOC_DAPM_PGA("Mic2 PGA", RT5625_PWR_MANAG_ADD3, 2, 0, NULL, 0), +SND_SOC_DAPM_PGA("VoDAC PGA", RT5625_PWR_MANAG_ADD1, 7, 0, NULL, 0), +SND_SOC_DAPM_MIXER("Left Rec Mixer", RT5625_PWR_MANAG_ADD2, 1, 0, + &rt5625_left_adc_rec_mixer_controls[0], ARRAY_SIZE(rt5625_left_adc_rec_mixer_controls)), +SND_SOC_DAPM_MIXER("Right Rec Mixer", RT5625_PWR_MANAG_ADD2, 0, 0, + &rt5625_right_adc_rec_mixer_controls[0], ARRAY_SIZE(rt5625_right_adc_rec_mixer_controls)), +SND_SOC_DAPM_MIXER_E("Left HP Mixer", RT5625_PWR_MANAG_ADD2, 5, 0, + &rt5625_left_hp_mixer_controls[0], ARRAY_SIZE(rt5625_left_hp_mixer_controls), + mixer_event, SND_SOC_DAPM_POST_REG), +SND_SOC_DAPM_MIXER_E("Right HP Mixer", RT5625_PWR_MANAG_ADD2, 4, 0, + &rt5625_right_hp_mixer_controls[0], ARRAY_SIZE(rt5625_right_hp_mixer_controls), + mixer_event, SND_SOC_DAPM_POST_REG), +SND_SOC_DAPM_MIXER("MoNo Mixer", RT5625_PWR_MANAG_ADD2, 2, 0, + &rt5625_mono_mixer_controls[0], ARRAY_SIZE(rt5625_mono_mixer_controls)), +SND_SOC_DAPM_MIXER("SPK Mixer", RT5625_PWR_MANAG_ADD2, 3, 0, + &rt5625_spk_mixer_controls[0], ARRAY_SIZE(rt5625_spk_mixer_controls)), +SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("DAC Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("Line Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_MUX("SPKOUT Mux", SND_SOC_NOPM, 0, 0, &rt5625_spkout_mux_out_controls), +SND_SOC_DAPM_MUX("HPLOUT Mux", SND_SOC_NOPM, 0, 0, &rt5625_hplout_mux_out_controls), +SND_SOC_DAPM_MUX("HPROUT Mux", SND_SOC_NOPM, 0, 0, &rt5625_hprout_mux_out_controls), +SND_SOC_DAPM_MUX("AUXOUT Mux", SND_SOC_NOPM, 0, 0, &rt5625_auxout_mux_out_controls), + +SND_SOC_DAPM_PGA_E("SPKL Out PGA", VIRTUAL_REG_FOR_MISC_FUNC, 4, 0, NULL, 0, + spk_pga_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_PGA_E("SPKR Out PGA", VIRTUAL_REG_FOR_MISC_FUNC, 5, 0, NULL, 0, + spk_pga_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_PGA_E("HPL Out PGA",VIRTUAL_REG_FOR_MISC_FUNC, 6, 0, NULL, 0, + hp_pga_event, SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("HPR Out PGA",VIRTUAL_REG_FOR_MISC_FUNC, 7, 0, NULL, 0, + hp_pga_event, SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("AUX Out PGA",RT5625_PWR_MANAG_ADD3, 14, 0, NULL, 0, + aux_pga_event, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_ADC("Left ADC", "Left ADC HiFi Capture", RT5625_PWR_MANAG_ADD2, 7, 0), +SND_SOC_DAPM_ADC("Right ADC", "Right ADC HiFi Capture", RT5625_PWR_MANAG_ADD2, 6, 0), +SND_SOC_DAPM_OUTPUT("SPKL"), +SND_SOC_DAPM_OUTPUT("SPKR"), +SND_SOC_DAPM_OUTPUT("HPL"), +SND_SOC_DAPM_OUTPUT("HPR"), +SND_SOC_DAPM_OUTPUT("AUX"), +SND_SOC_DAPM_MICBIAS("Mic1 Bias", RT5625_PWR_MANAG_ADD1, 3, 0), +SND_SOC_DAPM_MICBIAS("Mic2 Bias", RT5625_PWR_MANAG_ADD1, 2, 0), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /*Input PGA*/ + + {"Left LineIn PGA", NULL, "Left LineIn"}, + {"Right LineIn PGA", NULL, "Right LineIn"}, + {"Phone PGA", NULL, "Phone"}, + {"Mic1 Boost", NULL, "Mic1"}, + {"Mic2 Boost", NULL, "Mic2"}, + {"Mic1 PGA", NULL, "Mic1"}, + {"Mic2 PGA", NULL, "Mic2"}, + {"VoDAC PGA", NULL, "Voice DAC"}, + + /*Left ADC mixer*/ + {"Left Rec Mixer", "LineIn Capture Switch", "Left LineIn"}, + {"Left Rec Mixer", "Phone Capture Switch", "Phone"}, + {"Left Rec Mixer", "Mic1 Capture Switch", "Mic1 Boost"}, + {"Left Rec Mixer", "Mic2 Capture Switch", "Mic2 Boost"}, + {"Left Rec Mixer", "HP Mixer Capture Switch", "Left HP Mixer"}, + {"Left Rec Mixer", "SPK Mixer Capture Switch", "SPK Mixer"}, + {"Left Rec Mixer", "MoNo Mixer Capture Switch", "MoNo Mixer"}, + + /*Right ADC Mixer*/ + {"Right Rec Mixer", "LineIn Capture Switch", "Right LineIn"}, + {"Right Rec Mixer", "Phone Capture Switch", "Phone"}, + {"Right Rec Mixer", "Mic1 Capture Switch", "Mic1 Boost"}, + {"Right Rec Mixer", "Mic2 Capture Switch", "Mic2 Boost"}, + {"Right Rec Mixer", "HP Mixer Capture Switch", "Right HP Mixer"}, + {"Right Rec Mixer", "SPK Mixer Capture Switch", "SPK Mixer"}, + {"Right Rec Mixer", "MoNo Mixer Capture Switch", "MoNo Mixer"}, + + /*HPL mixer*/ + {"Left HP Mixer", "ADC Playback Switch", "Left Rec Mixer"}, + {"Left HP Mixer", "LineIn Playback Switch", "Left LineIn PGA"}, + {"Left HP Mixer", "Phone Playback Switch", "Phone PGA"}, + {"Left HP Mixer", "Mic1 Playback Switch", "Mic1 PGA"}, + {"Left HP Mixer", "Mic2 Playback Switch", "Mic2 PGA"}, + {"Left HP Mixer", "HIFI DAC Playback Switch", "Left DAC"}, + {"Left HP Mixer", "Voice DAC Playback Switch", "VoDAC PGA"}, + + /*HPR mixer*/ + {"Right HP Mixer", "ADC Playback Switch", "Right Rec Mixer"}, + {"Right HP Mixer", "LineIn Playback Switch", "Right LineIn PGA"}, + {"Right HP Mixer", "HIFI DAC Playback Switch", "Right DAC"}, + {"Right HP Mixer", "Phone Playback Switch", "Phone PGA"}, + {"Right HP Mixer", "Mic1 Playback Switch", "Mic1 PGA"}, + {"Right HP Mixer", "Mic2 Playback Switch", "Mic2 PGA"}, + {"Right HP Mixer", "Voice DAC Playback Switch", "VoDAC PGA"}, + + /*DAC Mixer*/ + {"DAC Mixer", NULL, "Left DAC"}, + {"DAC Mixer", NULL, "Right DAC"}, + + /*line mixer*/ + {"Line Mixer", NULL, "Left LineIn PGA"}, + {"Line Mixer", NULL, "Right LineIn PGA"}, + + /*spk mixer*/ + {"SPK Mixer", "Line Mixer Playback Switch", "Line Mixer"}, + {"SPK Mixer", "Phone Playback Switch", "Phone PGA"}, + {"SPK Mixer", "Mic1 Playback Switch", "Mic1 PGA"}, + {"SPK Mixer", "Mic2 Playback Switch", "Mic2 PGA"}, + {"SPK Mixer", "DAC Mixer Playback Switch", "DAC Mixer"}, + {"SPK Mixer", "Voice DAC Playback Switch", "VoDAC PGA"}, + + /*mono mixer*/ + {"MoNo Mixer", "Line Mixer Playback Switch", "Line Mixer"}, + {"MoNo Mixer", "ADCL Playback Switch","Left Rec Mixer"}, + {"MoNo Mixer", "ADCR Playback Switch","Right Rec Mixer"}, + {"MoNo Mixer", "Mic1 Playback Switch", "Mic1 PGA"}, + {"MoNo Mixer", "Mic2 Playback Switch", "Mic2 PGA"}, + {"MoNo Mixer", "DAC Mixer Playback Switch", "DAC Mixer"}, + {"MoNo Mixer", "Voice DAC Playback Switch", "VoDAC PGA"}, + + /*hp mixer*/ + {"HP Mixer", NULL, "Left HP Mixer"}, + {"HP Mixer", NULL, "Right HP Mixer"}, + + /*spkout mux*/ + {"SPKOUT Mux", "HP Mixer", "HP Mixer"}, + {"SPKOUT Mux", "SPK Mixer", "SPK Mixer"}, + {"SPKOUT Mux", "Mono Mixer", "MoNo Mixer"}, + + /*hpl out mux*/ + {"HPLOUT Mux", "HPL Mixer", "Left HP Mixer"}, + + /*hpr out mux*/ + {"HPROUT Mux", "HPR Mixer", "Right HP Mixer"}, + + /*aux out mux*/ + {"AUXOUT Mux", "HP Mixer", "HP Mixer"}, + {"AUXOUT Mux", "SPK Mixer", "SPK Mixer"}, + {"SPKOUT Mux", "Mono Mixer", "MoNo Mixer"}, + + /*spkl out pga*/ + {"SPKL Out PGA", NULL, "SPKOUT Mux"}, + + + /*spkr out pga*/ + {"SPKR Out PGA", NULL, "SPKOUT Mux"}, + + /*hpl out pga*/ + {"HPL Out PGA", NULL, "HPLOUT Mux"}, + + /*hpr out pga*/ + {"HPR Out PGA", NULL, "HPROUT Mux"}, + + /*aux out pga*/ + {"AUX Out PGA", NULL, "AUXOUT Mux"}, + + /*left adc*/ + {"Left ADC", NULL, "Left Rec Mixer"}, + + /*right adc*/ + {"Right ADC", NULL, "Right Rec Mixer"}, + + /*output*/ + {"SPKL", NULL, "SPKL Out PGA"}, + {"SPKR", NULL, "SPKR Out PGA"}, + {"HPL", NULL, "HPL Out PGA"}, + {"HPR", NULL, "HPR Out PGA"}, + {"AUX", NULL, "AUX Out PGA"}, +}; + + +static int rt5625_add_widgets(struct snd_soc_codec *codec) +{ + snd_soc_dapm_new_controls(codec, rt5625_dapm_widgets, + ARRAY_SIZE(rt5625_dapm_widgets)); + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_new_widgets(codec); + + return 0; +} + +struct _pll_div{ + u32 pll_in; + u32 pll_out; + u16 regvalue; +}; + + +/************************************************************** + * watch out! + * our codec support you to select different source as pll input, but if you + * use both of the I2S audio interface and pcm interface instantially. + * The two DAI must have the same pll setting params, so you have to offer + * the same pll input, and set our codec's sysclk the same one, we suggest + * 24576000. + **************************************************************/ +static const struct _pll_div codec_master_pll1_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_bclk_pll1_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}, + { 2822400, 11289600, 0x1ee0}, + { 3072000, 12288000, 0x1ee0}, +}; + +static const struct _pll_div codec_vbclk_pll1_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_stereo { + unsigned int mclk; + unsigned int rate; + unsigned int reg60; + unsigned int reg62; +}; + +struct _coeff_div_voice { + unsigned int mclk; + unsigned int rate; + unsigned int reg64; +}; + +static const struct _coeff_div_stereo coeff_div_stereo[] = { + /*bclk is config to 32fs, if codec is choose to be slave mode , input bclk should be 32*fs */ + {24576000, 48000, 0x3174, 0x1010}, + {12288000, 48000, 0x1174, 0x0000}, + {18432000, 48000, 0x2174, 0x1111}, + {36864000, 48000, 0x2274, 0x2020}, + {49152000, 48000, 0xf074, 0x3030}, + {24576000, 48000, 0x3172,0x1010}, + {24576000, 8000, 0xB274,0x2424}, + {24576000, 16000, 0xB174,0x2222}, + {24576000, 32000, 0xB074,0x2121}, + {22579200, 11025, 0X3374,0x1414}, + {22579200, 22050, 0X3274,0x1212}, + {22579200, 44100, 0X3174,0x1010}, + + {0, 0, 0, 0}, +}; + +static const struct _coeff_div_voice coeff_div_voice[] = { + /*bclk is config to 32fs, if codec is choose to be slave mode , input bclk should be 32*fs */ + {24576000, 16000, 0x2622}, + {24576000, 8000, 0x2824}, + {0, 0, 0}, +}; + +static int get_coeff(unsigned int mclk, unsigned int rate, int mode) +{ + int i; + + printk("get_coeff mclk = %d, rate = %d\n", mclk, rate); + if (!mode){ + for (i = 0; i < ARRAY_SIZE(coeff_div_stereo); i++) { + if ((coeff_div_stereo[i].rate == rate) && (coeff_div_stereo[i].mclk == mclk)) + return i; + } + } + else { + for (i = 0; i< ARRAY_SIZE(coeff_div_voice); i++) { + if ((coeff_div_voice[i].rate == rate) && (coeff_div_voice[i].mclk == mclk)) + return i; + } + } + + return -EINVAL; + printk(KERN_ERR "can't find a matched mclk and rate in %s\n", + (mode ? "coeff_div_voice[]" : "coeff_div_audio[]")); +} + + +static int rt5625_codec_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; + + printk(KERN_DEBUG "enter %s\n", __func__); + + if (pll_id < RT5625_PLL1_FROM_MCLK || pll_id > RT5625_PLL1_FROM_VBCLK) + return -EINVAL; + + if (!freq_in || !freq_out) + return 0; + + if (RT5625_PLL1_FROM_MCLK == pll_id) + { + for (i = 0; i < ARRAY_SIZE(codec_master_pll1_div); i ++) + { + if ((freq_in == codec_master_pll1_div[i].pll_in) && (freq_out == codec_master_pll1_div[i].pll_out)) + { + rt5625_write(codec, RT5625_GEN_CTRL_REG2, 0x0000); /*PLL source from MCLK*/ + rt5625_write(codec, RT5625_PLL_CTRL, codec_master_pll1_div[i].regvalue); /*set pll code*/ + rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD2, 0x8000, 0x8000); /*enable pll1 power*/ + rt5625_write_mask(codec, RT5625_GEN_CTRL_REG1, 0x8000, 0x8000); + ret = 0; + } + } + } + else if (RT5625_PLL1_FROM_BCLK == pll_id) + { + for (i = 0; i < ARRAY_SIZE(codec_bclk_pll1_div); i ++) + { + if ((freq_in == codec_bclk_pll1_div[i].pll_in) && (freq_out == codec_bclk_pll1_div[i].pll_out)) + { + rt5625_write(codec, RT5625_GEN_CTRL_REG2, 0x2000); /*PLL source from BCLK*/ + rt5625_write(codec, RT5625_PLL_CTRL, codec_bclk_pll1_div[i].regvalue); /*set pll1 code*/ + rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD2, 0x8000, 0x8000); /*enable pll1 power*/ + rt5625_write_mask(codec, RT5625_GEN_CTRL_REG1, 0x8000, 0x8000); + ret = 0; + } + } + } + else if (RT5625_PLL1_FROM_VBCLK == pll_id) + { + for (i = 0; i < ARRAY_SIZE(codec_vbclk_pll1_div); i ++) + { + if ((freq_in == codec_vbclk_pll1_div[i].pll_in) && (freq_out == codec_vbclk_pll1_div[i].pll_out)) + { + rt5625_write(codec, RT5625_GEN_CTRL_REG2, 0x3000); /*PLL source from VBCLK*/ + rt5625_write(codec, RT5625_PLL_CTRL, codec_vbclk_pll1_div[i].regvalue); /*set pll1 code*/ + rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD2, 0x8000, 0x8000); /*enable pll1 power*/ + rt5625_write_mask(codec, RT5625_GEN_CTRL_REG1, 0x8000, 0x8000); + ret = 0; + } + } + } + + + return 0; +} + + +static int rt5625_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 rt5625_priv * rt5625 = codec->private_data; + printk(KERN_DEBUG "enter %s\n", __func__); + + if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) { + rt5625->stereo_sysclk = freq; + return 0; + } + + printk(KERN_ERR "unsupported sysclk freq %u for audio i2s\n", freq); + rt5625->stereo_sysclk=24576000; + + return 0; +} + +static int rt5625_voice_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 rt5625_priv * rt5625 = codec->private_data; + + + if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) { + rt5625->voice_sysclk = freq; + return 0; + } + + printk(KERN_ERR "unsupported sysclk freq %u for voice pcm\n", freq); + rt5625->voice_sysclk = 24576000; + + return 0; +} + + +static int rt5625_hifi_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->card->codec; + struct rt5625_priv *rt5625 = codec->private_data; + + unsigned int iface = rt5625_read(codec, RT5625_MAIN_SDP_CTRL) & 0xfff3; + int rate = params_rate(params); + int coeff = get_coeff(rt5625->stereo_sysclk, rate, 0); + + printk(KERN_DEBUG "enter %s\n", __func__); + + + switch (params_format(params)) + { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + iface |= 0x0004; + case SNDRV_PCM_FORMAT_S24_LE: + iface |= 0x0008; + case SNDRV_PCM_FORMAT_S8: + iface |= 0x000c; + } + + + + rt5625_write(codec, RT5625_MAIN_SDP_CTRL, iface); + rt5625_write_mask(codec, 0x3a, 0xc801, 0xc801); /*power i2s and dac ref*/ + if (coeff >= 0) { + rt5625_write(codec, RT5625_STEREO_DAC_CLK_CTRL1, coeff_div_stereo[coeff].reg60); + rt5625_write(codec, RT5625_STEREO_DAC_CLK_CTRL2, coeff_div_stereo[coeff].reg62); + } + + return 0; +} + +static int rt5625_voice_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->card->codec; + struct rt5625_priv *rt5625 = codec->private_data; + struct snd_soc_dapm_widget *w; + unsigned int iface = rt5625_read(codec, RT5625_EXTEND_SDP_CTRL) & 0xfff3; + int rate = params_rate(params); + int coeff = get_coeff(rt5625->voice_sysclk, rate, 1); + + printk(KERN_DEBUG "enter %s\n", __func__); + + list_for_each_entry(w, &codec->dapm_widgets, list) + { + if (!w->sname) + continue; + if (!strcmp(w->name, "Right ADC")) + strcpy(w->sname, "Right ADC Voice Capture"); + } + + switch (params_format(params)) + { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + iface |= 0x0004; + case SNDRV_PCM_FORMAT_S24_LE: + iface |= 0x0008; + case SNDRV_PCM_FORMAT_S8: + iface |= 0x000c; + } + rt5625_write_mask(codec, 0x3a, 0x0801, 0x0801); /*power i2s and dac ref*/ + rt5625_write(codec, RT5625_EXTEND_SDP_CTRL, iface); + if (coeff >= 0) + rt5625_write(codec, RT5625_VOICE_DAC_PCMCLK_CTRL1, coeff_div_voice[coeff].reg64); + + return 0; +} + + +static int rt5625_hifi_codec_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + + struct snd_soc_codec *codec = codec_dai->codec; + u16 iface = 0; + + printk(KERN_DEBUG "enter %s\n", __func__); + + /*set master/slave 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_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; + } + + /*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 |= 0x0080; + break; + default: + return -EINVAL; + } + + rt5625_write(codec, RT5625_MAIN_SDP_CTRL, iface); + return 0; +} + +static int rt5625_voice_codec_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + int iface; + + printk(KERN_DEBUG "enter %s\n", __func__); + /*set slave/master mode*/ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) + { + case SND_SOC_DAIFMT_CBM_CFM: + iface = 0x0000; + break; + case SND_SOC_DAIFMT_CBS_CFS: + iface = 0x4000; + break; + default: + return -EINVAL; + } + + switch(fmt & SND_SOC_DAIFMT_FORMAT_MASK) + { + case SND_SOC_DAIFMT_I2S: + iface |= 0x0000; + 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; + } + + /*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 |= 0x0080; + break; + default: + return -EINVAL; + } + + iface |= 0x8000; /*enable vopcm*/ + rt5625_write(codec, RT5625_EXTEND_SDP_CTRL, iface); + return 0; +} + + +static int rt5625_hifi_codec_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + + if (mute) + rt5625_write_mask(codec, RT5625_STEREO_DAC_VOL, 0x8080, 0x8080); + else + rt5625_write_mask(codec, RT5625_STEREO_DAC_VOL, 0x0000, 0x8080); + return 0; +} + +static int rt5625_voice_codec_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + + if (mute) + rt5625_write_mask(codec, RT5625_VOICE_DAC_OUT_VOL, 0x1000, 0x1000); + else + rt5625_write_mask(codec, RT5625_VOICE_DAC_OUT_VOL, 0x0000, 0x1000); + return 0; +} + + +static int rt5625_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: + rt5625_write(codec, 0x26, 0x0000); + rt5625_write_mask(codec, 0x3c, 0x2000, 0x2000); + rt5625_write_mask(codec, 0x3a, 0x000e, 0x000e); + break; + case SND_SOC_BIAS_STANDBY: + break; + case SND_SOC_BIAS_OFF: + rt5625_write_mask(codec, 0x04, 0x8080, 0x8080); /*mute hp*/ + rt5625_write_mask(codec, 0x02, 0x8080, 0x8080); /*mute spk*/ + rt5625_write(codec, 0x3e, 0x0000); //power off all bit + rt5625_write(codec, 0x3a, 0x0000); //power off all bit + rt5625_write(codec, 0x3c, 0x0000); //power off all bit + + break; + } + codec->bias_level = level; + return 0; +} + + +#define RT5625_STEREO_RATES SNDRV_PCM_RATE_8000_48000 + +#define RT5626_VOICE_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000) + +#define RT5625_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S8) + +static struct snd_soc_dai_ops rt5625_dai_ops_hifi = { + + .hw_params = rt5625_hifi_pcm_hw_params, +// .digital_mute = rt5625_hifi_codec_mute, + .set_fmt = rt5625_hifi_codec_set_dai_fmt, + .set_pll = rt5625_codec_set_dai_pll, + .set_sysclk = rt5625_hifi_codec_set_dai_sysclk, + +}; + + +static struct snd_soc_dai_ops rt5625_dai_ops_voice = { + + .hw_params = rt5625_voice_pcm_hw_params, +// .digital_mute = rt5625_voice_codec_mute, + .set_fmt = rt5625_voice_codec_set_dai_fmt, + .set_pll = rt5625_codec_set_dai_pll, + .set_sysclk = rt5625_voice_codec_set_dai_sysclk, + +}; + + + +struct snd_soc_dai rt5625_dai[] = { + /*hifi codec dai*/ + { + .name = "RT5625 HiFi", + .id = 1, + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5625_STEREO_RATES, + .formats = RT5625_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5625_STEREO_RATES, + .formats = RT5625_FORMATS, + }, + + .ops = &rt5625_dai_ops_hifi, + }, + + /*voice codec dai*/ + { + .name = "RT5625 Voice", + .id = 2, + .playback = { + .stream_name = "Voice Playback", + .channels_min = 1, + .channels_max =1, + .rates = RT5626_VOICE_RATES, + .formats = RT5625_FORMATS, + }, + .capture = { + .stream_name = "Voice Capture", + .channels_min = 1, + .channels_max = 1, + .rates = RT5626_VOICE_RATES, + .formats = RT5625_FORMATS, + }, + + .ops = &rt5625_dai_ops_voice, + + }, +}; + +EXPORT_SYMBOL_GPL(rt5625_dai); + + +static void rt5625_work(struct work_struct *work) +{ + struct snd_soc_codec *codec = + container_of(work, struct snd_soc_codec, delayed_work.work); + rt5625_set_bias_level(codec, codec->bias_level); +} + + +#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); + } +} + +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 int rt5625_init(struct snd_soc_device *socdev) +{ + + struct snd_soc_codec *codec = socdev->card->codec; + int ret = 0; + + codec->name = "RT5625"; + codec->owner = THIS_MODULE; + codec->read = rt5625_read; + codec->write = rt5625_write; + codec->set_bias_level = rt5625_set_bias_level; + codec->dai= rt5625_dai; + codec->num_dai = 2; + codec->reg_cache_step = 2; + codec->reg_cache_size = ARRAY_SIZE(rt5625_reg)*2; + codec->reg_cache = kmemdup(rt5625_reg, sizeof(rt5625_reg), GFP_KERNEL); + if (codec->reg_cache == NULL) + return -ENOMEM; + + rt5625_reset(codec); + + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0 ) + { + printk(KERN_ERR "rt5625: failed to create pcms\n"); + goto pcm_err; + } + + rt5625_write(codec, RT5625_PD_CTRL_STAT, 0); + rt5625_write(codec, RT5625_PWR_MANAG_ADD1, PWR_MAIN_BIAS); + rt5625_write(codec, RT5625_PWR_MANAG_ADD2, PWR_MIXER_VREF); + rt5625_reg_init(codec); + rt5625_set_bias_level(codec, SND_SOC_BIAS_PREPARE); + codec->bias_level = SND_SOC_BIAS_STANDBY; + schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(80)); + +#if (RT5625_EQ_FUNC_ENA==1) + rt5625_update_eqmode(codec,POP); +#endif + + rt5625_add_controls(codec); + rt5625_add_widgets(codec); + + #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 "rt5625: failed to register card\n"); + goto card_err; + } + printk(KERN_DEBUG "rt5625: initial ok\n"); + return 0; + + card_err: + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); + + pcm_err: + kfree(codec->reg_cache); + return ret; + + +} + + +static int rt5625_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + struct snd_soc_device *socdev = rt5625_socdev; + struct snd_soc_codec *codec = socdev->card->codec; + int ret; + + i2c_set_clientdata(i2c, codec); + codec->control_data = i2c; + + ret = rt5625_init(socdev); + if (ret < 0) + pr_err("failed to initialise rt5625\n"); + + return ret; +} + +static int rt5625_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 rt5625_i2c_id[] = { + {"rt5625", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rt5625_i2c_id); +static struct i2c_driver rt5625_i2c_driver = { + .driver = { + .name = "RT5625 I2C Codec", + .owner = THIS_MODULE, + }, + .probe = rt5625_i2c_probe, + .remove = rt5625_i2c_remove, + .id_table = rt5625_i2c_id, +}; + + +static int rt5625_add_i2c_device(struct platform_device *pdev, + const struct rt5625_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(&rt5625_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, "rt5625", 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(&rt5625_i2c_driver); + return -ENODEV; +#endif +} + + +static int rt5625_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct rt5625_setup_data *setup; + struct snd_soc_codec *codec; + struct rt5625_priv *rt5625; + int ret; + + pr_info("RT5625 Audio Codec %s", RT5625_VERSION); + + if(socdev->codec_data) + { + setup = socdev->codec_data; + } + + codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (codec == NULL) + return -ENOMEM; + + rt5625 = kzalloc(sizeof(struct rt5625_priv), GFP_KERNEL); + if (rt5625 == NULL) { + kfree(codec); + return -ENOMEM; + } + codec->private_data = rt5625; + socdev->card->codec = codec; + + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + rt5625_socdev = socdev; + INIT_DELAYED_WORK(&codec->delayed_work, rt5625_work); + + ret = -ENODEV; +// if (setup->i2c_address) + { + codec->hw_write = (hw_write_t)i2c_master_send; + //codec->hw_read = (hw_read_t)i2c_master_recv; + ret = rt5625_add_i2c_device(pdev, setup); + } + + if (ret != 0) { + kfree(codec->private_data); + kfree(codec); + socdev->card->codec = NULL; + } + 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 rt5625_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) + rt5625_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(&rt5625_i2c_driver); + kfree(codec->private_data); + kfree(codec); + + return 0; +} + + +static int rt5625_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; + + rt5625_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int rt5625_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 + rt5625_reset(codec); + rt5625_write(codec, RT5625_PD_CTRL_STAT, 0); + rt5625_write(codec, RT5625_PWR_MANAG_ADD1, PWR_MAIN_BIAS); + rt5625_write(codec, RT5625_PWR_MANAG_ADD2, PWR_MIXER_VREF); + rt5625_reg_init(codec); +#else + /* Sync reg_cache with the hardware */ + for (i = 0; i < ARRAY_SIZE(rt5625_reg); i++) { + if (i == RT5625_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); + } + + rt5625_set_bias_level(codec, SND_SOC_BIAS_STANDBY); +#endif + + /* charge rt5625 caps */ + if (codec->suspend_bias_level == SND_SOC_BIAS_ON) { + rt5625_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_rt5625 = { + .probe = rt5625_probe, + .remove = rt5625_remove, + .suspend = rt5625_suspend, + .resume = rt5625_resume, +}; + +EXPORT_SYMBOL_GPL(soc_codec_dev_rt5625); + +static int __init rt5625_modinit(void) +{ + return snd_soc_register_dais(rt5625_dai, ARRAY_SIZE(rt5625_dai)); +} + +static void __exit rt5625_exit(void) +{ + snd_soc_unregister_dais(rt5625_dai, ARRAY_SIZE(rt5625_dai)); +} + +module_init(rt5625_modinit); +module_exit(rt5625_exit); +MODULE_LICENSE("GPL"); + diff --git a/sound/soc/codecs/rt5625.h b/sound/soc/codecs/rt5625.h new file mode 100644 index 000000000000..cb4f73e8bcca --- /dev/null +++ b/sound/soc/codecs/rt5625.h @@ -0,0 +1,742 @@ +#ifndef _RT5625_H +#define _RT5625_H + +#define RT5625_RESET 0X00 //RESET CODEC TO DEFAULT +#define RT5625_SPK_OUT_VOL 0X02 //SPEAKER OUT VOLUME +#define RT5625_HP_OUT_VOL 0X04 //HEADPHONE OUTPUT VOLUME +#define RT5625_AUX_OUT_VOL 0X06 //AUXOUT VOLUME +#define RT5625_PHONEIN_VOL 0X08 //PHONE INPUT VOLUME +#define RT5625_LINE_IN_VOL 0X0A //LINE IN VOLUME +#define RT5625_STEREO_DAC_VOL 0X0C //STEREO DAC VOLUME +#define RT5625_MIC_VOL 0X0E //MICROPHONE VOLUME +#define RT5625_DAC_AND_MIC_CTRL 0X10 //STEREO DAC AND MIC ROUTING CONTROL +#define RT5625_ADC_REC_GAIN 0X12 //ADC RECORD GAIN +#define RT5625_ADC_REC_MIXER 0X14 //ADC RECORD MIXER CONTROL +#define RT5625_VOICE_DAC_OUT_VOL 0X18 //VOICE DAC OUTPUT VOLUME +#define RT5625_VODSP_PDM_CTL 0X1A //VODSP & PDM CONTROL +#define RT5625_OUTPUT_MIXER_CTRL 0X1C //OUTPUT MIXER CONTROL +#define RT5625_VODSP_CTL 0X1E //VODSP CONTROL +#define RT5625_MIC_CTRL 0X22 //MICROPHONE CONTROL +#define RT5625_DMIC_CTRL 0x24 +#define RT5625_PD_CTRL_STAT 0X26 //POWER DOWN CONTROL/STATUS +#define RT5625_DAC_ADC_VODAC_FUN_SEL 0X2E //STEREO DAC,VOICE DAC,STEREO ADC FUNCTION SELECT +#define RT5625_MAIN_SDP_CTRL 0X34 //MAIN SERIAL DATA PORT CONTROL(STEREO I2S) +#define RT5625_EXTEND_SDP_CTRL 0X36 //EXTEND SERIAL DATA PORT CONTROL(VOICE I2S/PCM) +#define RT5625_PWR_MANAG_ADD1 0X3A //POWER MANAGMENT ADDITION 1 +#define RT5625_PWR_MANAG_ADD2 0X3C //POWER MANAGMENT ADDITION 2 +#define RT5625_PWR_MANAG_ADD3 0X3E //POWER MANAGMENT ADDITION 3 +#define RT5625_GEN_CTRL_REG1 0X40 //GENERAL PURPOSE CONTROL REGISTER 1 +#define RT5625_GEN_CTRL_REG2 0X42 //GENERAL PURPOSE CONTROL REGISTER 2 +#define RT5625_PLL_CTRL 0X44 //PLL1 CONTROL +#define RT5625_PLL2_CTRL 0X46 //PLL2 CONTROL +#define RT5625_LDO_CTRL 0X48 //LDO CONTROL +#define RT5625_GPIO_PIN_CONFIG 0X4C //GPIO PIN CONFIGURATION +#define RT5625_GPIO_PIN_POLARITY 0X4E //GPIO PIN POLARITY +#define RT5625_GPIO_PIN_STICKY 0X50 //GPIO PIN STICKY +#define RT5625_GPIO_PIN_WAKEUP 0X52 //GPIO PIN WAKE UP +#define RT5625_GPIO_PIN_STATUS 0X54 //GPIO PIN STATUS +#define RT5625_GPIO_PIN_SHARING 0X56 //GPIO PIN SHARING +#define RT5625_OVER_TEMP_CURR_STATUS 0X58 //OVER TEMPERATURE AND CURRENT STATUS +#define RT5625_SOFT_VOL_CTRL 0X5A //SOFT VOLUME CONTROL SETTING +#define RT5625_GPIO_OUT_CTRL 0X5C //GPIO OUTPUT PIN CONTRL +#define RT5625_MISC_CTRL 0X5E //MISC CONTROL +#define RT5625_STEREO_DAC_CLK_CTRL1 0X60 //STEREO DAC CLOCK CONTROL 1 +#define RT5625_STEREO_DAC_CLK_CTRL2 0X62 //STEREO DAC CLOCK CONTROL 2 +#define RT5625_VOICE_DAC_PCMCLK_CTRL1 0X64 //VOICE/PCM DAC CLOCK CONTROL 1 +#define RT5625_PSEDUEO_SPATIAL_CTRL 0X68 //PSEDUEO STEREO /SPATIAL EFFECT BLOCK CONTROL +#define RT5625_PRIV_ADDR 0X6A //PRIVATE ADDRESS +#define RT5625_PRIV_DATA 0X6C //PRIVATE DATA +#define RT5625_EQ_CTRL_ADC_HPF 0X6E //EQ CONTROL AND STATUS /ADC HPF CONTROL +#define RT5625_VODSP_REG_ADDR 0x70 //VODSP REGISTER ADDRESS +#define RT5625_VODSP_REG_DATA 0x72 //VODSP REGISTER DATA +#define RT5625_VODSP_REG_CMD 0x74 //VODSP REGISTER COMMAND + + +/************************************************************************************************** + *Bit define of Codec Register + *************************************************************************************************/ +//global definition +#define RT_L_MUTE (0x1<<15) //Mute Left Control +#define RT_L_ZC (0x1<<14) //Mute Left Zero-Cross Detector Control +#define RT_R_MUTE (0x1<<7) //Mute Right Control +#define RT_R_ZC (0x1<<6) //Mute Right Zero-Cross Detector Control +#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 + +//Phone Input Volume(0x08) +#define M_PHONEIN_TO_HP_MIXER (0x1<<15) //Mute Phone In volume to HP mixer +#define M_PHONEIN_TO_SPK_MIXER (0x1<<14) //Mute Phone In volume to speaker mixer + + +//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 M_MIC2_TO_HP_MIXER (0x1<<11) //Mute MIC2 to HP mixer +#define M_MIC2_TO_SPK_MIXER (0x1<<10) //Mute MiC2 to SPK mixer +#define M_MIC2_TO_MONO_MIXER (0x1<<9) //Mute MIC2 to MONO mixer +#define M_DAC_TO_HPL_MIXER (0x1<<3) //Mute DAC to HP left mixer +#define M_DAC_TO_HPR_MIXER (0x1<<2) //Mute DAC to HP right mixer +#define M_DAC_TO_SPK_MIXER (0x1<<1) //Mute DAC to SPK mixer +#define M_DAC_TO_MONO_MIXER (0x1<<0) //Mute DAC to MONO mixer + + + +//ADC Record Gain(0x12) +#define M_ADC_L_TO_HP_MIXER (0x1<<15) //Mute left of ADC to HP Mixer +#define M_ADC_L_TO_MONO_MIXER (0x1<<14) //Mute left of ADC to MONO Mixer +#define ADC_L_ZC_DET (0x1<<13) //ADC Zero-Cross Detector Control +#define ADC_L_GAIN_MASK (0x1f<<8) //ADC Record Gain Left channel Mask +#define M_ADC_R_TO_HP_MIXER (0x1<<7) //Mute right of ADC to HP Mixer +#define M_ADC_R_TO_MONO_MIXER (0x1<<6) //Mute right of ADC to MONO Mixer +#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 + +//Voice DAC Output Volume(0x18) +#define M_V_DAC_TO_HP_MIXER (0x1<<15) +#define M_V_DAC_TO_SPK_MIXER (0x1<<14) +#define M_V_DAC_TO_MONO_MIXER (0x1<<13) + + +//AEC & PDM Control(0x1A) +#define VODSP_SRC1_PWR (0x1<<15) //Enable SRC1 Power +#define VODSP_SRC2_PWR (0x1<<13) //Enable SRC2 Power + +#define VODSP_SRC2_S_SEL_MASK (0x1<<12) +#define VODSP_SRC2_S_SEL_TXDP (0x0<<12) //SRC2 source select AEC_TXDP +#define VODSP_SRC2_S_SEL_TXDC (0x1<<12) //SRC2 source select AEC_TXDC + +#define VODSP_RXDP_PWR (0x1<<11) //Enable AEC RXDP Power + +#define VODSP_RXDP_S_SEL_MASK (0x3<<9) +#define VODSP_RXDP_S_SEL_SRC1 (0x0<<9) //AEC RxDP source select SRC1 Output +#define VODSP_RXDP_S_SEL_ADCL (0x1<<9) //AEC RxDP source select ADC Left to AEC Digital Path +#define VODSP_RXDP_S_SEL_VOICE (0x2<<9) //AEC RxDP source select Voice to Stereo Digital Path +#define VODSP_RXDP_S_SEL_ADCR (0x3<<9) //AEC RxDP source select ADC Right to AEC Digital Path + +#define VODSP_RXDC_PWR (0x1<<8) //Enable AEC RXDC Power + +#define VOICE_PCM_S_SEL_MASK (0x1<<7) +#define VOICE_PCM_S_SEL_ADC_R (0x0<<7) //VSADC PCM interface source select ADC R +#define VOICE_PCM_S_SEL_AEC_TXDP (0x1<<7) //VSADC PCM interface source select AEC_TXDP + +#define REC_S_SEL_MASK (0x3<<4) +#define REC_S_SEL_ADC (0x0<<4) //Main Stereo Record I2S source select ADC L/R +#define REC_S_SEL_VOICE (0x1<<4) //Main Stereo Record I2S source select Voice to Stereo Digital Path +#define REC_S_SEL_SRC2 (0x2<<4) //Main Stereo Record I2S source select SRC2 + + +//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 SPKOUT_SEL_CLASS_D (0x1<<13) +#define SPKOUT_SEL_CLASS_AB (0x0<<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 AUXOUT_INPUT_SEL_MASK (0x3<<6) +#define AUXOUT_INPUT_SEL_MONOMIXER (0x3<<6) +#define AUXOUT_INPUT_SEL_SPKMIXER (0x2<<6) +#define AUXOUT_INPUT_SEL_HPMIXER (0x1<<6) +#define AUXOUT_INPUT_SEL_VMID (0x0<<6) + + +//Voice DSP Control(0x1E) +#define VODSP_SYSCLK_S_SEL_MASK (0x1<<15) +#define VODSP_SYSCLK_S_SEL_M_CLK (0x0<<15) +#define VODSP_SYSCLK_S_SEL_V_CLK (0x1<<15) + +#define VODSP_LRCK_SEL_MASK (0x1<<13) +#define VODSP_LRCK_SEL_8K (0x0<<13) +#define VODSP_LRCK_SEL_16K (0x1<<13) + +#define VODSP_TEST_MODE_ENA (0x1<<3) +#define VODSP_NO_BP_MODE_ENA (0x1<<2) +#define VODSP_NO_PD_MODE_ENA (0x1<<1) +#define VODSP_NO_RST_MODE_ENA (0x1<<0) + + + + +//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_CONTROL_MASK (0x3<<10) +#define MIC1_BOOST_CONTROL_BYPASS (0x0<<10) +#define MIC1_BOOST_CONTROL_20DB (0x1<<10) +#define MIC1_BOOST_CONTROL_30DB (0x2<<10) +#define MIC1_BOOST_CONTROL_40DB (0x3<<10) + +#define MIC2_BOOST_CONTROL_MASK (0x3<<8) +#define MIC2_BOOST_CONTROL_BYPASS (0x0<<8) +#define MIC2_BOOST_CONTROL_20DB (0x1<<8) +#define MIC2_BOOST_CONTROL_30DB (0x2<<8) +#define MIC2_BOOST_CONTROL_40DB (0x3<<8) + +#define MIC1_BIAS_VOLT_CTRL_MASK (0x1<<5) +#define MIC1_BIAS_VOLT_CTRL_90P (0x0<<5) +#define MIC1_BIAS_VOLT_CTRL_75P (0x1<<5) + +#define MIC2_BIAS_VOLT_CTRL_MASK (0x1<<4) +#define MIC2_BIAS_VOLT_CTRL_90P (0x0<<4) +#define MIC2_BIAS_VOLT_CTRL_75P (0x1<<4) + +//PowerDown control of register(0x26) +//power management bits +#define RT_PWR_PR7 (0x1<<15) //write this bit to power down the Speaker Amplifier +#define RT_PWR_PR6 (0x1<<14) //write this bit to power down the Headphone Out and MonoOut +#define RT_PWR_PR5 (0x1<<13) //write this bit to power down the internal clock(without PLL) +#define RT_PWR_PR3 (0x1<<11) //write this bit to power down the mixer(vref/vrefout out off) +#define RT_PWR_PR2 (0x1<<10) //write this bit to power down the mixer(vref/vrefout still on) +#define RT_PWR_PR1 (0x1<<9) //write this bit to power down the dac +#define RT_PWR_PR0 (0x1<<8) //write this bit to power down the adc +#define RT_PWR_REF (0x1<<3) //read only +#define RT_PWR_ANL (0x1<<2) //read only +#define RT_PWR_DAC (0x1<<1) //read only +#define RT_PWR_ADC (0x1) //read only + + +//Stereo DAC/Voice DAC/Stereo ADC function(0x2E) +#define DAC_FUNC_SEL_MASK (0x3<<12) +#define DAC_FUNC_SEL_DAC (0x0<<12) +#define DAC_FUNC_SEL_SRC2 (0x1<<12) +#define DAC_FUNC_SEL_VODSP_TXDP (0x2<<12) +#define DAC_FUNC_SEL_VODSP_TXDC (0x3<<12) + +#define VODAC_SOUR_SEL_MASK (0x7<<8) +#define VODAC_SOUR_SEL_VOICE (0x0<<8) +#define VODAC_SOUR_SEL_SRC2 (0x1<<8) +#define VODAC_SOUR_SEL_VODSP_TXDP (0x2<<8) +#define VODAC_SOUR_SEL_VODSP_TXDC (0x3<<8) + +#define ADCR_FUNC_SEL_MASK (0x3<<4) +#define ADCR_FUNC_SEL_ADC (0x0<<4) +#define ADCR_FUNC_SEL_VOADC (0x1<<4) +#define ADCR_FUNC_SEL_VODSP (0x2<<4) +#define ADCR_FUNC_SEL_PDM (0x3<<4) + +#define ADCL_FUNC_SEL_MASK (0x3<<0) +#define ADCL_FUNC_SEL_ADC (0x0<<0) +#define ADCL_FUNC_SEL_VODSP (0x1<<0) + +//Main Serial Data Port Control(0x34) +#define MAIN_I2S_MODE_SEL (0x1<<15) //0:Master mode 1:Slave mode +#define MAIN_I2S_SADLRCK_CTRL (0x1<<14) //0:Disable,ADC and DAC use the same fs,1:Enable + +#define MAIN_I2S_PCM_MODE (0x1<<6) //0:Normal SADLRCK/SDALRCK,1:Invert SADLRCK/SDALRCK +//Data Length Slection +#define MAIN_I2S_DL_MASK (0x3<<2) //main i2s Data Length mask +#define MAIN_I2S_DL_16 (0x0<<2) //16 bits +#define MAIN_I2S_DL_20 (0x1<<2) //20 bits +#define MAIN_I2S_DL_24 (0x2<<2) //24 bits +#define MAIN_I2S_DL_32 (0x3<<2) //8 bits + +//PCM Data Format Selection +#define MAIN_I2S_DF_MASK (0x3) //main i2s Data Format mask +#define MAIN_I2S_DF_I2S (0x0) //I2S FORMAT +#define MAIN_I2S_DF_LEFT (0x1) //LEFT JUSTIFIED format +#define MAIN_I2S_DF_PCM_A (0x2) //PCM Mode A +#define MAIN_I2S_DF_PCM_B (0x3) //PCM Mode B + +//Extend Serial Data Port Control(0x36) +#define EXT_I2S_FUNC_ENABLE (0x1<<15) //Enable PCM interface on GPIO 1,3,4,5 0:GPIO function,1:Voice PCM interface +#define EXT_I2S_MODE_SEL (0x1<<14) //0:Master ,1:Slave +#define EXT_I2S_AUTO_CLK_CTRL (0x1<<13) //0:Disable,1:Enable +#define EXT_I2S_BCLK_POLARITY (0x1<<7) //0:Normal 1:Invert +#define EXT_I2S_PCM_MODE (0x1<<6) //0:Normal VSLRCK,1:Invert VSLRCK +//Data Length Slection +#define EXT_I2S_DL_MASK (0x3<<2) //Extend i2s Data Length mask +#define EXT_I2S_DL_32 (0x3<<2) //8 bits +#define EXT_I2S_DL_24 (0x2<<2) //24 bits +#define EXT_I2S_DL_20 (0x1<<2) //20 bits +#define EXT_I2S_DL_16 (0x0<<2) //16 bits + +//Voice Data Format +#define EXT_I2S_DF_MASK (0x3) //Extend i2s Data Format mask +#define EXT_I2S_DF_I2S (0x0) //I2S FORMAT +#define EXT_I2S_DF_LEFT (0x1) //LEFT JUSTIFIED format +#define EXT_I2S_DF_PCM_A (0x2) //PCM Mode A +#define EXT_I2S_DF_PCM_B (0x3) //PCM Mode B + +//Power managment addition 1 (0x3A),0:Disable,1:Enable +#define PWR_DAC_DF2SE_L (0x1<<15) +#define PWR_DAC_DF2SE_R (0x1<<14) +#define PWR_ZC_DET_PD (0x1<<13) +#define PWR_I2S_INTERFACE (0x1<<11) +#define PWR_AMP_POWER (0x1<<10) +#define PWR_HP_OUT_AMP (0x1<<9) +#define PWR_HP_OUT_ENH_AMP (0x1<<8) +#define PWR_VOICE_DF2SE (0x1<<7) +#define PWR_SOFTGEN_EN (0x1<<6) +#define PWR_MIC_BIAS1_DET (0x1<<5) +#define PWR_MIC_BIAS2_DET (0x1<<4) +#define PWR_MIC_BIAS1 (0x1<<3) +#define PWR_MIC_BIAS2 (0x1<<2) +#define PWR_MAIN_BIAS (0x1<<1) +#define PWR_DAC_REF (0x1) + + +//Power managment addition 2(0x3C),0:Disable,1:Enable +#define PWR_PLL1 (0x1<<15) +#define PWR_PLL2 (0x1<<14) +#define PWR_MIXER_VREF (0x1<<13) +#define PWR_TEMP_SENSOR (0x1<<12) +#define PWR_AUX_ADC (0x1<<11) +#define PWR_VOICE_CLOCK (0x1<<10) +#define PWR_L_DAC_CLK (0x1<<9) +#define PWR_R_DAC_CLK (0x1<<8) +#define PWR_L_ADC_CLK (0x1<<7) +#define PWR_R_ADC_CLK (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_OSC_EN (0x1<<15) +#define PWR_AUXOUT_VOL (0x1<<14) +#define PWR_SPK_OUT (0x1<<13) +#define PWR_SPK_OUT_N (0x1<<12) +#define PWR_HP_L_OUT_VOL (0x1<<11) +#define PWR_HP_R_OUT_VOL (0x1<<10) +#define PWR_VODSP_INTERFACE (0x1<<9) +#define PWR_I2C_FOR_VODSP (0x1<<8) +#define PWR_LINE_IN_L (0x1<<7) +#define PWR_LINE_IN_R (0x1<<6) +#define PWR_PHONE_VOL (0x1<<5) +#define PWR_PHONE_ADMIXER (0x1<<4) +#define PWR_MIC1_VOL_CTRL (0x1<<3) +#define PWR_MIC2_VOL_CTRL (0x1<<2) +#define PWR_MIC1_BOOST (0x1<<1) +#define PWR_MIC2_BOOST (0x1) + +//General Purpose Control Register 1(0x40) +#define GP_CLK_FROM_PLL (0x1<<15) +#define GP_CLK_FROM_MCLK (0x0<<15) + +#define GP_DAC_HI_PA_ENA (0x1<<10) //Enable DAC High Pass Filter + +#define GP_EXTCLK_S_SEL_PLL2 (0x1<<6) +#define GP_EXTCLK_S_SEL_PLL1 (0x0<<6) + +#define GP_EXTCLK_DIR_SEL_OUTPUT (0x1<<5) +#define GP_EXTCLK_DIR_SEL_INTPUT (0x0<<5) + +#define GP_VOSYS_S_SEL_PLL2 (0x0<<4) +#define GP_VOSYS_S_SEL_EXTCLK (0x1<<4) + +#define GP_SPK_AMP_CTRL_MASK (0x7<<1) +#define GP_SPK_AMP_CTRL_RATIO_225 (0x0<<1) //2.25 Vdd +#define GP_SPK_AMP_CTRL_RATIO_200 (0x1<<1) //2.00 Vdd +#define GP_SPK_AMP_CTRL_RATIO_175 (0x2<<1) //1.75 Vdd +#define GP_SPK_AMP_CTRL_RATIO_150 (0x3<<1) //1.50 Vdd +#define GP_SPK_AMP_CTRL_RATIO_125 (0x4<<1) //1.25 Vdd +#define GP_SPK_AMP_CTRL_RATIO_100 (0x5<<1) //1.00 Vdd + +//General Purpose Control Register 2(0x42) +#define GP2_PLL1_SOUR_SEL_MASK (0x3<<12) +#define GP2_PLL1_SOUR_SEL_MCLK (0x0<<12) +#define GP2_PLL1_SOUR_SEL_BCLK (0x2<<12) +#define GP2_PLL1_SOUR_SEL_VBCLK (0x3<<12) + +//PLL Control(0x44) +#define PLL_M_CODE_MASK 0xF //PLL M code mask +#define PLL_K_CODE_MASK (0x7<<4) //PLL K code mask +#define PLL_BYPASS_N (0x1<<7) //bypass PLL N code +#define PLL_N_CODE_MASK (0xFF<<8) //PLL N code mask + +#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) + +//PLL2 CONTROL +#define PLL2_ENA (0x1<<15) +#define PLL_2_RATIO_8X (0x0) +#define PLL_2_RATIO_16X (0x1) + +//LDO Control(0x48) +#define LDO_ENABLE (0x1<<15) + +#define LDO_OUT_VOL_CTRL_MASK (0xf<<0) +#define LDO_OUT_VOL_CTRL_1_55V (0xf<<0) +#define LDO_OUT_VOL_CTRL_1_50V (0xe<<0) +#define LDO_OUT_VOL_CTRL_1_45V (0xd<<0) +#define LDO_OUT_VOL_CTRL_1_40V (0xc<<0) +#define LDO_OUT_VOL_CTRL_1_35V (0xb<<0) +#define LDO_OUT_VOL_CTRL_1_30V (0xa<<0) +#define LDO_OUT_VOL_CTRL_1_25V (0x9<<0) +#define LDO_OUT_VOL_CTRL_1_20V (0x8<<0) +#define LDO_OUT_VOL_CTRL_1_15V (0x7<<0) +#define LDO_OUT_VOL_CTRL_1_05V (0x6<<0) +#define LDO_OUT_VOL_CTRL_1_00V (0x5<<0) +#define LDO_OUT_VOL_CTRL_0_95V (0x4<<0) +#define LDO_OUT_VOL_CTRL_0_90V (0x3<<0) +#define LDO_OUT_VOL_CTRL_0_85V (0x2<<0) +#define LDO_OUT_VOL_CTRL_0_80V (0x1<<0) +#define LDO_OUT_VOL_CTRL_0_75V (0x0<<0) + + + +//GPIO Pin Configuration(0x4C) +#define GPIO_1 (0x1<<1) +#define GPIO_2 (0x1<<2) +#define GPIO_3 (0x1<<3) +#define GPIO_4 (0x1<<4) +#define GPIO_5 (0x1<<5) + + +////INTERRUPT CONTROL(0x5E) +#define DISABLE_FAST_VREG (0x1<<15) + +#define AVC_TARTGET_SEL_MASK (0x3<<12) +#define AVC_TARTGET_SEL_NONE (0x0<<12) +#define AVC_TARTGET_SEL_R (0x1<<12) +#define AVC_TARTGET_SEL_L (0x2<<12) +#define AVC_TARTGET_SEL_BOTH (0x3<<12) + +#define HP_DEPOP_MODE2_EN (0x1<<8) +#define HP_DEPOP_MODE1_EN (0x1<<9) +#define HP_L_M_UM_DEPOP_EN (0x1<<7) +#define HP_R_M_UM_DEPOP_EN (0x1<<6) +#define M_UM_DEPOP_EN (0x1<<5) + +//Stereo DAC Clock Control 1(0x60) +#define STEREO_BCLK_DIV1_MASK (0xF<<12) +#define STEREO_BCLK_DIV1_1 (0x0<<12) +#define STEREO_BCLK_DIV1_2 (0x1<<12) +#define STEREO_BCLK_DIV1_3 (0x2<<12) +#define STEREO_BCLK_DIV1_4 (0x3<<12) +#define STEREO_BCLK_DIV1_5 (0x4<<12) +#define STEREO_BCLK_DIV1_6 (0x5<<12) +#define STEREO_BCLK_DIV1_7 (0x6<<12) +#define STEREO_BCLK_DIV1_8 (0x7<<12) +#define STEREO_BCLK_DIV1_9 (0x8<<12) +#define STEREO_BCLK_DIV1_10 (0x9<<12) +#define STEREO_BCLK_DIV1_11 (0xA<<12) +#define STEREO_BCLK_DIV1_12 (0xB<<12) +#define STEREO_BCLK_DIV1_13 (0xC<<12) +#define STEREO_BCLK_DIV1_14 (0xD<<12) +#define STEREO_BCLK_DIV1_15 (0xE<<12) +#define STEREO_BCLK_DIV1_16 (0xF<<12) + +#define STEREO_BCLK_DIV2_MASK (0x7<<8) +#define STEREO_BCLK_DIV2_2 (0x0<<8) +#define STEREO_BCLK_DIV2_4 (0x1<<8) +#define STEREO_BCLK_DIV2_8 (0x2<<8) +#define STEREO_BCLK_DIV2_16 (0x3<<8) +#define STEREO_BCLK_DIV2_32 (0x4<<8) + +#define STEREO_AD_LRCK_DIV1_MASK (0xF<<4) +#define STEREO_AD_LRCK_DIV1_1 (0x0<<4) +#define STEREO_AD_LRCK_DIV1_2 (0x1<<4) +#define STEREO_AD_LRCK_DIV1_3 (0x2<<4) +#define STEREO_AD_LRCK_DIV1_4 (0x3<<4) +#define STEREO_AD_LRCK_DIV1_5 (0x4<<4) +#define STEREO_AD_LRCK_DIV1_6 (0x5<<4) +#define STEREO_AD_LRCK_DIV1_7 (0x6<<4) +#define STEREO_AD_LRCK_DIV1_8 (0x7<<4) +#define STEREO_AD_LRCK_DIV1_9 (0x8<<4) +#define STEREO_AD_LRCK_DIV1_10 (0x9<<4) +#define STEREO_AD_LRCK_DIV1_11 (0xA<<4) +#define STEREO_AD_LRCK_DIV1_12 (0xB<<4) +#define STEREO_AD_LRCK_DIV1_13 (0xC<<4) +#define STEREO_AD_LRCK_DIV1_14 (0xD<<4) +#define STEREO_AD_LRCK_DIV1_15 (0xE<<4) +#define STEREO_AD_LRCK_DIV1_16 (0xF<<4) + +#define STEREO_AD_LRCK_DIV2_MASK (0x7<<1) +#define STEREO_AD_LRCK_DIV2_2 (0x0<<1) +#define STEREO_AD_LRCK_DIV2_4 (0x1<<1) +#define STEREO_AD_LRCK_DIV2_8 (0x2<<1) +#define STEREO_AD_LRCK_DIV2_16 (0x3<<1) +#define STEREO_AD_LRCK_DIV2_32 (0x4<<1) + +#define STEREO_DA_LRCK_DIV_MASK (1) +#define STEREO_DA_LRCK_DIV_32 (0) +#define STEREO_DA_LRCK_DIV_64 (1) + +//Stereo DAC Clock Control 2(0x62) +#define STEREO_DA_FILTER_DIV1_MASK (0xF<<12) +#define STEREO_DA_FILTER_DIV1_1 (0x0<<12) +#define STEREO_DA_FILTER_DIV1_2 (0x1<<12) +#define STEREO_DA_FILTER_DIV1_3 (0x2<<12) +#define STEREO_DA_FILTER_DIV1_4 (0x3<<12) +#define STEREO_DA_FILTER_DIV1_5 (0x4<<12) +#define STEREO_DA_FILTER_DIV1_6 (0x5<<12) +#define STEREO_DA_FILTER_DIV1_7 (0x6<<12) +#define STEREO_DA_FILTER_DIV1_8 (0x7<<12) +#define STEREO_DA_FILTER_DIV1_9 (0x8<<12) +#define STEREO_DA_FILTER_DIV1_10 (0x9<<12) +#define STEREO_DA_FILTER_DIV1_11 (0xA<<12) +#define STEREO_DA_FILTER_DIV1_12 (0xB<<12) +#define STEREO_DA_FILTER_DIV1_13 (0xC<<12) +#define STEREO_DA_FILTER_DIV1_14 (0xD<<12) +#define STEREO_DA_FILTER_DIV1_15 (0xE<<12) +#define STEREO_DA_FILTER_DIV1_16 (0xF<<12) + +#define STEREO_DA_FILTER_DIV2_MASK (0x7<<9) +#define STEREO_DA_FILTER_DIV2_2 (0x0<<9) +#define STEREO_DA_FILTER_DIV2_4 (0x1<<9) +#define STEREO_DA_FILTER_DIV2_8 (0x2<<9) +#define STEREO_DA_FILTER_DIV2_16 (0x3<<9) +#define STEREO_DA_FILTER_DIV2_32 (0x4<<9) + +#define STEREO_AD_FILTER_DIV1_MASK (0xF<<4) +#define STEREO_AD_FILTER_DIV1_1 (0x0<<4) +#define STEREO_AD_FILTER_DIV1_2 (0x1<<4) +#define STEREO_AD_FILTER_DIV1_3 (0x2<<4) +#define STEREO_AD_FILTER_DIV1_4 (0x3<<4) +#define STEREO_AD_FILTER_DIV1_5 (0x4<<4) +#define STEREO_AD_FILTER_DIV1_6 (0x5<<4) +#define STEREO_AD_FILTER_DIV1_7 (0x6<<4) +#define STEREO_AD_FILTER_DIV1_8 (0x7<<4) +#define STEREO_AD_FILTER_DIV1_9 (0x8<<4) +#define STEREO_AD_FILTER_DIV1_10 (0x9<<4) +#define STEREO_AD_FILTER_DIV1_11 (0xA<<4) +#define STEREO_AD_FILTER_DIV1_12 (0xB<<4) +#define STEREO_AD_FILTER_DIV1_13 (0xC<<4) +#define STEREO_AD_FILTER_DIV1_14 (0xD<<4) +#define STEREO_AD_FILTER_DIV1_15 (0xE<<4) +#define STEREO_AD_FILTER_DIV1_16 (0xF<<4) + +#define STEREO_AD_FILTER_DIV2_MASK (0x7<<1) +#define STEREO_AD_FILTER_DIV2_1 (0x0<<1) +#define STEREO_AD_FILTER_DIV2_2 (0x1<<1) +#define STEREO_AD_FILTER_DIV2_4 (0x2<<1) +#define STEREO_AD_FILTER_DIV2_8 (0x3<<1) +#define STEREO_AD_FILTER_DIV2_16 (0x4<<1) +#define STEREO_AD_FILTER_DIV2_32 (0x5<<1) + + +//Voice DAC PCM Clock Control 1(0x64) +#define VOICE_BCLK_DIV1_MASK (0xF<<12) +#define VOICE_BCLK_DIV1_1 (0x0<<12) +#define VOICE_BCLK_DIV1_2 (0x1<<12) +#define VOICE_BCLK_DIV1_3 (0x2<<12) +#define VOICE_BCLK_DIV1_4 (0x3<<12) +#define VOICE_BCLK_DIV1_5 (0x4<<12) +#define VOICE_BCLK_DIV1_6 (0x5<<12) +#define VOICE_BCLK_DIV1_7 (0x6<<12) +#define VOICE_BCLK_DIV1_8 (0x7<<12) +#define VOICE_BCLK_DIV1_9 (0x8<<12) +#define VOICE_BCLK_DIV1_10 (0x9<<12) +#define VOICE_BCLK_DIV1_11 (0xA<<12) +#define VOICE_BCLK_DIV1_12 (0xB<<12) +#define VOICE_BCLK_DIV1_13 (0xC<<12) +#define VOICE_BCLK_DIV1_14 (0xD<<12) +#define VOICE_BCLK_DIV1_15 (0xE<<12) +#define VOICE_BCLK_DIV1_16 (0xF<<12) + +#define VOICE_BCLK_DIV2_MASK (0x7<<8) +#define VOICE_BCLK_DIV2_2 (0x0<<8) +#define VOICE_BCLK_DIV2_4 (0x1<<8) +#define VOICE_BCLK_DIV2_8 (0x2<<8) +#define VOICE_BCLK_DIV2_16 (0x3<<8) +#define VOICE_BCLK_DIV2_32 (0x4<<8) + +#define VOICE_AD_LRCK_DIV1_MASK (0xF<<4) +#define VOICE_AD_LRCK_DIV1_1 (0x0<<4) +#define VOICE_AD_LRCK_DIV1_2 (0x1<<4) +#define VOICE_AD_LRCK_DIV1_3 (0x2<<4) +#define VOICE_AD_LRCK_DIV1_4 (0x3<<4) +#define VOICE_AD_LRCK_DIV1_5 (0x4<<4) +#define VOICE_AD_LRCK_DIV1_6 (0x5<<4) +#define VOICE_AD_LRCK_DIV1_7 (0x6<<4) +#define VOICE_AD_LRCK_DIV1_8 (0x7<<4) +#define VOICE_AD_LRCK_DIV1_9 (0x8<<4) +#define VOICE_AD_LRCK_DIV1_10 (0x9<<4) +#define VOICE_AD_LRCK_DIV1_11 (0xA<<4) +#define VOICE_AD_LRCK_DIV1_12 (0xB<<4) +#define VOICE_AD_LRCK_DIV1_13 (0xC<<4) +#define VOICE_AD_LRCK_DIV1_14 (0xD<<4) +#define VOICE_AD_LRCK_DIV1_15 (0xE<<4) +#define VOICE_AD_LRCK_DIV1_16 (0xF<<4) + +#define VOICE_AD_LRCK_DIV2_MASK (0x7<<1) +#define VOICE_AD_LRCK_DIV2_2 (0x0<<1) +#define VOICE_AD_LRCK_DIV2_4 (0x1<<1) +#define VOICE_AD_LRCK_DIV2_8 (0x2<<1) +#define VOICE_AD_LRCK_DIV2_16 (0x3<<1) +#define VOICE_AD_LRCK_DIV2_32 (0x4<<1) + +#define VOICE_DA_LRCK_DIV_MASK (1) +#define VOICE_DA_LRCK_DIV_32 (0) +#define VOICE_DA_LRCK_DIV_64 (1) + + +//Psedueo Stereo & Spatial Effect Block Control(0x68) +#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 SPATIAL_3D_GAIN1_MASK (0x3<<10) +#define SPATIAL_3D_GAIN1_1_0 (0x0<<10) +#define SPATIAL_3D_GAIN1_1_5 (0x1<<10) +#define SPATIAL_3D_GAIN1_2_0 (0x2<<10) + +#define SPATIAL_3D_RATIO1_MASK (0x3<<8) +#define SPATIAL_3D_RATIO1_0_0 (0x0<<8) +#define SPATIAL_3D_RATIO1_0_66 (0x1<<8) +#define SPATIAL_3D_RATIO1_1_0 (0x2<<8) + +#define SPATIAL_3D_GAIN2_MASK (0x3<<6) +#define SPATIAL_3D_GAIN2_1_0 (0x0<<6) +#define SPATIAL_3D_GAIN2_1_5 (0x1<<6) +#define SPATIAL_3D_GAIN2_2_0 (0x2<<6) + +#define SPATIAL_3D_RATIO2_MASK (0x3<<4) +#define SPATIAL_3D_RATIO2_0_0 (0x0<<4) +#define SPATIAL_3D_RATIO2_0_66 (0x1<<4) +#define SPATIAL_3D_RATIO2_1_0 (0x2<<4) + +#define APF_MASK (0x3) +#define APF_FOR_48K (0x3) +#define APF_FOR_44_1K (0x2) +#define APF_FOR_32K (0x1) + +//EQ Control and Status /ADC HPF Control(0x6E) +#define EN_HW_EQ_BLK (0x1<<15) //HW EQ block control + +#define EQ_SOUR_SEL_DAC (0x0<<14) +#define EQ_SOUR_SEL_ADC (0x1<<14) + +#define EQ_CHANGE_EN (0x1<<7) //EQ parameter update control +#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 + + +//AEC register command(0x74) + +#define VODSP_BUSY (0x1<<15) //VODSP I2C busy flag + +#define VODSP_S_FROM_VODSP_RD (0x0<<14) +#define VODSP_S_FROM_MX72 (0x1<<14) + +#define VODSP_CLK_SEL_MASK (0x3<<12) //VODSP CLK select Mask +#define VODSP_CLK_SEL_12_288M (0x0<<12) //VODSP CLK select 12.288Mhz +#define VODSP_CLK_SEL_6_144M (0x1<<12) //VODSP CLK select 6.144Mhz +#define VODSP_CLK_SEL_3_072M (0x2<<12) //VODSP CLK select 3.072Mhz +#define VODSP_CLK_SEL_2_048M (0x3<<12) //VODSP CLK select 2.0488Mhz + +#define VODSP_READ_ENABLE (0x1<<9) //VODSP Read Enable +#define VODSP_WRITE_ENABLE (0x1<<8) //VODSP Write Enable + +#define VODSP_CMD_MASK (0xFF<<0) +#define VODSP_CMD_MW (0x3B<<0) //Memory Write +#define VODSP_CMD_MR (0x37<<0) //Memory Read +#define VODSP_CMD_RR (0x60<<0) //Register Read +#define VODSP_CMD_RW (0x68<<0) //Register Write + + +/************************************************************************************************* + *Index register of codec + *************************************************************************************************/ +/*Index(0x20) for Auto Volume Control*/ +#define AVC_CH_SEL_MASK (0x1<<7) +#define AVC_CH_SEL_L_CH (0x0<<7) +#define AVC_CH_SEL_R_CH (0x1<<7) +#define ENABLE_AVC_GAIN_CTRL (0x1<<15) +//************************************************************************************************* +//************************************************************************************************* + +#define REALTEK_HWDEP 0 + +#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 + + +enum pll_sel +{ + RT5625_PLL1_FROM_MCLK = 0, + RT5625_PLL1_FROM_BCLK, + RT5625_PLL1_FROM_VBCLK, +}; + +enum AEC_MODE +{ + PCM_IN_PCM_OUT = 0, + ANALOG_IN_ANALOG_OUT, + DAC_IN_ADC_OUT, + VODSP_AEC_DISABLE +}; + +enum +{ + PCM_MASTER_MODE_A=0, + PCM_MASTER_MODE_B, + PCM_SLAVE_MODE_A, + PCM_SLAVE_MODE_B, +}; + + +enum RT5625_FUNC_SEL +{ + RT5625_AEC_DISABLE =0, + RT5625_AEC_PCM_IN_OUT, + RT5625_AEC_IIS_IN_OUT, + RT5625_AEC_ANALOG_IN_OUT, + +}; + + +struct rt5625_setup_data { + int i2c_bus; + int i2c_address; +}; + +typedef struct +{ + unsigned short int VoiceDSPIndex; + unsigned short int VoiceDSPValue; + +}Voice_DSP_Reg; + +extern struct snd_soc_dai rt5625_dai[]; +extern struct snd_soc_codec_device soc_codec_dev_rt5625; + +#endif diff --git a/sound/soc/rk29/Kconfig b/sound/soc/rk29/Kconfig index 2eeac425d9b5..7d5c97f7066c 100644 --- a/sound/soc/rk29/Kconfig +++ b/sound/soc/rk29/Kconfig @@ -56,6 +56,16 @@ config SND_RK29_SOC_alc5631 help Say Y if you want to add support for SoC audio on rockchip with the alc5631. + +config SND_RK29_SOC_RT5625 + tristate "SoC I2S Audio support for rockchip - RT5625" + depends on SND_RK29_SOC && I2C_RK29 + select SND_RK29_SOC_I2S + select SND_SOC_RT5625 + help + Say Y if you want to add support for SoC audio on rockchip + with the RT5625. + config SND_RK29_SOC_WM8994 tristate "SoC I2S Audio support for rockchip - WM8994" depends on SND_RK29_SOC && I2C_RK29 @@ -74,7 +84,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 || SND_RK29_SOC_alc5621 || SND_RK29_SOC_alc5631 +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 || SND_RK29_SOC_RT5625 choice prompt "Set i2s type" config SND_RK29_CODEC_SOC_MASTER diff --git a/sound/soc/rk29/Makefile b/sound/soc/rk29/Makefile index 17e5b941e799..f4a9f8c4e333 100644 --- a/sound/soc/rk29/Makefile +++ b/sound/soc/rk29/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_SND_RK29_SOC_I2S) += snd-soc-rockchip-i2s.o snd-soc-wm8900-objs := rk29_wm8900.o snd-soc-alc5621-objs := rk29_alc5621.o snd-soc-alc5631-objs := rk29_rt5631.o +snd-soc-rt5625-objs := rk29_rt5625.o snd-soc-wm8988-objs := rk29_wm8988.o snd-soc-rk1000-objs := rk29_rk1000codec.o snd-soc-wm8994-objs := rk29_wm8994.o @@ -18,4 +19,5 @@ 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_RT5625) += snd-soc-rt5625.o obj-$(CONFIG_SND_RK29_SOC_RK1000) += snd-soc-rk1000.o diff --git a/sound/soc/rk29/rk29_rt5625.c b/sound/soc/rk29/rk29_rt5625.c new file mode 100644 index 000000000000..afca8822b411 --- /dev/null +++ b/sound/soc/rk29/rk29_rt5625.c @@ -0,0 +1,253 @@ +/* + * rk29_rt5625.c -- SoC audio for rockchip + * + * Driver for rockchip rt5625 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/rt5625.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; + 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 rt5625,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_rt5625: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_rt5625: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 rt5625_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*/ + {"Mic1 Bias", NULL, "Mic Jack"}, + {"Mic1", NULL, "Mic1 Bias"}, + /* HP_OUT --> Headphone Jack */ + {"Headphone Jack", NULL, "HPL"}, + {"Headphone Jack", NULL, "HPR"}, + /* LINE_OUT --> Ext Speaker */ + {"Ext Spk", NULL, "SPKL"}, + {"Ext Spk", NULL, "SPKR"}, + +} ; + +/* + * Logic for a rt5625 as connected on a rockchip board. + */ +static int rk29_rt5625_init(struct snd_soc_codec *codec) +{ + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + + /* Add specific widgets */ + snd_soc_dapm_new_controls(codec, rt5625_dapm_widgets, + ARRAY_SIZE(rt5625_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 = "RT5625", + .stream_name = "RT5625 PCM", + .cpu_dai = &rk29_i2s_dai[0], + .codec_dai = &rt5625_dai[0], + .init = rk29_rt5625_init, + .ops = &rk29_ops, +}; + +static struct snd_soc_card snd_soc_card_rk29 = { + .name = "RK29_RT5625", + .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_rt5625, +}; + +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