add rt5621 driver for kernel 3.0 and change wm8994 driver
author陈金泉 <chenjq@rock-chips.com>
Tue, 22 Nov 2011 10:10:23 +0000 (18:10 +0800)
committer陈金泉 <chenjq@rock-chips.com>
Tue, 22 Nov 2011 10:10:23 +0000 (18:10 +0800)
17 files changed:
arch/arm/configs/rk29_ddr3sdk_defconfig
arch/arm/configs/rk29_k97_defconfig
arch/arm/mach-rk29/board-rk29-ddr3sdk.c
arch/arm/mach-rk29/board-rk29-k97.c
drivers/mfd/wm8994-core.c
drivers/video/hdmi/hdmi-codec.c
include/linux/mfd/wm8994/core.h
include/linux/mfd/wm8994/pdata.h
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/rt5621.c [new file with mode: 0644]
sound/soc/codecs/rt5621.h [new file with mode: 0644]
sound/soc/codecs/wm8994.c
sound/soc/rk29/Kconfig
sound/soc/rk29/Makefile
sound/soc/rk29/rk29_rt5621.c [new file with mode: 0644]
sound/soc/rk29/rk29_wm8994.c

index 6a67e306859aa6255b0f221a60347d801432b576..f6ae57985f5fddf03504c0e05f8a32e5e6ed4bf4 100755 (executable)
@@ -1527,8 +1527,8 @@ CONFIG_SND_RK29_SOC_I2S=y
 CONFIG_SND_RK29_SOC_I2S_8CH=y
 # CONFIG_SND_RK29_SOC_WM8988 is not set
 CONFIG_SND_RK29_SOC_WM8900=y
-# CONFIG_SND_RK29_SOC_alc5621 is not set
-# CONFIG_SND_RK29_SOC_alc5631 is not set
+# CONFIG_SND_RK29_SOC_RT5621 is not set
+# CONFIG_SND_RK29_SOC_RT5631 is not set
 # CONFIG_SND_RK29_SOC_RT5625 is not set
 # CONFIG_SND_RK29_SOC_WM8994 is not set
 # CONFIG_SND_RK29_SOC_CS42L52 is not set
index c9dc742bedadc8f2caab65cda2a67601acd6259e..3253901ed3aec776eb30a864639e4a4ff60421b1 100644 (file)
@@ -1841,7 +1841,7 @@ CONFIG_SND_RK29_SOC_I2S_8CH=y
 CONFIG_SND_I2S_DMA_EVENT_STATIC=y
 # CONFIG_SND_RK29_SOC_WM8988 is not set
 # CONFIG_SND_RK29_SOC_WM8900 is not set
-# CONFIG_SND_RK29_SOC_alc5621 is not set
+# CONFIG_SND_RK29_SOC_RT5621 is not set
 CONFIG_SND_RK29_SOC_RT5631=y
 # CONFIG_SND_RK29_SOC_RT5625 is not set
 # CONFIG_SND_RK29_SOC_WM8994 is not set
index 295cb617c47f00bdba7f4d8758554c1e505282ca..cfb6a066dfe271670fd244e1ce3472685104ec5b 100755 (executable)
@@ -1411,9 +1411,9 @@ static struct i2c_board_info __initdata board_i2c0_devices[] = {
                .flags                  = 0,
        },
 #endif
-#if defined (CONFIG_SND_SOC_alc5621)
+#if defined (CONFIG_SND_SOC_RT5621)
         {
-                .type                   = "ALC5621",
+                .type                   = "rt5621",
                 .addr                   = 0x1a,
                 .flags                  = 0,
         },
index c99bfebec354bac7020b79232737cd386c14f136..58509d2542537c0ca690cf8713494610a11d29e0 100755 (executable)
@@ -1449,9 +1449,9 @@ static struct i2c_board_info __initdata board_i2c0_devices[] = {
                .flags                  = 0,
        },
 #endif
-#if defined (CONFIG_SND_SOC_alc5621)
+#if defined (CONFIG_SND_SOC_RT5621)
         {
-                .type                   = "ALC5621",
+                .type                   = "rt5621",
                 .addr                   = 0x1a,
                 .flags                  = 0,
         },
