--- /dev/null
+/*\r
+RK1 SPKOUT Mux\r
+RK11 HP Mixer\r
+RK12 SPK Mixer\r
+RK13 MoNo Mixer\r
+RK2 SPKOUT Playback Switch\r
+RK3 SPKOUT Playback Volume\r
+RK4 PCM Playback Volume\r
+RK5 Left HP Mixer\r
+RK6 Right HP Mixer\r
+RK7 HPLOUT Mux\r
+RK71 HPL Mixer\r
+RK8 HPROUT Mux\r
+RK81 HPR Mixer\r
+RK9 HPOUT Playback Switch\r
+RKA HPOUT Playback Volume\r
+RKB AUXOUT Mux\r
+RKC AUXOUT Playback Switch\r
+RKD AUXOUT Playback Volume\r
+RKE Left Rec Mixer\r
+RKF Right Rec Mixer\r
+RKG DAC Mixer Playback Switch\r
+RKH HIFI DAC Playback Switch\r
+RKI Mic1 Playback Switch\r
+RKJ Phone Playback Switch\r
+RKK Mic1 Capture Switch\r
+RKL Phone Capture Switch\r
+RKM Voice DAC Playback Switch\r
+*/\r
+\r
+\r
+#include <linux/module.h>\r
+#include <linux/moduleparam.h>\r
+#include <linux/kernel.h>\r
+#include <linux/init.h>\r
+#include <linux/delay.h>\r
+#include <linux/pm.h>\r
+#include <linux/i2c.h>\r
+#include <linux/platform_device.h>\r
+#include <linux/spi/spi.h>\r
+#include <linux/jiffies.h>\r
+#include <asm/delay.h>\r
+#include <sound/core.h>\r
+#include <sound/pcm.h>\r
+#include <sound/pcm_params.h>\r
+#include <sound/soc.h>\r
+#include <sound/soc-dapm.h>\r
+#include <sound/initval.h>\r
+#include <sound/tlv.h>\r
+#include <asm/div64.h>\r
+\r
+#include "rt5625.h"\r
+\r
+#if REALTEK_HWDEP\r
+\r
+#include <linux/ioctl.h>\r
+#include <linux/types.h>\r
+\r
+#endif\r
+\r
+#if 0\r
+#define DBG(x...) printk(KERN_INFO x)\r
+#else\r
+#define DBG(x...) do { } while (0)\r
+#endif\r
+\r
+#define AUDIO_NAME "rt5625"\r
+#define RT5625_VERSION "0.03 alsa 1.0.21"\r
+#define ALSA_SOC_VERSION "1.0.21"\r
+\r
+#define RT5625_EQ_FUNC_ENA 0\r
+\r
+static void hp_depop_mode2(struct snd_soc_codec *codec);\r
+static void hp_mute_unmute_depop(struct snd_soc_codec *codec,int mute); \r
+\r
+struct rt5625_priv {\r
+ unsigned int stereo_sysclk;\r
+ unsigned int voice_sysclk;\r
+};\r
+\r
+struct rt5625_init_reg {\r
+ u8 reg_index;\r
+ u16 reg_value; \r
+};\r
+\r
+static struct rt5625_init_reg rt5625_init_list[] = {\r
+\r
+ {RT5625_HP_OUT_VOL , 0x8888}, //default is -12db\r
+ {RT5625_SPK_OUT_VOL , 0x8080}, //default is 0db\r
+ {RT5625_DAC_AND_MIC_CTRL , 0xee03}, //DAC to hpmixer\r
+ {RT5625_OUTPUT_MIXER_CTRL , 0x0748}, //all output from hpmixer\r
+ {RT5625_MIC_CTRL , 0x0500}, //mic boost 20db\r
+ {RT5625_ADC_REC_MIXER , 0x3f3f}, //record source from mic1\r
+ {RT5625_GEN_CTRL_REG1 , 0x0c0a}, //speaker vdd ratio is 1\r
+ {RT5625_ADC_REC_GAIN , 0xd5d5}, //gain 15db of ADC by default\r
+\r
+};\r
+\r
+#define RT5625_INIT_REG_NUM ARRAY_SIZE(rt5625_init_list)\r
+\r
+#if (RT5625_EQ_FUNC_ENA==1)\r
+//*************************************************************************************************\r
+//eq table\r
+//*************************************************************************************************\r
+enum\r
+{\r
+ NORMAL=0,\r
+ CLUB,\r
+ DANCE,\r
+ LIVE, \r
+ POP,\r
+ ROCK,\r
+ OPPO,\r
+ TREBLE,\r
+ BASS \r
+};\r
+\r
+typedef struct _HW_EQ_PRESET\r
+{\r
+ u16 HwEqType;\r
+ u16 EqValue[14];\r
+ u16 HwEQCtrl;\r
+\r
+}HW_EQ_PRESET;\r
+\r
+\r
+HW_EQ_PRESET HwEq_Preset[]={\r
+/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0x6e*/\r
+ {NORMAL,{0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},0x0000}, \r
+ {CLUB ,{0x1C10,0x0000,0xC1CC,0x1E5D,0x0699,0xCD48,0x188D,0x0699,0xC3B6,0x1CD0,0x0699,0x0436,0x0000},0x800E},\r
+ {DANCE ,{0x1F2C,0x095B,0xC071,0x1F95,0x0616,0xC96E,0x1B11,0xFC91,0xDCF2,0x1194,0xFAF2,0x0436,0x0000},0x800F},\r
+ {LIVE ,{0x1EB5,0xFCB6,0xC24A,0x1DF8,0x0E7C,0xC883,0x1C10,0x0699,0xDA41,0x1561,0x0295,0x0436,0x0000},0x800F},\r
+ {POP ,{0x1EB5,0xFCB6,0xC1D4,0x1E5D,0x0E23,0xD92E,0x16E6,0xFCB6,0x0000,0x0969,0xF988,0x0436,0x0000},0x800F},\r
+ {ROCK ,{0x1EB5,0xFCB6,0xC071,0x1F95,0x0424,0xC30A,0x1D27,0xF900,0x0C5D,0x0FC7,0x0E23,0x0436,0x0000},0x800F},\r
+ {OPPO ,{0x0000,0x0000,0xCA4A,0x17F8,0x0FEC,0xCA4A,0x17F8,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},0x800F},\r
+ {TREBLE,{0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x188D,0x1699},0x8010},\r
+ {BASS ,{0x1A43,0x0C00,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},0x8001},\r
+ \r
+};\r
+\r
+#endif\r
+//*************************************************************************************************\r
+//*************************************************************************************************\r
+\r
+/*\r
+ * bit[0] for linein playback switch\r
+ * bit[1] phone\r
+ * bit[2] mic1\r
+ * bit[3] mic2\r
+ * bit[4] vopcm\r
+ * \r
+ */\r
+#define HPL_MIXER 0x80\r
+#define HPR_MIXER 0x82\r
+static unsigned int reg80 = 0, reg82 = 0;\r
+\r
+/*\r
+ * bit[0][1][2] use for aec control\r
+ * bit[3] for none \r
+ * bit[4] for SPKL pga\r
+ * bit[5] for SPKR pga\r
+ * bit[6] for hpl pga\r
+ * bit[7] for hpr pga\r
+ * bit[8] for dump dsp\r
+ * bit[12~15] for eq function\r
+ */\r
+ #define VIRTUAL_REG_FOR_MISC_FUNC 0x84\r
+static unsigned int reg84 = 0;\r
+\r
+\r
+static const u16 rt5625_reg[] = {\r
+ 0x59b4, 0x8080, 0x8080, 0x8080, /*reg00-reg06*/\r
+ 0xc800, 0xe808, 0x1010, 0x0808, /*reg08-reg0e*/\r
+ 0xe0ef, 0xcbcb, 0x7f7f, 0x0000, /*reg10-reg16*/\r
+ 0xe010, 0x0000, 0x8008, 0x2007, /*reg18-reg1e*/\r
+ 0x0000, 0x0000, 0x00c0, 0xef00, /*reg20-reg26*/\r
+ 0x0000, 0x0000, 0x0000, 0x0000, /*reg28-reg2e*/\r
+ 0x0000, 0x0000, 0x0000, 0x0000, /*reg30-reg36*/\r
+ 0x0000, 0x0000, 0x0000, 0x0000, /*reg38-reg3e*/\r
+ 0x0c0a, 0x0000, 0x0000, 0x0000, /*reg40-reg46*/\r
+ 0x0029, 0x0000, 0xbe3e, 0x3e3e, /*reg48-reg4e*/\r
+ 0x0000, 0x0000, 0x803a, 0x0000, /*reg50-reg56*/\r
+ 0x0000, 0x0009, 0x0000, 0x3000, /*reg58-reg5e*/\r
+ 0x3075, 0x1010, 0x3110, 0x0000, /*reg60-reg66*/\r
+ 0x0553, 0x0000, 0x0000, 0x0000, /*reg68-reg6e*/\r
+ 0x0000, 0x0000, 0x0000, 0x0000, /*reg70-reg76*/\r
+ 0x0000, 0x0000, 0x0000, 0x0000, /*reg78-reg7e*/\r
+};\r
+\r
+\r
+Voice_DSP_Reg VODSP_AEC_Init_Value[]=\r
+{\r
+ {0x232C, 0x0025},\r
+ {0x230B, 0x0001},\r
+ {0x2308, 0x007F},\r
+ {0x23F8, 0x4003},\r
+ {0x2301, 0x0002},\r
+ {0x2328, 0x0001},\r
+ {0x2304, 0x00FA},\r
+ {0x2305, 0x0500},\r
+ {0x2306, 0x4000},\r
+ {0x230D, 0x0900},\r
+ {0x230E, 0x0280},\r
+ {0x2312, 0x00B1},\r
+ {0x2314, 0xC000},\r
+ {0x2316, 0x0041},\r
+ {0x2317, 0x2200},\r
+ {0x2318, 0x0C00},\r
+ {0x231D, 0x0050},\r
+ {0x231F, 0x4000},\r
+ {0x2330, 0x0008},\r
+ {0x2335, 0x000A},\r
+ {0x2336, 0x0004},\r
+ {0x2337, 0x5000},\r
+ {0x233A, 0x0300},\r
+ {0x233B, 0x0030},\r
+ {0x2341, 0x0008},\r
+ {0x2343, 0x0800}, \r
+ {0x2352, 0x7FFF},\r
+ {0x237F, 0x0400},\r
+ {0x23A7, 0x2800},\r
+ {0x22CE, 0x0400},\r
+ {0x22D3, 0x1500},\r
+ {0x22D4, 0x2800},\r
+ {0x22D5, 0x3000},\r
+ {0x2399, 0x2800},\r
+ {0x230C, 0x0000}, //to enable VODSP AEC function\r
+};\r
+\r
+\r
+#define SET_VODSP_REG_INIT_NUM ARRAY_SIZE(VODSP_AEC_Init_Value)\r
+static struct snd_soc_device *rt5625_socdev;\r
+\r
+static inline unsigned int rt5625_read_reg_cache(struct snd_soc_codec *codec, \r
+ unsigned int reg)\r
+{\r
+ u16 *cache = codec->reg_cache;\r
+\r
+ if (reg > 0x7e)\r
+ return 0;\r
+ return cache[reg / 2];\r
+}\r
+\r
+\r
+static unsigned int rt5625_read_hw_reg(struct snd_soc_codec *codec, unsigned int reg) \r
+{\r
+ u8 data[2] = {0};\r
+ unsigned int value = 0x0;\r
+ \r
+ data[0] = reg;\r
+ \r
+ i2c_master_reg8_recv(codec->control_data,reg,data,2,100 * 1000); \r
+ \r
+ value = (data[0]<<8) | data[1];\r
+\r
+ DBG(KERN_INFO "rt5625_read ok, reg = %x, value = %x\n", reg, value);\r
+\r
+ return value; \r
+}\r
+\r
+\r
+static unsigned int rt5625_read(struct snd_soc_codec *codec, unsigned int reg)\r
+{\r
+ if ((reg == 0x80)\r
+ || (reg == 0x82)\r
+ || (reg == 0x84))\r
+ return (reg == 0x80) ? reg80 : ((reg == 0x82) ? reg82 : reg84);\r
+ \r
+ return rt5625_read_hw_reg(codec, reg);\r
+}\r
+\r
+\r
+static inline void rt5625_write_reg_cache(struct snd_soc_codec *codec,\r
+ unsigned int reg, unsigned int value)\r
+{\r
+ u16 *cache = codec->reg_cache;\r
+ if (reg > 0x7E)\r
+ return;\r
+ cache[reg / 2] = value;\r
+}\r
+\r
+static int rt5625_write(struct snd_soc_codec *codec, unsigned int reg,\r
+ unsigned int value)\r
+{\r
+ u8 data[3];\r
+ unsigned int *regvalue = NULL;\r
+\r
+ data[0] = reg;\r
+ data[1] = (value & 0xff00) >> 8;\r
+ data[2] = value & 0x00ff;\r
+ \r
+ if ((reg == 0x80)\r
+ || (reg == 0x82)\r
+ || (reg == 0x84))\r
+ { \r
+ regvalue = ((reg == 0x80) ? ®80 : ((reg == 0x82) ? ®82 : ®84));\r
+ *regvalue = value;\r
+ DBG("rt5625_write ok, reg = %x, value = %x\n", reg, value);\r
+ return 0;\r
+ }\r
+ rt5625_write_reg_cache(codec, reg, value);\r
+\r
+ if (codec->hw_write(codec->control_data, data, 3) == 3)\r
+ {\r
+ DBG("rt5625_write ok, reg = %x, value = %x\n", reg, value);\r
+ return 0;\r
+ }\r
+ else \r
+ {\r
+ printk("rt5625_write fail\n");\r
+ return -EIO;\r
+ }\r
+}\r
+\r
+int rt5625_write_mask(struct snd_soc_codec *codec, unsigned int reg,unsigned int value,unsigned int mask)\r
+{\r
+ \r
+ unsigned char RetVal=0;\r
+ unsigned int CodecData;\r
+\r
+ DBG("rt5625_write_mask ok, reg = %x, value = %x ,mask= %x\n", reg, value,mask);\r
+\r
+ if(!mask)\r
+ return 0; \r
+\r
+ if(mask!=0xffff)\r
+ {\r
+ CodecData=rt5625_read(codec,reg); \r
+ CodecData&=~mask;\r
+ CodecData|=(value&mask);\r
+ RetVal=rt5625_write(codec,reg,CodecData);\r
+ } \r
+ else\r
+ {\r
+ RetVal=rt5625_write(codec,reg,value);\r
+ }\r
+\r
+ return RetVal;\r
+}\r
+\r
+\r
+void rt5625_write_index(struct snd_soc_codec *codec, unsigned int reg,\r
+ unsigned int value)\r
+{\r
+ \r
+ rt5625_write(codec,0x6a,reg);\r
+ rt5625_write(codec,0x6c,value); \r
+}\r
+\r
+unsigned int rt5625_read_index(struct snd_soc_codec *codec, unsigned int reg)\r
+{\r
+ unsigned int value = 0x0;\r
+ rt5625_write(codec,0x6a,reg);\r
+ value=rt5625_read(codec,0x6c); \r
+ \r
+ return value;\r
+}\r
+\r
+void rt5625_write_index_mask(struct snd_soc_codec *codec, unsigned int reg,unsigned int value,unsigned int mask)\r
+{\r
+ \r
+// unsigned char RetVal=0;\r
+ unsigned int CodecData;\r
+\r
+ DBG("rt5625_write_index_mask ok, reg = %x, value = %x ,mask= %x\n", reg, value,mask);\r
+\r
+ if(!mask)\r
+ return; \r
+\r
+ if(mask!=0xffff)\r
+ {\r
+ CodecData=rt5625_read_index(codec,reg); \r
+ CodecData&=~mask;\r
+ CodecData|=(value&mask);\r
+ rt5625_write_index(codec,reg,CodecData);\r
+ } \r
+ else\r
+ {\r
+ rt5625_write_index(codec,reg,value);\r
+ }\r
+}\r
+\r
+//#define rt5625_write_mask(c, reg, value, mask) snd_soc_update_bits(c, reg, mask, value)\r
+\r
+#define rt5625_reset(c) rt5625_write(c, RT5625_RESET, 0)\r
+\r
+/*read/write dsp reg*/\r
+static int rt5625_wait_vodsp_i2c_done(struct snd_soc_codec *codec)\r
+{\r
+ unsigned int checkcount = 0, vodsp_data;\r
+\r
+ vodsp_data = rt5625_read(codec, RT5625_VODSP_REG_CMD);\r
+ while(vodsp_data & VODSP_BUSY)\r
+ {\r
+ if(checkcount > 10)\r
+ return -EBUSY;\r
+ vodsp_data = rt5625_read(codec, RT5625_VODSP_REG_CMD);\r
+ checkcount ++; \r
+ }\r
+ return 0;\r
+}\r
+\r
+static int rt5625_write_vodsp_reg(struct snd_soc_codec *codec, unsigned int vodspreg, unsigned int value)\r
+{\r
+ int ret = 0;\r
+\r
+ if(ret != rt5625_wait_vodsp_i2c_done(codec))\r
+ return -EBUSY;\r
+\r
+ rt5625_write(codec, RT5625_VODSP_REG_ADDR, vodspreg);\r
+ rt5625_write(codec, RT5625_VODSP_REG_DATA, value);\r
+ rt5625_write(codec, RT5625_VODSP_REG_CMD, VODSP_WRITE_ENABLE | VODSP_CMD_MW);\r
+ mdelay(10);\r
+ return ret;\r
+ \r
+}\r
+\r
+static unsigned int rt5625_read_vodsp_reg(struct snd_soc_codec *codec, unsigned int vodspreg)\r
+{\r
+ int ret = 0;\r
+ unsigned int nDataH, nDataL;\r
+ unsigned int value;\r
+\r
+ if(ret != rt5625_wait_vodsp_i2c_done(codec))\r
+ return -EBUSY;\r
+ \r
+ rt5625_write(codec, RT5625_VODSP_REG_ADDR, vodspreg);\r
+ rt5625_write(codec, RT5625_VODSP_REG_CMD, VODSP_READ_ENABLE | VODSP_CMD_MR);\r
+\r
+ if (ret != rt5625_wait_vodsp_i2c_done(codec))\r
+ return -EBUSY;\r
+ rt5625_write(codec, RT5625_VODSP_REG_ADDR, 0x26);\r
+ rt5625_write(codec, RT5625_VODSP_REG_CMD, VODSP_READ_ENABLE | VODSP_CMD_RR);\r
+\r
+ if(ret != rt5625_wait_vodsp_i2c_done(codec))\r
+ return -EBUSY;\r
+ nDataH = rt5625_read(codec, RT5625_VODSP_REG_DATA);\r
+ rt5625_write(codec, RT5625_VODSP_REG_ADDR, 0x25);\r
+ rt5625_write(codec, RT5625_VODSP_REG_CMD, VODSP_READ_ENABLE | VODSP_CMD_RR);\r
+\r
+ if(ret != rt5625_wait_vodsp_i2c_done(codec))\r
+ return -EBUSY;\r
+ nDataL = rt5625_read(codec, RT5625_VODSP_REG_DATA);\r
+ value = ((nDataH & 0xff) << 8) |(nDataL & 0xff);\r
+ DBG("%s vodspreg=0x%x, value=0x%x\n", __func__, vodspreg, value);\r
+ return value;\r
+}\r
+\r
+static int rt5625_reg_init(struct snd_soc_codec *codec)\r
+{\r
+ int i;\r
+\r
+ for (i = 0; i < RT5625_INIT_REG_NUM; i++)\r
+ rt5625_write(codec, rt5625_init_list[i].reg_index, rt5625_init_list[i].reg_value);\r
+\r
+ return 0;\r
+}\r
+\r
+//*************************************************************************************************\r
+//*************************************************************************************************\r
+#if (RT5625_EQ_FUNC_ENA==1) \r
+//eq function\r
+static void rt5625_update_eqmode(struct snd_soc_codec *codec, int mode)\r
+{\r
+ u16 HwEqIndex=0;\r
+\r
+ if(mode==NORMAL)\r
+ {\r
+ /*clear EQ parameter*/\r
+ for(HwEqIndex=0;HwEqIndex<=0x0C;HwEqIndex++)\r
+ {\r
+ rt5625_write_index(codec, HwEqIndex, HwEq_Preset[mode].EqValue[HwEqIndex]);\r
+ }\r
+ \r
+ 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*/\r
+ }\r
+ else\r
+ { \r
+ /*Fill EQ parameter*/\r
+ for(HwEqIndex=0;HwEqIndex<=0x0C;HwEqIndex++)\r
+ {\r
+ rt5625_write_index(codec, HwEqIndex, HwEq_Preset[mode].EqValue[HwEqIndex]); \r
+ } \r
+\r
+ //enable EQ block\r
+ 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); \r
+ \r
+ //update EQ parameter\r
+ rt5625_write_mask(codec, 0x6e,0x0080,0x0080);\r
+ }\r
+}\r
+\r
+\r
+static int rt5625_eq_sel_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)\r
+{\r
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);\r
+ u16 Virtual_reg = rt5625_read(codec, VIRTUAL_REG_FOR_MISC_FUNC);\r
+ int rt5625_mode=((Virtual_reg)&0xf000)>>12;\r
+ \r
+ if ( rt5625_mode == ucontrol->value.integer.value[0])\r
+ return 0;\r
+\r
+ rt5625_update_eqmode(codec, ucontrol->value.enumerated.item[0]);\r
+\r
+ Virtual_reg &= 0x0fff;\r
+ Virtual_reg |= (ucontrol->value.integer.value[0])<<12;\r
+ rt5625_write(codec, VIRTUAL_REG_FOR_MISC_FUNC, Virtual_reg); \r
+ \r
+ return 0;\r
+}\r
+#endif\r
+//*************************************************************************************************\r
+//*************************************************************************************************\r
+static const char *rt5625_aec_path_sel[] = {"aec func disable","aec func for pcm in/out",\r
+ "aec func for iis in/out","aec func for analog in/out"}; /*0*/ \r
+static const char *rt5625_spk_out_sel[] = {"Class AB", "Class D"}; /*1*/\r
+static const char *rt5625_spk_l_source_sel[] = {"LPRN", "LPRP", "LPLN", "MM"}; /*2*/ \r
+static const char *rt5625_spkmux_source_sel[] = {"VMID", "RK11", \r
+ "RK12", "RK13"}; /*3*/\r
+static const char *rt5625_hplmux_source_sel[] = {"VMID","RK71"}; /*4*/\r
+static const char *rt5625_hprmux_source_sel[] = {"VMID","RK81"}; /*5*/\r
+static const char *rt5625_auxmux_source_sel[] = {"VMID", "RK11", \r
+ "RK12", "RK13"}; /*6*/\r
+static const char *rt5625_spkamp_ratio_sel[] = {"2.25 Vdd", "2.00 Vdd",\r
+ "1.75 Vdd", "1.50 Vdd", "1.25 Vdd", "1.00 Vdd"}; /*7*/\r
+static const char *rt5625_mic1_boost_sel[] = {"Bypass", "+20db", "+30db", "+40db"}; /*8*/\r
+static const char *rt5625_mic2_boost_sel[] = {"Bypass", "+20db", "+30db", "+40db"}; /*9*/\r
+static const char *rt5625_dmic_boost_sel[] = {"Bypass", "+6db", "+12db", "+18db", \r
+ "+24db", "+30db", "+36db", "+42db"}; /*10*/\r
+static const char *rt5625_adcr_func_sel[] = {"Stereo ADC", "Voice ADC", \r
+ "VoDSP Interface", "PDM Slave Interface"}; /*11*/\r
+#if (RT5625_EQ_FUNC_ENA==1) \r
+static const char *rt5625_eq_sel[] = {"NORMAL", "CLUB","DANCE", "LIVE","POP", /*12*/\r
+ "ROCK", "OPPO", "TREBLE", "BASS"}; \r
+#endif\r
+\r
+static const struct soc_enum rt5625_enum[] = {\r
+\r
+SOC_ENUM_SINGLE(VIRTUAL_REG_FOR_MISC_FUNC, 0, 4, rt5625_aec_path_sel), /*0*/\r
+SOC_ENUM_SINGLE(RT5625_OUTPUT_MIXER_CTRL, 13, 2, rt5625_spk_out_sel), /*1*/\r
+SOC_ENUM_SINGLE(RT5625_OUTPUT_MIXER_CTRL, 14, 4, rt5625_spk_l_source_sel), /*2*/\r
+SOC_ENUM_SINGLE(RT5625_OUTPUT_MIXER_CTRL, 10, 4, rt5625_spkmux_source_sel),/*3*/\r
+SOC_ENUM_SINGLE(RT5625_OUTPUT_MIXER_CTRL, 9, 2, rt5625_hplmux_source_sel), /*4*/\r
+SOC_ENUM_SINGLE(RT5625_OUTPUT_MIXER_CTRL, 8, 2, rt5625_hprmux_source_sel),/*5*/\r
+SOC_ENUM_SINGLE(RT5625_OUTPUT_MIXER_CTRL, 6, 4, rt5625_auxmux_source_sel),/*6*/\r
+SOC_ENUM_SINGLE(RT5625_GEN_CTRL_REG1, 1, 6, rt5625_spkamp_ratio_sel), /*7*/\r
+SOC_ENUM_SINGLE(RT5625_MIC_CTRL, 10, 4, rt5625_mic1_boost_sel), /*8*/\r
+SOC_ENUM_SINGLE(RT5625_MIC_CTRL, 8, 4, rt5625_mic2_boost_sel), /*9*/\r
+SOC_ENUM_SINGLE(RT5625_DMIC_CTRL, 0, 8, rt5625_dmic_boost_sel), /*10*/\r
+SOC_ENUM_SINGLE(RT5625_DAC_ADC_VODAC_FUN_SEL, 4, 4, rt5625_adcr_func_sel), /*11*/\r
+#if (RT5625_EQ_FUNC_ENA==1)\r
+SOC_ENUM_SINGLE(VIRTUAL_REG_FOR_MISC_FUNC, 12, 9, rt5625_eq_sel), /*EQ mode select mode 12*/\r
+#endif\r
+};\r
+\r
+\r
+\r
+//*****************************************************************************\r
+//function:Enable the Voice PCM interface Path\r
+//*****************************************************************************\r
+static int ConfigPcmVoicePath(struct snd_soc_codec *codec,unsigned int bEnableVoicePath,unsigned int mode)\r
+{\r
+\r
+ if(bEnableVoicePath)\r
+ {\r
+ //Power on DAC reference\r
+ rt5625_write_mask(codec,RT5625_PWR_MANAG_ADD1,PWR_DAC_REF|PWR_VOICE_DF2SE,PWR_DAC_REF|PWR_VOICE_DF2SE);\r
+ //Power on Voice DAC/ADC \r
+ rt5625_write_mask(codec,RT5625_PWR_MANAG_ADD2,PWR_VOICE_CLOCK,PWR_VOICE_CLOCK);\r
+ //routing voice to HPMixer\r
+ rt5625_write_mask(codec,RT5625_VOICE_DAC_OUT_VOL,0,M_V_DAC_TO_HP_MIXER);\r
+ \r
+ switch(mode) \r
+ {\r
+ case PCM_SLAVE_MODE_B: //8kHz sampling rate,16 bits PCM mode and Slave mode,PCM mode is B,MCLK=24.576MHz from Oscillator.\r
+ //CSR PSKEY_PCM_CONFIG32 (HEX) = 0x08C00000,PSKEY_FORMAT=0x0060 \r
+\r
+ //Enable GPIO 1,3,4,5 to voice interface\r
+ //Set I2S to Slave mode\r
+ //Voice I2S SYSCLK Source select Main SYSCLK\r
+ //Set voice I2S VBCLK Polarity to Invert\r
+ //Set Data length to 16 bit\r
+ //set Data Fomrat to PCM mode B\r
+ //the register 0x36 value's should is 0xC083\r
+ rt5625_write(codec,RT5625_EXTEND_SDP_CTRL,0xC083);\r
+\r
+ //Set LRCK voice select divide 32\r
+ //set voice blck select divide 6 and 8 \r
+ //voice filter clock divide 3 and 16\r
+ //the register 0x64 value's should is 0x5524\r
+ rt5625_write(codec,RT5625_VOICE_DAC_PCMCLK_CTRL1,0x5524);\r
+ \r
+ break;\r
+\r
+ case PCM_SLAVE_MODE_A: //8kHz sampling rate,16 bits PCM and Slave mode,PCM mode is A,MCLK=24.576MHz from Oscillator.\r
+ //CSR PSKEY_PCM_CONFIG32 (HEX) = 0x08C00004,PSKEY_FORMAT=0x0060 \r
+\r
+ //Enable GPIO 1,3,4,5 to voice interface\r
+ //Set I2S to Slave mode\r
+ //Voice I2S SYSCLK Source select Main SYSCLK\r
+ //Set voice i2s VBCLK Polarity to Invert\r
+ //Set Data length to 16 bit\r
+ //set Data Fomrat to PCM mode A\r
+ //the register 0x36 value's should is 0xC082\r
+ rt5625_write(codec,RT5625_EXTEND_SDP_CTRL,0xC082);\r
+\r
+ //Set LRCK voice select divide 64\r
+ //set voice blck select divide 6 and 8 \r
+ //voice filter clock divide 3 and 16\r
+ //the register 0x64 value's should is 0x5524\r
+ rt5625_write(codec,RT5625_VOICE_DAC_PCMCLK_CTRL1,0x5524);\r
+ \r
+ break;\r
+\r
+ case PCM_MASTER_MODE_B: //8kHz sampling rate,16 bits PCM and Master mode,PCM mode is B,Clock from PLL OUT\r
+ //CSR PSKEY_PCM_CONFIG32 (HEX) = 0x08000002,PSKEY_FORMAT=0x0060 \r
+\r
+ //Enable GPIO 1,3,4,5 to voice interface\r
+ //Set I2S to master mode\r
+ //Set voice i2s VBCLK Polarity to Invert\r
+ //Set Data length to 16 bit\r
+ //set Data Fomrat to PCM mode B\r
+ //the register 0x36 value's should is 0x8083\r
+ rt5625_write(codec,RT5625_EXTEND_SDP_CTRL,0x8083);\r
+\r
+ //Set LRCK voice select divide 64\r
+ //set voice blck select divide 6 and 8 \r
+ //voice filter clock divide 3 and 16\r
+ //the register 0x64 value's should is 0x5524\r
+ rt5625_write(codec,RT5625_VOICE_DAC_PCMCLK_CTRL1,0x5524);\r
+ \r
+ break;\r
+\r
+ default:\r
+ //do nothing \r
+ break;\r
+\r
+ }\r
+ }\r
+ else\r
+ { \r
+ //Power down Voice Different to sing-end power\r
+ rt5625_write_mask(codec,RT5625_PWR_MANAG_ADD1,0,PWR_VOICE_DF2SE);\r
+ //Power down Voice DAC/ADC \r
+ rt5625_write_mask(codec,RT5625_PWR_MANAG_ADD2,0,PWR_VOICE_CLOCK);\r
+ //Disable Voice PCM interface \r
+ rt5625_write_mask(codec,RT5625_EXTEND_SDP_CTRL,0,EXT_I2S_FUNC_ENABLE); \r
+ }\r
+ \r
+ return 0;\r
+}\r
+\r
+static int init_vodsp_aec(struct snd_soc_codec *codec)\r
+{\r
+ int i;\r
+ int ret = 0;\r
+\r
+ /*disable LDO power*/\r
+ rt5625_write_mask(codec, RT5625_LDO_CTRL,0,LDO_ENABLE);\r
+ mdelay(20); \r
+ rt5625_write_mask(codec, RT5625_VODSP_CTL,VODSP_NO_PD_MODE_ENA,VODSP_NO_PD_MODE_ENA);\r
+ /*enable LDO power and set output voltage to 1.2V*/\r
+ rt5625_write_mask(codec, RT5625_LDO_CTRL,LDO_ENABLE|LDO_OUT_VOL_CTRL_1_20V,LDO_ENABLE|LDO_OUT_VOL_CTRL_MASK);\r
+ mdelay(20);\r
+ /*enable power of VODSP I2C interface*/ \r
+ rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD3,PWR_VODSP_INTERFACE|PWR_I2C_FOR_VODSP,PWR_VODSP_INTERFACE|PWR_I2C_FOR_VODSP);\r
+ mdelay(1);\r
+ rt5625_write_mask(codec, RT5625_VODSP_CTL,0,VODSP_NO_RST_MODE_ENA); /*Reset VODSP*/\r
+ mdelay(1);\r
+ rt5625_write_mask(codec, RT5625_VODSP_CTL,VODSP_NO_RST_MODE_ENA,VODSP_NO_RST_MODE_ENA); /*set VODSP to non-reset status*/ \r
+ mdelay(20);\r
+\r
+ /*initize AEC paramter*/\r
+ for(i = 0; i < SET_VODSP_REG_INIT_NUM; i++)\r
+ {\r
+ ret = rt5625_write_vodsp_reg(codec, VODSP_AEC_Init_Value[i].VoiceDSPIndex,VODSP_AEC_Init_Value[i].VoiceDSPValue);\r
+ if(ret)\r
+ return -EIO;\r
+ } \r
+\r
+ schedule_timeout_uninterruptible(msecs_to_jiffies(10)); \r
+\r
+ return 0;\r
+}\r
+\r
+//***********************************************************************************************\r
+//function:Enable/Disable the vodsp interface Path\r
+//For system clock only suport specific clock,realtek suggest customer to use 24.576Mhz or 22.5792Mhz\r
+//clock fro MCLK(MCLK=48k*512 or 44.1k*512Mhz)\r
+//***********************************************************************************************\r
+static int set_vodsp_aec_path(struct snd_soc_codec *codec, unsigned int mode)\r
+{\r
+\r
+ switch(mode)\r
+ {\r
+\r
+ case PCM_IN_PCM_OUT:\r
+ //set PCM format\r
+ ConfigPcmVoicePath(codec,1,PCM_MASTER_MODE_B);\r
+ //set AEC path\r
+ rt5625_write_mask(codec, 0x26,0x0300,0x0300);\r
+ rt5625_write_mask(codec, RT5625_VODSP_PDM_CTL,VODSP_RXDP_PWR|VODSP_RXDP_S_SEL_VOICE|VOICE_PCM_S_SEL_AEC_TXDP\r
+ ,VODSP_RXDP_PWR|VODSP_RXDP_S_SEL_MASK|VOICE_PCM_S_SEL_MASK);\r
+ rt5625_write_mask(codec, RT5625_DAC_ADC_VODAC_FUN_SEL,ADCR_FUNC_SEL_PDM|VODAC_SOUR_SEL_VODSP_TXDC\r
+ ,ADCR_FUNC_SEL_MASK|VODAC_SOUR_SEL_MASK);\r
+ rt5625_write_mask(codec, RT5625_VODSP_CTL,VODSP_LRCK_SEL_8K,VODSP_LRCK_SEL_MASK); \r
+ rt5625_write_mask(codec, 0x26,0x0000,0x0300);\r
+ //set input&output path and power\r
+ rt5625_write_mask(codec, 0x3a,0x0c8f,0x0c8f);//power on related bit\r
+ rt5625_write_mask(codec, 0x3c,0xa4cb,0xa4cb);//power on related bit\r
+ rt5625_write_mask(codec, 0x3e,0x3302,0xf302);//power on related bit\r
+ \r
+ rt5625_write(codec, 0x10, 0xee0f);//mute DAC to hpmixer\r
+ rt5625_write(codec, 0x0e, 0x8808);//set Mic1 to differential mode\r
+ rt5625_write(codec, 0x22, 0x0000);//Mic boost 0db\r
+ rt5625_write(codec, 0x12, 0xCBD3);//ADC_Mixer_R boost 10.5 db\r
+ rt5625_write(codec, 0x14, 0x7f3f);//Mic1->ADCMixer_R\r
+ rt5625_write(codec, 0x18, 0xa010);//VoDAC to speakerMixer,0db\r
+ rt5625_write(codec, 0x1c, 0x8808);//speaker source from speakermixer\r
+ \r
+ rt5625_write_mask(codec, 0x02,0x0000,0x8080); //unmute speaker \r
+\r
+ break;\r
+ \r
+ \r
+ case ANALOG_IN_ANALOG_OUT: \r
+ rt5625_write_mask(codec, 0x26,0x0300,0x0300);\r
+ rt5625_write_mask(codec, RT5625_VODSP_PDM_CTL,VODSP_RXDP_PWR|VODSP_RXDP_S_SEL_ADCL|VOICE_PCM_S_SEL_AEC_TXDP\r
+ ,VODSP_RXDP_PWR|VODSP_RXDP_S_SEL_MASK|VOICE_PCM_S_SEL_MASK);\r
+ 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\r
+ ,ADCR_FUNC_SEL_MASK|VODAC_SOUR_SEL_MASK|DAC_FUNC_SEL_MASK|ADCL_FUNC_SEL_MASK);\r
+ rt5625_write_mask(codec, RT5625_VODSP_CTL,VODSP_LRCK_SEL_16K,VODSP_LRCK_SEL_MASK); \r
+ rt5625_write_mask(codec, 0x26,0x0000,0x0300);\r
+ //set input&output path and power\r
+ rt5625_write_mask(codec, 0x3a,0xcc8f,0xcc8f);//power on related bit\r
+ rt5625_write_mask(codec, 0x3c,0xa7cf,0xa7cf);//power on related bit\r
+ rt5625_write_mask(codec, 0x3e,0xf312,0xf312);//power on related bit\r
+ \r
+ rt5625_write(codec, 0x0e, 0x8808);//set Mic1 to differential mode\r
+ rt5625_write(codec, 0x08, 0xe800);//set phone in to differential mode\r
+ rt5625_write(codec, 0x22, 0x0000);//Mic boost 0db\r
+ rt5625_write(codec, 0x14, 0x773f);//Mic1->ADCMixer_R,phone in-->ADCMixer_L\r
+ rt5625_write(codec, 0x12, 0xCBD3);//ADC_Mixer_R boost 10.5 db\r
+ rt5625_write(codec, 0x1c, 0x88c8);//speaker from spkmixer,monoOut from monoMixer\r
+ rt5625_write(codec, 0x18, 0xA010);//unmute VoDAC to spkmixer\r
+ rt5625_write(codec, 0x10, 0xee0e);//unmute DAC to monoMixer \r
+ rt5625_write(codec, 0x62, 0x2222);\r
+ rt5625_write(codec, 0x64, 0x3122);\r
+ rt5625_write_mask(codec, 0x02,0x0000,0x8080); //unmute speaker \r
+ rt5625_write_mask(codec, 0x06,0x0000,0x8080); //unmute auxout \r
+ break;\r
+\r
+ case DAC_IN_ADC_OUT: \r
+ rt5625_write_mask(codec, 0x26,0x0300,0x0300);\r
+ rt5625_write_mask(codec,RT5625_DAC_ADC_VODAC_FUN_SEL,ADCR_FUNC_SEL_PDM|DAC_FUNC_SEL_VODSP_TXDC\r
+ ,ADCR_FUNC_SEL_MASK|DAC_FUNC_SEL_MASK);\r
+ 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,\r
+ VODSP_SRC1_PWR|VODSP_SRC2_PWR|VODSP_RXDP_PWR|VODSP_RXDP_S_SEL_MASK|REC_S_SEL_MASK); \r
+ rt5625_write_mask(codec,RT5625_VODSP_CTL,VODSP_LRCK_SEL_16K,VODSP_LRCK_SEL_MASK);\r
+ rt5625_write_mask(codec, 0x26,0x0000,0x0300);\r
+ //set input&output path and power \r
+ rt5625_write_mask(codec, 0x3a,0xcc0f,0xcc0f);//power on related bit\r
+ rt5625_write_mask(codec, 0x3c,0xa7cb,0xa7cb);//power on related bit\r
+ rt5625_write_mask(codec, 0x3e,0x3302,0x3302);//power on related bit\r
+ \r
+ rt5625_write(codec, 0x0e, 0x8808);//set Mic1 to differential mode\r
+ rt5625_write(codec, 0x22, 0x0000);//Mic boost 0db\r
+ rt5625_write(codec, 0x14, 0x7f3f);//Mic1->ADCMixer_R\r
+ rt5625_write(codec, 0x12, 0xCBD3);//ADC_Mixer_R boost 10.5 db\r
+ rt5625_write(codec, 0x1c, 0x8808);//speaker out from spkMixer\r
+ rt5625_write(codec, 0x10, 0xee0d);//unmute DAC to spkMixer \r
+ rt5625_write(codec, 0x60, 0x3075);\r
+ rt5625_write(codec, 0x62, 0x1010); \r
+ rt5625_write_mask(codec, 0x02,0x0000,0x8080); //unmute speaker \r
+\r
+ break;\r
+\r
+ case VODSP_AEC_DISABLE:\r
+ default:\r
+ rt5625_write_mask(codec, 0x02,0x8080,0x8080);//mute speaker out\r
+ rt5625_write_mask(codec, 0x06,0x8080,0x8080);//mute auxout \r
+ rt5625_write(codec, 0x22, 0x0500);//Mic boost 20db by default\r
+ rt5625_write(codec, 0x14, 0x3f3f);//record from Mic1 by default\r
+ rt5625_write(codec, 0x12, 0xD5D5);//ADC_Mixer_R boost 15 db by default\r
+ rt5625_write(codec, 0x1c, 0x0748);//all output from HPmixer by default\r
+ rt5625_write(codec, 0x10, 0xee03);//DAC to HPmixer by default\r
+ rt5625_write(codec, 0x18, 0xe010);//mute VoDAC to mixer by default\r
+ rt5625_write_mask(codec, 0x26,0x0300,0x0300);\r
+ /*set stereo DAC&Voice DAC&Stereo ADC function select to default*/ \r
+ rt5625_write(codec, RT5625_DAC_ADC_VODAC_FUN_SEL,0); \r
+ /*set VODSP&PDM Control to default*/ \r
+ rt5625_write(codec, RT5625_VODSP_PDM_CTL,0);\r
+ rt5625_write_mask(codec, 0x26,0x0000,0x0300); \r
+ rt5625_write_mask(codec, 0x3e,0x0000,0xf312);//power down related bit\r
+ rt5625_write_mask(codec, 0x3a,0x0000,0xcc8d);//power down related bit\r
+ rt5625_write_mask(codec, 0x3c,0x0000,0x07cf);//power down related bit\r
+ \r
+ \r
+ break;\r
+ } \r
+\r
+ return 0;\r
+}\r
+\r
+static int enable_vodsp_aec(struct snd_soc_codec *codec, unsigned int VodspAEC_En, unsigned int AEC_mode)\r
+{\r
+ int ret = 0;\r
+\r
+ \r
+ if (VodspAEC_En != 0)\r
+ { \r
+\r
+ //enable power of VODSP I2C interface & VODSP interface\r
+ rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD3,PWR_VODSP_INTERFACE|PWR_I2C_FOR_VODSP,PWR_VODSP_INTERFACE|PWR_I2C_FOR_VODSP);\r
+ //enable power of VODSP I2S interface \r
+ rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD1,PWR_I2S_INTERFACE,PWR_I2S_INTERFACE); \r
+ //select input/output of VODSP AEC\r
+ set_vodsp_aec_path(codec, AEC_mode); \r
+\r
+ }\r
+ else\r
+ {\r
+ //disable VODSP AEC path\r
+ set_vodsp_aec_path(codec, VODSP_AEC_DISABLE);\r
+ //set VODSP AEC to power down mode \r
+ rt5625_write_mask(codec, RT5625_VODSP_CTL,0,VODSP_NO_PD_MODE_ENA);\r
+ //disable power of VODSP I2C interface & VODSP interface\r
+ rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD3,0,PWR_VODSP_INTERFACE|PWR_I2C_FOR_VODSP); \r
+ }\r
+\r
+ return ret;\r
+}\r
+\r
+static void rt5625_aec_config(struct snd_soc_codec *codec, unsigned int mode)\r
+{\r
+ DBG("rt5625_aec_config %d\n",mode);\r
+\r
+ if (mode == VODSP_AEC_DISABLE)\r
+ {\r
+ enable_vodsp_aec(codec,0, mode); \r
+ /*disable LDO power*/\r
+ rt5625_write_mask(codec, RT5625_LDO_CTRL,0,LDO_ENABLE);\r
+ }\r
+ else\r
+ {\r
+ init_vodsp_aec(codec);\r
+ \r
+ enable_vodsp_aec(codec,1, mode); \r
+ }\r
+}\r
+//****************************************************************************************************************\r
+//*\r
+//*function:disable rt5625's function.\r
+//*\r
+//*\r
+//****************************************************************************************************************\r
+static int rt5625_func_aec_disable(struct snd_soc_codec *codec,int mode)\r
+{\r
+\r
+ switch(mode)\r
+ {\r
+ case RT5625_AEC_PCM_IN_OUT:\r
+ case RT5625_AEC_IIS_IN_OUT:\r
+ case RT5625_AEC_ANALOG_IN_OUT:\r
+ \r
+ rt5625_aec_config(codec,VODSP_AEC_DISABLE); //disable AEC function and path\r
+ \r
+ break;\r
+ \r
+ default:\r
+\r
+ break;\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+static int rt5625_get_dsp_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)\r
+{\r
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);\r
+ /*cause we choose bit[0][1] to store the mode type*/\r
+ int mode = (rt5625_read(codec, VIRTUAL_REG_FOR_MISC_FUNC)) & 0x03; \r
+\r
+ ucontrol->value.integer.value[0] = mode;\r
+ return 0;\r
+}\r
+\r
+\r
+static int rt5625_set_dsp_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)\r
+{\r
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);\r
+ u16 Virtual_reg = rt5625_read(codec, VIRTUAL_REG_FOR_MISC_FUNC);\r
+ int rt5625_mode=(Virtual_reg)&0x03;\r
+\r
+ DBG("rt5625_mode=%d,value[0]=%ld,Virtual_reg=%x\n",rt5625_mode,ucontrol->value.integer.value[0],Virtual_reg);\r
+\r
+ if ( rt5625_mode == ucontrol->value.integer.value[0])\r
+ return 0;\r
+\r
+ switch(ucontrol->value.integer.value[0])\r
+ {\r
+ case RT5625_AEC_PCM_IN_OUT:\r
+\r
+ rt5625_aec_config(codec,PCM_IN_PCM_OUT);//enable AEC PCM in/out function and path\r
+\r
+ break;\r
+\r
+ case RT5625_AEC_IIS_IN_OUT:\r
+\r
+ rt5625_aec_config(codec,DAC_IN_ADC_OUT);//enable AEC IIS in/out function and path\r
+\r
+ break;\r
+\r
+ case RT5625_AEC_ANALOG_IN_OUT:\r
+ \r
+ rt5625_aec_config(codec,ANALOG_IN_ANALOG_OUT);//enable AEC analog in/out function and path\r
+ \r
+ break;\r
+\r
+ case RT5625_AEC_DISABLE:\r
+ \r
+ rt5625_func_aec_disable(codec,rt5625_mode); //disable previous select function. \r
+ \r
+ break; \r
+\r
+ default:\r
+\r
+ break;\r
+ }\r
+\r
+ Virtual_reg &= 0xfffc;\r
+ Virtual_reg |= (ucontrol->value.integer.value[0]);\r
+ rt5625_write(codec, VIRTUAL_REG_FOR_MISC_FUNC, Virtual_reg);\r
+\r
+ DBG("2rt5625_mode=%d,value[0]=%ld,Virtual_reg=%x\n",rt5625_mode,ucontrol->value.integer.value[0],Virtual_reg);\r
+ return 0;\r
+}\r
+\r
+static int rt5625_dump_dsp_reg(struct snd_soc_codec *codec)\r
+{\r
+ int i;\r
+\r
+ rt5625_write_mask(codec, RT5625_VODSP_CTL, VODSP_NO_PD_MODE_ENA,VODSP_NO_PD_MODE_ENA);\r
+ for (i = 0; i < SET_VODSP_REG_INIT_NUM; i++) {\r
+ rt5625_read_vodsp_reg(codec, VODSP_AEC_Init_Value[i].VoiceDSPIndex);\r
+ }\r
+ return 0;\r
+}\r
+\r
+\r
+static int rt5625_dump_dsp_put(struct snd_kcontrol *kcontrol, \r
+ struct snd_ctl_elem_value *ucontrol)\r
+{\r
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);\r
+ int mode = rt5625_read(codec, VIRTUAL_REG_FOR_MISC_FUNC);\r
+\r
+ mode &= ~(0x01 << 8);\r
+ mode |= (ucontrol->value.integer.value[0] << 8);\r
+ rt5625_write(codec, VIRTUAL_REG_FOR_MISC_FUNC, mode);\r
+ rt5625_dump_dsp_reg(codec);\r
+ \r
+ return 0;\r
+}\r
+\r
+static const struct snd_kcontrol_new rt5625_snd_controls[] = {\r
+SOC_ENUM_EXT("rt5625 aec mode sel", rt5625_enum[0], rt5625_get_dsp_mode, rt5625_set_dsp_mode),\r
+SOC_ENUM("SPK Amp Type", rt5625_enum[1]),\r
+SOC_ENUM("Left SPK Source", rt5625_enum[2]),\r
+SOC_ENUM("SPK Amp Ratio", rt5625_enum[7]),\r
+SOC_ENUM("Mic1 Boost", rt5625_enum[8]),\r
+SOC_ENUM("Mic2 Boost", rt5625_enum[9]),\r
+SOC_ENUM("Dmic Boost", rt5625_enum[10]),\r
+SOC_ENUM("ADCR Func", rt5625_enum[11]),\r
+SOC_DOUBLE("RK4", RT5625_STEREO_DAC_VOL, 8, 0, 63, 1),\r
+SOC_DOUBLE("LineIn Playback Volume", RT5625_LINE_IN_VOL, 8, 0, 31, 1),\r
+SOC_SINGLE("Phone Playback Volume", RT5625_PHONEIN_VOL, 8, 31, 1),\r
+SOC_SINGLE("Mic1 Playback Volume", RT5625_MIC_VOL, 8, 31, 1),\r
+SOC_SINGLE("Mic2 Playback Volume", RT5625_MIC_VOL, 0, 31, 1),\r
+SOC_DOUBLE("PCM Capture Volume", RT5625_ADC_REC_GAIN, 8, 0, 31, 1),\r
+SOC_DOUBLE("RK3", RT5625_SPK_OUT_VOL, 8, 0, 31, 1),\r
+SOC_DOUBLE("RK2", RT5625_SPK_OUT_VOL, 15, 7, 1, 1),\r
+SOC_DOUBLE("RKA", RT5625_HP_OUT_VOL, 8, 0, 31, 1),\r
+SOC_DOUBLE("RK9", RT5625_HP_OUT_VOL, 15, 7, 1, 1),\r
+SOC_DOUBLE("RKD", RT5625_AUX_OUT_VOL, 8, 0, 31, 1),\r
+SOC_DOUBLE("RKC", RT5625_AUX_OUT_VOL, 15, 7, 1, 1),\r
+SOC_DOUBLE("ADC Record Gain", RT5625_ADC_REC_GAIN, 8, 0, 31, 0),\r
+SOC_SINGLE_EXT("VoDSP Dump", VIRTUAL_REG_FOR_MISC_FUNC, 8, 1, 0,\r
+ snd_soc_get_volsw, rt5625_dump_dsp_put),\r
+#if (RT5625_EQ_FUNC_ENA==1) \r
+SOC_ENUM_EXT("EQ Mode", rt5625_enum[12], snd_soc_get_enum_double, rt5625_eq_sel_put), \r
+#endif\r
+};\r
+\r
+static int rt5625_add_controls(struct snd_soc_codec *codec)\r
+{\r
+ int err, i;\r
+\r
+ for (i = 0; i < ARRAY_SIZE(rt5625_snd_controls); i++){\r
+ err = snd_ctl_add(codec->card, \r
+ snd_soc_cnew(&rt5625_snd_controls[i],\r
+ codec, NULL));\r
+ if (err < 0)\r
+ return err;\r
+ }\r
+ return 0;\r
+}\r
+\r
+static void hp_depop_mode2(struct snd_soc_codec *codec)\r
+{\r
+ rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD1, PWR_SOFTGEN_EN, PWR_SOFTGEN_EN);\r
+ rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD3, PWR_HP_R_OUT_VOL|PWR_HP_L_OUT_VOL,\r
+ PWR_HP_R_OUT_VOL|PWR_HP_L_OUT_VOL);\r
+ rt5625_write(codec, RT5625_MISC_CTRL,HP_DEPOP_MODE2_EN);\r
+\r
+ DBG("delay 500 msec\n");\r
+\r
+ schedule_timeout_uninterruptible(msecs_to_jiffies(500));\r
+ \r
+\r
+ rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD1, PWR_HP_OUT_AMP|PWR_HP_OUT_ENH_AMP,\r
+ PWR_HP_OUT_AMP|PWR_HP_OUT_ENH_AMP);\r
+ //rt5625_write_mask(codec, RT5625_MISC_CTRL, 0, HP_DEPOP_MODE2_EN);\r
+\r
+}\r
+\r
+//enable depop function for mute/unmute\r
+static void hp_mute_unmute_depop(struct snd_soc_codec *codec,int mute)\r
+{\r
+ if(mute)\r
+ {\r
+ rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD1, PWR_SOFTGEN_EN, PWR_SOFTGEN_EN);\r
+ rt5625_write(codec, RT5625_MISC_CTRL,M_UM_DEPOP_EN|HP_R_M_UM_DEPOP_EN|HP_L_M_UM_DEPOP_EN);\r
+ //Mute headphone right/left channel\r
+ rt5625_write_mask(codec,RT5625_HP_OUT_VOL,RT_L_MUTE|RT_R_MUTE,RT_L_MUTE|RT_R_MUTE); \r
+ mdelay(50);\r
+ }\r
+ else\r
+ {\r
+ rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD1, PWR_SOFTGEN_EN, PWR_SOFTGEN_EN);\r
+ rt5625_write(codec, RT5625_MISC_CTRL, M_UM_DEPOP_EN|HP_R_M_UM_DEPOP_EN|HP_L_M_UM_DEPOP_EN);\r
+ //unMute headphone right/left channel\r
+ rt5625_write_mask(codec,RT5625_HP_OUT_VOL,0,RT_L_MUTE|RT_R_MUTE); \r
+ mdelay(50);\r
+ }\r
+\r
+}\r
+\r
+\r
+/*\r
+ * _DAPM_ Controls\r
+ */\r
+ /*Left ADC Rec mixer*/\r
+ /*Left ADC Rec mixer*/\r
+static const struct snd_kcontrol_new rt5625_left_adc_rec_mixer_controls[] = {\r
+SOC_DAPM_SINGLE("RKK", RT5625_ADC_REC_MIXER, 14, 1, 1),\r
+SOC_DAPM_SINGLE("Mic2 Capture Switch", RT5625_ADC_REC_MIXER, 13, 1, 1),\r
+SOC_DAPM_SINGLE("LineIn Capture Switch", RT5625_ADC_REC_MIXER, 12, 1, 1),\r
+SOC_DAPM_SINGLE("RKL", RT5625_ADC_REC_MIXER, 11, 1, 1),\r
+SOC_DAPM_SINGLE("HP Mixer Capture Switch", RT5625_ADC_REC_MIXER, 10, 1, 1),\r
+SOC_DAPM_SINGLE("SPK Mixer Capture Switch", RT5625_ADC_REC_MIXER, 9, 1, 1),\r
+SOC_DAPM_SINGLE("MoNo Mixer Capture Switch", RT5625_ADC_REC_MIXER, 8, 1, 1),\r
+\r
+};\r
+\r
+/*Left ADC Rec mixer*/\r
+static const struct snd_kcontrol_new rt5625_right_adc_rec_mixer_controls[] = {\r
+SOC_DAPM_SINGLE("RKK", RT5625_ADC_REC_MIXER, 6, 1, 1),\r
+SOC_DAPM_SINGLE("Mic2 Capture Switch", RT5625_ADC_REC_MIXER, 5, 1, 1),\r
+SOC_DAPM_SINGLE("LineIn Capture Switch", RT5625_ADC_REC_MIXER, 4, 1, 1),\r
+SOC_DAPM_SINGLE("RKL", RT5625_ADC_REC_MIXER, 3, 1, 1),\r
+SOC_DAPM_SINGLE("HP Mixer Capture Switch", RT5625_ADC_REC_MIXER, 2, 1, 1),\r
+SOC_DAPM_SINGLE("SPK Mixer Capture Switch", RT5625_ADC_REC_MIXER, 1, 1, 1),\r
+SOC_DAPM_SINGLE("MoNo Mixer Capture Switch", RT5625_ADC_REC_MIXER, 0, 1, 1),\r
+};\r
+\r
+/*Left hpmixer mixer*/\r
+static const struct snd_kcontrol_new rt5625_left_hp_mixer_controls[] = {\r
+SOC_DAPM_SINGLE("ADC Playback Switch", RT5625_ADC_REC_GAIN, 15, 1, 1),\r
+SOC_DAPM_SINGLE("LineIn Playback Switch", HPL_MIXER, 0, 1, 0),\r
+SOC_DAPM_SINGLE("RKJ", HPL_MIXER, 1, 1, 0),\r
+SOC_DAPM_SINGLE("RKI", HPL_MIXER, 2, 1, 0),\r
+SOC_DAPM_SINGLE("Mic2 Playback Switch", HPL_MIXER, 3, 1, 0),\r
+SOC_DAPM_SINGLE("RKM", HPL_MIXER, 4, 1, 0),\r
+SOC_DAPM_SINGLE("RKH", RT5625_DAC_AND_MIC_CTRL, 3, 1, 1),\r
+\r
+};\r
+\r
+/*Right hpmixer mixer*/\r
+static const struct snd_kcontrol_new rt5625_right_hp_mixer_controls[] = {\r
+SOC_DAPM_SINGLE("ADC Playback Switch", RT5625_ADC_REC_GAIN, 7, 1, 1),\r
+SOC_DAPM_SINGLE("LineIn Playback Switch", HPR_MIXER, 0, 1, 0),\r
+SOC_DAPM_SINGLE("RKJ", HPR_MIXER, 1, 1, 0),\r
+SOC_DAPM_SINGLE("RKI", HPR_MIXER, 2, 1, 0),\r
+SOC_DAPM_SINGLE("Mic2 Playback Switch", HPR_MIXER, 3, 1, 0),\r
+SOC_DAPM_SINGLE("RKM", HPR_MIXER, 4, 1, 0),\r
+SOC_DAPM_SINGLE("RKH", RT5625_DAC_AND_MIC_CTRL, 2, 1, 1),\r
+\r
+};\r
+\r
+/*mono mixer*/\r
+static const struct snd_kcontrol_new rt5625_mono_mixer_controls[] = {\r
+SOC_DAPM_SINGLE("ADCL Playback Switch", RT5625_ADC_REC_GAIN, 14, 1, 1),\r
+SOC_DAPM_SINGLE("ADCR Playback Switch", RT5625_ADC_REC_GAIN, 6, 1, 1),\r
+SOC_DAPM_SINGLE("Line Mixer Playback Switch", RT5625_LINE_IN_VOL, 13, 1, 1),\r
+SOC_DAPM_SINGLE("RKI", RT5625_DAC_AND_MIC_CTRL, 13, 1, 1),\r
+SOC_DAPM_SINGLE("Mic2 Playback Switch", RT5625_DAC_AND_MIC_CTRL, 9, 1, 1),\r
+SOC_DAPM_SINGLE("RKG", RT5625_DAC_AND_MIC_CTRL, 0, 1, 1),\r
+SOC_DAPM_SINGLE("RKM", RT5625_VOICE_DAC_OUT_VOL, 13, 1, 1),\r
+};\r
+\r
+/*speaker mixer*/\r
+static const struct snd_kcontrol_new rt5625_spk_mixer_controls[] = {\r
+SOC_DAPM_SINGLE("Line Mixer Playback Switch", RT5625_LINE_IN_VOL, 14, 1, 1), \r
+SOC_DAPM_SINGLE("RKJ", RT5625_PHONEIN_VOL, 14, 1, 1),\r
+SOC_DAPM_SINGLE("RKI", RT5625_DAC_AND_MIC_CTRL, 14, 1, 1),\r
+SOC_DAPM_SINGLE("Mic2 Playback Switch", RT5625_DAC_AND_MIC_CTRL, 10, 1, 1),\r
+SOC_DAPM_SINGLE("RKG", RT5625_DAC_AND_MIC_CTRL, 1, 1, 1),\r
+SOC_DAPM_SINGLE("RKM", RT5625_VOICE_DAC_OUT_VOL, 14, 1, 1),\r
+};\r
+\r
+static int mixer_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event)\r
+{\r
+ struct snd_soc_codec *codec = w->codec;\r
+ unsigned int l, r;\r
+\r
+ DBG("enter %s\n", __func__);\r
+\r
+ l= rt5625_read(codec, HPL_MIXER);\r
+ r = rt5625_read(codec, HPR_MIXER);\r
+ \r
+ if ((l & 0x1) || (r & 0x1))\r
+ rt5625_write_mask(codec, 0x0a, 0x0000, 0x8000);\r
+ else\r
+ rt5625_write_mask(codec, 0x0a, 0x8000, 0x8000);\r
+\r
+ if ((l & 0x2) || (r & 0x2))\r
+ rt5625_write_mask(codec, 0x08, 0x0000, 0x8000);\r
+ else\r
+ rt5625_write_mask(codec, 0x08, 0x8000, 0x8000);\r
+\r
+ if ((l & 0x4) || (r & 0x4))\r
+ rt5625_write_mask(codec, 0x10, 0x0000, 0x8000);\r
+ else\r
+ rt5625_write_mask(codec, 0x10, 0x8000, 0x8000);\r
+\r
+ if ((l & 0x8) || (r & 0x8))\r
+ rt5625_write_mask(codec, 0x10, 0x0000, 0x0800);\r
+ else\r
+ rt5625_write_mask(codec, 0x10, 0x0800, 0x0800);\r
+\r
+ if ((l & 0x10) || (r & 0x10))\r
+ rt5625_write_mask(codec, 0x18, 0x0000, 0x8000);\r
+ else\r
+ rt5625_write_mask(codec, 0x18, 0x8000, 0x8000);\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+/*\r
+ * bit[0][1] use for aec control\r
+ * bit[2][3] for ADCR func\r
+ * bit[4] for SPKL pga\r
+ * bit[5] for SPKR pga\r
+ * bit[6] for hpl pga\r
+ * bit[7] for hpr pga\r
+ */\r
+static int spk_pga_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event)\r
+ {\r
+ struct snd_soc_codec *codec = w->codec;\r
+ int reg;\r
+ \r
+ DBG("enter %s\n", __func__);\r
+ reg = rt5625_read(codec, VIRTUAL_REG_FOR_MISC_FUNC) & (0x3 << 4);\r
+ if ((reg >> 4) != 0x3 && reg != 0)\r
+ return 0;\r
+\r
+ switch (event)\r
+ {\r
+ case SND_SOC_DAPM_POST_PMU:\r
+ DBG("after virtual spk power up!\n");\r
+ rt5625_write_mask(codec, 0x3e, 0x3000, 0x3000);\r
+ rt5625_write_mask(codec, 0x02, 0x0000, 0x8080);\r
+ rt5625_write_mask(codec, 0x3a, 0x0400, 0x0400);//power on spk amp\r
+ break;\r
+ case SND_SOC_DAPM_POST_PMD:\r
+ DBG("aftet virtual spk power down!\n");\r
+ rt5625_write_mask(codec, 0x3a, 0x0000, 0x0400);//power off spk amp\r
+ rt5625_write_mask(codec, 0x02, 0x8080, 0x8080);\r
+ rt5625_write_mask(codec, 0x3e, 0x0000, 0x3000); \r
+ break;\r
+ default:\r
+ return 0;\r
+ }\r
+ return 0;\r
+}\r
+\r
+\r
+\r
+\r
+static int hp_pga_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event)\r
+{\r
+ struct snd_soc_codec *codec = w->codec;\r
+ int reg;\r
+\r
+ DBG("enter %s\n", __func__);\r
+\r
+ reg = rt5625_read(codec, VIRTUAL_REG_FOR_MISC_FUNC) & (0x3 << 6);\r
+ if ((reg >> 6) != 0x3 && reg != 0)\r
+ return 0;\r
+ \r
+ switch (event)\r
+ {\r
+ case SND_SOC_DAPM_POST_PMD:\r
+\r
+ DBG("aftet virtual hp power down!\n");\r
+\r
+ hp_mute_unmute_depop(codec,1);//mute hp\r
+ rt5625_write_mask(codec, 0x3a, 0x0000, 0x0300);\r
+ rt5625_write_mask(codec, 0x3e, 0x0000, 0x0c00); \r
+ break;\r
+\r
+ case SND_SOC_DAPM_POST_PMU: \r
+\r
+ DBG("after virtual hp power up!\n");\r
+ hp_depop_mode2(codec);\r
+ hp_mute_unmute_depop(codec,0);//unmute hp\r
+ break;\r
+\r
+ default:\r
+ return 0;\r
+ } \r
+\r
+ return 0;\r
+}\r
+\r
+\r
+\r
+static int aux_pga_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event)\r
+{\r
+ return 0;\r
+}\r
+\r
+/*SPKOUT Mux*/\r
+static const struct snd_kcontrol_new rt5625_spkout_mux_out_controls = \r
+SOC_DAPM_ENUM("Route", rt5625_enum[3]);\r
+\r
+/*HPLOUT MUX*/\r
+static const struct snd_kcontrol_new rt5625_hplout_mux_out_controls = \r
+SOC_DAPM_ENUM("Route", rt5625_enum[4]);\r
+\r
+/*HPROUT MUX*/\r
+static const struct snd_kcontrol_new rt5625_hprout_mux_out_controls = \r
+SOC_DAPM_ENUM("Route", rt5625_enum[5]);\r
+/*AUXOUT MUX*/\r
+static const struct snd_kcontrol_new rt5625_auxout_mux_out_controls = \r
+SOC_DAPM_ENUM("Route", rt5625_enum[6]);\r
+\r
+static const struct snd_soc_dapm_widget rt5625_dapm_widgets[] = {\r
+SND_SOC_DAPM_INPUT("Left LineIn"),\r
+SND_SOC_DAPM_INPUT("Right LineIn"),\r
+SND_SOC_DAPM_INPUT("Phone"),\r
+SND_SOC_DAPM_INPUT("Mic1"),\r
+SND_SOC_DAPM_INPUT("Mic2"),\r
+\r
+SND_SOC_DAPM_PGA("Mic1 Boost", RT5625_PWR_MANAG_ADD3, 1, 0, NULL, 0),\r
+SND_SOC_DAPM_PGA("Mic2 Boost", RT5625_PWR_MANAG_ADD3, 0, 0, NULL, 0),\r
+\r
+SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback DAC", RT5625_PWR_MANAG_ADD2, 9, 0),\r
+SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback DAC", RT5625_PWR_MANAG_ADD2, 8, 0),\r
+SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback DAC", RT5625_PWR_MANAG_ADD2, 10, 0),\r
+\r
+SND_SOC_DAPM_PGA("Left LineIn PGA", RT5625_PWR_MANAG_ADD3, 7, 0, NULL, 0),\r
+SND_SOC_DAPM_PGA("Right LineIn PGA", RT5625_PWR_MANAG_ADD3, 6, 0, NULL, 0),\r
+SND_SOC_DAPM_PGA("Phone PGA", RT5625_PWR_MANAG_ADD3, 5, 0, NULL, 0),\r
+SND_SOC_DAPM_PGA("Mic1 PGA", RT5625_PWR_MANAG_ADD3, 3, 0, NULL, 0),\r
+SND_SOC_DAPM_PGA("Mic2 PGA", RT5625_PWR_MANAG_ADD3, 2, 0, NULL, 0),\r
+SND_SOC_DAPM_PGA("VoDAC PGA", RT5625_PWR_MANAG_ADD1, 7, 0, NULL, 0),\r
+SND_SOC_DAPM_MIXER("RKE", RT5625_PWR_MANAG_ADD2, 1, 0,\r
+ &rt5625_left_adc_rec_mixer_controls[0], ARRAY_SIZE(rt5625_left_adc_rec_mixer_controls)),\r
+SND_SOC_DAPM_MIXER("RKF", RT5625_PWR_MANAG_ADD2, 0, 0,\r
+ &rt5625_right_adc_rec_mixer_controls[0], ARRAY_SIZE(rt5625_right_adc_rec_mixer_controls)),\r
+SND_SOC_DAPM_MIXER_E("RK5", RT5625_PWR_MANAG_ADD2, 5, 0,\r
+ &rt5625_left_hp_mixer_controls[0], ARRAY_SIZE(rt5625_left_hp_mixer_controls),\r
+ mixer_event, SND_SOC_DAPM_POST_REG),\r
+SND_SOC_DAPM_MIXER_E("RK6", RT5625_PWR_MANAG_ADD2, 4, 0,\r
+ &rt5625_right_hp_mixer_controls[0], ARRAY_SIZE(rt5625_right_hp_mixer_controls),\r
+ mixer_event, SND_SOC_DAPM_POST_REG),\r
+SND_SOC_DAPM_MIXER("RK13", RT5625_PWR_MANAG_ADD2, 2, 0, \r
+ &rt5625_mono_mixer_controls[0], ARRAY_SIZE(rt5625_mono_mixer_controls)),\r
+SND_SOC_DAPM_MIXER("RK12", RT5625_PWR_MANAG_ADD2, 3, 0,\r
+ &rt5625_spk_mixer_controls[0], ARRAY_SIZE(rt5625_spk_mixer_controls)), \r
+SND_SOC_DAPM_MIXER("RK11", SND_SOC_NOPM, 0, 0, NULL, 0),\r
+SND_SOC_DAPM_MIXER("DAC Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),\r
+SND_SOC_DAPM_MIXER("Line Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),\r
+\r
+SND_SOC_DAPM_MUX("RK1", SND_SOC_NOPM, 0, 0, &rt5625_spkout_mux_out_controls),\r
+SND_SOC_DAPM_MUX("RK7", SND_SOC_NOPM, 0, 0, &rt5625_hplout_mux_out_controls),\r
+SND_SOC_DAPM_MUX("RK8", SND_SOC_NOPM, 0, 0, &rt5625_hprout_mux_out_controls),\r
+SND_SOC_DAPM_MUX("RKB", SND_SOC_NOPM, 0, 0, &rt5625_auxout_mux_out_controls),\r
+\r
+SND_SOC_DAPM_PGA_E("SPKL Out PGA", VIRTUAL_REG_FOR_MISC_FUNC, 4, 0, NULL, 0,\r
+ spk_pga_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),\r
+SND_SOC_DAPM_PGA_E("SPKR Out PGA", VIRTUAL_REG_FOR_MISC_FUNC, 5, 0, NULL, 0,\r
+ spk_pga_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),\r
+SND_SOC_DAPM_PGA_E("HPL Out PGA",VIRTUAL_REG_FOR_MISC_FUNC, 6, 0, NULL, 0, \r
+ hp_pga_event, SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),\r
+SND_SOC_DAPM_PGA_E("HPR Out PGA",VIRTUAL_REG_FOR_MISC_FUNC, 7, 0, NULL, 0, \r
+ hp_pga_event, SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),\r
+SND_SOC_DAPM_PGA_E("AUX Out PGA",RT5625_PWR_MANAG_ADD3, 14, 0, NULL, 0, \r
+ aux_pga_event, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),\r
+ \r
+SND_SOC_DAPM_ADC("Left ADC", "Left ADC HiFi Capture", RT5625_PWR_MANAG_ADD2, 7, 0),\r
+SND_SOC_DAPM_ADC("Right ADC", "Right ADC HiFi Capture", RT5625_PWR_MANAG_ADD2, 6, 0),\r
+SND_SOC_DAPM_OUTPUT("SPKL"),\r
+SND_SOC_DAPM_OUTPUT("SPKR"),\r
+SND_SOC_DAPM_OUTPUT("HPL"),\r
+SND_SOC_DAPM_OUTPUT("HPR"),\r
+SND_SOC_DAPM_OUTPUT("AUX"),\r
+SND_SOC_DAPM_MICBIAS("Mic1 Bias", RT5625_PWR_MANAG_ADD1, 3, 0),\r
+SND_SOC_DAPM_MICBIAS("Mic2 Bias", RT5625_PWR_MANAG_ADD1, 2, 0),\r
+};\r
+\r
+static const struct snd_soc_dapm_route audio_map[] = {\r
+ /*output*/\r
+ {"SPKL", NULL, "SPKL Out PGA"},\r
+ {"SPKR", NULL, "SPKR Out PGA"},\r
+ {"HPL", NULL, "HPL Out PGA"},\r
+ {"HPR", NULL, "HPR Out PGA"},\r
+ {"AUX", NULL, "AUX Out PGA"},\r
+\r
+ /*Input PGA*/\r
+ {"Left LineIn PGA", NULL, "Left LineIn"},\r
+ {"Right LineIn PGA", NULL, "Right LineIn"},\r
+ {"Phone PGA", NULL, "Phone"},\r
+ {"Mic1 Boost", NULL, "Mic1"},\r
+ {"Mic2 Boost", NULL, "Mic2"},\r
+ {"Mic1 PGA", NULL, "Mic1"},\r
+ {"Mic2 PGA", NULL, "Mic2"},\r
+ {"VoDAC PGA", NULL, "Voice DAC"},\r
+ \r
+ /*Left ADC mixer*/\r
+ {"RKE", "LineIn Capture Switch", "Left LineIn"},\r
+ {"RKE", "RKL", "Phone"},\r
+ {"RKE", "RKK", "Mic1 Boost"},\r
+ {"RKE", "Mic2 Capture Switch", "Mic2 Boost"},\r
+ {"RKE", "HP Mixer Capture Switch", "RK5"},\r
+ {"RKE", "SPK Mixer Capture Switch", "RK12"},\r
+ {"RKE", "MoNo Mixer Capture Switch", "RK13"},\r
+\r
+ /*Right ADC Mixer*/\r
+ {"RKF", "LineIn Capture Switch", "Right LineIn"},\r
+ {"RKF", "RKL", "Phone"},\r
+ {"RKF", "RKK", "Mic1 Boost"},\r
+ {"RKF", "Mic2 Capture Switch", "Mic2 Boost"},\r
+ {"RKF", "HP Mixer Capture Switch", "RK6"},\r
+ {"RKF", "SPK Mixer Capture Switch", "RK12"},\r
+ {"RKF", "MoNo Mixer Capture Switch", "RK13"},\r
+ \r
+ /*HPL mixer*/\r
+ {"RK5", "ADC Playback Switch", "RKE"},\r
+ {"RK5", "LineIn Playback Switch", "Left LineIn PGA"},\r
+ {"RK5", "RKJ", "Phone PGA"},\r
+ {"RK5", "RKI", "Mic1 PGA"},\r
+ {"RK5", "Mic2 Playback Switch", "Mic2 PGA"},\r
+ {"RK5", "RKH", "Left DAC"},\r
+ {"RK5", "RKM", "VoDAC PGA"},\r
+\r
+ /*DAC Mixer*/\r
+ {"DAC Mixer", NULL, "Left DAC"},\r
+ {"DAC Mixer", NULL, "Right DAC"},\r
+\r
+ /*line mixer*/\r
+ {"Line Mixer", NULL, "Left LineIn PGA"},\r
+ {"Line Mixer", NULL, "Right LineIn PGA"},\r
+\r
+ /*HPR mixer*/\r
+ {"RK6", "ADC Playback Switch", "RKF"},\r
+ {"RK6", "LineIn Playback Switch", "Right LineIn PGA"},\r
+ {"RK6", "RKH", "Right DAC"},\r
+ {"RK6", "RKJ", "Phone PGA"},\r
+ {"RK6", "RKI", "Mic1 PGA"},\r
+ {"RK6", "Mic2 Playback Switch", "Mic2 PGA"},\r
+ {"RK6", "RKM", "VoDAC PGA"},\r
+\r
+ /*spk mixer*/\r
+ {"RK12", "Line Mixer Playback Switch", "Line Mixer"},\r
+ {"RK12", "RKJ", "Phone PGA"},\r
+ {"RK12", "RKI", "Mic1 PGA"},\r
+ {"RK12", "Mic2 Playback Switch", "Mic2 PGA"},\r
+ {"RK12", "RKG", "DAC Mixer"},\r
+ {"RK12", "RKM", "VoDAC PGA"},\r
+\r
+ /*mono mixer*/\r
+ {"RK13", "Line Mixer Playback Switch", "Line Mixer"},\r
+ {"RK13", "ADCL Playback Switch","RKE"},\r
+ {"RK13", "ADCR Playback Switch","RKF"},\r
+ {"RK13", "RKI", "Mic1 PGA"},\r
+ {"RK13", "Mic2 Playback Switch", "Mic2 PGA"},\r
+ {"RK13", "RKG", "DAC Mixer"},\r
+ {"RK13", "RKM", "VoDAC PGA"},\r
+ \r
+ /*hp mixer*/\r
+ {"RK11", NULL, "RK5"},\r
+ {"RK11", NULL, "RK6"},\r
+\r
+ /*spkout mux*/\r
+ {"RK1", "RK11", "RK11"},\r
+ {"RK1", "RK12", "RK12"},\r
+ {"RK1", "RK13", "RK13"},\r
+ \r
+ /*hpl out mux*/\r
+ {"RK7", "RK71", "RK5"},\r
+ \r
+ /*hpr out mux*/\r
+ {"RK8", "RK81", "RK6"},\r
+\r
+ /*aux out mux*/\r
+ {"RKB", "RK11", "RK11"},\r
+ {"RKB", "RK12", "RK12"},\r
+ {"RKB", "RK13", "RK13"},\r
+\r
+ /*spkl out pga*/\r
+ {"SPKL Out PGA", NULL, "RK1"},\r
+\r
+ /*spkr out pga*/\r
+ {"SPKR Out PGA", NULL, "RK1"},\r
+ \r
+ /*hpl out pga*/\r
+ {"HPL Out PGA", NULL, "RK7"},\r
+\r
+ /*hpr out pga*/\r
+ {"HPR Out PGA", NULL, "RK8"},\r
+\r
+ /*aux out pga*/\r
+ {"AUX Out PGA", NULL, "RKB"}, \r
+ \r
+ /*left adc*/\r
+ {"Left ADC", NULL, "RKE"},\r
+ \r
+ /*right adc*/\r
+ {"Right ADC", NULL, "RKF"},\r
+ \r
+\r
+};\r
+\r
+\r
+static int rt5625_add_widgets(struct snd_soc_codec *codec)\r
+{\r
+ snd_soc_dapm_new_controls(codec, rt5625_dapm_widgets, \r
+ ARRAY_SIZE(rt5625_dapm_widgets));\r
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));\r
+ snd_soc_dapm_new_widgets(codec);\r
+\r
+ return 0;\r
+}\r
+\r
+struct _pll_div{\r
+ u32 pll_in;\r
+ u32 pll_out;\r
+ u16 regvalue;\r
+};\r
+\r
+\r
+/**************************************************************\r
+ * watch out!\r
+ * our codec support you to select different source as pll input, but if you \r
+ * use both of the I2S audio interface and pcm interface instantially. \r
+ * The two DAI must have the same pll setting params, so you have to offer\r
+ * the same pll input, and set our codec's sysclk the same one, we suggest \r
+ * 24576000.\r
+ **************************************************************/\r
+static const struct _pll_div codec_master_pll1_div[] = {\r
+ \r
+ { 2048000, 8192000, 0x0ea0},\r
+ { 3686400, 8192000, 0x4e27},\r
+ { 12000000, 8192000, 0x456b},\r
+ { 13000000, 8192000, 0x495f},\r
+ { 13100000, 8192000, 0x0320},\r
+ { 2048000, 11289600, 0xf637},\r
+ { 3686400, 11289600, 0x2f22},\r
+ { 12000000, 11289600, 0x3e2f},\r
+ { 13000000, 11289600, 0x4d5b},\r
+ { 13100000, 11289600, 0x363b},\r
+ { 2048000, 16384000, 0x1ea0},\r
+ { 3686400, 16384000, 0x9e27},\r
+ { 12000000, 16384000, 0x452b},\r
+ { 13000000, 16384000, 0x542f},\r
+ { 13100000, 16384000, 0x03a0},\r
+ { 2048000, 16934400, 0xe625},\r
+ { 3686400, 16934400, 0x9126},\r
+ { 12000000, 16934400, 0x4d2c},\r
+ { 13000000, 16934400, 0x742f},\r
+ { 13100000, 16934400, 0x3c27},\r
+ { 2048000, 22579200, 0x2aa0},\r
+ { 3686400, 22579200, 0x2f20},\r
+ { 12000000, 22579200, 0x7e2f},\r
+ { 13000000, 22579200, 0x742f},\r
+ { 13100000, 22579200, 0x3c27},\r
+ { 2048000, 24576000, 0x2ea0},\r
+ { 3686400, 24576000, 0xee27},\r
+ { 11289600, 24576000, 0x950F},\r
+ { 12000000, 24576000, 0x2915},\r
+ { 12288000, 24576000, 0x0600},\r
+ { 13000000, 24576000, 0x772e},\r
+ { 13100000, 24576000, 0x0d20},\r
+ { 26000000, 24576000, 0x2027},\r
+ { 26000000, 22579200, 0x392f},\r
+ { 24576000, 22579200, 0x0921},\r
+ { 24576000, 24576000, 0x02a0},\r
+};\r
+\r
+static const struct _pll_div codec_bclk_pll1_div[] = {\r
+\r
+ { 256000, 4096000, 0x3ea0},\r
+ { 352800, 5644800, 0x3ea0},\r
+ { 512000, 8192000, 0x3ea0},\r
+ { 705600, 11289600, 0x3ea0},\r
+ { 1024000, 16384000, 0x3ea0}, \r
+ { 1411200, 22579200, 0x3ea0},\r
+ { 1536000, 24576000, 0x3ea0}, \r
+ { 2048000, 16384000, 0x1ea0}, \r
+ { 2822400, 22579200, 0x1ea0},\r
+ { 3072000, 24576000, 0x1ea0},\r
+ { 705600, 11289600, 0x3ea0},\r
+ { 705600, 8467200, 0x3ab0},\r
+ { 2822400, 11289600, 0x1ee0},\r
+ { 3072000, 12288000, 0x1ee0}, \r
+};\r
+\r
+static const struct _pll_div codec_vbclk_pll1_div[] = {\r
+\r
+ { 256000, 4096000, 0x3ea0},\r
+ { 352800, 5644800, 0x3ea0},\r
+ { 512000, 8192000, 0x3ea0},\r
+ { 705600, 11289600, 0x3ea0},\r
+ { 1024000, 16384000, 0x3ea0}, \r
+ { 1411200, 22579200, 0x3ea0},\r
+ { 1536000, 24576000, 0x3ea0}, \r
+ { 2048000, 16384000, 0x1ea0}, \r
+ { 2822400, 22579200, 0x1ea0},\r
+ { 3072000, 24576000, 0x1ea0},\r
+ { 705600, 11289600, 0x3ea0},\r
+ { 705600, 8467200, 0x3ab0},\r
+};\r
+\r
+\r
+struct _coeff_div_stereo {\r
+ unsigned int mclk;\r
+ unsigned int rate;\r
+ unsigned int reg60;\r
+ unsigned int reg62;\r
+};\r
+\r
+struct _coeff_div_voice {\r
+ unsigned int mclk;\r
+ unsigned int rate;\r
+ unsigned int reg64;\r
+};\r
+\r
+static const struct _coeff_div_stereo coeff_div_stereo[] = {\r
+\r
+ /*bclk is config to 32fs, if codec is choose to be slave mode , input bclk should be 32*fs */\r
+ {24576000, 48000, 0x3174, 0x1010}, \r
+ {12288000, 48000, 0x1174, 0x0000},\r
+ {18432000, 48000, 0x2174, 0x1111},\r
+ {36864000, 48000, 0x2274, 0x2020},\r
+ {49152000, 48000, 0xf074, 0x3030},\r
+ {24576000, 48000, 0x3172, 0x1010},\r
+ {24576000, 8000, 0xB274, 0x2424},\r
+ {24576000, 16000, 0xB174, 0x2222},\r
+ {24576000, 32000, 0xB074, 0x2121},\r
+ {22579200, 11025, 0X3374, 0x1414},\r
+ {22579200, 22050, 0X3274, 0x1212},\r
+ {22579200, 44100, 0X3174, 0x1010},\r
+ {0, 0, 0, 0},\r
+};\r
+\r
+static const struct _coeff_div_voice coeff_div_voice[] = {\r
+\r
+ /*bclk is config to 32fs, if codec is choose to be slave mode , input bclk should be 32*fs */\r
+ {24576000, 16000, 0x2622}, \r
+ {24576000, 8000, 0x2824},\r
+ {0, 0, 0},\r
+};\r
+\r
+static int get_coeff(unsigned int mclk, unsigned int rate, int mode)\r
+{\r
+ int i;\r
+\r
+ DBG("get_coeff mclk = %d, rate = %d, mode = %d\n", mclk, rate, mode);\r
+\r
+ if (!mode) {\r
+ for (i = 0; i < ARRAY_SIZE(coeff_div_stereo); i++) {\r
+ if ((coeff_div_stereo[i].rate == rate) && (coeff_div_stereo[i].mclk == mclk))\r
+ return i;\r
+ }\r
+ } else {\r
+ for (i = 0; i< ARRAY_SIZE(coeff_div_voice); i++) {\r
+ if ((coeff_div_voice[i].rate == rate) && (coeff_div_voice[i].mclk == mclk))\r
+ return i;\r
+ }\r
+ }\r
+\r
+ printk("can't find a matched mclk and rate in %s\n", \r
+ (mode ? "coeff_div_voice[]" : "coeff_div_audio[]"));\r
+\r
+ return -EINVAL;\r
+}\r
+\r
+\r
+static int rt5625_codec_set_dai_pll(struct snd_soc_dai *codec_dai, \r
+ int pll_id, unsigned int freq_in, unsigned int freq_out)\r
+{\r
+ int i;\r
+ int ret = -EINVAL;\r
+ struct snd_soc_codec *codec = codec_dai->codec;\r
+\r
+ DBG("enter %s pll_id = %d freq_in = %d freq_out = %d\n",\r
+ __func__, pll_id, freq_in, freq_out);\r
+\r
+ if (pll_id < RT5625_PLL1_FROM_MCLK || pll_id > RT5625_PLL1_FROM_VBCLK)\r
+ return -EINVAL;\r
+\r
+ if (!freq_in || !freq_out)\r
+ return 0;\r
+\r
+ if (RT5625_PLL1_FROM_MCLK == pll_id) {\r
+\r
+ for (i = 0; i < ARRAY_SIZE(codec_master_pll1_div); i ++)\r
+ {\r
+ if ((freq_in == codec_master_pll1_div[i].pll_in) && (freq_out == codec_master_pll1_div[i].pll_out))\r
+ {\r
+ rt5625_write(codec, RT5625_GEN_CTRL_REG2, 0x0000); /*PLL source from MCLK*/\r
+ rt5625_write(codec, RT5625_PLL_CTRL, codec_master_pll1_div[i].regvalue); /*set pll code*/\r
+ rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD2, 0x8000, 0x8000); /*enable pll1 power*/\r
+ rt5625_write_mask(codec, RT5625_GEN_CTRL_REG1, 0x8000, 0x8000);\r
+ ret = 0;\r
+ }\r
+ }\r
+ } else if (RT5625_PLL1_FROM_BCLK == pll_id) {\r
+\r
+ for (i = 0; i < ARRAY_SIZE(codec_bclk_pll1_div); i ++)\r
+ {\r
+ if ((freq_in == codec_bclk_pll1_div[i].pll_in) && (freq_out == codec_bclk_pll1_div[i].pll_out))\r
+ {\r
+ rt5625_write(codec, RT5625_GEN_CTRL_REG2, 0x2000); /*PLL source from BCLK*/\r
+ rt5625_write(codec, RT5625_PLL_CTRL, codec_bclk_pll1_div[i].regvalue); /*set pll1 code*/\r
+ rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD2, 0x8000, 0x8000); /*enable pll1 power*/\r
+ rt5625_write_mask(codec, RT5625_GEN_CTRL_REG1, 0x8000, 0x8000);\r
+ ret = 0;\r
+ }\r
+ }\r
+ } else if (RT5625_PLL1_FROM_VBCLK == pll_id) {\r
+\r
+ for (i = 0; i < ARRAY_SIZE(codec_vbclk_pll1_div); i ++)\r
+ {\r
+ if ((freq_in == codec_vbclk_pll1_div[i].pll_in) && (freq_out == codec_vbclk_pll1_div[i].pll_out))\r
+ {\r
+ rt5625_write(codec, RT5625_GEN_CTRL_REG2, 0x3000); /*PLL source from VBCLK*/\r
+ rt5625_write(codec, RT5625_PLL_CTRL, codec_vbclk_pll1_div[i].regvalue); /*set pll1 code*/\r
+ rt5625_write_mask(codec, RT5625_PWR_MANAG_ADD2, 0x8000, 0x8000); /*enable pll1 power*/\r
+ rt5625_write_mask(codec, RT5625_GEN_CTRL_REG1, 0x8000, 0x8000);\r
+ ret = 0;\r
+ }\r
+ }\r
+ }\r
+ return 0;\r
+}\r
+\r
+\r
+static int rt5625_hifi_codec_set_dai_sysclk(struct snd_soc_dai *codec_dai, \r
+ int clk_id, unsigned int freq, int dir)\r
+{\r
+ struct snd_soc_codec *codec = codec_dai->codec;\r
+ struct rt5625_priv * rt5625 = codec->private_data;\r
+\r
+ DBG("sysclk freq %u for audio i2s\n", freq);\r
+ \r
+ if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) {\r
+ rt5625->stereo_sysclk = freq;\r
+ return 0;\r
+ }\r
+ \r
+ printk("unsupported sysclk freq %u for audio i2s\n", freq);\r
+ rt5625->stereo_sysclk=24576000;\r
+ \r
+ return 0;\r
+}\r
+\r
+static int rt5625_voice_codec_set_dai_sysclk(struct snd_soc_dai *codec_dai, \r
+ int clk_id, unsigned int freq, int dir)\r
+{\r
+ struct snd_soc_codec *codec = codec_dai->codec;\r
+ struct rt5625_priv * rt5625 = codec->private_data;\r
+\r
+ DBG("sysclk freq %u for voice pcm\n", freq);\r
+\r
+ if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) {\r
+ rt5625->voice_sysclk = freq;\r
+ return 0;\r
+ } \r
+\r
+ printk("unsupported sysclk freq %u for voice pcm\n", freq);\r
+ rt5625->voice_sysclk = 24576000;\r
+ \r
+ return 0;\r
+}\r
+\r
+\r
+static int rt5625_hifi_pcm_hw_params(struct snd_pcm_substream *substream, \r
+ struct snd_pcm_hw_params *params,\r
+ struct snd_soc_dai *dai)\r
+{\r
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;\r
+ struct snd_soc_device *socdev = rtd->socdev;\r
+ struct snd_soc_codec *codec = socdev->card->codec;\r
+ struct rt5625_priv *rt5625 = codec->private_data;\r
+\r
+ unsigned int iface = rt5625_read(codec, RT5625_MAIN_SDP_CTRL) & 0xfff3;\r
+ int rate = params_rate(params);\r
+ int coeff = get_coeff(rt5625->stereo_sysclk, rate, 0);\r
+ \r
+ DBG("enter %s rate = %d \n", __func__, rate);\r
+\r
+ switch (params_format(params))\r
+ {\r
+ case SNDRV_PCM_FORMAT_S16_LE:\r
+ break;\r
+ case SNDRV_PCM_FORMAT_S20_3LE:\r
+ iface |= 0x0004;\r
+ case SNDRV_PCM_FORMAT_S24_LE:\r
+ iface |= 0x0008;\r
+ case SNDRV_PCM_FORMAT_S8:\r
+ iface |= 0x000c;\r
+ }\r
+\r
+ rt5625_write(codec, RT5625_MAIN_SDP_CTRL, iface);\r
+ rt5625_write_mask(codec, 0x3a, 0xc801, 0xc801); /*power i2s and dac ref*/\r
+ if (coeff >= 0) {\r
+ rt5625_write(codec, RT5625_STEREO_DAC_CLK_CTRL1, coeff_div_stereo[coeff].reg60);\r
+ rt5625_write(codec, RT5625_STEREO_DAC_CLK_CTRL2, coeff_div_stereo[coeff].reg62);\r
+ }\r
+ \r
+ return 0;\r
+}\r
+\r
+static int rt5625_voice_pcm_hw_params(struct snd_pcm_substream *substream, \r
+ struct snd_pcm_hw_params *params,\r
+ struct snd_soc_dai *dai)\r
+{\r
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;\r
+ struct snd_soc_device *socdev = rtd->socdev;\r
+ struct snd_soc_codec *codec = socdev->card->codec;\r
+ struct rt5625_priv *rt5625 = codec->private_data;\r
+ struct snd_soc_dapm_widget *w;\r
+ unsigned int iface = rt5625_read(codec, RT5625_EXTEND_SDP_CTRL) & 0xfff3;\r
+ int rate = params_rate(params);\r
+ int coeff = get_coeff(rt5625->voice_sysclk, rate, 1);\r
+\r
+ DBG("enter %s rate = %d \n", __func__, rate);\r
+\r
+ list_for_each_entry(w, &codec->dapm_widgets, list)\r
+ {\r
+ if (!w->sname)\r
+ continue;\r
+ if (!strcmp(w->name, "Right ADC"))\r
+ strcpy(w->sname, "Right ADC Voice Capture");\r
+ }\r
+ \r
+ switch (params_format(params))\r
+ {\r
+ case SNDRV_PCM_FORMAT_S16_LE:\r
+ break;\r
+ case SNDRV_PCM_FORMAT_S20_3LE:\r
+ iface |= 0x0004;\r
+ case SNDRV_PCM_FORMAT_S24_LE:\r
+ iface |= 0x0008;\r
+ case SNDRV_PCM_FORMAT_S8:\r
+ iface |= 0x000c;\r
+ }\r
+ rt5625_write_mask(codec, 0x3a, 0x0801, 0x0801); /*power i2s and dac ref*/\r
+ rt5625_write(codec, RT5625_EXTEND_SDP_CTRL, iface);\r
+ if (coeff >= 0)\r
+ rt5625_write(codec, RT5625_VOICE_DAC_PCMCLK_CTRL1, coeff_div_voice[coeff].reg64);\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+static int rt5625_hifi_codec_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)\r
+{\r
+\r
+ struct snd_soc_codec *codec = codec_dai->codec;\r
+ u16 iface = 0;\r
+\r
+ DBG("enter %s fmt = %d\n", __func__, fmt);\r
+\r
+ /*set master/slave interface*/\r
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK)\r
+ {\r
+ case SND_SOC_DAIFMT_CBM_CFM:\r
+ iface = 0x0000;\r
+ break;\r
+ case SND_SOC_DAIFMT_CBS_CFS:\r
+ iface = 0x8000;\r
+ break;\r
+ default:\r
+ return -EINVAL;\r
+ }\r
+\r
+ /*interface format*/\r
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK)\r
+ {\r
+ case SND_SOC_DAIFMT_I2S:\r
+ iface |= 0x0000;\r
+ break;\r
+ case SND_SOC_DAIFMT_LEFT_J:\r
+ iface |= 0x0001;\r
+ break;\r
+ case SND_SOC_DAIFMT_DSP_A:\r
+ iface |= 0x0002;\r
+ break;\r
+ case SND_SOC_DAIFMT_DSP_B:\r
+ iface |= 0x0003;\r
+ break;\r
+ default:\r
+ return -EINVAL; \r
+ }\r
+\r
+ /*clock inversion*/\r
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK)\r
+ {\r
+ case SND_SOC_DAIFMT_NB_NF:\r
+ iface |= 0x0000;\r
+ break;\r
+ case SND_SOC_DAIFMT_IB_NF:\r
+ iface |= 0x0080;\r
+ break;\r
+ default:\r
+ return -EINVAL;\r
+ }\r
+\r
+ rt5625_write(codec, RT5625_MAIN_SDP_CTRL, iface);\r
+ return 0;\r
+}\r
+\r
+static int rt5625_voice_codec_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)\r
+{\r
+ struct snd_soc_codec *codec = codec_dai->codec;\r
+ int iface;\r
+\r
+ DBG("enter %s\n", __func__);\r
+ /*set slave/master mode*/\r
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK)\r
+ {\r
+ case SND_SOC_DAIFMT_CBM_CFM:\r
+ iface = 0x0000;\r
+ break;\r
+ case SND_SOC_DAIFMT_CBS_CFS:\r
+ iface = 0x4000;\r
+ break;\r
+ default:\r
+ return -EINVAL; \r
+ }\r
+\r
+ switch(fmt & SND_SOC_DAIFMT_FORMAT_MASK)\r
+ {\r
+ case SND_SOC_DAIFMT_I2S:\r
+ iface |= 0x0000;\r
+ break;\r
+ case SND_SOC_DAIFMT_LEFT_J:\r
+ iface |= 0x0001;\r
+ break;\r
+ case SND_SOC_DAIFMT_DSP_A:\r
+ iface |= 0x0002;\r
+ break;\r
+ case SND_SOC_DAIFMT_DSP_B:\r
+ iface |= 0x0003;\r
+ break;\r
+ default:\r
+ return -EINVAL; \r
+ }\r
+\r
+ /*clock inversion*/\r
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK)\r
+ {\r
+ case SND_SOC_DAIFMT_NB_NF:\r
+ iface |= 0x0000;\r
+ break;\r
+ case SND_SOC_DAIFMT_IB_NF:\r
+ iface |= 0x0080;\r
+ break;\r
+ default:\r
+ return -EINVAL; \r
+ }\r
+\r
+ iface |= 0x8000; /*enable vopcm*/\r
+ rt5625_write(codec, RT5625_EXTEND_SDP_CTRL, iface); \r
+ return 0;\r
+}\r
+\r
+\r
+static int rt5625_hifi_codec_mute(struct snd_soc_dai *dai, int mute)\r
+{\r
+ struct snd_soc_codec *codec = dai->codec;\r
+\r
+ if (mute) \r
+ rt5625_write_mask(codec, RT5625_STEREO_DAC_VOL, 0x8080, 0x8080);\r
+ else\r
+ rt5625_write_mask(codec, RT5625_STEREO_DAC_VOL, 0x0000, 0x8080);\r
+ return 0;\r
+}\r
+\r
+static int rt5625_voice_codec_mute(struct snd_soc_dai *dai, int mute)\r
+{\r
+ struct snd_soc_codec *codec = dai->codec;\r
+\r
+ if (mute)\r
+ rt5625_write_mask(codec, RT5625_VOICE_DAC_OUT_VOL, 0x1000, 0x1000);\r
+ else \r
+ rt5625_write_mask(codec, RT5625_VOICE_DAC_OUT_VOL, 0x0000, 0x1000);\r
+ return 0;\r
+}\r
+\r
+\r
+static int rt5625_set_bias_level(struct snd_soc_codec *codec, \r
+ enum snd_soc_bias_level level)\r
+{\r
+ switch(level) {\r
+ case SND_SOC_BIAS_ON:\r
+ break;\r
+ case SND_SOC_BIAS_PREPARE:\r
+ rt5625_write(codec, 0x26, 0x0000);\r
+ rt5625_write_mask(codec, 0x3c, 0x2000, 0x2000); \r
+ rt5625_write_mask(codec, 0x3a, 0x000e, 0x000e); \r
+ break;\r
+ case SND_SOC_BIAS_STANDBY:\r
+ break;\r
+ case SND_SOC_BIAS_OFF:\r
+ rt5625_write_mask(codec, 0x04, 0x8080, 0x8080); /*mute hp*/\r
+ rt5625_write_mask(codec, 0x02, 0x8080, 0x8080); /*mute spk*/\r
+ rt5625_write(codec, 0x3e, 0x0000); //power off all bit\r
+ rt5625_write(codec, 0x3a, 0x0000); //power off all bit\r
+ rt5625_write(codec, 0x3c, 0x0000); //power off all bit\r
+ \r
+ break;\r
+ }\r
+ codec->bias_level = level;\r
+ return 0;\r
+}\r
+\r
+\r
+#define RT5625_STEREO_RATES SNDRV_PCM_RATE_8000_48000\r
+\r
+#define RT5626_VOICE_RATES SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000\r
+\r
+#define RT5625_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\\r
+ SNDRV_PCM_FMTBIT_S20_3LE |\\r
+ SNDRV_PCM_FMTBIT_S24_LE |\\r
+ SNDRV_PCM_FMTBIT_S8)\r
+\r
+static struct snd_soc_dai_ops rt5625_dai_ops_hifi = {\r
+\r
+ .hw_params = rt5625_hifi_pcm_hw_params,\r
+// .digital_mute = rt5625_hifi_codec_mute,\r
+ .set_fmt = rt5625_hifi_codec_set_dai_fmt,\r
+ .set_pll = rt5625_codec_set_dai_pll,\r
+ .set_sysclk = rt5625_hifi_codec_set_dai_sysclk,\r
+\r
+};\r
+\r
+\r
+static struct snd_soc_dai_ops rt5625_dai_ops_voice = {\r
+\r
+ .hw_params = rt5625_voice_pcm_hw_params,\r
+// .digital_mute = rt5625_voice_codec_mute,\r
+ .set_fmt = rt5625_voice_codec_set_dai_fmt,\r
+ .set_pll = rt5625_codec_set_dai_pll,\r
+ .set_sysclk = rt5625_voice_codec_set_dai_sysclk,\r
+\r
+};\r
+\r
+\r
+\r
+struct snd_soc_dai rt5625_dai[] = {\r
+ /*hifi codec dai*/\r
+ {\r
+ .name = "RT5625 HiFi",\r
+ .id = 1,\r
+ .playback = {\r
+ .stream_name = "HiFi Playback",\r
+ .channels_min = 1,\r
+ .channels_max = 2,\r
+ .rates = RT5625_STEREO_RATES,\r
+ .formats = RT5625_FORMATS,\r
+ },\r
+ .capture = {\r
+ .stream_name = "HiFi Capture",\r
+ .channels_min = 1,\r
+ .channels_max = 2,\r
+ .rates = RT5625_STEREO_RATES,\r
+ .formats = RT5625_FORMATS,\r
+ },\r
+ \r
+ .ops = &rt5625_dai_ops_hifi,\r
+ },\r
+\r
+ /*voice codec dai*/\r
+ {\r
+ .name = "RT5625 Voice",\r
+ .id = 2,\r
+ .playback = {\r
+ .stream_name = "Voice Playback",\r
+ .channels_min = 1,\r
+ .channels_max = 1,\r
+ .rates = RT5626_VOICE_RATES,\r
+ .formats = RT5625_FORMATS,\r
+ },\r
+ .capture = {\r
+ .stream_name = "Voice Capture",\r
+ .channels_min = 1,\r
+ .channels_max = 1,\r
+ .rates = RT5626_VOICE_RATES,\r
+ .formats = RT5625_FORMATS,\r
+ },\r
+ \r
+ .ops = &rt5625_dai_ops_voice,\r
+\r
+ },\r
+};\r
+\r
+EXPORT_SYMBOL_GPL(rt5625_dai);\r
+\r
+\r
+static void rt5625_work(struct work_struct *work)\r
+{\r
+ struct snd_soc_codec *codec =\r
+ container_of(work, struct snd_soc_codec, delayed_work.work);\r
+ rt5625_set_bias_level(codec, codec->bias_level);\r
+}\r
+\r
+\r
+#if defined(CONFIG_SND_HWDEP)\r
+\r
+#if REALTEK_HWDEP\r
+\r
+#define RT_CE_CODEC_HWDEP_NAME "rt56xx hwdep "\r
+\r
+\r
+static int rt56xx_hwdep_open(struct snd_hwdep *hw, struct file *file)\r
+{\r
+ DBG("enter %s\n", __func__);\r
+ return 0;\r
+}\r
+\r
+static int rt56xx_hwdep_release(struct snd_hwdep *hw, struct file *file)\r
+{\r
+ DBG("enter %s\n", __func__);\r
+ return 0;\r
+}\r
+\r
+\r
+static int rt56xx_hwdep_ioctl_common(struct snd_hwdep *hw, struct file *file, unsigned int cmd, unsigned long arg)\r
+{\r
+ struct rt56xx_cmd rt56xx;\r
+ struct rt56xx_cmd __user *_rt56xx = arg;\r
+ struct rt56xx_reg_state *buf;\r
+ struct rt56xx_reg_state *p;\r
+ struct snd_soc_codec *codec = hw->private_data;\r
+\r
+ if (copy_from_user(&rt56xx, _rt56xx, sizeof(rt56xx)))\r
+ return -EFAULT;\r
+ buf = kmalloc(sizeof(*buf) * rt56xx.number, GFP_KERNEL);\r
+ if (buf == NULL)\r
+ return -ENOMEM;\r
+ if (copy_from_user(buf, rt56xx.buf, sizeof(*buf) * rt56xx.number)) {\r
+ goto err;\r
+ }\r
+ switch (cmd) {\r
+ case RT_READ_CODEC_REG_IOCTL:\r
+ for (p = buf; p < buf + rt56xx.number; p++)\r
+ {\r
+ p->reg_value = codec->read(codec, p->reg_index);\r
+ }\r
+ if (copy_to_user(rt56xx.buf, buf, sizeof(*buf) * rt56xx.number))\r
+ goto err;\r
+ \r
+ break;\r
+ case RT_WRITE_CODEC_REG_IOCTL:\r
+ for (p = buf; p < buf + rt56xx.number; p++)\r
+ codec->write(codec, p->reg_index, p->reg_value);\r
+ break;\r
+ }\r
+\r
+ kfree(buf);\r
+ return 0;\r
+\r
+err:\r
+ kfree(buf);\r
+ return -EFAULT;\r
+ \r
+}\r
+\r
+static int rt56xx_codec_dump_reg(struct snd_hwdep *hw, struct file *file, unsigned long arg)\r
+{\r
+ struct rt56xx_cmd rt56xx;\r
+ struct rt56xx_cmd __user *_rt56xx = arg;\r
+ struct rt56xx_reg_state *buf;\r
+ struct snd_soc_codec *codec = hw->private_data;\r
+ int number = codec->reg_cache_size;\r
+ int i;\r
+\r
+ DBG("enter %s, number = %d\n", __func__, number); \r
+ if (copy_from_user(&rt56xx, _rt56xx, sizeof(rt56xx)))\r
+ return -EFAULT;\r
+ \r
+ buf = kmalloc(sizeof(*buf) * number, GFP_KERNEL);\r
+ if (buf == NULL)\r
+ return -ENOMEM;\r
+\r
+ for (i = 0; i < number; i++)\r
+ {\r
+ buf[i].reg_index = i << 1;\r
+ buf[i].reg_value = codec->read(codec, buf[i].reg_index);\r
+ }\r
+ if (copy_to_user(rt56xx.buf, buf, sizeof(*buf) * i))\r
+ goto err;\r
+ rt56xx.number = number;\r
+ if (copy_to_user(_rt56xx, &rt56xx, sizeof(rt56xx)))\r
+ goto err;\r
+ kfree(buf);\r
+ return 0;\r
+err:\r
+ kfree(buf);\r
+ return -EFAULT;\r
+ \r
+}\r
+\r
+static int rt56xx_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigned int cmd, unsigned long arg)\r
+{\r
+ if (cmd == RT_READ_ALL_CODEC_REG_IOCTL)\r
+ {\r
+ return rt56xx_codec_dump_reg(hw, file, arg);\r
+ }\r
+ else\r
+ {\r
+ return rt56xx_hwdep_ioctl_common(hw, file, cmd, arg);\r
+ }\r
+}\r
+\r
+int realtek_ce_init_hwdep(struct snd_soc_codec *codec)\r
+{\r
+ struct snd_hwdep *hw;\r
+ struct snd_card *card = codec->card;\r
+ int err;\r
+\r
+ if ((err = snd_hwdep_new(card, RT_CE_CODEC_HWDEP_NAME, 0, &hw)) < 0)\r
+ return err;\r
+ \r
+ strcpy(hw->name, RT_CE_CODEC_HWDEP_NAME);\r
+ hw->private_data = codec;\r
+ hw->ops.open = rt56xx_hwdep_open;\r
+ hw->ops.release = rt56xx_hwdep_release;\r
+ hw->ops.ioctl = rt56xx_hwdep_ioctl;\r
+ return 0;\r
+}\r
+\r
+#endif\r
+\r
+#endif\r
+\r
+static int rt5625_init(struct snd_soc_device *socdev)\r
+{\r
+\r
+ struct snd_soc_codec *codec = socdev->card->codec;\r
+ int ret = 0;\r
+\r
+ codec->name = "RT5625";\r
+ codec->owner = THIS_MODULE;\r
+ codec->read = rt5625_read;\r
+ codec->write = rt5625_write;\r
+ codec->set_bias_level = rt5625_set_bias_level;\r
+ codec->dai= rt5625_dai;\r
+ codec->num_dai = 2;\r
+ codec->reg_cache_step = 2; \r
+ codec->reg_cache_size = ARRAY_SIZE(rt5625_reg)*2;\r
+ codec->reg_cache = kmemdup(rt5625_reg, sizeof(rt5625_reg), GFP_KERNEL);\r
+ if (codec->reg_cache == NULL)\r
+ return -ENOMEM;\r
+\r
+ rt5625_reset(codec);\r
+\r
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);\r
+ if (ret < 0 )\r
+ {\r
+ printk(KERN_ERR "rt5625: failed to create pcms\n");\r
+ goto pcm_err;\r
+ }\r
+ \r
+ rt5625_write(codec, RT5625_PD_CTRL_STAT, 0);\r
+ rt5625_write(codec, RT5625_PWR_MANAG_ADD1, PWR_MAIN_BIAS);\r
+ rt5625_write(codec, RT5625_PWR_MANAG_ADD2, PWR_MIXER_VREF);\r
+ rt5625_reg_init(codec);\r
+ rt5625_set_bias_level(codec, SND_SOC_BIAS_PREPARE);\r
+ codec->bias_level = SND_SOC_BIAS_STANDBY;\r
+ schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(80));\r
+\r
+#if (RT5625_EQ_FUNC_ENA==1) \r
+ rt5625_update_eqmode(codec,POP);\r
+#endif\r
+ \r
+ rt5625_add_controls(codec);\r
+ rt5625_add_widgets(codec);\r
+\r
+ #if defined(CONFIG_SND_HWDEP)\r
+\r
+ #if REALTEK_HWDEP\r
+\r
+ realtek_ce_init_hwdep(codec);\r
+\r
+ #endif\r
+\r
+ #endif\r
+\r
+ ret = snd_soc_init_card(socdev);\r
+\r
+ if (ret < 0)\r
+ {\r
+ printk(KERN_ERR "rt5625: failed to register card\n");\r
+ goto card_err;\r
+ }\r
+ DBG("rt5625: initial ok\n");\r
+ return 0;\r
+\r
+ card_err:\r
+ snd_soc_free_pcms(socdev);\r
+ snd_soc_dapm_free(socdev);\r
+ \r
+ pcm_err:\r
+ kfree(codec->reg_cache);\r
+ return ret;\r
+ \r
+ \r
+}\r
+\r
+\r
+static int rt5625_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)\r
+{\r
+ struct snd_soc_device *socdev = rt5625_socdev;\r
+ struct snd_soc_codec *codec = socdev->card->codec;\r
+ int ret;\r
+\r
+ i2c_set_clientdata(i2c, codec);\r
+ codec->control_data = i2c;\r
+\r
+ ret = rt5625_init(socdev);\r
+ if (ret < 0)\r
+ pr_err("failed to initialise rt5625\n");\r
+\r
+ return ret;\r
+}\r
+\r
+static int rt5625_i2c_remove(struct i2c_client *client)\r
+{\r
+ struct snd_soc_codec *codec = i2c_get_clientdata(client);\r
+ kfree(codec->reg_cache);\r
+ return 0;\r
+}\r
+\r
+static const struct i2c_device_id rt5625_i2c_id[] = {\r
+ {"rt5625", 0},\r
+ {}\r
+};\r
+MODULE_DEVICE_TABLE(i2c, rt5625_i2c_id);\r
+static struct i2c_driver rt5625_i2c_driver = {\r
+ .driver = {\r
+ .name = "RT5625 I2C Codec",\r
+ .owner = THIS_MODULE,\r
+ },\r
+ .probe = rt5625_i2c_probe,\r
+ .remove = rt5625_i2c_remove,\r
+ .id_table = rt5625_i2c_id,\r
+};\r
+\r
+\r
+static int rt5625_add_i2c_device(struct platform_device *pdev,\r
+ const struct rt5625_setup_data *setup)\r
+{\r
+#if 0\r
+ struct i2c_board_info info;\r
+ struct i2c_adapter *adapter;\r
+ struct i2c_client *client;\r
+#endif\r
+ int ret;\r
+\r
+ ret = i2c_add_driver(&rt5625_i2c_driver);\r
+ if (ret != 0) {\r
+ dev_err(&pdev->dev, "can't add i2c driver\n");\r
+ return ret;\r
+ }\r
+#if 0\r
+ memset(&info, 0, sizeof(struct i2c_board_info));\r
+ info.addr = setup->i2c_address;\r
+ strlcpy(info.type, "rt5625", I2C_NAME_SIZE);\r
+\r
+ adapter = i2c_get_adapter(setup->i2c_bus);\r
+ if (!adapter) {\r
+ dev_err(&pdev->dev, "can't get i2c adapter %d\n",\r
+ setup->i2c_bus);\r
+ goto err_driver;\r
+ }\r
+\r
+ client = i2c_new_device(adapter, &info);\r
+ i2c_put_adapter(adapter);\r
+ if (!client) {\r
+ dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",\r
+ (unsigned int)info.addr);\r
+ goto err_driver;\r
+ }\r
+#endif\r
+ return 0;\r
+#if 0\r
+err_driver:\r
+ i2c_del_driver(&rt5625_i2c_driver);\r
+ return -ENODEV;\r
+#endif\r
+}\r
+\r
+\r
+static int rt5625_probe(struct platform_device *pdev)\r
+{\r
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);\r
+ struct rt5625_setup_data *setup;\r
+ struct snd_soc_codec *codec;\r
+ struct rt5625_priv *rt5625;\r
+ int ret;\r
+\r
+ pr_info("RT5625 Audio Codec %s", RT5625_VERSION);\r
+\r
+ if(socdev->codec_data)\r
+ {\r
+ setup = socdev->codec_data; \r
+ }\r
+\r
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);\r
+ if (codec == NULL)\r
+ return -ENOMEM;\r
+\r
+ rt5625 = kzalloc(sizeof(struct rt5625_priv), GFP_KERNEL);\r
+ if (rt5625 == NULL) {\r
+ kfree(codec);\r
+ return -ENOMEM;\r
+ }\r
+ codec->private_data = rt5625;\r
+ socdev->card->codec = codec;\r
+\r
+ mutex_init(&codec->mutex);\r
+ INIT_LIST_HEAD(&codec->dapm_widgets);\r
+ INIT_LIST_HEAD(&codec->dapm_paths);\r
+ rt5625_socdev = socdev;\r
+ INIT_DELAYED_WORK(&codec->delayed_work, rt5625_work);\r
+\r
+ ret = -ENODEV;\r
+// if (setup->i2c_address) \r
+ {\r
+ codec->hw_write = (hw_write_t)i2c_master_send;\r
+ //codec->hw_read = (hw_read_t)i2c_master_recv;\r
+ ret = rt5625_add_i2c_device(pdev, setup);\r
+ }\r
+\r
+ if (ret != 0) {\r
+ kfree(codec->private_data);\r
+ kfree(codec);\r
+ socdev->card->codec = NULL;\r
+ }\r
+ return ret;\r
+}\r
+\r
+static int run_delayed_work(struct delayed_work *dwork)\r
+{\r
+ int ret;\r
+\r
+ /* cancel any work waiting to be queued. */\r
+ ret = cancel_delayed_work(dwork);\r
+\r
+ /* if there was any work waiting then we run it now and\r
+ * wait for it's completion */\r
+ if (ret) {\r
+ schedule_delayed_work(dwork, 0);\r
+ flush_scheduled_work();\r
+ }\r
+ return ret;\r
+}\r
+\r
+\r
+static int rt5625_remove(struct platform_device *pdev)\r
+{\r
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);\r
+ struct snd_soc_codec *codec = socdev->card->codec;\r
+\r
+ if (codec->control_data)\r
+ rt5625_set_bias_level(codec, SND_SOC_BIAS_OFF);\r
+ run_delayed_work(&codec->delayed_work);\r
+ snd_soc_free_pcms(socdev);\r
+ snd_soc_dapm_free(socdev);\r
+ i2c_unregister_device(codec->control_data);\r
+ i2c_del_driver(&rt5625_i2c_driver);\r
+ kfree(codec->private_data);\r
+ kfree(codec);\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+static int rt5625_suspend(struct platform_device *pdev, pm_message_t state)\r
+{\r
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);\r
+ struct snd_soc_codec *codec = socdev->card->codec;\r
+\r
+ rt5625_set_bias_level(codec, SND_SOC_BIAS_OFF);\r
+\r
+ return 0;\r
+}\r
+\r
+static int rt5625_resume(struct platform_device *pdev)\r
+{\r
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);\r
+ struct snd_soc_codec *codec = socdev->card->codec;\r
+\r
+ \r
+// int i;\r
+// u8 data[3];\r
+// u16 *cache = codec->reg_cache;\r
+\r
+#if 1\r
+ rt5625_reset(codec);\r
+ rt5625_write(codec, RT5625_PD_CTRL_STAT, 0);\r
+ rt5625_write(codec, RT5625_PWR_MANAG_ADD1, PWR_MAIN_BIAS);\r
+ rt5625_write(codec, RT5625_PWR_MANAG_ADD2, PWR_MIXER_VREF);\r
+ rt5625_reg_init(codec);\r
+#else\r
+ /* Sync reg_cache with the hardware */\r
+ for (i = 0; i < ARRAY_SIZE(rt5625_reg); i++) {\r
+ if (i == RT5625_RESET)\r
+ continue;\r
+ data[0] = i << 1;\r
+ data[1] = (0xff00 & cache[i]) >> 8;\r
+ data[2] = 0x00ff & cache[i];\r
+ codec->hw_write(codec->control_data, data, 3);\r
+ }\r
+\r
+ rt5625_set_bias_level(codec, SND_SOC_BIAS_STANDBY);\r
+#endif\r
+\r
+ /* charge rt5625 caps */\r
+ if (codec->suspend_bias_level == SND_SOC_BIAS_ON) {\r
+ rt5625_set_bias_level(codec, SND_SOC_BIAS_PREPARE);\r
+ codec->bias_level = SND_SOC_BIAS_ON;\r
+ schedule_delayed_work(&codec->delayed_work,\r
+ msecs_to_jiffies(100));\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+struct snd_soc_codec_device soc_codec_dev_rt5625 = {\r
+ .probe = rt5625_probe,\r
+ .remove = rt5625_remove,\r
+ .suspend = rt5625_suspend,\r
+ .resume = rt5625_resume,\r
+};\r
+\r
+EXPORT_SYMBOL_GPL(soc_codec_dev_rt5625);\r
+\r
+static int __init rt5625_modinit(void)\r
+{\r
+ return snd_soc_register_dais(rt5625_dai, ARRAY_SIZE(rt5625_dai));\r
+}\r
+\r
+static void __exit rt5625_exit(void)\r
+{\r
+ snd_soc_unregister_dais(rt5625_dai, ARRAY_SIZE(rt5625_dai));\r
+}\r
+\r
+module_init(rt5625_modinit);\r
+module_exit(rt5625_exit);\r
+MODULE_LICENSE("GPL");\r
+\r