/* RK1 SPKOUT Mux RK11 HP Mixer RK12 SPK Mixer RK13 MoNo Mixer RK2 SPKOUT Playback Switch RK3 SPKOUT Playback Volume RK4 PCM Playback Volume RK5 Left HP Mixer RK6 Right HP Mixer RK7 HPLOUT Mux RK71 HPL Mixer RK8 HPROUT Mux RK81 HPR Mixer RK9 HPOUT Playback Switch RKA HPOUT Playback Volume RKB AUXOUT Mux RKC AUXOUT Playback Switch RKD AUXOUT Playback Volume RKE Left Rec Mixer RKF Right Rec Mixer RKG DAC Mixer Playback Switch RKH HIFI DAC Playback Switch RKI Mic1 Playback Switch RKJ Phone Playback Switch RKK Mic1 Capture Switch RKL Phone Capture Switch RKM Voice DAC Playback Switch */ #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 #if 0 #define DBG(x...) printk(KERN_INFO x) #else #define DBG(x...) do { } while (0) #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]; DBG(KERN_INFO "rt5625_read ok, reg = %x, value = %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; DBG("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) { DBG("rt5625_write ok, reg = %x, value = %x\n", reg, value); return 0; } else { printk("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; DBG("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; DBG("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); DBG("%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", "RK11", "RK12", "RK13"}; /*3*/ static const char *rt5625_hplmux_source_sel[] = {"VMID","RK71"}; /*4*/ static const char *rt5625_hprmux_source_sel[] = {"VMID","RK81"}; /*5*/ static const char *rt5625_auxmux_source_sel[] = {"VMID", "RK11", "RK12", "RK13"}; /*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) { DBG("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; DBG("rt5625_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); DBG("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("RK4", 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("RK3", RT5625_SPK_OUT_VOL, 8, 0, 31, 1), SOC_DOUBLE("RK2", RT5625_SPK_OUT_VOL, 15, 7, 1, 1), SOC_DOUBLE("RKA", RT5625_HP_OUT_VOL, 8, 0, 31, 1), SOC_DOUBLE("RK9", RT5625_HP_OUT_VOL, 15, 7, 1, 1), SOC_DOUBLE("RKD", RT5625_AUX_OUT_VOL, 8, 0, 31, 1), SOC_DOUBLE("RKC", 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); DBG("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("RKK", 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("RKL", 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("RKK", 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("RKL", 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("RKJ", HPL_MIXER, 1, 1, 0), SOC_DAPM_SINGLE("RKI", HPL_MIXER, 2, 1, 0), SOC_DAPM_SINGLE("Mic2 Playback Switch", HPL_MIXER, 3, 1, 0), SOC_DAPM_SINGLE("RKM", HPL_MIXER, 4, 1, 0), SOC_DAPM_SINGLE("RKH", 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("RKJ", HPR_MIXER, 1, 1, 0), SOC_DAPM_SINGLE("RKI", HPR_MIXER, 2, 1, 0), SOC_DAPM_SINGLE("Mic2 Playback Switch", HPR_MIXER, 3, 1, 0), SOC_DAPM_SINGLE("RKM", HPR_MIXER, 4, 1, 0), SOC_DAPM_SINGLE("RKH", 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("RKI", 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("RKG", RT5625_DAC_AND_MIC_CTRL, 0, 1, 1), SOC_DAPM_SINGLE("RKM", 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("RKJ", RT5625_PHONEIN_VOL, 14, 1, 1), SOC_DAPM_SINGLE("RKI", 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("RKG", RT5625_DAC_AND_MIC_CTRL, 1, 1, 1), SOC_DAPM_SINGLE("RKM", 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; DBG("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; DBG("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: DBG("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: DBG("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; DBG("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: DBG("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: DBG("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("RKE", 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("RKF", 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("RK5", 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("RK6", 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("RK13", RT5625_PWR_MANAG_ADD2, 2, 0, &rt5625_mono_mixer_controls[0], ARRAY_SIZE(rt5625_mono_mixer_controls)), SND_SOC_DAPM_MIXER("RK12", RT5625_PWR_MANAG_ADD2, 3, 0, &rt5625_spk_mixer_controls[0], ARRAY_SIZE(rt5625_spk_mixer_controls)), SND_SOC_DAPM_MIXER("RK11", 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("RK1", SND_SOC_NOPM, 0, 0, &rt5625_spkout_mux_out_controls), SND_SOC_DAPM_MUX("RK7", SND_SOC_NOPM, 0, 0, &rt5625_hplout_mux_out_controls), SND_SOC_DAPM_MUX("RK8", SND_SOC_NOPM, 0, 0, &rt5625_hprout_mux_out_controls), SND_SOC_DAPM_MUX("RKB", 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[] = { /*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"}, /*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*/ {"RKE", "LineIn Capture Switch", "Left LineIn"}, {"RKE", "RKL", "Phone"}, {"RKE", "RKK", "Mic1 Boost"}, {"RKE", "Mic2 Capture Switch", "Mic2 Boost"}, {"RKE", "HP Mixer Capture Switch", "RK5"}, {"RKE", "SPK Mixer Capture Switch", "RK12"}, {"RKE", "MoNo Mixer Capture Switch", "RK13"}, /*Right ADC Mixer*/ {"RKF", "LineIn Capture Switch", "Right LineIn"}, {"RKF", "RKL", "Phone"}, {"RKF", "RKK", "Mic1 Boost"}, {"RKF", "Mic2 Capture Switch", "Mic2 Boost"}, {"RKF", "HP Mixer Capture Switch", "RK6"}, {"RKF", "SPK Mixer Capture Switch", "RK12"}, {"RKF", "MoNo Mixer Capture Switch", "RK13"}, /*HPL mixer*/ {"RK5", "ADC Playback Switch", "RKE"}, {"RK5", "LineIn Playback Switch", "Left LineIn PGA"}, {"RK5", "RKJ", "Phone PGA"}, {"RK5", "RKI", "Mic1 PGA"}, {"RK5", "Mic2 Playback Switch", "Mic2 PGA"}, {"RK5", "RKH", "Left DAC"}, {"RK5", "RKM", "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"}, /*HPR mixer*/ {"RK6", "ADC Playback Switch", "RKF"}, {"RK6", "LineIn Playback Switch", "Right LineIn PGA"}, {"RK6", "RKH", "Right DAC"}, {"RK6", "RKJ", "Phone PGA"}, {"RK6", "RKI", "Mic1 PGA"}, {"RK6", "Mic2 Playback Switch", "Mic2 PGA"}, {"RK6", "RKM", "VoDAC PGA"}, /*spk mixer*/ {"RK12", "Line Mixer Playback Switch", "Line Mixer"}, {"RK12", "RKJ", "Phone PGA"}, {"RK12", "RKI", "Mic1 PGA"}, {"RK12", "Mic2 Playback Switch", "Mic2 PGA"}, {"RK12", "RKG", "DAC Mixer"}, {"RK12", "RKM", "VoDAC PGA"}, /*mono mixer*/ {"RK13", "Line Mixer Playback Switch", "Line Mixer"}, {"RK13", "ADCL Playback Switch","RKE"}, {"RK13", "ADCR Playback Switch","RKF"}, {"RK13", "RKI", "Mic1 PGA"}, {"RK13", "Mic2 Playback Switch", "Mic2 PGA"}, {"RK13", "RKG", "DAC Mixer"}, {"RK13", "RKM", "VoDAC PGA"}, /*hp mixer*/ {"RK11", NULL, "RK5"}, {"RK11", NULL, "RK6"}, /*spkout mux*/ {"RK1", "RK11", "RK11"}, {"RK1", "RK12", "RK12"}, {"RK1", "RK13", "RK13"}, /*hpl out mux*/ {"RK7", "RK71", "RK5"}, /*hpr out mux*/ {"RK8", "RK81", "RK6"}, /*aux out mux*/ {"RKB", "RK11", "RK11"}, {"RKB", "RK12", "RK12"}, {"RKB", "RK13", "RK13"}, /*spkl out pga*/ {"SPKL Out PGA", NULL, "RK1"}, /*spkr out pga*/ {"SPKR Out PGA", NULL, "RK1"}, /*hpl out pga*/ {"HPL Out PGA", NULL, "RK7"}, /*hpr out pga*/ {"HPR Out PGA", NULL, "RK8"}, /*aux out pga*/ {"AUX Out PGA", NULL, "RKB"}, /*left adc*/ {"Left ADC", NULL, "RKE"}, /*right adc*/ {"Right ADC", NULL, "RKF"}, }; 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}, { 11289600, 24576000, 0x950F}, { 12000000, 24576000, 0x2915}, { 12288000, 24576000, 0x0600}, { 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; DBG("get_coeff mclk = %d, rate = %d, mode = %d\n", mclk, rate, mode); 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; } } printk("can't find a matched mclk and rate in %s\n", (mode ? "coeff_div_voice[]" : "coeff_div_audio[]")); return -EINVAL; } 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; DBG("enter %s pll_id = %d freq_in = %d freq_out = %d\n", __func__, pll_id, freq_in, freq_out); 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; DBG("sysclk freq %u for audio i2s\n", freq); if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) { rt5625->stereo_sysclk = freq; return 0; } printk("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; DBG("sysclk freq %u for voice pcm\n", freq); if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) { rt5625->voice_sysclk = freq; return 0; } printk("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); DBG("enter %s rate = %d \n", __func__, rate); 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); DBG("enter %s rate = %d \n", __func__, rate); 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; DBG("enter %s fmt = %d\n", __func__, fmt); /*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; DBG("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) { DBG("enter %s\n", __func__); return 0; } static int rt56xx_hwdep_release(struct snd_hwdep *hw, struct file *file) { DBG("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; DBG("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; } DBG("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");