index e198d40292e7f5cb9c1ed5e78c4d3f41e30f1bc5..7ecd7de78c3366894660f09fe81e8a7e85fbfe86 100644 (file)
@@ -651,7 +651,7 @@ static UNIVERSAL_DEV_PM_OPS(wm8994_pm_ops, wm8994_suspend, wm8994_resume,
 
 static struct i2c_driver wm8994_i2c_driver = {
        .driver = {
-               .name = "wm8994",
+               .name = "WM8994",
                .owner = THIS_MODULE,
                .pm = &wm8994_pm_ops,
        },
index 49c824899e5e9b56cd7618def1d98656e4bad8ee..e0360ceb7fd6a025acc72b5037a7d6178411cd59 100755 (executable)
@@ -1,5 +1,5 @@
 #include <linux/hdmi.h>
-#if defined CONFIG_SND_SOC_WM8900 || defined CONFIG_SND_SOC_RT5631
+#if defined CONFIG_SND_SOC_WM8900 || defined CONFIG_SND_SOC_RT5631 || defined CONFIG_SND_SOC_RT5621
 /* sound/soc/codecs/wm8900.c */
 extern void codec_set_spk(bool on);
 #else
index 7929fc9d67091c6e2502b9caaf584b35a545943f..f0b69cdae41cc94f24c995d9812301d9eb711451 100644 (file)
@@ -15,7 +15,6 @@
 #ifndef __MFD_WM8994_CORE_H__
 #define __MFD_WM8994_CORE_H__
 
-<<<<<<< HEAD
 #include <linux/interrupt.h>
 
 enum wm8994_type {
@@ -23,16 +22,36 @@ enum wm8994_type {
        WM8958 = 1,
 };
 
-=======
->>>>>>> parent of 15f7fab... temp revert rk change
 struct regulator_dev;
 struct regulator_bulk_data;
 
 #define WM8994_NUM_GPIO_REGS 11
-#define WM8994_NUM_LDO_REGS 2
+#define WM8994_NUM_LDO_REGS   2
+#define WM8994_NUM_IRQ_REGS   2
+
+#define WM8994_IRQ_TEMP_SHUT           0
+#define WM8994_IRQ_MIC1_DET            1
+#define WM8994_IRQ_MIC1_SHRT           2
+#define WM8994_IRQ_MIC2_DET            3
+#define WM8994_IRQ_MIC2_SHRT           4
+#define WM8994_IRQ_FLL1_LOCK           5
+#define WM8994_IRQ_FLL2_LOCK           6
+#define WM8994_IRQ_SRC1_LOCK           7
+#define WM8994_IRQ_SRC2_LOCK           8
+#define WM8994_IRQ_AIF1DRC1_SIG_DET    9
+#define WM8994_IRQ_AIF1DRC2_SIG_DET    10
+#define WM8994_IRQ_AIF2DRC_SIG_DET     11
+#define WM8994_IRQ_FIFOS_ERR           12
+#define WM8994_IRQ_WSEQ_DONE           13
+#define WM8994_IRQ_DCS_DONE            14
+#define WM8994_IRQ_TEMP_WARN           15
+
+/* GPIOs in the chip are numbered from 1-11 */
+#define WM8994_IRQ_GPIO(x) (x + WM8994_IRQ_TEMP_WARN)
 
 struct wm8994 {
        struct mutex io_lock;
+       struct mutex irq_lock;
 
        enum wm8994_type type;
 
@@ -45,6 +64,11 @@ struct wm8994 {
        void *control_data;
 
        int gpio_base;
+       int irq_base;
+
+       int irq;
+       u16 irq_masks_cur[WM8994_NUM_IRQ_REGS];
+       u16 irq_masks_cache[WM8994_NUM_IRQ_REGS];
 
        /* Used over suspend/resume */
        bool suspended;
@@ -67,4 +91,26 @@ int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg,
 int wm8994_bulk_write(struct wm8994 *wm8994, unsigned short reg,
                     int count, const u16 *buf);
 
+
+/* Helper to save on boilerplate */
+static inline int wm8994_request_irq(struct wm8994 *wm8994, int irq,
+                                    irq_handler_t handler, const char *name,
+                                    void *data)
+{
+       if (!wm8994->irq_base)
+               return -EINVAL;
+       return request_threaded_irq(wm8994->irq_base + irq, NULL, handler,
+                                   IRQF_TRIGGER_RISING, name,
+                                   data);
+}
+static inline void wm8994_free_irq(struct wm8994 *wm8994, int irq, void *data)
+{
+       if (!wm8994->irq_base)
+               return;
+       free_irq(wm8994->irq_base + irq, data);
+}
+
+int wm8994_irq_init(struct wm8994 *wm8994);
+void wm8994_irq_exit(struct wm8994 *wm8994);
+
 #endif
index f63b990b366e77a3a2ee87a89d4de34b6314d432..97cf4f27d6470120a389bb8a2e1217871b8e3411 100755 (executable)
@@ -26,10 +26,16 @@ struct wm8994_ldo_pdata {
        struct regulator_init_data *init_data;
 };
 
-#define WM8994_CONFIGURE_GPIO 0x8000
+#define WM8994_CONFIGURE_GPIO 0x10000
 
 #define WM8994_DRC_REGS 5
-#define WM8994_EQ_REGS  19
+#define WM8994_EQ_REGS  20
+#define WM8958_MBC_CUTOFF_REGS 20
+#define WM8958_MBC_COEFF_REGS  48
+#define WM8958_MBC_COMBINED_REGS 56
+#define WM8958_VSS_HPF_REGS 2
+#define WM8958_VSS_REGS 148
+#define WM8958_ENH_EQ_REGS 32
 
 /**
  * DRC configurations are specified with a label and a set of register
@@ -59,8 +65,53 @@ struct wm8994_retune_mobile_cfg {
         u16 regs[WM8994_EQ_REGS];
 };
 
-#define PCM_BB 1
-#define NO_PCM_BB 0
+/**
+ * Multiband compressor configurations are specified with a label and
+ * two sets of values to write.  Configurations are expected to be
+ * generated using the multiband compressor configuration panel in
+ * WISCE - see http://www.wolfsonmicro.com/wisce/
+ */
+struct wm8958_mbc_cfg {
+       const char *name;
+       u16 cutoff_regs[WM8958_MBC_CUTOFF_REGS];
+       u16 coeff_regs[WM8958_MBC_COEFF_REGS];
+
+       /* Coefficient layout when using MBC+VSS firmware */
+       u16 combined_regs[WM8958_MBC_COMBINED_REGS];
+};
+
+/**
+ * VSS HPF configurations are specified with a label and two values to
+ * write.  Configurations are expected to be generated using the
+ * multiband compressor configuration panel in WISCE - see
+ * http://www.wolfsonmicro.com/wisce/
+ */
+struct wm8958_vss_hpf_cfg {
+       const char *name;
+       u16 regs[WM8958_VSS_HPF_REGS];
+};
+
+/**
+ * VSS configurations are specified with a label and array of values
+ * to write.  Configurations are expected to be generated using the
+ * multiband compressor configuration panel in WISCE - see
+ * http://www.wolfsonmicro.com/wisce/
+ */
+struct wm8958_vss_cfg {
+       const char *name;
+       u16 regs[WM8958_VSS_REGS];
+};
+
+/**
+ * Enhanced EQ configurations are specified with a label and array of
+ * values to write.  Configurations are expected to be generated using
+ * the multiband compressor configuration panel in WISCE - see
+ * http://www.wolfsonmicro.com/wisce/
+ */
+struct wm8958_enh_eq_cfg {
+       const char *name;
+       u16 regs[WM8958_ENH_EQ_REGS];
+};
 
 struct wm8994_pdata {
        int gpio_base;
@@ -73,6 +124,7 @@ struct wm8994_pdata {
 
        struct wm8994_ldo_pdata ldo[WM8994_NUM_LDO];
 
+       int irq_base;  /** Base IRQ number for WM8994, required for IRQs */
 
         int num_drc_cfgs;
         struct wm8994_drc_cfg *drc_cfgs;
@@ -80,6 +132,18 @@ struct wm8994_pdata {
         int num_retune_mobile_cfgs;
         struct wm8994_retune_mobile_cfg *retune_mobile_cfgs;
 
+       int num_mbc_cfgs;
+       struct wm8958_mbc_cfg *mbc_cfgs;
+
+       int num_vss_cfgs;
+       struct wm8958_vss_cfg *vss_cfgs;
+
+       int num_vss_hpf_cfgs;
+       struct wm8958_vss_hpf_cfg *vss_hpf_cfgs;
+
+       int num_enh_eq_cfgs;
+       struct wm8958_enh_eq_cfg *enh_eq_cfgs;
+
         /* LINEOUT can be differential or single ended */
         unsigned int lineout1_diff:1;
         unsigned int lineout2_diff:1;
@@ -88,42 +152,21 @@ struct wm8994_pdata {
         unsigned int lineout1fb:1;
         unsigned int lineout2fb:1;
 
-        /* Microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */
+       /* IRQ for microphone detection if brought out directly as a
+        * signal.
+        */
+       int micdet_irq;
+
+        /* WM8994 microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */
         unsigned int micbias1_lvl:1;
         unsigned int micbias2_lvl:1;
 
-        /* Jack detect threashold levels, see datasheet for values */
+        /* WM8994 jack detect threashold levels, see datasheet for values */
         unsigned int jd_scthr:2;
         unsigned int jd_thr:2;
 
-               //for phonepad
-               unsigned int no_earpiece:1;      // =1  don't have a earpiece,  =0   has a earpiece
-               unsigned int sp_hp_same_channel:1;
-               
-               //BB input can be differential or single ended
-               unsigned int BB_input_diff:1;   //  =0  single ended     =1  differential
-               unsigned int BB_class:1;//PCM_BB= 1  NO_PCM_BB=0
-               
-               //If an external amplifier speakers wm8994              enable>0 disable=0
-               unsigned int PA_control_pin;
-
-               //wm8994 LDO1_ENA and LDO2_ENA
-               unsigned int Power_EN_Pin;
-               char    PowerEN_iomux_name[50];
-               int             PowerEN_iomux_mode;     
-
-       //volume
-       int speaker_incall_vol;                 //max = 6, min = -21
-       int speaker_incall_mic_vol;             //max = 30, min = -22
-       int speaker_normal_vol;                 //max = 6, min = -57
-       int earpiece_incall_vol;                //max = 6, min = -21
-       int headset_incall_vol;                 //max = 6, min = -12
-       int headset_incall_mic_vol;             //max = 30, min = -22
-       int headset_normal_vol;                 //max = 6, min = -57
-       int BT_incall_vol;                              //max = 30, min = -16
-       int BT_incall_mic_vol;                  //max = 6, min = -57
-       int recorder_vol;                               //max = 60 , min = -16
-               
+       /* WM8958 microphone bias configuration */
+       int micbias[2];
 };
 
 #endif
index 9f50f4f54a675f1c2a1bc83f13175d5848b39bdd..9dfeb311759f634520b068d5c444b24274219481 100644 (file)
@@ -74,7 +74,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8804 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8900 if I2C
-       select SND_SOC_alc5621 if I2C
+       select SND_SOC_RT5621 if I2C
        select SND_SOC_RT5631 if I2C
        select SND_SOC_RT5625 if I2C
        select SND_SOC_WM8903 if I2C
@@ -312,7 +312,7 @@ config SND_SOC_WM8804
 config SND_SOC_WM8900
        tristate
 
-config SND_SOC_alc5621
+config SND_SOC_RT5621
        tristate
 
 config SND_SOC_RT5631
index b4bf7e53b76b18c4dc14f3101cc88a8a00028ad6..b5c69a02e817578f477c5330314c1e2d1cd6c309 100644 (file)
@@ -57,7 +57,7 @@ snd-soc-wm8770-objs := wm8770.o
 snd-soc-wm8776-objs := wm8776.o
 snd-soc-wm8804-objs := wm8804.o
 snd-soc-wm8900-objs := wm8900.o
-snd-soc-alc5621-objs := alc5621.o
+snd-soc-rt5621-objs := rt5621.o
 snd-soc-rt5631-objs := rt5631.o
 snd-soc-rt5625-objs := rt5625.o
 snd-soc-cs42l52-objs := cs42l52.o
@@ -154,7 +154,7 @@ obj-$(CONFIG_SND_SOC_WM8770)        += snd-soc-wm8770.o
 obj-$(CONFIG_SND_SOC_WM8776)   += snd-soc-wm8776.o
 obj-$(CONFIG_SND_SOC_WM8804)   += snd-soc-wm8804.o
 obj-$(CONFIG_SND_SOC_WM8900)   += snd-soc-wm8900.o
-obj-$(CONFIG_SND_SOC_alc5621)  += snd-soc-alc5621.o
+obj-$(CONFIG_SND_SOC_RT5621)   += snd-soc-rt5621.o
 obj-$(CONFIG_SND_SOC_RT5631)   += snd-soc-rt5631.o
 obj-$(CONFIG_SND_SOC_RT5625)   += snd-soc-rt5625.o
 obj-$(CONFIG_SND_SOC_CS42L52)  += snd-soc-cs42l52.o
diff --git a/sound/soc/codecs/rt5621.c b/sound/soc/codecs/rt5621.c
new file mode 100644 (file)
index 0000000..c0ee91b
--- /dev/null
@@ -0,0 +1,1221 @@
+/*
+ * rt5621.c  --  RT5621 ALSA SoC audio codec driver
+ *
+ * Copyright 2011 Realtek Semiconductor Corp.
+ * Author: Johnny Hsu <johnnyhsu@realtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rt5621.h"
+
+#if REALTEK_HWDEP
+#include <linux/ioctl.h>
+#include <linux/types.h>
+#endif
+
+#if 0
+#define DBG(x...)      printk(x)
+#else
+#define DBG(x...)
+#endif
+
+#define RT5621_VERSION "0.01 alsa 1.0.24"
+
+static int caps_charge = 500;
+module_param(caps_charge, int, 0);
+MODULE_PARM_DESC(caps_charge, "RT5621 cap charge time (msecs)");
+
+struct snd_soc_codec *rt5621_codec;
+
+static void rt5621_work(struct work_struct *work);
+
+static struct workqueue_struct *rt5621_workq;
+static DECLARE_DELAYED_WORK(delayed_work, rt5621_work);
+
+#define ENABLE_EQ_HREQ 1
+
+struct rt5621_priv {
+       unsigned int sysclk;
+};
+
+enum {
+       NORMAL,
+       CLUB,
+       DANCE,
+       LIVE,
+       POP,
+       ROCK,
+       OPPO,
+       TREBLE,
+       BASS,
+       HFREQ,  
+       EQTEST,
+       SPK_FR  
+};
+
+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 0x 0x9 0xa 0x 0xc 0x62*/
+       {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   ,{0x1E98,0xFCB6,0xC340,0x1D60,0x095B,0xC6AC,0x1BBC,0x0556,0x0689,0x0F33,0x0000,0xEDD1,0xF805},0x801F},
+       {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},
+       {EQTEST,{0x1F8C,0x1830,0xC118,0x1EEF,0xFD77,0xD8CB,0x1BBC,0x0556,0x0689,0x0F33,0x0000,0xF17F,0x0FEC},0x8003},
+};
+
+static const u16 rt5621_reg[RT5621_VENDOR_ID2 + 1] = {
+       [RT5621_RESET] = 0x59b4,
+       [RT5621_SPK_OUT_VOL] = 0x8080,
+       [RT5621_HP_OUT_VOL] = 0x8080,
+       [RT5621_MONO_AUX_OUT_VOL] = 0x8080,
+       [RT5621_AUXIN_VOL] = 0xe808,
+       [RT5621_LINE_IN_VOL] = 0xe808,
+       [RT5621_STEREO_DAC_VOL] = 0xe808,
+       [RT5621_MIC_VOL] = 0x0808,
+       [RT5621_MIC_ROUTING_CTRL] = 0xe0e0,
+       [RT5621_ADC_REC_GAIN] = 0xf58b,
+       [RT5621_ADC_REC_MIXER] = 0x7f7f,
+       [RT5621_SOFT_VOL_CTRL_TIME] = 0x000a,
+       [RT5621_OUTPUT_MIXER_CTRL] = 0xc000,
+       [RT5621_AUDIO_INTERFACE] = 0x8000,
+       [RT5621_STEREO_AD_DA_CLK_CTRL] = 0x166d,
+       [RT5621_ADD_CTRL_REG] = 0x5300,
+       [RT5621_GPIO_PIN_CONFIG] = 0x1c0e,
+       [RT5621_GPIO_PIN_POLARITY] = 0x1c0e,
+       [RT5621_GPIO_PIN_STATUS] = 0x0002,
+       [RT5621_OVER_TEMP_CURR_STATUS] = 0x003c,
+       [RT5621_PSEDUEO_SPATIAL_CTRL] = 0x0497,
+       [RT5621_AVC_CTRL] = 0x000b,
+       [RT5621_VENDOR_ID1] = 0x10ec,
+       [RT5621_VENDOR_ID2] = 0x2003,
+};
+
+#define rt5621_write_mask(c, reg, value, mask) snd_soc_update_bits(c, reg, mask, value)
+
+#define rt5621_write_index_reg(c, addr, data) \
+{ \
+       snd_soc_write(c, 0x6a, addr); \
+       snd_soc_write(c, 0x6c, data); \
+}
+
+static int rt5621_reset(struct snd_soc_codec *codec)
+{
+       return snd_soc_write(codec, RT5621_RESET, 0);
+}
+
+static int rt5621_volatile_register(
+       struct snd_soc_codec *codec, unsigned int reg)
+{
+       switch (reg) {
+       case RT5621_RESET:
+       case RT5621_HID_CTRL_DATA:
+       case RT5621_GPIO_PIN_STATUS:
+       case RT5621_OVER_TEMP_CURR_STATUS:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static int rt5621_readable_register(
+       struct snd_soc_codec *codec, unsigned int reg)
+{
+       switch (reg) {
+       case RT5621_RESET:
+       case RT5621_SPK_OUT_VOL:
+       case RT5621_HP_OUT_VOL:
+       case RT5621_MONO_AUX_OUT_VOL:
+       case RT5621_AUXIN_VOL:
+       case RT5621_LINE_IN_VOL:
+       case RT5621_STEREO_DAC_VOL:
+       case RT5621_MIC_VOL:
+       case RT5621_MIC_ROUTING_CTRL:
+       case RT5621_ADC_REC_GAIN:
+       case RT5621_ADC_REC_MIXER:
+       case RT5621_SOFT_VOL_CTRL_TIME:
+       case RT5621_OUTPUT_MIXER_CTRL:
+       case RT5621_MIC_CTRL:
+       case RT5621_AUDIO_INTERFACE:
+       case RT5621_STEREO_AD_DA_CLK_CTRL:
+       case RT5621_COMPANDING_CTRL:
+       case RT5621_PWR_MANAG_ADD1:
+       case RT5621_PWR_MANAG_ADD2:
+       case RT5621_PWR_MANAG_ADD3:
+       case RT5621_ADD_CTRL_REG:
+       case RT5621_GLOBAL_CLK_CTRL_REG:
+       case RT5621_PLL_CTRL:
+       case RT5621_GPIO_OUTPUT_PIN_CTRL:
+       case RT5621_GPIO_PIN_CONFIG:
+       case RT5621_GPIO_PIN_POLARITY:
+       case RT5621_GPIO_PIN_STICKY:
+       case RT5621_GPIO_PIN_WAKEUP:
+       case RT5621_GPIO_PIN_STATUS:
+       case RT5621_GPIO_PIN_SHARING:
+       case RT5621_OVER_TEMP_CURR_STATUS:
+       case RT5621_JACK_DET_CTRL:
+       case RT5621_MISC_CTRL:
+       case RT5621_PSEDUEO_SPATIAL_CTRL:
+       case RT5621_EQ_CTRL:
+       case RT5621_EQ_MODE_ENABLE:
+       case RT5621_AVC_CTRL:
+       case RT5621_HID_CTRL_INDEX:
+       case RT5621_HID_CTRL_DATA:
+       case RT5621_VENDOR_ID1:
+       case RT5621_VENDOR_ID2:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+
+//static const char *rt5621_spkl_pga[] = {"Vmid","HPL mixer","SPK mixer","Mono Mixer"};
+static const char *rt5621_spkn_source_sel[] = {"RN", "RP", "LN"};
+static const char *rt5621_spk_pga[] = {"Vmid","HP mixer","SPK mixer","Mono Mixer"};
+static const char *rt5621_hpl_pga[]  = {"Vmid","HPL mixer"};
+static const char *rt5621_hpr_pga[]  = {"Vmid","HPR mixer"};
+static const char *rt5621_mono_pga[] = {"Vmid","HP mixer","SPK mixer","Mono Mixer"};
+static const char *rt5621_amp_type_sel[] = {"Class AB","Class D"};
+static const char *rt5621_mic_boost_sel[] = {"Bypass","20db","30db","40db"};
+
+static const struct soc_enum rt5621_enum[] = {
+SOC_ENUM_SINGLE(RT5621_OUTPUT_MIXER_CTRL, 14, 3, rt5621_spkn_source_sel), /* spkn source from hp mixer */      
+SOC_ENUM_SINGLE(RT5621_OUTPUT_MIXER_CTRL, 10, 4, rt5621_spk_pga), /* spk input sel 1 */        
+SOC_ENUM_SINGLE(RT5621_OUTPUT_MIXER_CTRL, 9, 2, rt5621_hpl_pga), /* hp left input sel 2 */     
+SOC_ENUM_SINGLE(RT5621_OUTPUT_MIXER_CTRL, 8, 2, rt5621_hpr_pga), /* hp right input sel 3 */    
+SOC_ENUM_SINGLE(RT5621_OUTPUT_MIXER_CTRL, 6, 4, rt5621_mono_pga), /* mono input sel 4 */
+SOC_ENUM_SINGLE(RT5621_MIC_CTRL, 10,4, rt5621_mic_boost_sel), /*Mic1 boost sel 5 */
+SOC_ENUM_SINGLE(RT5621_MIC_CTRL, 8,4,rt5621_mic_boost_sel), /*Mic2 boost sel 6 */
+SOC_ENUM_SINGLE(RT5621_OUTPUT_MIXER_CTRL,13,2,rt5621_amp_type_sel), /*Speaker AMP sel 7 */
+};
+
+static int rt5621_amp_sel_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       unsigned short val;
+       unsigned short mask, bitmask;
+
+       for (bitmask = 1; bitmask < e->max; bitmask <<= 1);
+
+       if (ucontrol->value.enumerated.item[0] > e->max - 1)
+               return -EINVAL;
+       val = ucontrol->value.enumerated.item[0] << e->shift_l;
+       mask = (bitmask - 1) << e->shift_l;
+       if (e->shift_l != e->shift_r) {
+               if (ucontrol->value.enumerated.item[1] > e->max - 1)
+                       return -EINVAL;
+               val |= ucontrol->value.enumerated.item[1] << e->shift_r;
+               mask |= (bitmask - 1) << e->shift_r;
+       }
+
+       snd_soc_update_bits(codec, e->reg, mask, val);
+       val &= (0x1 << 13);
+
+       if (val == 0)
+       {
+               snd_soc_update_bits(codec, 0x3c, 0x0000, 0x4000);       /*power off classd*/
+               snd_soc_update_bits(codec, 0x3c, 0x8000, 0x8000);       /*power on classab*/
+       } else {
+               snd_soc_update_bits(codec, 0x3c, 0x0000, 0x8000);       /*power off classab*/
+               snd_soc_update_bits(codec, 0x3c, 0x4000, 0x4000);       /*power on classd*/
+       }
+
+       return 0;
+}
+
+//*****************************************************************************
+//
+//function:Change audio codec power status
+//
+//*****************************************************************************
+static int rt5621_ChangeCodecPowerStatus(struct snd_soc_codec *codec,int power_state)
+{
+       unsigned short int PowerDownState=0;
+
+       switch(power_state) {
+       case POWER_STATE_D0: //FULL ON-----power on all power
+                       
+               snd_soc_write(codec,RT5621_PWR_MANAG_ADD1,~PowerDownState);
+               snd_soc_write(codec,RT5621_PWR_MANAG_ADD2,~PowerDownState);
+               snd_soc_write(codec,RT5621_PWR_MANAG_ADD3,~PowerDownState);
+               break;
+       case POWER_STATE_D1: //LOW ON-----
+               rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD2 ,PWR_VREF |
+                       PWR_DAC_REF_CIR | PWR_L_DAC_CLK | PWR_R_DAC_CLK |
+                       PWR_L_HP_MIXER | PWR_R_HP_MIXER | PWR_L_ADC_CLK_GAIN |
+                       PWR_R_ADC_CLK_GAIN | PWR_L_ADC_REC_MIXER | PWR_R_ADC_REC_MIXER |
+                       PWR_CLASS_AB, PWR_VREF | PWR_DAC_REF_CIR | PWR_L_DAC_CLK |
+                       PWR_R_DAC_CLK | PWR_L_HP_MIXER | PWR_R_HP_MIXER |
+                       PWR_L_ADC_CLK_GAIN | PWR_R_ADC_CLK_GAIN |
+                       PWR_L_ADC_REC_MIXER | PWR_R_ADC_REC_MIXER | PWR_CLASS_AB);
+
+               rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD3 ,PWR_MAIN_BIAS |
+                       PWR_HP_R_OUT_VOL | PWR_HP_L_OUT_VOL | PWR_SPK_OUT |
+                       PWR_MIC1_FUN_CTRL | PWR_MIC1_BOOST_MIXER, PWR_MAIN_BIAS |
+                       PWR_HP_R_OUT_VOL | PWR_HP_L_OUT_VOL | PWR_SPK_OUT |
+                       PWR_MIC1_FUN_CTRL | PWR_MIC1_BOOST_MIXER);
+
+               rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD1 ,PWR_MAIN_I2S_EN |
+                       PWR_HP_OUT_ENH_AMP | PWR_HP_OUT_AMP | PWR_MIC1_BIAS_EN,
+                       PWR_MAIN_I2S_EN | PWR_HP_OUT_ENH_AMP | PWR_HP_OUT_AMP |
+                       PWR_MIC1_BIAS_EN);
+               break;
+
+       case POWER_STATE_D1_PLAYBACK: //Low on of Playback
+               rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD2, PWR_VREF | PWR_DAC_REF_CIR |
+                       PWR_L_DAC_CLK | PWR_R_DAC_CLK | PWR_L_HP_MIXER | PWR_R_HP_MIXER |
+                       PWR_CLASS_AB | PWR_CLASS_D, PWR_VREF | PWR_DAC_REF_CIR |
+                       PWR_L_DAC_CLK | PWR_R_DAC_CLK | PWR_L_HP_MIXER | PWR_R_HP_MIXER |
+                       PWR_CLASS_AB | PWR_CLASS_D);
+
+               rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD3, PWR_MAIN_BIAS |
+                       PWR_HP_R_OUT_VOL | PWR_HP_L_OUT_VOL | PWR_SPK_OUT, 
+                       PWR_MAIN_BIAS | PWR_HP_R_OUT_VOL | PWR_HP_L_OUT_VOL | PWR_SPK_OUT);             
+
+               rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD1, PWR_MAIN_I2S_EN |
+                       PWR_HP_OUT_ENH_AMP | PWR_HP_OUT_AMP, PWR_MAIN_I2S_EN |
+                       PWR_HP_OUT_ENH_AMP | PWR_HP_OUT_AMP);
+               break;
+
+       case POWER_STATE_D1_RECORD: //Low on of Record
+               rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD1, PWR_MAIN_I2S_EN |
+                       PWR_MIC1_BIAS_EN, PWR_MAIN_I2S_EN | PWR_MIC1_BIAS_EN);                                                   
+               rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD2, PWR_VREF |
+                       PWR_L_ADC_CLK_GAIN | PWR_R_ADC_CLK_GAIN | PWR_L_ADC_REC_MIXER |
+                       PWR_R_ADC_REC_MIXER, PWR_VREF | PWR_L_ADC_CLK_GAIN |
+                       PWR_R_ADC_CLK_GAIN | PWR_L_ADC_REC_MIXER | PWR_R_ADC_REC_MIXER);
+               rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD3, PWR_MAIN_BIAS |
+                       PWR_MIC2_BOOST_MIXER | PWR_MIC1_BOOST_MIXER, PWR_MAIN_BIAS |
+                       PWR_MIC2_BOOST_MIXER | PWR_MIC1_BOOST_MIXER);
+               break;
+
+       case POWER_STATE_D2: //STANDBY----
+               rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD1, 0, PWR_MAIN_I2S_EN |
+                       PWR_HP_OUT_ENH_AMP | PWR_HP_OUT_AMP | PWR_MIC1_BIAS_EN);
+               rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD3, 0, PWR_HP_R_OUT_VOL |
+                       PWR_HP_L_OUT_VOL | PWR_SPK_OUT | PWR_MIC1_FUN_CTRL |
+                       PWR_MIC1_BOOST_MIXER);
+               rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD2, 0, PWR_DAC_REF_CIR |
+                       PWR_L_DAC_CLK | PWR_R_DAC_CLK | PWR_L_HP_MIXER | PWR_R_HP_MIXER|
+                       PWR_L_ADC_CLK_GAIN | PWR_R_ADC_CLK_GAIN | PWR_L_ADC_REC_MIXER |
+                       PWR_R_ADC_REC_MIXER | PWR_CLASS_AB | PWR_CLASS_D);
+               break;
+
+       case POWER_STATE_D2_PLAYBACK: //STANDBY of playback
+               rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD3, 0, /*PWR_HP_R_OUT_VOL |
+                       PWR_HP_L_OUT_VOL |*/ PWR_SPK_OUT);
+               rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD1, 0, PWR_HP_OUT_ENH_AMP |
+                       PWR_HP_OUT_AMP);
+               rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD2, 0, PWR_DAC_REF_CIR |
+                       PWR_L_DAC_CLK | PWR_R_DAC_CLK | PWR_L_HP_MIXER | PWR_R_HP_MIXER |
+                       PWR_CLASS_AB | PWR_CLASS_D);
+               break;
+
+       case POWER_STATE_D2_RECORD: //STANDBY of record
+               rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD1, 0, PWR_MIC1_BIAS_EN);
+               rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD2, 0, PWR_L_ADC_CLK_GAIN |
+                       PWR_R_ADC_CLK_GAIN | PWR_L_ADC_REC_MIXER | PWR_R_ADC_REC_MIXER);
+               rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD3, 0, PWR_MIC2_BOOST_MIXER |
+                       PWR_MIC1_BOOST_MIXER);          
+               break;
+
+       case POWER_STATE_D3: //SLEEP
+       case POWER_STATE_D4: //OFF----power off all power
+               rt5621_write_mask(codec, RT5621_PWR_MANAG_ADD1, 0, PWR_HP_OUT_ENH_AMP |
+                       PWR_HP_OUT_AMP);                
+               snd_soc_write(codec, RT5621_PWR_MANAG_ADD3, 0);
+               snd_soc_write(codec, RT5621_PWR_MANAG_ADD1, 0);
+               snd_soc_write(codec, RT5621_PWR_MANAG_ADD2, 0);
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+
+//*****************************************************************************
+//
+//function AudioOutEnable:Mute/Unmute audio out channel
+//WavOutPath:output channel
+//Mute :Mute/Unmute output channel
+//
+//*****************************************************************************
+static int rt5621_AudioOutEnable(struct snd_soc_codec *codec,
+       unsigned short int WavOutPath, int Mute)
+{
+       int RetVal=0;   
+
+       if(Mute) {
+               switch(WavOutPath) {
+               case RT_WAVOUT_ALL_ON:
+                       RetVal = rt5621_write_mask(codec, RT5621_SPK_OUT_VOL, RT_L_MUTE | RT_R_MUTE,
+                               RT_L_MUTE | RT_R_MUTE); //Mute Speaker right/left channel
+                       RetVal = rt5621_write_mask(codec, RT5621_HP_OUT_VOL, RT_L_MUTE | RT_R_MUTE,
+                               RT_L_MUTE | RT_R_MUTE); //Mute headphone right/left channel
+                       RetVal = rt5621_write_mask(codec, RT5621_MONO_AUX_OUT_VOL, RT_L_MUTE |
+                               RT_R_MUTE, RT_L_MUTE | RT_R_MUTE); //Mute Aux/Mono right/left channel
+                       RetVal = rt5621_write_mask(codec, RT5621_STEREO_DAC_VOL, RT_M_HP_MIXER |
+                               RT_M_SPK_MIXER | RT_M_MONO_MIXER, RT_M_HP_MIXER |
+                               RT_M_SPK_MIXER | RT_M_MONO_MIXER); //Mute DAC to HP,Speaker,Mono Mixer
+                       break;
+
+               case RT_WAVOUT_HP:
+                       RetVal = rt5621_write_mask(codec, RT5621_HP_OUT_VOL, RT_L_MUTE | RT_R_MUTE,
+                               RT_L_MUTE | RT_R_MUTE); //Mute headphone right/left channel
+                       break;
+
+               case RT_WAVOUT_SPK:
+                       RetVal = rt5621_write_mask(codec, RT5621_SPK_OUT_VOL, RT_L_MUTE | RT_R_MUTE,
+                               RT_L_MUTE | RT_R_MUTE); //Mute Speaker right/left channel                       
+                       break;
+
+               case RT_WAVOUT_AUXOUT:
+                       RetVal = rt5621_write_mask(codec, RT5621_MONO_AUX_OUT_VOL, RT_L_MUTE |
+                               RT_R_MUTE, RT_L_MUTE | RT_R_MUTE); //Mute AuxOut right/left channel
+                       break;
+
+               case RT_WAVOUT_MONO:
+
+                       RetVal = rt5621_write_mask(codec, RT5621_MONO_AUX_OUT_VOL, RT_L_MUTE,
+                               RT_L_MUTE); //Mute MonoOut channel
+                       break;
+
+               case RT_WAVOUT_DAC:
+                       RetVal = rt5621_write_mask(codec, RT5621_STEREO_DAC_VOL, RT_M_HP_MIXER |
+                               RT_M_SPK_MIXER | RT_M_MONO_MIXER, RT_M_HP_MIXER | RT_M_SPK_MIXER |
+                               RT_M_MONO_MIXER); //Mute DAC to HP,Speaker,Mono Mixer                           
+                       break;
+
+               default:
+                       return 0;
+               }
+       } else {
+               switch(WavOutPath) {
+               case RT_WAVOUT_ALL_ON:
+                       RetVal = rt5621_write_mask(codec, RT5621_SPK_OUT_VOL, 0, RT_L_MUTE |
+                               RT_R_MUTE); //Mute Speaker right/left channel
+                       RetVal = rt5621_write_mask(codec, RT5621_HP_OUT_VOL, 0, RT_L_MUTE |
+                               RT_R_MUTE); //Mute headphone right/left channel
+                       RetVal = rt5621_write_mask(codec, RT5621_MONO_AUX_OUT_VOL, 0, RT_L_MUTE |
+                               RT_R_MUTE); //Mute Aux/Mono right/left channel
+                       RetVal = rt5621_write_mask(codec, RT5621_STEREO_DAC_VOL, 0, RT_M_HP_MIXER |
+                               RT_M_SPK_MIXER | RT_M_MONO_MIXER); //Mute DAC to HP,Speaker,Mono Mixer
+                       break;
+               case RT_WAVOUT_HP:
+                       RetVal = rt5621_write_mask(codec, RT5621_HP_OUT_VOL, 0, RT_L_MUTE |
+                               RT_R_MUTE); //UnMute headphone right/left channel
+                       break;
+
+               case RT_WAVOUT_SPK:
+                       RetVal = rt5621_write_mask(codec, RT5621_SPK_OUT_VOL, 0, RT_L_MUTE |
+                               RT_R_MUTE); //unMute Speaker right/left channel
+                       break;
+               case RT_WAVOUT_AUXOUT:
+                       RetVal = rt5621_write_mask(codec, RT5621_MONO_AUX_OUT_VOL, 0, RT_L_MUTE |
+                               RT_R_MUTE); //unMute AuxOut right/left channel
+                       break;
+
+               case RT_WAVOUT_MONO:
+                       RetVal = rt5621_write_mask(codec, RT5621_MONO_AUX_OUT_VOL, 0, 
+                               RT_L_MUTE); //unMute MonoOut channel
+                       break;
+
+               case RT_WAVOUT_DAC:
+                       RetVal = rt5621_write_mask(codec, RT5621_STEREO_DAC_VOL, 0, RT_M_HP_MIXER |
+                               RT_M_SPK_MIXER | RT_M_MONO_MIXER); //unMute DAC to HP,Speaker,Mono Mixer
+                       break;
+               default:
+                       return 0;
+               }
+
+       }
+
+       return RetVal;
+}
+
+
+//*****************************************************************************
+//
+//function:Enable/Disable ADC input source control
+//
+//*****************************************************************************
+static int Enable_ADC_Input_Source(struct snd_soc_codec *codec,unsigned short int ADC_Input_Sour,int Enable)
+{
+       int bRetVal=0;
+       
+       if(Enable) {
+               //Enable ADC source 
+               bRetVal=rt5621_write_mask(codec,RT5621_ADC_REC_MIXER,0,ADC_Input_Sour);
+       } else {
+               //Disable ADC source            
+               bRetVal=rt5621_write_mask(codec,RT5621_ADC_REC_MIXER,ADC_Input_Sour,ADC_Input_Sour);
+       }
+
+       return bRetVal;
+}
+
+static void rt5621_update_eqmode(struct snd_soc_codec *codec, int mode)
+{
+       u16 HwEqIndex=0;
+
+       if (mode == NORMAL) {
+               /*clear EQ parameter*/
+               for (HwEqIndex=0; HwEqIndex<=0x0C; HwEqIndex++) {
+
+                       rt5621_write_index_reg(codec, HwEqIndex, HwEq_Preset[mode].EqValue[HwEqIndex])
+               }
+               
+               snd_soc_write(codec, 0x62, 0x0);                /*disable EQ block*/
+       } else {
+               snd_soc_write(codec, 0x62, HwEq_Preset[mode].HwEQCtrl);
+
+               /*Fill EQ parameter*/
+               for (HwEqIndex=0; HwEqIndex<=0x0C; HwEqIndex++) {
+
+                       rt5621_write_index_reg(codec, HwEqIndex, HwEq_Preset[mode].EqValue[HwEqIndex]) 
+               }               
+               //update EQ parameter
+               snd_soc_write(codec, 0x66, 0x1f);
+               schedule_timeout_uninterruptible(msecs_to_jiffies(1));
+               snd_soc_write(codec, 0x66, 0x0);
+       }
+}
+
+static const struct snd_kcontrol_new rt5621_snd_controls[] = {
+SOC_DOUBLE("Speaker Playback Volume",  RT5621_SPK_OUT_VOL, 8, 0, 31, 1),       
+SOC_DOUBLE("Speaker Playback Switch",  RT5621_SPK_OUT_VOL, 15, 7, 1, 1),
+SOC_DOUBLE("Headphone Playback Volume", RT5621_HP_OUT_VOL, 8, 0, 31, 1),
+SOC_DOUBLE("Headphone Playback Switch", RT5621_HP_OUT_VOL,15, 7, 1, 1),
+SOC_DOUBLE("AUX Playback Volume",              RT5621_MONO_AUX_OUT_VOL, 8, 0, 31, 1),
+SOC_DOUBLE("AUX Playback Switch",              RT5621_MONO_AUX_OUT_VOL, 15, 7, 1, 1),
+SOC_DOUBLE("PCM Playback Volume",              RT5621_STEREO_DAC_VOL, 8, 0, 31, 1),
+SOC_DOUBLE("Line In Volume",                   RT5621_LINE_IN_VOL, 8, 0, 31, 1),
+SOC_SINGLE("Mic 1 Volume",                             RT5621_MIC_VOL, 8, 31, 1),
+SOC_SINGLE("Mic 2 Volume",                             RT5621_MIC_VOL, 0, 31, 1),
+SOC_ENUM("Mic 1 Boost",                                rt5621_enum[5]),
+SOC_ENUM("Mic 2 Boost",                                rt5621_enum[6]),
+SOC_ENUM_EXT("Speaker Amp Type",                       rt5621_enum[7], snd_soc_get_enum_double, rt5621_amp_sel_put),
+SOC_DOUBLE("AUX In Volume",                    RT5621_AUXIN_VOL, 8, 0, 31, 1),
+SOC_DOUBLE("Capture Volume",                   RT5621_ADC_REC_GAIN, 7, 0, 31, 0),
+};
+
+void hp_depop_mode2(struct snd_soc_codec *codec)
+{
+       snd_soc_update_bits(codec, 0x3e, 0x8000, 0x8000);
+       snd_soc_update_bits(codec, 0x04, 0x8080, 0x8080);
+       snd_soc_update_bits(codec, 0x3a, 0x0100, 0x0100);
+       snd_soc_update_bits(codec, 0x3c, 0x2000, 0x2000);
+       snd_soc_update_bits(codec, 0x3e, 0x0600, 0x0600);
+       snd_soc_update_bits(codec, 0x5e, 0x0200, 0x0200);
+       schedule_timeout_uninterruptible(msecs_to_jiffies(300));
+}
+
+void aux_depop_mode2(struct snd_soc_codec *codec)
+{
+       snd_soc_update_bits(codec, 0x3e, 0x8000, 0x8000);
+       snd_soc_update_bits(codec, 0x06, 0x8080, 0x8080);
+       snd_soc_update_bits(codec, 0x3a, 0x0100, 0x0100);
+       snd_soc_update_bits(codec, 0x3c, 0x2000, 0x2000);
+       snd_soc_update_bits(codec, 0x3e, 0x6000, 0x6000);
+       snd_soc_update_bits(codec, 0x5e, 0x0020, 0x0200);
+       schedule_timeout_uninterruptible(msecs_to_jiffies(300));
+       snd_soc_update_bits(codec, 0x3a, 0x0002, 0x0002);
+       snd_soc_update_bits(codec, 0x3a, 0x0001, 0x0001);       
+}
+
+static int rt5621_pcm_hw_prepare(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *codec_dai)
+{
+
+       struct snd_soc_codec *codec = codec_dai->codec;
+       int stream = substream->stream;
+
+       switch (stream)
+       {
+               case SNDRV_PCM_STREAM_PLAYBACK:
+
+                       rt5621_ChangeCodecPowerStatus(codec,POWER_STATE_D1_PLAYBACK);   //power on dac to hp and speaker out
+                                               
+                       rt5621_AudioOutEnable(codec,RT_WAVOUT_SPK,0);   //unmute speaker out
+                       
+                       rt5621_AudioOutEnable(codec,RT_WAVOUT_HP,0);    //unmute hp
+                       
+                       rt5621_AudioOutEnable(codec,RT_WAVOUT_AUXOUT,0);        //unmute auxout out
+
+                       break;
+               case SNDRV_PCM_STREAM_CAPTURE:
+
+                       rt5621_ChangeCodecPowerStatus(codec,POWER_STATE_D1_RECORD);     //power on input to adc
+
+                       Enable_ADC_Input_Source(codec,RT_WAVIN_L_MIC1|RT_WAVIN_R_MIC1,1);       //enable record source from mic1
+
+                       break;                  
+       }
+       
+       return 0;
+}
+
+/* PLL divisors */
+struct _pll_div {
+       u32 pll_in;
+       u32 pll_out;
+       u16 regvalue;
+};
+
+static const struct _pll_div codec_pll_div[] = {
+       
+       {  2048000,  8192000,   0x0ea0},                
+       {  3686400,  8192000,   0x4e27},        
+       { 12000000,  8192000,   0x456b},   
+       { 13000000,  8192000,   0x495f},
+       { 13100000,      8192000,       0x0320},        
+       {  2048000,  11289600,  0xf637},
+       {  3686400,  11289600,  0x2f22},        
+       { 12000000,  11289600,  0x3e2f},   
+       { 13000000,  11289600,  0x4d5b},
+       { 13100000,      11289600,      0x363b},        
+       {  2048000,  16384000,  0x1ea0},
+       {  3686400,  16384000,  0x9e27},        
+       { 12000000,  16384000,  0x452b},   
+       { 13000000,  16384000,  0x542f},
+       { 13100000,      16384000,      0x03a0},        
+       {  2048000,  16934400,  0xe625},
+       {  3686400,  16934400,  0x9126},        
+       { 12000000,  16934400,  0x4d2c},   
+       { 13000000,  16934400,  0x742f},
+       { 13100000,      16934400,      0x3c27},                        
+       {  2048000,  22579200,  0x2aa0},
+       {  3686400,  22579200,  0x2f20},        
+       { 12000000,  22579200,  0x7e2f},   
+       { 13000000,  22579200,  0x742f},
+       { 13100000,      22579200,      0x3c27},                
+       {  2048000,  24576000,  0x2ea0},
+       {  3686400,  24576000,  0xee27},        
+       { 12000000,  24576000,  0x2915},   
+       { 13000000,  24576000,  0x772e},
+       { 13100000,      24576000,      0x0d20},        
+};
+
+static const struct _pll_div codec_bclk_pll_div[] = {
+
+       {  1536000,      24576000,      0x3ea0},        
+       {  3072000,      24576000,      0x1ea0},
+       {  512000,       24576000,  0x8e90},
+       {  256000,   24576000,  0xbe80},
+       {  2822400,      11289600,      0x1ee0},
+       {  3072000,      12288000,      0x1ee0},                
+};
+
+
+static int rt5621_set_dai_pll(struct snd_soc_dai *dai,
+               int pll_id,int source, unsigned int freq_in, unsigned int freq_out)
+{
+       int i;
+       int ret = -EINVAL;
+       struct snd_soc_codec *codec = dai->codec;
+
+       if (pll_id < RT5621_PLL_FR_MCLK || pll_id > RT5621_PLL_FR_BCLK)
+               return -EINVAL;
+
+       //rt5621_write_mask(codec,RT5621_PWR_MANAG_ADD2, 0x0000,0x1000);        //disable PLL power     
+       
+       if (!freq_in || !freq_out) {
+
+               return 0;
+       }               
+
+       if (RT5621_PLL_FR_MCLK == pll_id) {
+       for (i = 0; i < ARRAY_SIZE(codec_pll_div); i++) {
+                       
+               if (codec_pll_div[i].pll_in == freq_in && codec_pll_div[i].pll_out == freq_out)
+                       {
+                               snd_soc_update_bits(codec, RT5621_GLOBAL_CLK_CTRL_REG, 0x0000, 0x4000);                         
+                               snd_soc_write(codec,RT5621_PLL_CTRL,codec_pll_div[i].regvalue);//set PLL parameter      
+                               snd_soc_update_bits(codec,RT5621_PWR_MANAG_ADD2, 0x1000,0x1000);        //enable PLL power      
+                               ret = 0;
+                       }
+       }
+       }
+       else if (RT5621_PLL_FR_BCLK == pll_id)
+       {
+               for (i = 0; i < ARRAY_SIZE(codec_bclk_pll_div); i++)
+               {
+                       if ((freq_in == codec_bclk_pll_div[i].pll_in) && (freq_out == codec_bclk_pll_div[i].pll_out))
+                       {
+                               snd_soc_update_bits(codec, RT5621_GLOBAL_CLK_CTRL_REG, 0x4000, 0x4000);
+                               snd_soc_write(codec,RT5621_PLL_CTRL,codec_bclk_pll_div[i].regvalue);//set PLL parameter 
+                               snd_soc_update_bits(codec,RT5621_PWR_MANAG_ADD2, 0x1000,0x1000);        //enable PLL power      
+                               ret = 0;
+                       }
+               }
+       }
+
+       snd_soc_update_bits(codec,RT5621_GLOBAL_CLK_CTRL_REG,0x8000,0x8000);//Codec sys-clock from PLL  
+       return ret;
+}
+
+
+struct _coeff_div {
+       u32 mclk;
+       u32 rate;
+       u16 fs;
+       u16 regvalue;
+};
+
+/* codec hifi mclk (after PLL) clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+       /* 8k */
+       { 8192000,  8000, 256*4, 0x2a2d},
+       {12288000,  8000, 384*4, 0x2c2f},
+
+       /* 11.025k */
+       {11289600, 11025, 256*4, 0x2a2d},
+       {16934400, 11025, 384*4, 0x2c2f},
+
+       /* 16k */
+       {12288000, 16000, 384*2, 0x1c2f},
+       {16384000, 16000, 256*4, 0x2a2d},
+       {24576000, 16000, 384*4, 0x2c2f},
+
+       /* 22.05k */    
+       {11289600, 22050, 256*2, 0x1a2d},
+       {16934400, 22050, 384*2, 0x1c2f},
+
+       /* 32k */
+       {12288000, 32000, 384  , 0x0c2f},
+       {16384000, 32000, 256*2, 0x1a2d},
+       {24576000, 32000, 384*2, 0x1c2f},
+
+       /* 44.1k */
+       {11289600, 44100, 256*1, 0x0a2d},
+       {22579200, 44100, 256*2, 0x1a2d},
+       {45158400, 44100, 256*4, 0x2a2d},       
+
+       /* 48k */
+       {12288000, 48000, 256*1, 0x0a2d},
+       {24576000, 48000, 256*2, 0x1a2d},
+       {49152000, 48000, 256*4, 0x2a2d},
+
+       //MCLK is 24.576Mhz(for 8k,16k,32k)
+       {24576000,  8000, 384*8, 0x3c6b},
+       {24576000, 16000, 384*4, 0x2c6b},
+       {24576000, 32000, 384*2, 0x1c6b},
+
+       //MCLK is 22.5792mHz(for 11k,22k)
+       {22579200, 11025, 256*8, 0x3a2d},
+       {22579200, 22050, 256*4, 0x2a2d},
+};
+
+
+
+static int get_coeff(int mclk, int rate)
+{
+       int i;
+       
+       DBG("get_coeff mclk=%d,rate=%d\n",mclk,rate);
+
+       for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+               if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+                       return i;
+       }
+       return -EINVAL;
+}
+
+
+/*
+ * Clock after PLL and dividers
+ */
+ /*in this driver, you have to set sysclk to be 24576000,
+ * but you don't need to give a clk to be 24576000, our 
+ * internal pll will generate this clock! so it won't make
+ * you any difficult.
+ */
+static int rt5621_set_dai_sysclk(struct snd_soc_dai *dai,
+               int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct rt5621_priv *rt5621 = snd_soc_codec_get_drvdata(codec);
+
+       if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) {
+               rt5621->sysclk = freq;
+               return 0;
+       }
+
+       printk("unsupported sysclk freq %u for audio i2s\n", freq);
+
+       return -EINVAL;
+}
+
+
+static int rt5621_set_dai_fmt(struct snd_soc_dai *dai,
+               unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u16 iface = 0;
+
+       /* set master/slave audio interface */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               iface = 0x0000;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               iface = 0x8000;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* interface format */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               iface |= 0x0000;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               iface |= 0x0001;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               iface |= 0x0002;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               iface |= 0x0003;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               iface |= 0x4003;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* clock inversion */
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               iface |= 0x0000;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               iface |= 0x0100;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_write(codec,RT5621_AUDIO_INTERFACE,iface);
+       return 0;
+}
+
+
+static int rt5621_pcm_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct rt5621_priv *rt5621 = snd_soc_codec_get_drvdata(codec);
+       u16 iface = snd_soc_read(codec,RT5621_AUDIO_INTERFACE)&0xfff3;
+       int coeff = get_coeff(rt5621->sysclk, params_rate(params));
+
+       DBG("rt5621_pcm_hw_params\n");
+       if (coeff < 0)
+               coeff = get_coeff(24576000, params_rate(params));         /*if not set sysclk, default to be 24.576MHz*/
+
+       /* bit size */
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               iface |= 0x0000;
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               iface |= 0x0004;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               iface |= 0x0008;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               iface |= 0x000c;
+               break;
+       }
+
+       /* set iface & srate */
+       snd_soc_write(codec, RT5621_AUDIO_INTERFACE, iface);
+
+       if (coeff >= 0)
+               snd_soc_write(codec, RT5621_STEREO_AD_DA_CLK_CTRL, coeff_div[coeff].regvalue);
+       else
+       {
+               printk(KERN_ERR "cant find matched sysclk and rate config\n");
+               return -EINVAL;
+               
+       }
+       return 0;
+}
+
+static int rt5621_set_bias_level(struct snd_soc_codec *codec,
+                       enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               snd_soc_update_bits(codec, RT5621_SPK_OUT_VOL, RT_L_MUTE | RT_R_MUTE, 0);
+               snd_soc_update_bits(codec, RT5621_HP_OUT_VOL, RT_L_MUTE | RT_R_MUTE, 0);
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               snd_soc_update_bits(codec, RT5621_SPK_OUT_VOL, RT_L_MUTE | RT_R_MUTE, RT_L_MUTE | RT_R_MUTE);
+               snd_soc_update_bits(codec, RT5621_HP_OUT_VOL, RT_L_MUTE | RT_R_MUTE, RT_L_MUTE | RT_R_MUTE);
+               if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) {
+                       snd_soc_write(codec, RT5621_PWR_MANAG_ADD3, 0x8000);//enable Main bias
+                       snd_soc_write(codec, RT5621_PWR_MANAG_ADD2, 0x2000);//enable Vref
+                       codec->cache_only = false;
+                       snd_soc_cache_sync(codec);
+               }
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               snd_soc_update_bits(codec, RT5621_SPK_OUT_VOL, RT_L_MUTE | RT_R_MUTE, RT_L_MUTE | RT_R_MUTE);
+               snd_soc_update_bits(codec, RT5621_HP_OUT_VOL, RT_L_MUTE | RT_R_MUTE, RT_L_MUTE | RT_R_MUTE);
+               snd_soc_write(codec, RT5621_PWR_MANAG_ADD3, 0x0000);
+               snd_soc_write(codec, RT5621_PWR_MANAG_ADD2, 0x0000);
+               snd_soc_write(codec, RT5621_PWR_MANAG_ADD1, 0x0000);
+               break;
+
+       default:
+               break;
+       }
+       codec->dapm.bias_level = level;
+
+       return 0;
+}
+
+
+
+struct rt5621_init_reg{
+
+       u8 reg_index;
+       u16 reg_value;
+};
+
+static struct rt5621_init_reg init_data[] = {
+       {RT5621_AUDIO_INTERFACE,        0x8000},    //set I2S codec to slave mode
+       {RT5621_STEREO_DAC_VOL,         0x0505},    //default stereo DAC volume to 0db
+       {RT5621_OUTPUT_MIXER_CTRL,      0x2b40},    //default output mixer control      
+       {RT5621_ADC_REC_MIXER,          0x3f3f},    //set record source is Mic1 by default
+       {RT5621_MIC_CTRL,               0x0a00},    //set Mic1,Mic2 boost 20db  
+       {RT5621_SPK_OUT_VOL,            0x8080},    //default speaker volume to 0db 
+       {RT5621_HP_OUT_VOL,             0x8080},    //default HP volume to -12db        
+       {RT5621_ADD_CTRL_REG,           0x4b00},    //Class AB/D speaker ratio is 1.25VDD
+       {RT5621_STEREO_AD_DA_CLK_CTRL,  0x066d},    //set Dac filter to 256fs
+       {RT5621_ADC_REC_GAIN,               0xfa95},    //set ADC boost to 15db 
+       {RT5621_HID_CTRL_INDEX,         0x46},      //Class D setting
+       {RT5621_MIC_VOL,              0x1f08},
+       {RT5621_HID_CTRL_DATA,          0xFFFF},    //power on Class D Internal register
+       {RT5621_JACK_DET_CTRL,          0x4810},    //power on Class D Internal register
+};
+#define RT5621_INIT_REG_NUM ARRAY_SIZE(init_data)
+
+static int rt5621_reg_init(struct snd_soc_codec *codec)
+{
+       int i;
+
+       for (i = 0; i < RT5621_INIT_REG_NUM; i++)
+               snd_soc_write(codec, init_data[i].reg_index, init_data[i].reg_value);
+
+       return 0;
+}
+
+void codec_set_spk(bool on)
+{
+       struct snd_soc_codec *codec = rt5621_codec; 
+
+       DBG("%s: %d\n", __func__, on);
+
+       if(!codec)
+               return;
+
+       if(on){
+               DBG("snd_soc_dapm_enable_pin\n");
+               snd_soc_dapm_enable_pin(&codec->dapm, "Headphone Jack");
+               snd_soc_dapm_enable_pin(&codec->dapm, "Ext Spk");
+       }
+       else{
+
+               DBG("snd_soc_dapm_disable_pin\n");
+               snd_soc_dapm_disable_pin(&codec->dapm, "Headphone Jack");
+               snd_soc_dapm_disable_pin(&codec->dapm, "Ext Spk");
+       }
+
+       snd_soc_dapm_sync(&codec->dapm);
+
+       return;
+}
+
+static void rt5621_work(struct work_struct *work)
+{
+       struct snd_soc_codec *codec = rt5621_codec;
+       
+       rt5621_set_bias_level(codec, codec->dapm.bias_level);
+}
+
+static int rt5621_probe(struct snd_soc_codec *codec)
+{
+       int ret;
+
+       ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+               return ret;
+       }
+       codec->cache_bypass = 1;
+
+       rt5621_reset(codec);
+       snd_soc_write(codec, RT5621_PWR_MANAG_ADD3, 0x8000);//enable Main bias
+       snd_soc_write(codec, RT5621_PWR_MANAG_ADD2, 0x2000);//enable Vref
+       hp_depop_mode2(codec);
+       rt5621_reg_init(codec);
+
+#if ENABLE_EQ_HREQ             
+
+       rt5621_write_index_reg(codec, 0x11,0x1);
+       rt5621_write_index_reg(codec, 0x12,0x1);
+       rt5621_update_eqmode(codec, HFREQ);
+       
+#endif
+       rt5621_workq = create_freezable_workqueue("rt5621");
+       if (rt5621_workq == NULL) {
+               printk("wm8900_probe::create_freezeable_workqueue ERROR !");
+               kfree(codec);
+               return -ENOMEM;
+       }
+
+       rt5621_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+       codec->dapm.bias_level = SND_SOC_BIAS_STANDBY;
+
+       queue_delayed_work(rt5621_workq, &delayed_work,
+               msecs_to_jiffies(caps_charge));
+
+       codec->dapm.bias_level = SND_SOC_BIAS_STANDBY;
+
+       rt5621_codec = codec;
+
+       return 0;
+}
+
+static int rt5621_remove(struct snd_soc_codec *codec)
+{
+       rt5621_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       cancel_delayed_work_sync(&delayed_work);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int rt5621_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+       rt5621_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int rt5621_resume(struct snd_soc_codec *codec)
+{
+
+       rt5621_reset(codec);
+       snd_soc_write(codec, RT5621_PWR_MANAG_ADD3, 0x8000);//enable Main bias
+       snd_soc_write(codec, RT5621_PWR_MANAG_ADD2, 0x2000);//enable Vref
+
+       hp_depop_mode2(codec);
+
+       rt5621_reg_init(codec);
+
+#if ENABLE_EQ_HREQ             
+
+       rt5621_write_index_reg(codec, 0x11,0x1);
+       rt5621_write_index_reg(codec, 0x12,0x1);
+       rt5621_update_eqmode(codec, HFREQ);
+#endif 
+
+       rt5621_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) {
+               rt5621_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+               codec->dapm.bias_level = SND_SOC_BIAS_ON;
+               queue_delayed_work(rt5621_workq, &delayed_work,
+                       msecs_to_jiffies(caps_charge));
+       }
+       return 0;
+}
+#else
+#define rt5621_suspend NULL
+#define rt5621_resume NULL
+#endif
+
+static void rt5621_shutdown(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *codec_dai)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       int stream = substream->stream;
+       
+       switch (stream)
+       {
+               case SNDRV_PCM_STREAM_PLAYBACK:
+
+                       rt5621_AudioOutEnable(codec,RT_WAVOUT_SPK,1);   //mute speaker out
+                       
+                       rt5621_AudioOutEnable(codec,RT_WAVOUT_HP,1);    //mute hp out
+                       
+                       rt5621_AudioOutEnable(codec,RT_WAVOUT_AUXOUT,1);        //mute auxout
+
+                       rt5621_ChangeCodecPowerStatus(codec,POWER_STATE_D2_PLAYBACK);   //power off dac to hp and speaker out and auxout
+                                               
+
+
+                       break;
+               case SNDRV_PCM_STREAM_CAPTURE:
+
+                       Enable_ADC_Input_Source(codec,RT_WAVIN_L_MIC1|RT_WAVIN_R_MIC1,0);       //disable record source from mic1
+
+                       rt5621_ChangeCodecPowerStatus(codec,POWER_STATE_D2_RECORD);
+                       
+
+                       break;                  
+       }       
+}
+
+//#define RT5621_HIFI_RATES SNDRV_PCM_RATE_8000_48000
+#define RT5621_HIFI_RATES (SNDRV_PCM_RATE_44100) // zyy 20110704, playback and record use same sample rate
+
+#define RT5621_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+               SNDRV_PCM_FMTBIT_S24_LE)
+
+struct snd_soc_dai_ops rt5621_hifi_ops = {
+       .hw_params = rt5621_pcm_hw_params,      
+       .set_fmt = rt5621_set_dai_fmt,
+       .set_sysclk = rt5621_set_dai_sysclk,
+       .set_pll = rt5621_set_dai_pll,
+       .prepare = rt5621_pcm_hw_prepare,
+       .shutdown = rt5621_shutdown,
+};
+
+struct snd_soc_dai_driver rt5621_dai = { 
+       .name = "RT5621 HiFi",
+       .playback = {
+               .stream_name = "HiFi Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = RT5621_HIFI_RATES,
+               .formats = RT5621_FORMATS,
+       },
+       .capture = {
+               .stream_name = "HiFi Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = RT5621_HIFI_RATES,
+               .formats = RT5621_FORMATS,
+       },
+       .ops = &rt5621_hifi_ops,
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_rt5621 = {
+       .probe =        rt5621_probe,
+       .remove = rt5621_remove,
+       .suspend = rt5621_suspend,
+       .resume = rt5621_resume,
+       .set_bias_level = rt5621_set_bias_level,
+       .reg_cache_size = RT5621_VENDOR_ID2 + 1,
+       .reg_word_size = sizeof(u16),
+       .reg_cache_default = rt5621_reg,
+       .volatile_register = rt5621_volatile_register,
+       .readable_register = rt5621_readable_register,
+       .reg_cache_step = 1,
+       .controls = rt5621_snd_controls,
+       .num_controls = ARRAY_SIZE(rt5621_snd_controls),
+};
+
+static const struct i2c_device_id rt5621_i2c_id[] = {
+       { "rt5621", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5621_i2c_id);
+
+static int rt5621_i2c_probe(struct i2c_client *i2c,
+                   const struct i2c_device_id *id)
+{
+       struct rt5621_priv *rt5621;
+       int ret;
+
+       rt5621 = kzalloc(sizeof(struct rt5621_priv), GFP_KERNEL);
+       if (NULL == rt5621)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, rt5621);
+
+       ret = snd_soc_register_codec(&i2c->dev,
+               &soc_codec_dev_rt5621, &rt5621_dai, 1);
+       if (ret < 0)
+               kfree(rt5621);
+
+       return ret;
+}
+
+static __devexit int rt5621_i2c_remove(struct i2c_client *i2c)
+{
+       snd_soc_unregister_codec(&i2c->dev);
+       kfree(i2c_get_clientdata(i2c));
+       return 0;
+}
+
+struct i2c_driver rt5621_i2c_driver = {
+       .driver = {
+               .name = "RT5621",
+               .owner = THIS_MODULE,
+       },
+       .probe = rt5621_i2c_probe,
+       .remove   = __devexit_p(rt5621_i2c_remove),
+       .id_table = rt5621_i2c_id,
+};
+
+static int __init rt5621_modinit(void)
+{
+       return i2c_add_driver(&rt5621_i2c_driver);
+}
+module_init(rt5621_modinit);
+
+static void __exit rt5621_modexit(void)
+{
+       i2c_del_driver(&rt5621_i2c_driver);
+}
+module_exit(rt5621_modexit);
+
+MODULE_DESCRIPTION("ASoC RT5621 driver");
+MODULE_AUTHOR("Johnny Hsu <johnnyhsu@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt5621.h b/sound/soc/codecs/rt5621.h
new file mode 100644 (file)
index 0000000..9c1459c
--- /dev/null
@@ -0,0 +1,515 @@
+/*
+ * rt5621.h  --  RT5621 ALSA SoC audio driver
+ *
+ * Copyright 2011 Realtek Microelectronics
+ * Author: Johnny Hsu <johnnyhsu@realtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __RT5621_H__
+#define __RT5621_H__
+
+#define RT5621_RESET                                           0X00                    //RESET CODEC TO DEFAULT
+#define RT5621_SPK_OUT_VOL                                     0X02                    //SPEAKER OUT VOLUME
+#define RT5621_HP_OUT_VOL                                      0X04                    //HEADPHONE OUTPUT VOLUME
+#define RT5621_MONO_AUX_OUT_VOL                                0X06                    //MONO OUTPUT/AUXOUT VOLUME
+#define RT5621_AUXIN_VOL                                       0X08                    //AUXIN VOLUME
+#define RT5621_LINE_IN_VOL                                     0X0A                    //LINE IN VOLUME
+#define RT5621_STEREO_DAC_VOL                          0X0C                    //STEREO DAC VOLUME
+#define RT5621_MIC_VOL                                         0X0E                    //MICROPHONE VOLUME
+#define RT5621_MIC_ROUTING_CTRL                                0X10                    //MIC ROUTING CONTROL
+#define RT5621_ADC_REC_GAIN                                    0X12                    //ADC RECORD GAIN
+#define RT5621_ADC_REC_MIXER                           0X14                    //ADC RECORD MIXER CONTROL
+#define RT5621_SOFT_VOL_CTRL_TIME                      0X16                    //SOFT VOLUME CONTROL TIME
+#define RT5621_OUTPUT_MIXER_CTRL                       0X1C                    //OUTPUT MIXER CONTROL
+#define RT5621_MIC_CTRL                                                0X22                    //MICROPHONE CONTROL
+#define        RT5621_AUDIO_INTERFACE                          0X34                    //AUDIO INTERFACE
+#define RT5621_STEREO_AD_DA_CLK_CTRL           0X36                    //STEREO AD/DA CLOCK CONTROL
+#define        RT5621_COMPANDING_CTRL                          0X38                    //COMPANDING CONTROL
+#define        RT5621_PWR_MANAG_ADD1                           0X3A                    //POWER MANAGMENT ADDITION 1
+#define RT5621_PWR_MANAG_ADD2                          0X3C                    //POWER MANAGMENT ADDITION 2
+#define RT5621_PWR_MANAG_ADD3                          0X3E                    //POWER MANAGMENT ADDITION 3
+#define RT5621_ADD_CTRL_REG                                    0X40                    //ADDITIONAL CONTROL REGISTER
+#define        RT5621_GLOBAL_CLK_CTRL_REG                      0X42                    //GLOBAL CLOCK CONTROL REGISTER
+#define RT5621_PLL_CTRL                                                0X44                    //PLL CONTROL
+#define RT5621_GPIO_OUTPUT_PIN_CTRL                    0X4A                    //GPIO OUTPUT PIN CONTROL
+#define RT5621_GPIO_PIN_CONFIG                         0X4C                    //GPIO PIN CONFIGURATION
+#define RT5621_GPIO_PIN_POLARITY                       0X4E                    //GPIO PIN POLARITY/TYPE        
+#define RT5621_GPIO_PIN_STICKY                         0X50                    //GPIO PIN STICKY       
+#define RT5621_GPIO_PIN_WAKEUP                         0X52                    //GPIO PIN WAKE UP
+#define RT5621_GPIO_PIN_STATUS                         0X54                    //GPIO PIN STATUS
+#define RT5621_GPIO_PIN_SHARING                                0X56                    //GPIO PIN SHARING
+#define        RT5621_OVER_TEMP_CURR_STATUS            0X58                    //OVER TEMPERATURE AND CURRENT STATUS
+#define RT5621_JACK_DET_CTRL                           0X5A                    //JACK DETECT CONTROL REGISTER
+#define RT5621_MISC_CTRL                                       0X5E                    //MISC CONTROL
+#define        RT5621_PSEDUEO_SPATIAL_CTRL                     0X60                    //PSEDUEO STEREO & SPATIAL EFFECT BLOCK CONTROL
+#define RT5621_EQ_CTRL                                         0X62                    //EQ CONTROL
+#define RT5621_EQ_MODE_ENABLE                          0X66                    //EQ MODE CHANGE ENABLE
+#define RT5621_AVC_CTRL                                                0X68                    //AVC CONTROL
+#define RT5621_HID_CTRL_INDEX                          0X6A                    //HIDDEN CONTROL INDEX PORT
+#define RT5621_HID_CTRL_DATA                           0X6C                    //HIDDEN CONTROL DATA PORT
+#define RT5621_VENDOR_ID1                              0x7C                    //VENDOR ID1
+#define RT5621_VENDOR_ID2                              0x7E                    //VENDOR ID2
+
+
+//global definition
+#define RT_L_MUTE                                              (0x1<<15)               //MUTE LEFT CONTROL BIT
+#define RT_L_ZC                                                        (0x1<<14)               //LEFT ZERO CROSS CONTROL BIT
+#define RT_L_SM                                                        (0x1<<13)               //LEFT SOFTMUTE CONTROL BIT
+#define RT_R_MUTE                                              (0x1<<7)                //MUTE RIGHT CONTROL BIT
+#define RT_R_ZC                                                        (0x1<<6)                //RIGHT ZERO CROSS CONTROL BIT
+#define RT_R_SM                                                        (0x1<<5)                //RIGHT SOFTMUTE CONTROL BIT
+#define RT_M_HP_MIXER                                  (0x1<<15)               //Mute source to HP Mixer
+#define RT_M_SPK_MIXER                                 (0x1<<14)               //Mute source to Speaker Mixer
+#define RT_M_MONO_MIXER                                        (0x1<<13)               //Mute source to Mono Mixer
+#define SPK_CLASS_AB                                           0
+#define SPK_CLASS_D                                                    1
+
+//Mic Routing Control(0x10)
+#define M_MIC1_TO_HP_MIXER                             (0x1<<15)               //Mute MIC1 to HP mixer
+#define M_MIC1_TO_SPK_MIXER                            (0x1<<14)               //Mute MiC1 to SPK mixer
+#define M_MIC1_TO_MONO_MIXER                   (0x1<<13)               //Mute MIC1 to MONO mixer
+#define MIC1_DIFF_INPUT_CTRL                   (0x1<<12)               //MIC1 different input control
+#define M_MIC2_TO_HP_MIXER                             (0x1<<7)                //Mute MIC2 to HP mixer
+#define M_MIC2_TO_SPK_MIXER                            (0x1<<6)                //Mute MiC2 to SPK mixer
+#define M_MIC2_TO_MONO_MIXER                   (0x1<<5)                //Mute MIC2 to MONO mixer
+#define MIC2_DIFF_INPUT_CTRL                   (0x1<<4)                //MIC2 different input control
+
+//ADC Record Gain(0x12)
+#define M_ADC_L_TO_HP_MIXER                            (0x1<<15)               //Mute left of ADC to HP Mixer
+#define M_ADC_R_TO_HP_MIXER                            (0x1<<14)               //Mute right of ADC to HP Mixer
+#define M_ADC_L_TO_MONO_MIXER                  (0x1<<13)               //Mute left of ADC to MONO Mixer
+#define M_ADC_R_TO_MONO_MIXER                  (0x1<<12)               //Mute right of ADC to MONO Mixer
+#define ADC_L_GAIN_MASK                                        (0x1f<<7)               //ADC Record Gain Left channel Mask
+#define ADC_L_ZC_DET                                   (0x1<<6)                //ADC Zero-Cross Detector Control
+#define ADC_R_ZC_DET                                   (0x1<<5)                //ADC Zero-Cross Detector Control
+#define ADC_R_GAIN_MASK                                        (0x1f<<0)               //ADC Record Gain Right channel Mask
+
+//ADC Input Mixer Control(0x14)
+#define M_MIC1_TO_ADC_L_MIXER                          (0x1<<14)               //Mute mic1 to left channel of ADC mixer
+#define M_MIC2_TO_ADC_L_MIXER                          (0x1<<13)               //Mute mic2 to left channel of ADC mixer
+#define M_LINEIN_L_TO_ADC_L_MIXER                      (0x1<<12)               //Mute line In left channel to left channel of ADC mixer
+#define M_AUXIN_L_TO_ADC_L_MIXER                       (0x1<<11)               //Mute aux In left channel to left channel of ADC mixer
+#define M_HPMIXER_L_TO_ADC_L_MIXER                     (0x1<<10)               //Mute HP mixer left channel to left channel of ADC mixer
+#define M_SPKMIXER_L_TO_ADC_L_MIXER                    (0x1<<9)                //Mute SPK mixer left channel to left channel of ADC mixer
+#define M_MONOMIXER_L_TO_ADC_L_MIXER           (0x1<<8)                //Mute MONO mixer left channel to left channel of ADC mixer
+#define M_MIC1_TO_ADC_R_MIXER                          (0x1<<6)                //Mute mic1 to right channel of ADC mixer
+#define M_MIC2_TO_ADC_R_MIXER                          (0x1<<5)                //Mute mic2 to right channel of ADC mixer
+#define M_LINEIN_R_TO_ADC_R_MIXER                      (0x1<<4)                //Mute lineIn right channel to right channel of ADC mixer
+#define M_AUXIN_R_TO_ADC_R_MIXER                       (0x1<<3)                //Mute aux In right channel to right channel of ADC mixer
+#define M_HPMIXER_R_TO_ADC_R_MIXER                     (0x1<<2)                //Mute HP mixer right channel to right channel of ADC mixer
+#define M_SPKMIXER_R_TO_ADC_R_MIXER                    (0x1<<1)                //Mute SPK mixer right channel to right channel of ADC mixer
+#define M_MONOMIXER_R_TO_ADC_R_MIXER           (0x1<<0)                //Mute MONO mixer right channel to right channel of ADC mixer
+
+//Output Mixer Control(0x1C)
+#define        SPKOUT_N_SOUR_MASK                                      (0x3<<14)       
+#define        SPKOUT_N_SOUR_LN                                        (0x2<<14)
+#define        SPKOUT_N_SOUR_RP                                        (0x1<<14)
+#define        SPKOUT_N_SOUR_RN                                        (0x0<<14)
+#define SPK_OUTPUT_CLASS_AB                                    (0x0<<13)
+#define SPK_OUTPUT_CLASS_D                                     (0x1<<13)
+#define SPK_CLASS_AB_S_AMP                                     (0x0<<12)
+#define SPK_CALSS_AB_W_AMP                                     (0x1<<12)
+#define SPKOUT_INPUT_SEL_MASK                          (0x3<<10)
+#define SPKOUT_INPUT_SEL_MONOMIXER                     (0x3<<10)
+#define SPKOUT_INPUT_SEL_SPKMIXER                      (0x2<<10)
+#define SPKOUT_INPUT_SEL_HPMIXER                       (0x1<<10)
+#define SPKOUT_INPUT_SEL_VMID                          (0x0<<10)
+#define HPL_INPUT_SEL_HPLMIXER                         (0x1<<9)
+#define HPR_INPUT_SEL_HPRMIXER                         (0x1<<8)        
+#define MONO_AUX_INPUT_SEL_MASK                                (0x3<<6)
+#define MONO_AUX_INPUT_SEL_MONO                                (0x3<<6)
+#define MONO_AUX_INPUT_SEL_SPK                         (0x2<<6)
+#define MONO_AUX_INPUT_SEL_HP                          (0x1<<6)
+#define MONO_AUX_INPUT_SEL_VMID                                (0x0<<6)
+
+//Micphone Control define(0x22)
+#define MIC1           1
+#define MIC2           2
+#define MIC_BIAS_90_PRECNET_AVDD       1
+#define        MIC_BIAS_75_PRECNET_AVDD        2
+
+#define MIC1_BOOST_CTRL_MASK           (0x3<<10)
+#define MIC1_BOOST_CTRL_BYPASS         (0x0<<10)
+#define MIC1_BOOST_CTRL_20DB           (0x1<<10)
+#define MIC1_BOOST_CTRL_30DB           (0x2<<10)
+#define MIC1_BOOST_CTRL_40DB           (0x3<<10)
+
+#define MIC2_BOOST_CTRL_MASK           (0x3<<8)
+#define MIC2_BOOST_CTRL_BYPASS         (0x0<<8)
+#define MIC2_BOOST_CTRL_20DB           (0x1<<8)
+#define MIC2_BOOST_CTRL_30DB           (0x2<<8)
+#define MIC2_BOOST_CTRL_40DB           (0x3<<8)
+
+#define MICBIAS_VOLT_CTRL_MASK         (0x1<<5)
+#define MICBIAS_VOLT_CTRL_90P          (0x0<<5)
+#define MICBIAS_VOLT_CTRL_75P          (0x1<<5)
+
+#define MICBIAS_SHORT_CURR_DET_MASK            (0x3)
+#define MICBIAS_SHORT_CURR_DET_600UA   (0x0)
+#define MICBIAS_SHORT_CURR_DET_1200UA  (0x1)
+#define MICBIAS_SHORT_CURR_DET_1800UA  (0x2)
+
+//Audio Interface(0x34)                        
+#define SDP_MASTER_MODE                                (0x0<<15)               //Main I2S interface select Master mode
+#define SDP_SLAVE_MODE                         (0x1<<15)               //Main I2S interface select Slave mode
+#define I2S_PCM_MODE                           (0x1<<14)               //PCM           0:mode A                                ,1:mode B                                                                                                       
+#define MAIN_I2S_BCLK_POL_CTRL         (0x1<<7)                //0:Normal 1:Invert
+#define ADC_DATA_L_R_SWAP                      (0x1<<5)                //0:ADC data appear at left phase of LRCK
+                                                                                                       //1:ADC data appear at right phase of LRCK
+#define DAC_DATA_L_R_SWAP                      (0x1<<4)                //0:DAC data appear at left phase of LRCK
+                                                                                                       //1:DAC data appear at right phase of LRCK      
+//Data Length Slection
+#define I2S_DL_MASK                            (0x3<<2)                //main i2s Data Length mask     
+#define I2S_DL_16                              (0x0<<2)                //16 bits
+#define I2S_DL_20                              (0x1<<2)                //20 bits
+#define        I2S_DL_24                               (0x2<<2)                //24 bits
+#define I2S_DL_32                              (0x3<<2)                //32 bits
+                                                                                                       
+//PCM Data Format Selection
+#define I2S_DF_MASK                            (0x3)                   //main i2s Data Format mask
+#define I2S_DF_I2S                             (0x0)                   //I2S FORMAT 
+#define I2S_DF_RIGHT                   (0x1)                   //RIGHT JUSTIFIED format
+#define        I2S_DF_LEFT                             (0x2)                   //LEFT JUSTIFIED  format
+#define I2S_DF_PCM                             (0x3)                   //PCM format
+
+//Stereo AD/DA Clock Control(0x36h)
+#define I2S_PRE_DIV_MASK               (0x7<<12)                       
+#define I2S_PRE_DIV_1                  (0x0<<12)                       //DIV 1
+#define I2S_PRE_DIV_2                  (0x1<<12)                       //DIV 2
+#define I2S_PRE_DIV_4                  (0x2<<12)                       //DIV 4
+#define I2S_PRE_DIV_8                  (0x3<<12)                       //DIV 8
+#define I2S_PRE_DIV_16                 (0x4<<12)                       //DIV 16
+#define I2S_PRE_DIV_32                 (0x5<<12)                       //DIV 32
+
+#define I2S_SCLK_DIV_MASK              (0x7<<9)                        
+#define I2S_SCLK_DIV_1                 (0x0<<9)                        //DIV 1
+#define I2S_SCLK_DIV_2                 (0x1<<9)                        //DIV 2
+#define I2S_SCLK_DIV_3                 (0x2<<9)                        //DIV 3
+#define I2S_SCLK_DIV_4                 (0x3<<9)                        //DIV 4
+#define I2S_SCLK_DIV_6                 (0x4<<9)                        //DIV 6
+#define I2S_SCLK_DIV_8                 (0x5<<9)                        //DIV 8
+#define I2S_SCLK_DIV_12                        (0x6<<9)                        //DIV 12
+#define I2S_SCLK_DIV_16                        (0x7<<9)                        //DIV 16
+
+#define I2S_WCLK_DIV_PRE_MASK          (0xF<<5)                        
+#define I2S_WCLK_PRE_DIV_1                     (0x0<<5)                        //DIV 1
+#define I2S_WCLK_PRE_DIV_2                     (0x1<<5)                        //DIV 2
+#define I2S_WCLK_PRE_DIV_3                     (0x2<<5)                        //DIV 3
+#define I2S_WCLK_PRE_DIV_4                     (0x3<<5)                        //DIV 4
+#define I2S_WCLK_PRE_DIV_5                     (0x4<<5)                        //DIV 5
+#define I2S_WCLK_PRE_DIV_6                     (0x5<<5)                        //DIV 6
+#define I2S_WCLK_PRE_DIV_7                     (0x6<<5)                        //DIV 7
+#define I2S_WCLK_PRE_DIV_8                     (0x7<<5)                        //DIV 8
+//........................
+
+#define I2S_WCLK_DIV_MASK              (0x7<<2)                        
+#define I2S_WCLK_DIV_2                 (0x0<<2)                        //DIV 2
+#define I2S_WCLK_DIV_4                 (0x1<<2)                        //DIV 4
+#define I2S_WCLK_DIV_8                 (0x2<<2)                        //DIV 8
+#define I2S_WCLK_DIV_16                        (0x3<<2)                        //DIV 16
+#define I2S_WCLK_DIV_32                        (0x4<<2)                        //DIV 32
+
+#define ADDA_FILTER_CLK_SEL_256FS      (0<<1)                  //256FS
+#define ADDA_FILTER_CLK_SEL_384FS      (1<<1)                  //384FS
+
+#define ADDA_OSR_SEL_64FS      (0)                                             //64FS
+#define ADDA_OSR_SEL_128FS     (1)                                             //128FS
+
+//Power managment addition 1 (0x3A),0:Disable,1:Enable
+#define PWR_MAIN_I2S_EN                                (0x1<<15)
+#define PWR_ZC_DET_PD_EN                       (0x1<<14)       
+#define PWR_MIC1_BIAS_EN                       (0x1<<11)
+#define PWR_SHORT_CURR_DET_EN          (0x1<<10)
+#define PWR_SOFTGEN_EN                         (0x1<<8)
+#define        PWR_DEPOP_BUF_HP                        (0x1<<6)
+#define        PWR_HP_OUT_AMP                          (0x1<<5)
+#define        PWR_HP_OUT_ENH_AMP                      (0x1<<4)
+#define PWR_DEPOP_BUF_AUX                      (0x1<<2)
+#define PWR_AUX_OUT_AMP                                (0x1<<1)
+#define PWR_AUX_OUT_ENH_AMP                    (0x1)
+
+
+//Power managment addition 2(0x3C),0:Disable,1:Enable
+#define PWR_CLASS_AB                           (0x1<<15)
+#define PWR_CLASS_D                                    (0x1<<14)
+#define PWR_VREF                                       (0x1<<13)
+#define PWR_PLL                                                (0x1<<12)
+#define PWR_DAC_REF_CIR                                (0x1<<10)
+#define PWR_L_DAC_CLK                          (0x1<<9)
+#define PWR_R_DAC_CLK                          (0x1<<8)
+#define PWR_L_ADC_CLK_GAIN                     (0x1<<7)
+#define PWR_R_ADC_CLK_GAIN                     (0x1<<6)
+#define PWR_L_HP_MIXER                         (0x1<<5)
+#define PWR_R_HP_MIXER                         (0x1<<4)
+#define PWR_SPK_MIXER                          (0x1<<3)
+#define PWR_MONO_MIXER                         (0x1<<2)
+#define PWR_L_ADC_REC_MIXER                    (0x1<<1)
+#define PWR_R_ADC_REC_MIXER                    (0x1)
+
+//Power managment addition 3(0x3E),0:Disable,1:Enable
+#define PWR_MAIN_BIAS                          (0x1<<15)
+#define PWR_AUXOUT_L_VOL_AMP           (0x1<<14)
+#define PWR_AUXOUT_R_VOL_AMP           (0x1<<13)
+#define PWR_SPK_OUT                                    (0x1<<12)
+#define PWR_HP_L_OUT_VOL                       (0x1<<10)
+#define PWR_HP_R_OUT_VOL                       (0x1<<9)
+#define PWR_LINEIN_L_VOL                       (0x1<<7)
+#define PWR_LINEIN_R_VOL                       (0x1<<6)
+#define PWR_AUXIN_L_VOL                                (0x1<<5)
+#define PWR_AUXIN_R_VOL                                (0x1<<4)
+#define PWR_MIC1_FUN_CTRL                      (0x1<<3)
+#define PWR_MIC2_FUN_CTRL                      (0x1<<2)
+#define PWR_MIC1_BOOST_MIXER           (0x1<<1)
+#define PWR_MIC2_BOOST_MIXER           (0x1)
+
+
+//Additional Control Register(0x40)
+#define AUXOUT_SEL_DIFF                                        (0x1<<15)       //Differential Mode
+#define AUXOUT_SEL_SE                                  (0x1<<15)       //Single-End Mode
+
+#define SPK_AB_AMP_CTRL_MASK                   (0x7<<12)
+#define SPK_AB_AMP_CTRL_RATIO_225              (0x0<<12)               //2.25 Vdd
+#define SPK_AB_AMP_CTRL_RATIO_200              (0x1<<12)               //2.00 Vdd
+#define SPK_AB_AMP_CTRL_RATIO_175              (0x2<<12)               //1.75 Vdd
+#define SPK_AB_AMP_CTRL_RATIO_150              (0x3<<12)               //1.50 Vdd
+#define SPK_AB_AMP_CTRL_RATIO_125              (0x4<<12)               //1.25 Vdd      
+#define SPK_AB_AMP_CTRL_RATIO_100              (0x5<<12)               //1.00 Vdd
+
+#define SPK_D_AMP_CTRL_MASK                            (0x3<<10)
+#define SPK_D_AMP_CTRL_RATIO_175               (0x0<<10)               //1.75 Vdd
+#define SPK_D_AMP_CTRL_RATIO_150               (0x1<<10)               //1.50 Vdd      
+#define SPK_D_AMP_CTRL_RATIO_125               (0x2<<10)               //1.25 Vdd
+#define SPK_D_AMP_CTRL_RATIO_100               (0x3<<10)               //1.00 Vdd
+
+#define STEREO_DAC_HI_PASS_FILTER_EN   (0x1<<9)                //Stereo DAC high pass filter enable
+#define STEREO_ADC_HI_PASS_FILTER_EN   (0x1<<8)                //Stereo ADC high pass filter enable
+
+#define DIG_VOL_BOOST_MASK                             (0x3<<4)                //Digital volume Boost mask
+#define DIG_VOL_BOOST_0DB                              (0x0<<4)                //Digital volume Boost 0DB
+#define DIG_VOL_BOOST_6DB                              (0x1<<4)                //Digital volume Boost 6DB
+#define DIG_VOL_BOOST_12DB                             (0x2<<4)                //Digital volume Boost 12DB
+#define DIG_VOL_BOOST_18DB                             (0x3<<4)                //Digital volume Boost 18DB
+
+
+//Global Clock Control Register(0x42)
+#define SYSCLK_SOUR_SEL_MASK                   (0x1<<15)
+#define SYSCLK_SOUR_SEL_MCLK                   (0x0<<15)               //system Clock source from MCLK
+#define SYSCLK_SOUR_SEL_PLL                            (0x1<<15)               //system Clock source from PLL
+#define PLLCLK_SOUR_SEL_MCLK                   (0x0<<14)               //PLL clock source from MCLK
+#define PLLCLK_SOUR_SEL_BITCLK                 (0x1<<14)               //PLL clock source from BITCLK
+
+#define PLLCLK_DIV_RATIO_MASK                  (0x3<<1)                
+#define PLLCLK_DIV_RATIO_DIV1                  (0x0<<1)                //DIV 1
+#define PLLCLK_DIV_RATIO_DIV2                  (0x1<<1)                //DIV 2
+#define PLLCLK_DIV_RATIO_DIV4                  (0x2<<1)                //DIV 4
+#define PLLCLK_DIV_RATIO_DIV8                  (0x3<<1)                //DIV 8
+
+#define PLLCLK_PRE_DIV1                                        (0x0)                   //DIV 1
+#define PLLCLK_PRE_DIV2                                        (0x1)                   //DIV 2
+
+//PLL Control(0x44)
+
+#define PLL_CTRL_M_VAL(m)              ((m)&0xf)
+#define PLL_CTRL_K_VAL(k)              (((k)&0x7)<<4)
+#define PLL_CTRL_N_VAL(n)              (((n)&0xff)<<8)
+
+//GPIO Pin Configuration(0x4C)
+#define GPIO_PIN_MASK                          (0x1<<1)
+#define GPIO_PIN_SET_INPUT                     (0x1<<1)
+#define GPIO_PIN_SET_OUTPUT                    (0x0<<1)
+
+//Pin Sharing(0x56)
+#define LINEIN_L_PIN_SHARING           (0x1<<15)
+#define LINEIN_L_PIN_AS_LINEIN_L       (0x0<<15)
+#define LINEIN_L_PIN_AS_JD1                    (0x1<<15)
+
+#define LINEIN_R_PIN_SHARING           (0x1<<14)
+#define LINEIN_R_PIN_AS_LINEIN_R       (0x0<<14)
+#define LINEIN_R_PIN_AS_JD2                    (0x1<<14)
+
+#define GPIO_PIN_SHARING                       (0x3)
+#define GPIO_PIN_AS_GPIO                       (0x0)
+#define GPIO_PIN_AS_IRQOUT                     (0x1)
+#define GPIO_PIN_AS_PLLOUT                     (0x3)
+
+//Jack Detect Control Register(0x5A)
+#define JACK_DETECT_MASK                       (0x3<<14)
+#define JACK_DETECT_USE_JD2                    (0x3<<14)
+#define JACK_DETECT_USE_JD1                    (0x2<<14)
+#define JACK_DETECT_USE_GPIO           (0x1<<14)
+#define JACK_DETECT_OFF                                (0x0<<14)
+
+#define        SPK_EN_IN_HI                            (0x1<<11)
+#define AUX_R_EN_IN_HI                         (0x1<<10)
+#define AUX_L_EN_IN_HI                         (0x1<<9)
+#define HP_EN_IN_HI                                    (0x1<<8)
+#define        SPK_EN_IN_LO                            (0x1<<7)
+#define AUX_R_EN_IN_LO                         (0x1<<6)
+#define AUX_L_EN_IN_LO                         (0x1<<5)
+#define HP_EN_IN_LO                                    (0x1<<4)
+
+////MISC CONTROL(0x5E)
+#define DISABLE_FAST_VREG                      (0x1<<15)
+#define SPK_CLASS_AB_OC_PD                     (0x1<<13)
+#define SPK_CLASS_AB_OC_DET                    (0x1<<12)
+#define HP_DEPOP_MODE3_EN                      (0x1<<10)
+#define HP_DEPOP_MODE2_EN                      (0x1<<9)
+#define HP_DEPOP_MODE1_EN                      (0x1<<8)
+#define AUXOUT_DEPOP_MODE3_EN          (0x1<<6)
+#define AUXOUT_DEPOP_MODE2_EN          (0x1<<5)
+#define AUXOUT_DEPOP_MODE1_EN          (0x1<<4)
+#define M_DAC_L_INPUT                          (0x1<<3)
+#define M_DAC_R_INPUT                          (0x1<<2)
+#define IRQOUT_INV_CTRL                                (0x1<<0)
+
+//Psedueo Stereo & Spatial Effect Block Control(0x60)
+#define SPATIAL_CTRL_EN                                (0x1<<15)
+#define ALL_PASS_FILTER_EN                     (0x1<<14)
+#define PSEUDO_STEREO_EN                       (0x1<<13)
+#define STEREO_EXPENSION_EN                    (0x1<<12)
+
+#define GAIN_3D_PARA_L_MASK                    (0x7<<9)
+#define GAIN_3D_PARA_L_1_00                    (0x0<<9)
+#define GAIN_3D_PARA_L_1_25                    (0x1<<9)
+#define GAIN_3D_PARA_L_1_50                    (0x2<<9)
+#define GAIN_3D_PARA_L_1_75                    (0x3<<9)
+#define GAIN_3D_PARA_L_2_00                    (0x4<<9)
+
+#define GAIN_3D_PARA_R_MASK                    (0x7<<6)
+#define GAIN_3D_PARA_R_1_00                    (0x0<<6)
+#define GAIN_3D_PARA_R_1_25                    (0x1<<6)
+#define GAIN_3D_PARA_R_1_50                    (0x2<<6)
+#define GAIN_3D_PARA_R_1_75                    (0x3<<6)
+#define GAIN_3D_PARA_R_2_00                    (0x4<<6)
+
+#define RATIO_3D_L_MASK                                (0x3<<4)
+#define RATIO_3D_L_0_0                         (0x0<<4)
+#define RATIO_3D_L_0_66                                (0x1<<4)
+#define RATIO_3D_L_1_0                         (0x2<<4)
+
+#define RATIO_3D_R_MASK                                (0x3<<2)
+#define RATIO_3D_R_0_0                         (0x0<<2)
+#define RATIO_3D_R_0_66                                (0x1<<2)
+#define RATIO_3D_R_1_0                         (0x2<<2)
+
+#define APF_MASK                                       (0x3)
+#define APF_FOR_48K                                    (0x3)
+#define APF_FOR_44_1K                          (0x2)
+#define APF_FOR_32K                                    (0x1)
+
+//EQ CONTROL(0x62)
+
+#define EN_HW_EQ_BLK                   (0x1<<15)               //HW EQ block control
+#define EN_HW_EQ_HPF_MODE              (0x1<<14)               //High Frequency shelving filter mode
+#define EN_HW_EQ_SOUR                  (0x1<<11)               //0:DAC PATH,1:ADC PATH
+#define EN_HW_EQ_HPF                   (0x1<<4)                //EQ High Pass Filter Control
+#define EN_HW_EQ_BP3                   (0x1<<3)                //EQ Band-3 Control
+#define EN_HW_EQ_BP2                   (0x1<<2)                //EQ Band-2 Control
+#define EN_HW_EQ_BP1                   (0x1<<1)                //EQ Band-1 Control
+#define EN_HW_EQ_LPF                   (0x1<<0)                //EQ Low Pass Filter Control
+
+//EQ Mode Change Enable(0x66)
+#define EQ_HPF_CHANGE_EN               (0x1<<4)                //EQ High Pass Filter Mode Change Enable
+#define EQ_BP3_CHANGE_EN               (0x1<<3)                //EQ Band-3 Pass Filter Mode Change Enable
+#define EQ_BP2_CHANGE_EN               (0x1<<2)                //EQ Band-2 Pass Filter Mode Change Enable
+#define EQ_BP1_CHANGE_EN               (0x1<<1)                //EQ Band-1 Pass Filter Mode Change Enable
+#define EQ_LPF_CHANGE_EN               (0x1<<0)                //EQ Low Pass Filter Mode Change Enable
+
+
+//AVC Control(0x68)
+#define AVC_ENABLE                             (0x1<<15)
+#define AVC_TARTGET_SEL_MASK   (0x1<<14)
+#define AVC_TARTGET_SEL_R              (0x1<<14)
+#define AVC_TARTGET_SEL_L              (0x0<<14)
+
+
+#define RT5621_PLL_FR_MCLK                     0
+#define RT5621_PLL_FR_BCLK                     1
+
+
+#define REALTEK_HWDEP 0
+
+//WaveOut channel for realtek codec
+enum 
+{
+       RT_WAVOUT_SPK                           =(0x1<<0),
+       RT_WAVOUT_SPK_R                         =(0x1<<1),
+       RT_WAVOUT_SPK_L                         =(0x1<<2),
+       RT_WAVOUT_HP                            =(0x1<<3),
+       RT_WAVOUT_HP_R                          =(0x1<<4),
+       RT_WAVOUT_HP_L                          =(0x1<<5),      
+       RT_WAVOUT_MONO                          =(0x1<<6),
+       RT_WAVOUT_AUXOUT                        =(0x1<<7),
+       RT_WAVOUT_AUXOUT_R                      =(0x1<<8),
+       RT_WAVOUT_AUXOUT_L                      =(0x1<<9),
+       RT_WAVOUT_LINEOUT                       =(0x1<<10),
+       RT_WAVOUT_LINEOUT_R                     =(0x1<<11),
+       RT_WAVOUT_LINEOUT_L                     =(0x1<<12),     
+       RT_WAVOUT_DAC                           =(0x1<<13),             
+       RT_WAVOUT_ALL_ON                        =(0x1<<14),
+};
+
+//WaveIn channel for realtek codec
+enum
+{
+       RT_WAVIN_R_MONO_MIXER           =(0x1<<0),
+       RT_WAVIN_R_SPK_MIXER            =(0x1<<1),
+       RT_WAVIN_R_HP_MIXER                     =(0x1<<2),
+       RT_WAVIN_R_PHONE                        =(0x1<<3),
+       RT_WAVIN_R_AUXIN                        =(0x1<<3),      
+       RT_WAVIN_R_LINE_IN                      =(0x1<<4),
+       RT_WAVIN_R_MIC2                         =(0x1<<5),
+       RT_WAVIN_R_MIC1                         =(0x1<<6),
+
+       RT_WAVIN_L_MONO_MIXER           =(0x1<<8),
+       RT_WAVIN_L_SPK_MIXER            =(0x1<<9),
+       RT_WAVIN_L_HP_MIXER                     =(0x1<<10),
+       RT_WAVIN_L_PHONE                        =(0x1<<11),
+       RT_WAVIN_L_AUXIN                        =(0x1<<11),
+       RT_WAVIN_L_LINE_IN                      =(0x1<<12),
+       RT_WAVIN_L_MIC2                         =(0x1<<13),
+       RT_WAVIN_L_MIC1                         =(0x1<<14),
+};
+
+enum 
+{
+       POWER_STATE_D0=0,
+       POWER_STATE_D1,
+       POWER_STATE_D1_PLAYBACK,
+       POWER_STATE_D1_RECORD,
+       POWER_STATE_D2,
+       POWER_STATE_D2_PLAYBACK,
+       POWER_STATE_D2_RECORD,
+       POWER_STATE_D3,
+       POWER_STATE_D4
+
+}; 
+
+#if REALTEK_HWDEP
+
+struct rt56xx_reg_state
+{
+       unsigned int reg_index;
+       unsigned int reg_value;
+};
+
+struct rt56xx_cmd
+{
+       size_t number;
+       struct rt56xx_reg_state __user *buf;            
+};
+
+enum 
+{
+       RT_READ_CODEC_REG_IOCTL = _IOR('R', 0x01, struct rt56xx_cmd),
+       RT_READ_ALL_CODEC_REG_IOCTL = _IOR('R', 0x02, struct rt56xx_cmd),
+       RT_WRITE_CODEC_REG_IOCTL = _IOW('R', 0x03, struct rt56xx_cmd),
+};
+
+#endif
+
+#endif /* __RT5621_H__ */
index 83014a7c2e142ccf0a63cafa3c38519b4f2fd5c8..8da1cc17b79cf46ee3fbb8edaaa982a73885f0fe 100755 (executable)
@@ -10,7 +10,7 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-
+#define DEBUG
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
@@ -3228,7 +3228,7 @@ static int __devexit wm8994_remove(struct platform_device *pdev)
 
 static struct platform_driver wm8994_codec_driver = {
        .driver = {
-                  .name = "wm8994-codec",
+                  .name = "WM8994",
                   .owner = THIS_MODULE,
                   },
        .probe = wm8994_probe,
index 433fb2ba9732465310f7f159a2fa044a0f3b90ad..9474a30ea572fac100511a68ad051a16cf54bacb 100755 (executable)
@@ -52,14 +52,14 @@ config SND_RK29_SOC_WM8900
        help
          Say Y if you want to add support for SoC audio on rockchip
          with the WM8900.
-config SND_RK29_SOC_alc5621
-       tristate "SoC I2S Audio support for rockchip - alc5621"
+config SND_RK29_SOC_RT5621
+       tristate "SoC I2S Audio support for rockchip - rt5621"
        depends on SND_RK29_SOC && I2C_RK29
        select SND_RK29_SOC_I2S
-       select SND_SOC_alc5621
+       select SND_SOC_RT5621
        help
          Say Y if you want to add support for SoC audio on rockchip
-         with the alc5621.
+         with the rt5621.
 config SND_RK29_SOC_RT5631
        tristate "SoC I2S Audio support for rockchip - RT5631"
        depends on SND_RK29_SOC && I2C_RK29
@@ -105,7 +105,7 @@ config SND_RK29_SOC_RK1000
          Say Y if you want to add support for SoC audio on rockchip
          with the RK1000.       
           
-if SND_RK29_SOC_WM8988 || SND_RK29_SOC_RK1000 || SND_RK29_SOC_WM8994 || SND_RK29_SOC_WM8900 || SND_RK29_SOC_alc5621 || SND_RK29_SOC_RT5631 || SND_RK29_SOC_RT5625 || SND_RK29_SOC_CS42L52
+if SND_RK29_SOC_WM8988 || SND_RK29_SOC_RK1000 || SND_RK29_SOC_WM8994 || SND_RK29_SOC_WM8900 || SND_RK29_SOC_RT5621 || SND_RK29_SOC_RT5631 || SND_RK29_SOC_RT5625 || SND_RK29_SOC_CS42L52
 choice
   prompt "Set i2s type"
        config SND_RK29_CODEC_SOC_MASTER
index 657cdab9133151bb8bffa5f48085abd988f8f47a..cc0c09b0b40d4a350612a80bca0794fc51d5ba54 100644 (file)
@@ -7,7 +7,7 @@ obj-$(CONFIG_SND_RK29_SOC_I2S) += snd-soc-rockchip-i2s.o
 
 # ROCKCHIP Machine Support
 snd-soc-wm8900-objs := rk29_wm8900.o
-snd-soc-alc5621-objs := rk29_alc5621.o
+snd-soc-rt5621-objs := rk29_rt5621.o
 snd-soc-rt5631-objs := rk29_rt5631.o
 snd-soc-rt5625-objs := rk29_rt5625.o
 snd-soc-cs42l52-objs := rk29_cs42l52.o
@@ -18,7 +18,7 @@ snd-soc-wm8994-objs := rk29_wm8994.o
 obj-$(CONFIG_SND_RK29_SOC_WM8994) += snd-soc-wm8994.o
 obj-$(CONFIG_SND_RK29_SOC_WM8988) += snd-soc-wm8988.o
 obj-$(CONFIG_SND_RK29_SOC_WM8900) += snd-soc-wm8900.o
-obj-$(CONFIG_SND_RK29_SOC_alc5621) += snd-soc-alc5621.o
+obj-$(CONFIG_SND_RK29_SOC_RT5621) += snd-soc-rt5621.o
 obj-$(CONFIG_SND_RK29_SOC_RT5631) += snd-soc-rt5631.o
 obj-$(CONFIG_SND_RK29_SOC_RT5625) += snd-soc-rt5625.o
 obj-$(CONFIG_SND_RK29_SOC_RK1000) += snd-soc-rk1000.o
diff --git a/sound/soc/rk29/rk29_rt5621.c b/sound/soc/rk29/rk29_rt5621.c
new file mode 100644 (file)
index 0000000..92c221e
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * rk29_rt5621.c  --  SoC audio for rockchip
+ *
+ * Driver for rockchip rt5621 audio
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <asm/io.h>
+#include <mach/hardware.h>
+#include <mach/rk29_iomap.h>
+#include "../codecs/rt5621.h"
+#include "rk29_pcm.h"
+#include "rk29_i2s.h"
+
+#if 0
+#define        DBG(x...)       printk(KERN_INFO x)
+#else
+#define        DBG(x...)
+#endif
+
+static int rk29_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       unsigned int pll_out = 0; 
+       unsigned int lrclk = 0;
+       int ret;
+         
+       DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);    
+
+       /*by Vincent Hsiung for EQ Vol Change*/
+       #define HW_PARAMS_FLAG_EQVOL_ON 0x21
+       #define HW_PARAMS_FLAG_EQVOL_OFF 0x22
+       if ((params->flags == HW_PARAMS_FLAG_EQVOL_ON)||(params->flags == HW_PARAMS_FLAG_EQVOL_OFF))
+       {
+               ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai); //by Vincent
+               DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+       } else {
+               /* set codec DAI configuration */
+#if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) 
+               ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+                       SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); 
+#endif
+#if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) 
+               ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+                       SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM ); 
+#endif
+               if (ret < 0)
+                       return ret; 
+               /* set cpu DAI configuration */
+#if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) 
+               ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+                       SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+#endif
+#if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) 
+               ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+                       SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); 
+#endif
+               if (ret < 0)
+                       return ret;
+       }
+
+       switch(params_rate(params)) {
+       case 8000:
+       case 16000:
+       case 24000:
+       case 32000:
+       case 48000:
+               pll_out = 12288000;
+               break;
+       case 11025:
+       case 22050:
+       case 44100:
+               pll_out = 11289600;
+               break;
+       default:
+               DBG("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params));
+               return -EINVAL;
+               break;
+       }
+       DBG("Enter:%s, %d, rate=%d\n",__FUNCTION__,__LINE__,params_rate(params));
+
+#if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE)
+#if 0  //use pll from blck
+       /*Set the pll of rt5621,the Pll source from BITCLK on CPU is master mode*/
+       //bitclk is 64fs           
+       ret=snd_soc_dai_set_pll(codec_dai,RT5621_PLL_FR_BCLK,params_rate(params)*64,pll_out);
+       if (ret < 0) { 
+               DBG("rk29_hw_params_rt5621:failed to set the pll for codec side\n"); 
+               return ret;
+       }
+#endif     
+       /*Set the system clk for codec*/
+       ret=snd_soc_dai_set_sysclk(codec_dai, 0,pll_out,SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               DBG("rk29_hw_params_rt5621:failed to set the sysclk for codec side\n"); 
+               return ret;
+       }           
+#endif
+  
+
+  #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) 
+
+       if((24576000%params_rate(params))==0)   //for 8k,16k,32k,48k
+       {
+               snd_soc_dai_set_pll(codec_dai,RT5621_PLL_FR_MCLK,pll_out, 24576000);
+               snd_soc_dai_set_sysclk(codec_dai,0, 24576000, SND_SOC_CLOCK_IN);                        
+       }
+       else if((22579200%params_rate(params))==0)      //for 11k,22k,44k
+       {
+               snd_soc_dai_set_pll(codec_dai,RT5621_PLL_FR_MCLK,pll_out, 22579200);
+               snd_soc_dai_set_sysclk(codec_dai,0, 22579200, SND_SOC_CLOCK_IN);                        
+       }
+      
+#endif
+
+
+#if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE)
+       snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
+       snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK, (pll_out/4)/params_rate(params)-1);
+       snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, 3);
+#endif
+
+       DBG("Enter:%s, %d, LRCK=%d\n",__FUNCTION__,__LINE__,(pll_out/4)/params_rate(params));
+        
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget rt5621_dapm_widgets[] = {
+       
+       SND_SOC_DAPM_MIC("Mic Jack", NULL),
+       SND_SOC_DAPM_SPK("Ext Spk", NULL),
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+
+};
+
+static const struct snd_soc_dapm_route audio_map[]={
+
+       /* Mic Jack --> MIC_IN*/
+       {"Mic Bias1", NULL, "Mic Jack"},
+       {"MIC1", NULL, "Mic Bias1"},
+       /* HP_OUT --> Headphone Jack */
+       {"Headphone Jack", NULL, "HPOL"},
+       {"Headphone Jack", NULL, "HPOR"},
+       /* LINE_OUT --> Ext Speaker */
+       {"Ext Spk", NULL, "SPOL"},
+       {"Ext Spk", NULL, "SPOR"},
+
+} ;
+
+/*
+ * Logic for a rt5621 as connected on a rockchip board.
+ */
+static int rk29_wm8988_init(struct snd_soc_pcm_runtime *rtd)
+{
+        return 0;
+}
+
+static struct snd_soc_ops rk29_ops = {
+         .hw_params = rk29_hw_params,
+};
+
+static struct snd_soc_dai_link rk29_dai = {
+       .name = "RT5621",
+       .stream_name = "RT5621 PCM",
+       .codec_name = "RT5621.0-001a",
+       .platform_name = "rockchip-audio",
+       .cpu_dai_name = "rk29_i2s.0",
+       .codec_dai_name = "RT5621 HiFi",
+       .init = rk29_wm8988_init,
+       .ops = &rk29_ops,
+};
+
+static struct snd_soc_card snd_soc_card_rk29 = {
+       .name = "RK29_RT5621",
+       .dai_link = &rk29_dai,
+       .num_links = 1,
+};
+
+static struct platform_device *rk29_snd_device;
+
+static int __init audio_card_init(void)
+{
+    int ret =0;        
+       
+    //rk29_speaker = rk29_speaker_init(RK29_PIN6_PB6, GPIO_HIGH, 2, (200*1000*1000));
+
+    DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+       rk29_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!rk29_snd_device) {
+                 DBG("platform device allocation failed\n");
+                 ret = -ENOMEM;
+                 return ret;
+       }
+       platform_set_drvdata(rk29_snd_device, &snd_soc_card_rk29);
+       ret = platform_device_add(rk29_snd_device);
+       if (ret) {
+           DBG("platform device add failed\n");
+           platform_device_put(rk29_snd_device);
+               return ret;
+       }
+       return ret;
+}
+
+static void __exit audio_card_exit(void)
+{
+       platform_device_unregister(rk29_snd_device);
+}
+
+module_init(audio_card_init);
+module_exit(audio_card_exit);
+/* Module information */
+MODULE_AUTHOR("rockchip");
+MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface");
+MODULE_LICENSE("GPL");
index b666b0c2090645f23e7eec5348a69f0213dd1241..84222e918eefbfe120bf8876bd85b896a8e09bc6 100755 (executable)
 #include "rk29_i2s.h"
 #include <linux/clk.h>
 
-#if 0
+#if 1
 #define        DBG(x...)       printk(KERN_INFO x)
 #else
 #define        DBG(x...)
 #endif
 
-static int rk29_hw_params(struct snd_pcm_substream *substream,
+#define HW_PARAMS_FLAG_EQVOL_ON 0x21
+#define HW_PARAMS_FLAG_EQVOL_OFF 0x22
+
+static int rk29_aif1_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params)
 {
-    struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
-       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;    
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        unsigned int pll_out = 0; 
        int div_bclk,div_mclk;
        int ret;
        struct clk      *general_pll;
-         
-    DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);    
+
+       DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+
+       if ((params->flags == HW_PARAMS_FLAG_EQVOL_ON)||(params->flags == HW_PARAMS_FLAG_EQVOL_OFF))
+       {
+               DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+
+               if (codec_dai->driver->ops->hw_params)
+                       ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai); //by Vincent
+
+               return 0;
+       }
+
        /* set codec DAI configuration */
 #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) 
-       DBG("Set codec_dai slave\n");    
+       DBG("Set codec_dai slave\n");
        ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
-                           SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+               SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 #endif 
 #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER)                            
-    ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
-                           SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
-       DBG("Set codec_dai master\n");                                          
+       ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+               SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+       DBG("Set codec_dai master\n");
 #endif
-    if (ret < 0)
-        return ret; 
+       if (ret < 0)
+               return ret; 
 
        /* set cpu DAI configuration */
 #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) 
-       DBG("Set cpu_dai slave\n");    
+       DBG("Set cpu_dai slave\n");
        ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
-                            SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+               SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
 #endif 
 #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER)  
        ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
-                            SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);    
-       DBG("Set cpu_dai master\n");                            
+               SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); 
+       DBG("Set cpu_dai master\n"); 
 #endif         
        if (ret < 0)
                return ret;
 
-    switch(params_rate(params)) {
-        case 8000:
-        case 16000:
-        case 24000:
-        case 32000:
-        case 48000:
-            pll_out = 12288000;
-            break;
-        case 11025:
-        case 22050:
-        case 44100:
-            pll_out = 11289600;
-            break;
-        default:
-            DBG("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params));
-            return -EINVAL;
-            break;
-     }
-     DBG("Enter:%s, %d, rate=%d\n",__FUNCTION__,__LINE__,params_rate(params));
+       switch(params_rate(params)) {
+               case 8000:
+               case 16000:
+               case 24000:
+               case 32000:
+               case 48000:
+                       pll_out = 12288000;
+                       break;
+               case 11025:
+               case 22050:
+               case 44100:
+                       pll_out = 11289600;
+                       break;
+               default:
+                       DBG("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params));
+                       return -EINVAL;
+                       break;
+       }
+
+       DBG("Enter:%s, %d, rate=%d\n",__FUNCTION__,__LINE__,params_rate(params));
+
 #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER)         
        snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
-#endif 
-       
+#endif
+
 #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE)
        general_pll=clk_get(NULL, "general_pll");
-       if(clk_get_rate(general_pll)>260000000)
-       {
+
+       if(clk_get_rate(general_pll)>260000000) {
                div_bclk=(pll_out/4)/params_rate(params)-1;
                div_mclk=3;
        }
@@ -113,21 +129,174 @@ static int rk29_hw_params(struct snd_pcm_substream *substream,
                div_bclk=(pll_out)/params_rate(params)-1;
                div_mclk=0;
        }
-       DBG("func is%s,gpll=%ld,pll_out=%ld,div_mclk=%ld\n",
-                       __FUNCTION__,clk_get_rate(general_pll),pll_out,div_mclk);
+       DBG("func is%s,gpll=%ld,pll_out=%d,div_mclk=%d\n",
+               __FUNCTION__,clk_get_rate(general_pll),pll_out,div_mclk);
        snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
        snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK,div_bclk);
        snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, div_mclk);
        
+       if(div_mclk == 3)
+               snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1, pll_out, 0);
+       else
+               snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1, pll_out, 0);      
+#endif
+
+       return 0;
+}
+
+static int rk29_aif2_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       unsigned int pll_out = 0; 
+       int div_bclk,div_mclk;
+       int ret;
+       struct clk      *general_pll;
+
+       DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+
+       /* set codec DAI configuration */
+#if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) 
+       DBG("Set codec_dai slave\n");
+       ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+               SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+#endif 
+#if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER)                            
+       ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+               SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+       DBG("Set codec_dai master\n");
+#endif
+       if (ret < 0)
+               return ret; 
+
+       switch(params_rate(params)) {
+               case 8000:
+               case 16000:
+               case 24000:
+               case 32000:
+               case 48000:
+                       pll_out = 12288000;
+                       break;
+               case 11025:
+               case 22050:
+               case 44100:
+                       pll_out = 11289600;
+                       break;
+               default:
+                       DBG("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params));
+                       return -EINVAL;
+                       break;
+       }
+
+       DBG("Enter:%s, %d, rate=%d\n",__FUNCTION__,__LINE__,params_rate(params));
+
+#if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER)         
+       snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
+#endif
+
+#if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE)
+       general_pll=clk_get(NULL, "general_pll");
+
+       if(clk_get_rate(general_pll)>260000000) {
+               div_bclk=(pll_out/4)/params_rate(params)-1;
+               div_mclk=3;
+       }
+       else if(clk_get_rate(general_pll)>130000000)
+       {
+               div_bclk=(pll_out/2)/params_rate(params)-1;
+               div_mclk=1;
+       }
+       else
+       {
+               pll_out=pll_out/4;
+               div_bclk=(pll_out)/params_rate(params)-1;
+               div_mclk=0;
+       }
+       DBG("func is%s,gpll=%ld,pll_out=%d,div_mclk=%d\n",
+               __FUNCTION__,clk_get_rate(general_pll),pll_out,div_mclk);
+       snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
+       
        if(div_mclk == 3)
                snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1, pll_out, 0);
        else
                snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1, pll_out, 0);
-       DBG("Enter:%s, %d, LRCK=%d\n",__FUNCTION__,__LINE__,(pll_out/4)/params_rate(params));           
 #endif
 
-    return 0;
+       return 0;
+}
+
+static int rk29_aif3_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       unsigned int pll_out = 0; 
+       int div_bclk,div_mclk;
+       int ret;
+       struct clk      *general_pll;
+
+       DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+
+       /* set codec DAI configuration */                          
+       ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+               SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+       DBG("Set codec_dai master\n");
+
+       if (ret < 0)
+               return ret; 
+
+       switch(params_rate(params)) {
+               case 8000:
+               case 16000:
+               case 24000:
+               case 32000:
+               case 48000:
+                       pll_out = 12288000;
+                       break;
+               case 11025:
+               case 22050:
+               case 44100:
+                       pll_out = 11289600;
+                       break;
+               default:
+                       DBG("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params));
+                       return -EINVAL;
+                       break;
+       }
+
+       DBG("Enter:%s, %d, rate=%d\n",__FUNCTION__,__LINE__,params_rate(params));
+
+       snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
+
+       general_pll=clk_get(NULL, "general_pll");
+
+       if(clk_get_rate(general_pll)>260000000) {
+               div_bclk=(pll_out/4)/params_rate(params)-1;
+               div_mclk=3;
+       }
+       else if(clk_get_rate(general_pll)>130000000)
+       {
+               div_bclk=(pll_out/2)/params_rate(params)-1;
+               div_mclk=1;
+       }
+       else
+       {
+               pll_out=pll_out/4;
+               div_bclk=(pll_out)/params_rate(params)-1;
+               div_mclk=0;
+       }
+       
+       if(div_mclk == 3)
+               snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1, pll_out, 0);
+       else
+               snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1, pll_out, 0);
+
+       return 0;
 }
+
 /*
 static const struct snd_soc_dapm_widget rk2818_dapm_widgets[] = {
        SND_SOC_DAPM_LINE("Audio Out", NULL),
@@ -149,80 +318,108 @@ static const struct snd_soc_dapm_route audio_map[]= {
 /*
  * Logic for a wm8994 as connected on a rockchip board.
  */
-static int rk29_wm8994_init(struct snd_soc_codec *codec)
+static int rk29_wm8994_init(struct snd_soc_pcm_runtime *rtd)
 {
-       struct snd_soc_dai *codec_dai = &codec->dai[0];
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_codec *codec = rtd->codec;
+       struct snd_soc_dapm_context *dapm = &codec->dapm;
        int ret;
 
-    DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+       DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
     
-    ret = snd_soc_dai_set_sysclk(codec_dai, 0,
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0,
                12000000, SND_SOC_CLOCK_IN);
        if (ret < 0) {
                printk(KERN_ERR "Failed to set WM8994 SYSCLK: %d\n", ret);
                return ret;
        }
        
-    /* Add specific widgets */
-//     snd_soc_dapm_new_controls(codec, rk2818_dapm_widgets,
-//                               ARRAY_SIZE(rk2818_dapm_widgets));     
-    /* Set up specific audio path audio_mapnects */
-//    snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));       
-//    snd_soc_dapm_sync(codec);
+       /* Add specific widgets */
+//     snd_soc_dapm_new_controls(dapm, rk2818_dapm_widgets,
+//             ARRAY_SIZE(rk2818_dapm_widgets));       
+       /* Set up specific audio path audio_mapnects */
+//     snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));       
+//     snd_soc_dapm_sync(codec);
  
     return 0;
 }
 
-static struct snd_soc_ops rk29_ops = {
-         .hw_params = rk29_hw_params,
+static struct snd_soc_ops rk29_aif1_ops = {
+         .hw_params = rk29_aif1_hw_params,
 };
 
-static struct snd_soc_dai_link rk29_dai = {
-         .name = "WM8994",
-         .stream_name = "WM8994 PCM",
-#ifdef CONFIG_MACH_RK29_PHONEPADSDK
-         .cpu_dai = &rk29_i2s_dai[1],
-#else
-          .cpu_dai = &rk29_i2s_dai[0],
-#endif
-         .codec_dai = &wm8994_dai,
-         .init = rk29_wm8994_init,
-         .ops = &rk29_ops,
+static struct snd_soc_ops rk29_aif2_ops = {
+         .hw_params = rk29_aif2_hw_params,
 };
 
-static struct snd_soc_card snd_soc_card_rk29 = {
-         .name = "RK29_WM8994",
-         .platform = &rk29_soc_platform,
-         .dai_link = &rk29_dai,
-         .num_links = 1,
+static struct snd_soc_ops rk29_aif3_ops = {
+         .hw_params = rk29_aif3_hw_params,
 };
 
+static struct snd_soc_dai_link rk29_dai[] = {
+       {
+               .name = "WM8994",
+               .stream_name = "WM8994 I2S1",
+               .codec_name = "WM8994.0-001a",
+               .platform_name = "rockchip-audio",
+               .cpu_dai_name = "rk29_i2s.0",
+               .codec_dai_name = "wm8994-aif1",
+               .init = rk29_wm8994_init,
+               .ops = &rk29_aif1_ops,
+       },
+       {
+               .name = "WM8994",
+               .stream_name = "WM8994 I2S2",
+               .codec_name = "WM8994.1-001a",
+               .platform_name = "rockchip-audio",
+               .cpu_dai_name = "rk29_i2s.0",
+               .codec_dai_name = "wm8994-aif2",
+               .init = rk29_wm8994_init,
+               .ops = &rk29_aif2_ops,
+       },
+       {
+               .name = "WM8994",
+               .stream_name = "WM8994 I2S3",
+               .codec_name = "WM8994.2-001a",
+               .platform_name = "rockchip-audio",
+               .cpu_dai_name = "rk29_i2s.0",
+               .codec_dai_name = "wm8994-aif3",
+               .init = rk29_wm8994_init,
+               .ops = &rk29_aif3_ops,
+       },
+};
 
-static struct snd_soc_device rk29_snd_devdata = {
-         .card = &snd_soc_card_rk29,
-         .codec_dev = &soc_codec_dev_wm8994,
+static struct snd_soc_card snd_soc_card_rk29 = {
+       .name = "RK29_WM8994",
+       .dai_link = rk29_dai,
+       .num_links = 3,
 };
 
 static struct platform_device *rk29_snd_device;
 
 static int __init audio_card_init(void)
 {
-       int ret =0;     
-        DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+       int ret =0;
+
+       DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+
        rk29_snd_device = platform_device_alloc("soc-audio", -1);
        if (!rk29_snd_device) {
                  DBG("platform device allocation failed\n");
                  ret = -ENOMEM;
                  return ret;
        }
-       platform_set_drvdata(rk29_snd_device, &rk29_snd_devdata);
-       rk29_snd_devdata.dev = &rk29_snd_device->dev;
+
+       platform_set_drvdata(rk29_snd_device, &snd_soc_card_rk29);
        ret = platform_device_add(rk29_snd_device);
        if (ret) {
-               DBG("platform device add failed\n");
-               platform_device_put(rk29_snd_device);
+               DBG("platform device add failed\n");
+
+               platform_device_put(rk29_snd_device);
+               return ret;
        }
-       return ret;
+               
+        return ret;
 }
 
 static void __exit audio_card_exit(void)