Support the Rk312X Codec & SPDIF
authorSun Mingjun <smj@rock-chips.com>
Fri, 8 Aug 2014 07:59:44 +0000 (15:59 +0800)
committerSun Mingjun <smj@rock-chips.com>
Tue, 12 Aug 2014 00:43:43 +0000 (08:43 +0800)
Documentation/devicetree/bindings/sound/rockchip-audio.txt
arch/arm/boot/dts/rk312x.dtsi
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/rk312x_codec.c [new file with mode: 0755]
sound/soc/codecs/rk312x_codec.h [new file with mode: 0755]
sound/soc/rockchip/Kconfig
sound/soc/rockchip/Makefile
sound/soc/rockchip/rk_rk312x.c [new file with mode: 0755]
sound/soc/rockchip/rk_spdif.c [changed mode: 0644->0755]

index a4d728a61d4a9c553f5bcb82676e87e228e1d61d..413bfe500d13547bacda636a5c32264046909120 100644 (file)
@@ -34,3 +34,100 @@ rockchip-audio {
                };
        };
 };
+
+
+- compatible : "audio-rk312x"
+- reg: physical base address of the controller and length of memory mapped
+  region.
+- clocks: must include clock specifiers corresponding to entries in the
+  clock-names property.
+- clocks-names: list of clock names sorted in the same order as the clocks
+  property. Must contain "rockchip-i2s" and "rk3036-codec".
+- interrupts: interrupt number to the cpu.
+- dmas: list of DMA controller phandle and DMA request line ordered pairs.
+- dma-names: identifier string for each DMA request line in the dmas property.
+  These strings correspond 1:1 with the ordered pairs in dmas.
+- pinctrl-names: must contain a "default" entry.
+- pinctrl-0: pin control group to be used for this controller.
+- pinctrl-1: pin control group to be used for gpio.
+
+Example:
+
+rockchip-audio {
+        compatible = "audio-rk312x";
+        dais {
+                dai0 {
+                        audio-codec = <&codec>;
+                        i2s-controller = <&i2s1>;
+                        format = "i2s";
+                        //continuous-clock;
+                        //bitclock-inversion;
+                        //frame-inversion;
+                        //bitclock-master;
+                        //frame-master;
+                };
+        };
+};
+
+
+- compatible : "rk312x-spdif"
+- reg: physical base address of the controller and length of memory mapped
+  region.
+- clocks: must include clock specifiers corresponding to entries in the
+  clock-names property.
+- clocks-names: list of clock names sorted in the same order as the clocks
+  property. Must contain "rockchip-i2s" and "rk3036-codec".
+- interrupts: interrupt number to the cpu.
+- dmas: list of DMA controller phandle and DMA request line ordered pairs.
+- dma-names: identifier string for each DMA request line in the dmas property.
+  These strings correspond 1:1 with the ordered pairs in dmas.
+- pinctrl-names: must contain a "default" entry.
+- pinctrl-0: pin control group to be used for this controller.
+- pinctrl-1: pin control group to be used for gpio.
+
+Example:
+
+spdif: spdif@10204000 {
+       compatible = "rk312x-spdif";
+       reg = <0x10204000 0x1000>;
+       clocks = <&clk_spdif>, <&clk_gates10 9>;
+       clock-names = "spdif_8ch_mclk", "spdif_hclk";
+       interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
+       dmas = <&pdma 13>;
+       //#dma-cells = <1>;
+       dma-names = "tx";
+       pinctrl-names = "default";
+       pinctrl-0 = <&spdif_tx>;
+};
+
+
+
+- compatible : "rk312x-codec"
+- reg: physical base address of the controller and length of memory mapped
+  region.
+- clocks: must include clock specifiers corresponding to entries in the
+  clock-names property.
+- clocks-names: list of clock names sorted in the same order as the clocks
+  property. Must contain "rockchip-i2s" and "rk3036-codec".
+- interrupts: interrupt number to the cpu.
+- dmas: list of DMA controller phandle and DMA request line ordered pairs.
+- dma-names: identifier string for each DMA request line in the dmas property.
+  These strings correspond 1:1 with the ordered pairs in dmas.
+- pinctrl-names: must contain a "default" entry.
+- pinctrl-0: pin control group to be used for this controller.
+- pinctrl-1: pin control group to be used for gpio.
+
+Example:
+
+codec: codec@20030000 {
+       compatible = "rk312x-codec";
+       reg = <0x20030000 0x1000>;
+       spk_ctl_io = <&gpio1 GPIO_A3 0>;
+       //pinctrl-names = "default";
+       //pinctrl-0 = <&i2s_gpio>;
+
+       boot_depop = <1>;
+       pa_enable_time = <1000>;
+       clocks = <&clk_gates5 14>;
+       clock-names = "g_pclk_acodec";
+};
index f3049a7ed965f6cbcd2a14113cd03351a6cff292..23c340418a92be9403f1e3c61401d6655b91f9e5 100755 (executable)
                dma-names = "tx", "rx";
        };
 
+       codec: codec@20030000 {
+               compatible = "rk312x-codec";
+               reg = <0x20030000 0x1000>;
+               spk_ctl_io = <&gpio1 GPIO_A3 0>;
+               //pinctrl-names = "default";
+               //pinctrl-0 = <&i2s_gpio>;
+
+               boot_depop = <1>;
+               pa_enable_time = <1000>;
+               clocks = <&clk_gates5 14>;
+               clock-names = "g_pclk_acodec";
+       };
+
        spdif: spdif@10204000 {
-               compatible = "rockchip-spdif";
+               compatible = "rk312x-spdif";
                reg = <0x10204000 0x1000>;
-               clocks = <&clk_spdif>, <&clk_gates10 8>;
-               clock-names = "spdif_8ch_mclk", "spdif_hclk";
+               clocks = <&clk_spdif>, <&clk_gates10 9>;
+               clock-names = "spdif_mclk", "spdif_hclk";
                interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
                dmas = <&pdma 13>;
                //#dma-cells = <1>;
                dma-names = "tx";
-               //pinctrl-names = "default";
-               //pinctrl-0 = <&spdif_tx>;
-       };
+               pinctrl-names = "default";
+               pinctrl-0 = <&spdif_tx>;
+       };      
 
        dsihost0: mipi@10110000{
                compatible = "rockchip,rk32-dsi";
             clock-names = "pd_cif0", "aclk_cif0","hclk_cif0","cif0_in","cif0_out";
             status = "okay";
             };
+
+       codec_hdmi_spdif: codec-hdmi-spdif {
+               compatible = "hdmi-spdif";
+       };
+
+       rockchip-hdmi-spdif {
+               compatible = "rockchip-hdmi-spdif";
+               dais {
+                       dai0 {
+                               audio-codec = <&codec_hdmi_spdif>;
+                               i2s-controller = <&spdif>;
+                       };
+               };
+       };
+
+       rockchip-audio {
+               compatible = "audio-rk312x";
+               dais {
+                       dai0 {
+                               audio-codec = <&codec>;
+                               i2s-controller = <&i2s1>;
+                               format = "i2s";
+                               //continuous-clock;
+                               //bitclock-inversion;
+                               //frame-inversion;
+                               //bitclock-master;
+                               //frame-master;
+                       };
+                       dai1 {
+                               audio-codec = <&codec>;
+                               i2s-controller = <&i2s1>;
+                               format = "i2s";
+                               //continuous-clock;
+                               //bitclock-inversion;
+                               //frame-inversion;
+                               //bitclock-master;
+                               //frame-master;
+                       };
+               };
+       };
 };
index a11ae2b7dcb22dacb0a04c08080d83518c487618..b7ef0b9655c30307ef51044a5cfcb0af19634f55 100644 (file)
@@ -317,6 +317,9 @@ config SND_SOC_PCM3008
 config SND_SOC_RK3036
        tristate
 
+config SND_SOC_RK312X
+        tristate
+
 #Freescale sgtl5000 codec
 config SND_SOC_SGTL5000
        tristate
index 7389cb5e4d89bcb2d5c4a938dcf4cb138aba39cf..592a6ff3cbde4621c318eb940a20e0f71e63a161 100644 (file)
@@ -136,6 +136,7 @@ snd-soc-rt3261-objs := rt3261-dsp.o
 snd-soc-cs42l52-objs := cs42l52.o
 snd-soc-rk1000-objs := rk1000_codec.o
 snd-soc-rk3036-objs := rk3036_codec.o
+snd-soc-rk312x-objs := rk312x_codec.o
 snd-soc-rk610-objs := rk610_codec.o
 snd-soc-rk616-objs := rk616_codec.o
 snd-soc-rk2928-objs := rk2928_codec.o
@@ -284,6 +285,7 @@ obj-$(CONFIG_SND_SOC_RT5640)    += snd-soc-rt5640.o
 obj-$(CONFIG_SND_SOC_CS42L52)  += snd-soc-cs42l52.o
 obj-$(CONFIG_SND_SOC_RK1000)   += snd-soc-rk1000.o
 obj-$(CONFIG_SND_SOC_RK3036)   += snd-soc-rk3036.o
+obj-$(CONFIG_SND_SOC_RK312X)   += snd-soc-rk312x.o
 obj-$(CONFIG_SND_SOC_RK610)    += snd-soc-rk610.o
 obj-$(CONFIG_SND_SOC_RK616)    += snd-soc-rk616.o
 obj-$(CONFIG_SND_SOC_RK2928)   += snd-soc-rk2928.o
diff --git a/sound/soc/codecs/rk312x_codec.c b/sound/soc/codecs/rk312x_codec.c
new file mode 100755 (executable)
index 0000000..6771363
--- /dev/null
@@ -0,0 +1,2360 @@
+/*
+ * rk312x_codec.c
+ *
+ * Driver for rockchip rk3036 codec
+ * Copyright (C) 2014
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/version.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/rockchip/iomap.h>
+#include <linux/rockchip/grf.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <asm/dma.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/dmaengine_pcm.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <sound/tlv.h>
+#include "rk312x_codec.h"
+
+static int debug = 7;
+module_param(debug, int, S_IRUGO|S_IWUSR);
+
+#define dbg_codec(level, fmt, arg...)          \
+       do {                                    \
+               if (debug >= level)             \
+                       printk(fmt , ## arg);   \
+        } while (0)
+
+#define        DBG(fmt, ...)   dbg_codec(0, fmt, ## __VA_ARGS__)
+#define RK2928_ACODEC_PHYS 0x20030000
+
+#define INVALID_GPIO -1
+#define SPK_CTRL_OPEN  0
+#define SPK_CTRL_CLOSE 1
+
+/* volume setting
+ *  0: -39dB
+ *  26: 0dB
+ *  31: 6dB
+ *  Step: 1.5dB
+*/
+#define  OUT_VOLUME    25
+
+/* capture vol set
+ * 0: -18db
+ * 12: 0db
+ * 31: 28.5db
+ * step: 1.5db
+*/
+#define CAP_VOL                31      /*0-31 */
+/*with capacity or not*/
+#define WITH_CAP
+
+struct rk312x_codec_priv {
+       void __iomem    *regbase;
+       struct snd_soc_codec *codec;
+
+       unsigned int stereo_sysclk;
+       unsigned int rate;
+
+       int playback_active;
+       int capture_active;
+       int spk_ctl_gpio;
+       int hp_ctl_gpio;
+       int delay_time;
+
+       long int playback_path;
+       long int capture_path;
+       long int voice_call_path;
+       struct clk      *pclk;
+};
+static struct rk312x_codec_priv *rk312x_priv;
+
+#define RK312x_CODEC_ALL       0
+#define RK312x_CODEC_PLAYBACK  1
+#define RK312x_CODEC_CAPTURE   2
+#define RK312x_CODEC_INCALL    3
+
+#define RK312x_CODEC_WORK_NULL 0
+#define RK312x_CODEC_WORK_POWER_DOWN   1
+#define RK312x_CODEC_WORK_POWER_UP     2
+static struct workqueue_struct *rk312x_codec_workq;
+
+static void rk312x_codec_capture_work(struct work_struct *work);
+static DECLARE_DELAYED_WORK(capture_delayed_work, rk312x_codec_capture_work);
+static int rk312x_codec_work_capture_type = RK312x_CODEC_WORK_NULL;
+static bool rk312x_for_mid = 1;
+static int rk312x_codec_power_up(int type);
+static const unsigned int rk312x_reg_defaults[RK312x_PGAR_AGC_CTL5+1] = {
+       [RK312x_RESET] = 0x0003,
+       [RK312x_ADC_INT_CTL1] = 0x0050,
+       [RK312x_ADC_INT_CTL2] = 0x000e,
+       [RK312x_DAC_INT_CTL1] = 0x0050,
+       [RK312x_DAC_INT_CTL2] = 0x000e,
+       [RK312x_DAC_INT_CTL3] = 0x22,
+       [RK312x_ADC_MIC_CTL] = 0x0000,
+       [RK312x_BST_CTL] = 0x000,
+       [RK312x_ALC_MUNIN_CTL] = 0x0044,
+       [RK312x_BSTL_ALCL_CTL] = 0x000c,
+       [RK312x_ALCR_GAIN_CTL] = 0x000C,
+       [RK312x_ADC_ENABLE] = 0x0000,
+       [RK312x_DAC_CTL] = 0x0000,
+       [RK312x_DAC_ENABLE] = 0x0000,
+       [RK312x_HPMIX_CTL] = 0x0000,
+       [RK312x_HPMIX_S_SELECT] = 0x0000,
+       [RK312x_HPOUT_CTL] = 0x0000,
+       [RK312x_HPOUTL_GAIN] = 0x0000,
+       [RK312x_HPOUTR_GAIN] = 0x0000,
+       [RK312x_SELECT_CURRENT] = 0x003e,
+       [RK312x_PGAL_AGC_CTL1] = 0x0000,
+       [RK312x_PGAL_AGC_CTL2] = 0x0046,
+       [RK312x_PGAL_AGC_CTL3] = 0x0041,
+       [RK312x_PGAL_AGC_CTL4] = 0x002c,
+       [RK312x_PGAL_ASR_CTL] = 0x0000,
+       [RK312x_PGAL_AGC_MAX_H] = 0x0026,
+       [RK312x_PGAL_AGC_MAX_L] = 0x0040,
+       [RK312x_PGAL_AGC_MIN_H] = 0x0036,
+       [RK312x_PGAL_AGC_MIN_L] = 0x0020,
+       [RK312x_PGAL_AGC_CTL5] = 0x0038,
+       [RK312x_PGAR_AGC_CTL1] = 0x0000,
+       [RK312x_PGAR_AGC_CTL2] = 0x0046,
+       [RK312x_PGAR_AGC_CTL3] = 0x0041,
+       [RK312x_PGAR_AGC_CTL4] = 0x002c,
+       [RK312x_PGAR_ASR_CTL] = 0x0000,
+       [RK312x_PGAR_AGC_MAX_H] = 0x0026,
+       [RK312x_PGAR_AGC_MAX_L] = 0x0040,
+       [RK312x_PGAR_AGC_MIN_H] = 0x0036,
+       [RK312x_PGAR_AGC_MIN_L] = 0x0020,
+       [RK312x_PGAR_AGC_CTL5] = 0x0038,
+};
+
+static struct rk312x_init_bit_typ rk312x_init_bit_list[] = {
+       {RK312x_HPOUT_CTL, RK312x_HPOUTL_EN,
+        RK312x_HPOUTL_WORK, RK312x_HPVREF_EN},
+       {RK312x_HPOUT_CTL, RK312x_HPOUTR_EN,
+        RK312x_HPOUTR_WORK, RK312x_HPVREF_WORK},
+       {RK312x_HPMIX_CTL, RK312x_HPMIXR_EN,
+        RK312x_HPMIXR_WORK2, RK312x_HPMIXR_WORK1},
+       {RK312x_HPMIX_CTL, RK312x_HPMIXL_EN,
+        RK312x_HPMIXL_WORK2, RK312x_HPMIXL_WORK1},
+};
+#define RK312x_INIT_BIT_LIST_LEN ARRAY_SIZE(rk312x_init_bit_list)
+
+static int rk312x_init_bit_register(unsigned int reg, int i)
+{
+       for (; i < RK312x_INIT_BIT_LIST_LEN; i++) {
+               if (rk312x_init_bit_list[i].reg == reg)
+                       return i;
+       }
+
+       return -1;
+}
+
+static unsigned int
+rk312x_codec_read(struct snd_soc_codec *codec,
+                 unsigned int reg);
+static inline void
+rk312x_write_reg_cache(struct snd_soc_codec *codec,
+                      unsigned int reg,
+                      unsigned int value);
+
+static unsigned int
+rk312x_set_init_value(struct snd_soc_codec *codec,
+                     unsigned int reg, unsigned int value)
+{
+       unsigned int read_value, power_bit, set_bit2, set_bit1;
+       int i;
+       int tmp = 0;
+
+       /* read codec init register */
+       i = rk312x_init_bit_register(reg, 0);
+
+       /* set codec init bit
+       widget init bit should be setted 0 after widget power up or unmute,
+       and should be setted 1 after widget power down or mute.
+       */
+       if (i >= 0) {
+               read_value = rk312x_codec_read(codec, reg);
+               while (i >= 0) {
+                       power_bit = rk312x_init_bit_list[i].power_bit;
+                       set_bit2 = rk312x_init_bit_list[i].init2_bit;
+                       set_bit1 = rk312x_init_bit_list[i].init1_bit;
+
+                       if ((read_value & power_bit) != (value & power_bit)) {
+                               if (value & power_bit) {
+                                       tmp = value | set_bit2 | set_bit1;
+                                       writel(value, rk312x_priv->regbase+reg);
+                                       writel(tmp, rk312x_priv->regbase+reg);
+
+                               } else {
+                                       tmp = value & (~set_bit2) & (~set_bit1);
+                                       writel(tmp, rk312x_priv->regbase+reg);
+                                       writel(value, rk312x_priv->regbase+reg);
+                               }
+                               value = tmp;
+                       } else {
+                               if (read_value != value)
+                                       writel(value, rk312x_priv->regbase+reg);
+                       }
+
+                       i = rk312x_init_bit_register(reg, ++i);
+
+                       rk312x_write_reg_cache(codec, reg, value);
+               }
+       } else {
+               return i;
+       }
+
+       return value;
+}
+
+static int rk312x_volatile_register(struct snd_soc_codec *codec,
+                                   unsigned int reg)
+{
+       switch (reg) {
+       case RK312x_RESET:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static int rk312x_codec_register(struct snd_soc_codec *codec, unsigned int reg)
+{
+       switch (reg) {
+       case RK312x_RESET:
+       case RK312x_ADC_INT_CTL1:
+       case RK312x_ADC_INT_CTL2:
+       case RK312x_DAC_INT_CTL1:
+       case RK312x_DAC_INT_CTL2:
+       case RK312x_DAC_INT_CTL3:
+       case RK312x_ADC_MIC_CTL:
+       case RK312x_BST_CTL:
+       case RK312x_ALC_MUNIN_CTL:
+       case RK312x_BSTL_ALCL_CTL:
+       case RK312x_ALCR_GAIN_CTL:
+       case RK312x_ADC_ENABLE:
+       case RK312x_DAC_CTL:
+       case RK312x_DAC_ENABLE:
+       case RK312x_HPMIX_CTL:
+       case RK312x_HPMIX_S_SELECT:
+       case RK312x_HPOUT_CTL:
+       case RK312x_HPOUTL_GAIN:
+       case RK312x_HPOUTR_GAIN:
+       case RK312x_SELECT_CURRENT:
+       case RK312x_PGAL_AGC_CTL1:
+       case RK312x_PGAL_AGC_CTL2:
+       case RK312x_PGAL_AGC_CTL3:
+       case RK312x_PGAL_AGC_CTL4:
+       case RK312x_PGAL_ASR_CTL:
+       case RK312x_PGAL_AGC_MAX_H:
+       case RK312x_PGAL_AGC_MAX_L:
+       case RK312x_PGAL_AGC_MIN_H:
+       case RK312x_PGAL_AGC_MIN_L:
+       case RK312x_PGAL_AGC_CTL5:
+       case RK312x_PGAR_AGC_CTL1:
+       case RK312x_PGAR_AGC_CTL2:
+       case RK312x_PGAR_AGC_CTL3:
+       case RK312x_PGAR_AGC_CTL4:
+       case RK312x_PGAR_ASR_CTL:
+       case RK312x_PGAR_AGC_MAX_H:
+       case RK312x_PGAR_AGC_MAX_L:
+       case RK312x_PGAR_AGC_MIN_H:
+       case RK312x_PGAR_AGC_MIN_L:
+       case RK312x_PGAR_AGC_CTL5:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static inline unsigned int rk312x_read_reg_cache(struct snd_soc_codec *codec,
+                                                unsigned int reg)
+{
+       unsigned int *cache = codec->reg_cache;
+
+       if (rk312x_codec_register(codec, reg))
+               return  cache[reg];
+
+       DBG("%s : reg error!\n", __func__);
+
+       return -EINVAL;
+}
+
+static inline void rk312x_write_reg_cache(struct snd_soc_codec *codec,
+                                         unsigned int reg,
+                                         unsigned int value)
+{
+       unsigned int *cache = codec->reg_cache;
+
+       if (rk312x_codec_register(codec, reg)) {
+               cache[reg] = value;
+               return;
+       }
+
+       DBG("%s : reg error!\n", __func__);
+}
+
+static unsigned int rk312x_codec_read(struct snd_soc_codec *codec,
+                                     unsigned int reg)
+{
+       unsigned int value;
+
+       if (!rk312x_priv) {
+               printk("%s : rk312x is NULL\n", __func__);
+               return -EINVAL;
+       }
+
+       if (!rk312x_codec_register(codec, reg)) {
+               DBG("%s : reg error!\n", __func__);
+               return -EINVAL;
+       }
+
+       if (rk312x_volatile_register(codec, reg) == 0)
+               value = rk312x_read_reg_cache(codec, reg);
+       else
+               value = readl_relaxed(rk312x_priv->regbase+reg);
+
+       value = readl_relaxed(rk312x_priv->regbase+reg);
+       dbg_codec(2, "%s : reg = 0x%x, val= 0x%x\n", __func__,
+                 reg, value);
+
+       return value;
+}
+
+static int rk312x_codec_write(struct snd_soc_codec *codec,
+                             unsigned int reg, unsigned int value)
+{
+       int new_value;
+
+       if (!rk312x_priv) {
+               DBG("%s : rk312x is NULL\n", __func__);
+               return -EINVAL;
+       } else if (!rk312x_codec_register(codec, reg)) {
+               DBG("%s : reg error!\n", __func__);
+               return -EINVAL;
+       }
+       new_value = rk312x_set_init_value(codec, reg, value);
+
+       if (new_value == -1) {
+               writel(value, rk312x_priv->regbase+reg);
+               rk312x_write_reg_cache(codec, reg, value);
+       }
+       rk312x_codec_read(codec, reg);
+       return 0;
+}
+
+static int rk312x_hw_write(const struct i2c_client *client,
+                          const char *buf, int count)
+{
+       unsigned int reg, value;
+
+       if (!rk312x_priv || !rk312x_priv->codec) {
+               DBG("%s : rk312x_priv or rk312x_priv->codec is NULL\n",
+                   __func__);
+               return -EINVAL;
+       }
+
+       if (count == 3) {
+               reg = (unsigned int)buf[0];
+               value = (buf[1] & 0xff00) | (0x00ff & buf[2]);
+               writel(value, rk312x_priv->regbase+reg);
+       } else {
+               DBG("%s : i2c len error\n", __func__);
+       }
+
+       return  count;
+}
+
+static int rk312x_reset(struct snd_soc_codec *codec)
+{
+       writel(0x00, rk312x_priv->regbase+RK312x_RESET);
+       mdelay(10);
+       writel(0x43, rk312x_priv->regbase+RK312x_RESET);
+       mdelay(10);
+
+       memcpy(codec->reg_cache, rk312x_reg_defaults,
+              sizeof(rk312x_reg_defaults));
+
+       return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -3900, 150, 0);
+static const DECLARE_TLV_DB_SCALE(pga_vol_tlv, -1800, 150, 0);
+static const DECLARE_TLV_DB_SCALE(bst_vol_tlv, 0, 2000, 0);
+static const DECLARE_TLV_DB_SCALE(pga_agc_max_vol_tlv, -1350, 600, 0);
+static const DECLARE_TLV_DB_SCALE(pga_agc_min_vol_tlv, -1800, 600, 0);
+
+static const char *const rk312x_input_mode[] = {
+                       "Differential", "Single-Ended"};
+
+static const char *const rk312x_micbias_ratio[] = {
+                       "1.0 Vref", "1.1 Vref",
+                       "1.2 Vref", "1.3 Vref",
+                       "1.4 Vref", "1.5 Vref",
+                       "1.6 Vref", "1.7 Vref",};
+
+static const char *const rk312x_dis_en_sel[] = {"Disable", "Enable"};
+
+static const char *const rk312x_pga_agc_way[] = {"Normal", "Jack"};
+
+static const char *const rk312x_agc_backup_way[] = {
+                       "Normal", "Jack1", "Jack2", "Jack3"};
+
+static const char *const rk312x_pga_agc_hold_time[] = {
+                       "0ms", "2ms", "4ms", "8ms",
+                       "16ms", "32ms", "64ms",
+                       "128ms", "256ms", "512ms", "1s"};
+
+static const char *const rk312x_pga_agc_ramp_up_time[] = {
+               "Normal:500us Jack:125us",
+               "Normal:1ms Jack:250us",
+               "Normal:2ms Jack:500us",
+               "Normal:4ms Jack:1ms",
+               "Normal:8ms Jack:2ms",
+               "Normal:16ms Jack:4ms",
+               "Normal:32ms Jack:8ms",
+               "Normal:64ms Jack:16ms",
+               "Normal:128ms Jack:32ms",
+               "Normal:256ms Jack:64ms",
+               "Normal:512ms Jack:128ms"};
+
+static const char *const rk312x_pga_agc_ramp_down_time[] = {
+               "Normal:125us Jack:32us",
+               "Normal:250us Jack:64us",
+               "Normal:500us Jack:125us",
+               "Normal:1ms Jack:250us",
+               "Normal:2ms Jack:500us",
+               "Normal:4ms Jack:1ms",
+               "Normal:8ms Jack:2ms",
+               "Normal:16ms Jack:4ms",
+               "Normal:32ms Jack:8ms",
+               "Normal:64ms Jack:16ms",
+               "Normal:128ms Jack:32ms"};
+
+static const char *const rk312x_pga_agc_mode[] = {"Normal", "Limiter"};
+
+static const char *const rk312x_pga_agc_recovery_mode[] = {
+               "Right Now", "After AGC to Limiter"};
+
+static const char *const rk312x_pga_agc_noise_gate_threhold[] = {
+               "-39dB", "-45dB", "-51dB",
+               "-57dB", "-63dB", "-69dB", "-75dB", "-81dB"};
+
+static const char *const rk312x_pga_agc_update_gain[] = {
+               "Right Now", "After 1st Zero Cross"};
+
+static const char *const rk312x_pga_agc_approximate_sample_rate[] = {
+               "96KHZ", "48KHz", "441KHZ", "32KHz",
+               "24KHz", "16KHz", "12KHz", "8KHz"};
+
+static const struct soc_enum rk312x_bst_enum[] = {
+               SOC_ENUM_SINGLE(RK312x_BSTL_ALCL_CTL,
+                               RK312x_BSTL_MODE_SFT, 2,
+                               rk312x_input_mode),
+};
+
+
+static const struct soc_enum rk312x_micbias_enum[] = {
+       SOC_ENUM_SINGLE(RK312x_ADC_MIC_CTL,
+                       RK312x_MICBIAS_VOL_SHT, 8,
+                       rk312x_micbias_ratio),
+};
+
+static const struct soc_enum rk312x_agcl_enum[] = {
+       SOC_ENUM_SINGLE(RK312x_PGAL_AGC_CTL1,
+                       RK312x_PGA_AGC_BK_WAY_SFT, 4,
+                       rk312x_agc_backup_way),/*0*/
+       SOC_ENUM_SINGLE(RK312x_PGAL_AGC_CTL1,
+                       RK312x_PGA_AGC_WAY_SFT, 2,
+                       rk312x_pga_agc_way),/*1*/
+       SOC_ENUM_SINGLE(RK312x_PGAL_AGC_CTL1,
+                       RK312x_PGA_AGC_HOLD_T_SFT, 11,
+                       rk312x_pga_agc_hold_time),/*2*/
+       SOC_ENUM_SINGLE(RK312x_PGAL_AGC_CTL2,
+                       RK312x_PGA_AGC_GRU_T_SFT, 11,
+                       rk312x_pga_agc_ramp_up_time),/*3*/
+       SOC_ENUM_SINGLE(RK312x_PGAL_AGC_CTL2,
+                       RK312x_PGA_AGC_GRD_T_SFT, 11,
+                       rk312x_pga_agc_ramp_down_time),/*4*/
+       SOC_ENUM_SINGLE(RK312x_PGAL_AGC_CTL3,
+                       RK312x_PGA_AGC_MODE_SFT, 2,
+                       rk312x_pga_agc_mode),/*5*/
+       SOC_ENUM_SINGLE(RK312x_PGAL_AGC_CTL3,
+                       RK312x_PGA_AGC_ZO_SFT, 2,
+                       rk312x_dis_en_sel),/*6*/
+       SOC_ENUM_SINGLE(RK312x_PGAL_AGC_CTL3,
+                       RK312x_PGA_AGC_REC_MODE_SFT, 2,
+                       rk312x_pga_agc_recovery_mode),/*7*/
+       SOC_ENUM_SINGLE(RK312x_PGAL_AGC_CTL3,
+                       RK312x_PGA_AGC_FAST_D_SFT, 2,
+                       rk312x_dis_en_sel),/*8*/
+       SOC_ENUM_SINGLE(RK312x_PGAL_AGC_CTL3,
+                       RK312x_PGA_AGC_NG_SFT, 2,
+                       rk312x_dis_en_sel),/*9*/
+       SOC_ENUM_SINGLE(RK312x_PGAL_AGC_CTL3,
+                       RK312x_PGA_AGC_NG_THR_SFT, 8,
+                       rk312x_pga_agc_noise_gate_threhold),/*10*/
+       SOC_ENUM_SINGLE(RK312x_PGAL_AGC_CTL4,
+                       RK312x_PGA_AGC_ZO_MODE_SFT, 2,
+                       rk312x_pga_agc_update_gain),/*11*/
+       SOC_ENUM_SINGLE(RK312x_PGAL_ASR_CTL,
+                       RK312x_PGA_SLOW_CLK_SFT, 2,
+                       rk312x_dis_en_sel),/*12*/
+       SOC_ENUM_SINGLE(RK312x_PGAL_ASR_CTL,
+                       RK312x_PGA_ASR_SFT, 8,
+                       rk312x_pga_agc_approximate_sample_rate),/*13*/
+       SOC_ENUM_SINGLE(RK312x_PGAL_AGC_CTL5,
+                       RK312x_PGA_AGC_SFT, 2,
+                       rk312x_dis_en_sel),/*14*/
+};
+
+static const struct soc_enum rk312x_agcr_enum[] = {
+       SOC_ENUM_SINGLE(RK312x_PGAR_AGC_CTL1,
+                       RK312x_PGA_AGC_BK_WAY_SFT, 4,
+                       rk312x_agc_backup_way),/*0*/
+       SOC_ENUM_SINGLE(RK312x_PGAR_AGC_CTL1,
+                       RK312x_PGA_AGC_WAY_SFT, 2,
+                       rk312x_pga_agc_way),/*1*/
+       SOC_ENUM_SINGLE(RK312x_PGAR_AGC_CTL1,
+                       RK312x_PGA_AGC_HOLD_T_SFT, 11,
+                       rk312x_pga_agc_hold_time),/*2*/
+       SOC_ENUM_SINGLE(RK312x_PGAR_AGC_CTL2,
+                       RK312x_PGA_AGC_GRU_T_SFT, 11,
+                       rk312x_pga_agc_ramp_up_time),/*3*/
+       SOC_ENUM_SINGLE(RK312x_PGAR_AGC_CTL2,
+                       RK312x_PGA_AGC_GRD_T_SFT, 11,
+                       rk312x_pga_agc_ramp_down_time),/*4*/
+       SOC_ENUM_SINGLE(RK312x_PGAR_AGC_CTL3,
+                       RK312x_PGA_AGC_MODE_SFT, 2,
+                       rk312x_pga_agc_mode),/*5*/
+       SOC_ENUM_SINGLE(RK312x_PGAR_AGC_CTL3,
+                       RK312x_PGA_AGC_ZO_SFT, 2,
+                       rk312x_dis_en_sel),/*6*/
+       SOC_ENUM_SINGLE(RK312x_PGAR_AGC_CTL3,
+                       RK312x_PGA_AGC_REC_MODE_SFT, 2,
+                       rk312x_pga_agc_recovery_mode),/*7*/
+       SOC_ENUM_SINGLE(RK312x_PGAR_AGC_CTL3,
+                       RK312x_PGA_AGC_FAST_D_SFT, 2,
+                       rk312x_dis_en_sel),/*8*/
+       SOC_ENUM_SINGLE(RK312x_PGAR_AGC_CTL3,
+                       RK312x_PGA_AGC_NG_SFT, 2, rk312x_dis_en_sel),/*9*/
+       SOC_ENUM_SINGLE(RK312x_PGAR_AGC_CTL3,
+                       RK312x_PGA_AGC_NG_THR_SFT, 8,
+                       rk312x_pga_agc_noise_gate_threhold),/*10*/
+       SOC_ENUM_SINGLE(RK312x_PGAR_AGC_CTL4,
+                       RK312x_PGA_AGC_ZO_MODE_SFT, 2,
+                       rk312x_pga_agc_update_gain),/*11*/
+       SOC_ENUM_SINGLE(RK312x_PGAR_ASR_CTL,
+                       RK312x_PGA_SLOW_CLK_SFT, 2,
+                       rk312x_dis_en_sel),/*12*/
+       SOC_ENUM_SINGLE(RK312x_PGAR_ASR_CTL,
+                       RK312x_PGA_ASR_SFT, 8,
+                       rk312x_pga_agc_approximate_sample_rate),/*13*/
+       SOC_ENUM_SINGLE(RK312x_PGAR_AGC_CTL5,
+                       RK312x_PGA_AGC_SFT, 2,
+                       rk312x_dis_en_sel),/*14*/
+};
+
+static const struct snd_kcontrol_new rk312x_snd_controls[] = {
+       /* Add for set voice volume */
+       SOC_DOUBLE_R_TLV("Speaker Playback Volume", RK312x_HPOUTL_GAIN,
+                        RK312x_HPOUTR_GAIN, RK312x_HPOUT_GAIN_SFT,
+                        31, 0, out_vol_tlv),
+       SOC_DOUBLE("Speaker Playback Switch", RK312x_HPOUT_CTL,
+                  RK312x_HPOUTL_MUTE_SHT, RK312x_HPOUTR_MUTE_SHT, 1, 0),
+       SOC_DOUBLE_R_TLV("Headphone Playback Volume", RK312x_HPOUTL_GAIN,
+                        RK312x_HPOUTR_GAIN, RK312x_HPOUT_GAIN_SFT,
+                        31, 0, out_vol_tlv),
+       SOC_DOUBLE("Headphone Playback Switch", RK312x_HPOUT_CTL,
+                  RK312x_HPOUTL_MUTE_SHT, RK312x_HPOUTR_MUTE_SHT, 1, 0),
+       SOC_DOUBLE_R_TLV("Earpiece Playback Volume", RK312x_HPOUTL_GAIN,
+                        RK312x_HPOUTR_GAIN, RK312x_HPOUT_GAIN_SFT,
+                        31, 0, out_vol_tlv),
+       SOC_DOUBLE("Earpiece Playback Switch", RK312x_HPOUT_CTL,
+                  RK312x_HPOUTL_MUTE_SHT, RK312x_HPOUTR_MUTE_SHT, 1, 0),
+
+
+       /* Add for set capture mute */
+       SOC_SINGLE_TLV("Main Mic Capture Volume", RK312x_BST_CTL,
+                      RK312x_BSTL_GAIN_SHT, 1, 0, bst_vol_tlv),
+       SOC_SINGLE("Main Mic Capture Switch", RK312x_BST_CTL,
+                  RK312x_BSTL_MUTE_SHT, 1, 0),
+       SOC_SINGLE_TLV("Headset Mic Capture Volume", RK312x_BST_CTL,
+                      RK312x_BSTR_GAIN_SHT, 1, 0, bst_vol_tlv),
+       SOC_SINGLE("Headset Mic Capture Switch", RK312x_BST_CTL,
+                  RK312x_BSTR_MUTE_SHT, 1, 0),
+
+       SOC_SINGLE("ALCL Switch", RK312x_ALC_MUNIN_CTL,
+                  RK312x_ALCL_MUTE_SHT, 1, 0),
+       SOC_SINGLE_TLV("ALCL Capture Volume", RK312x_BSTL_ALCL_CTL,
+                      RK312x_ALCL_GAIN_SHT, 31, 0, pga_vol_tlv),
+       SOC_SINGLE("ALCR Switch", RK312x_ALC_MUNIN_CTL,
+                  RK312x_ALCR_MUTE_SHT, 1, 0),
+       SOC_SINGLE_TLV("ALCR Capture Volume", RK312x_ALCR_GAIN_CTL,
+                      RK312x_ALCL_GAIN_SHT, 31, 0, pga_vol_tlv),
+
+       SOC_ENUM("BST_L Mode",  rk312x_bst_enum[0]),
+
+       SOC_ENUM("Micbias Voltage",  rk312x_micbias_enum[0]),
+       SOC_ENUM("PGAL AGC Back Way",  rk312x_agcl_enum[0]),
+       SOC_ENUM("PGAL AGC Way",  rk312x_agcl_enum[1]),
+       SOC_ENUM("PGAL AGC Hold Time",  rk312x_agcl_enum[2]),
+       SOC_ENUM("PGAL AGC Ramp Up Time",  rk312x_agcl_enum[3]),
+       SOC_ENUM("PGAL AGC Ramp Down Time",  rk312x_agcl_enum[4]),
+       SOC_ENUM("PGAL AGC Mode",  rk312x_agcl_enum[5]),
+       SOC_ENUM("PGAL AGC Gain Update Zero Enable",  rk312x_agcl_enum[6]),
+       SOC_ENUM("PGAL AGC Gain Recovery LPGA VOL",  rk312x_agcl_enum[7]),
+       SOC_ENUM("PGAL AGC Fast Decrement Enable",  rk312x_agcl_enum[8]),
+       SOC_ENUM("PGAL AGC Noise Gate Enable",  rk312x_agcl_enum[9]),
+       SOC_ENUM("PGAL AGC Noise Gate Threhold",  rk312x_agcl_enum[10]),
+       SOC_ENUM("PGAL AGC Upate Gain",  rk312x_agcl_enum[11]),
+       SOC_ENUM("PGAL AGC Slow Clock Enable",  rk312x_agcl_enum[12]),
+       SOC_ENUM("PGAL AGC Approximate Sample Rate",  rk312x_agcl_enum[13]),
+       SOC_ENUM("PGAL AGC Enable",  rk312x_agcl_enum[14]),
+
+       SOC_SINGLE_TLV("PGAL AGC Volume", RK312x_PGAL_AGC_CTL4,
+                      RK312x_PGA_AGC_VOL_SFT, 31, 0, pga_vol_tlv),
+
+       SOC_SINGLE("PGAL AGC Max Level High 8 Bits",
+                  RK312x_PGAL_AGC_MAX_H,
+                  0, 255, 0),
+       SOC_SINGLE("PGAL AGC Max Level Low 8 Bits",
+                  RK312x_PGAL_AGC_MAX_L,
+                  0, 255, 0),
+       SOC_SINGLE("PGAL AGC Min Level High 8 Bits",
+                  RK312x_PGAL_AGC_MIN_H,
+                  0, 255, 0),
+       SOC_SINGLE("PGAL AGC Min Level Low 8 Bits",
+                  RK312x_PGAL_AGC_MIN_L,
+                  0, 255, 0),
+
+       SOC_SINGLE_TLV("PGAL AGC Max Gain",
+                      RK312x_PGAL_AGC_CTL5,
+                      RK312x_PGA_AGC_MAX_G_SFT, 7, 0,
+                      pga_agc_max_vol_tlv),
+       /* AGC enable and 0x0a bit 5 is 1 */
+       SOC_SINGLE_TLV("PGAL AGC Min Gain", RK312x_PGAL_AGC_CTL5,
+                      RK312x_PGA_AGC_MIN_G_SFT, 7, 0, pga_agc_min_vol_tlv),
+       /* AGC enable and 0x0a bit 5 is 1 */
+
+       SOC_ENUM("PGAR AGC Back Way",  rk312x_agcr_enum[0]),
+       SOC_ENUM("PGAR AGC Way",  rk312x_agcr_enum[1]),
+       SOC_ENUM("PGAR AGC Hold Time",  rk312x_agcr_enum[2]),
+       SOC_ENUM("PGAR AGC Ramp Up Time",  rk312x_agcr_enum[3]),
+       SOC_ENUM("PGAR AGC Ramp Down Time",  rk312x_agcr_enum[4]),
+       SOC_ENUM("PGAR AGC Mode",  rk312x_agcr_enum[5]),
+       SOC_ENUM("PGAR AGC Gain Update Zero Enable",  rk312x_agcr_enum[6]),
+       SOC_ENUM("PGAR AGC Gain Recovery LPGA VOL",  rk312x_agcr_enum[7]),
+       SOC_ENUM("PGAR AGC Fast Decrement Enable",  rk312x_agcr_enum[8]),
+       SOC_ENUM("PGAR AGC Noise Gate Enable",  rk312x_agcr_enum[9]),
+       SOC_ENUM("PGAR AGC Noise Gate Threhold",  rk312x_agcr_enum[10]),
+       SOC_ENUM("PGAR AGC Upate Gain",  rk312x_agcr_enum[11]),
+       SOC_ENUM("PGAR AGC Slow Clock Enable",  rk312x_agcr_enum[12]),
+       SOC_ENUM("PGAR AGC Approximate Sample Rate",  rk312x_agcr_enum[13]),
+       SOC_ENUM("PGAR AGC Enable",  rk312x_agcr_enum[14]),
+       /* AGC disable and 0x0a bit 4 is 1 */
+       SOC_SINGLE_TLV("PGAR AGC Volume", RK312x_PGAR_AGC_CTL4,
+                      RK312x_PGA_AGC_VOL_SFT, 31, 0, pga_vol_tlv),
+
+       SOC_SINGLE("PGAR AGC Max Level High 8 Bits", RK312x_PGAR_AGC_MAX_H,
+                  0, 255, 0),
+       SOC_SINGLE("PGAR AGC Max Level Low 8 Bits", RK312x_PGAR_AGC_MAX_L,
+                  0, 255, 0),
+       SOC_SINGLE("PGAR AGC Min Level High 8 Bits", RK312x_PGAR_AGC_MIN_H,
+                  0, 255, 0),
+       SOC_SINGLE("PGAR AGC Min Level Low 8 Bits", RK312x_PGAR_AGC_MIN_L,
+                  0, 255, 0),
+       /* AGC enable and 0x06 bit 4 is 1 */
+       SOC_SINGLE_TLV("PGAR AGC Max Gain", RK312x_PGAR_AGC_CTL5,
+                      RK312x_PGA_AGC_MAX_G_SFT, 7, 0, pga_agc_max_vol_tlv),
+       /*  AGC enable and 0x06 bit 4 is 1 */
+       SOC_SINGLE_TLV("PGAR AGC Min Gain", RK312x_PGAR_AGC_CTL5,
+                      RK312x_PGA_AGC_MIN_G_SFT, 7, 0, pga_agc_min_vol_tlv),
+
+};
+
+/* For tiny alsa playback/capture/voice call path */
+static const char *const rk312x_playback_path_mode[] = {
+               "OFF", "RCV", "SPK", "HP", "HP_NO_MIC",
+               "BT", "SPK_HP", "RING_SPK", "RING_HP",
+               "RING_HP_NO_MIC", "RING_SPK_HP"};
+
+static const char *const rk312x_capture_path_mode[] = {
+               "MIC OFF", "Main Mic", "Hands Free Mic", "BT Sco Mic"};
+
+static const char *const rk312x_voice_call_path_mode[] = {
+               "OFF", "RCV", "SPK", "HP", "HP_NO_MIC", "BT"};
+
+
+static const SOC_ENUM_SINGLE_DECL(rk312x_playback_path_type, 0, 0,
+                                 rk312x_playback_path_mode);
+static const SOC_ENUM_SINGLE_DECL(rk312x_capture_path_type, 0, 0,
+                                 rk312x_capture_path_mode);
+static const SOC_ENUM_SINGLE_DECL(rk312x_voice_call_path_type, 0, 0,
+                                 rk312x_voice_call_path_mode);
+
+
+/* static int rk312x_codec_power_up(int type); */
+static int rk312x_codec_power_down(int type);
+
+static int rk312x_playback_path_get(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+       if (!rk312x_priv) {
+               DBG("%s : rk312x_priv is NULL\n", __func__);
+               return -EINVAL;
+       }
+
+       DBG("%s : playback_path = %ld\n",
+           __func__, ucontrol->value.integer.value[0]);
+
+       ucontrol->value.integer.value[0] = rk312x_priv->playback_path;
+
+       return 0;
+}
+
+static int rk312x_playback_path_put(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+       /* struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); */
+       long int pre_path;
+
+       if (!rk312x_priv) {
+               DBG("%s : rk312x_priv is NULL\n", __func__);
+               return -EINVAL;
+       }
+
+       if (rk312x_priv->playback_path ==
+           ucontrol->value.integer.value[0]) {
+               DBG("%s : playback_path is not changed!\n", __func__);
+               return 0;
+       }
+
+       pre_path = rk312x_priv->playback_path;
+       rk312x_priv->playback_path = ucontrol->value.integer.value[0];
+
+       DBG("%s : set playback_path = %ld\n", __func__,
+           rk312x_priv->playback_path);
+
+       switch (rk312x_priv->playback_path) {
+       case OFF:
+               if (pre_path != OFF)
+                       rk312x_codec_power_down(RK312x_CODEC_PLAYBACK);
+               break;
+       case RCV:
+               break;
+       case SPK_PATH:
+       case RING_SPK:
+               if (pre_path == OFF)
+                       rk312x_codec_power_up(RK312x_CODEC_PLAYBACK);
+               break;
+       case HP_PATH:
+       case HP_NO_MIC:
+       case RING_HP:
+       case RING_HP_NO_MIC:
+               if (pre_path == OFF)
+                       rk312x_codec_power_up(RK312x_CODEC_PLAYBACK);
+               break;
+       case BT:
+               break;
+       case SPK_HP:
+       case RING_SPK_HP:
+               if (pre_path == OFF)
+                       rk312x_codec_power_up(RK312x_CODEC_PLAYBACK);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int rk312x_capture_path_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       if (!rk312x_priv) {
+               DBG("%s : rk312x_priv is NULL\n", __func__);
+               return -EINVAL;
+       }
+
+       DBG("%s : capture_path = %ld\n", __func__,
+           ucontrol->value.integer.value[0]);
+
+       ucontrol->value.integer.value[0] = rk312x_priv->capture_path;
+
+       return 0;
+}
+
+static int rk312x_capture_path_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       /* struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); */
+       long int pre_path;
+
+       if (!rk312x_priv) {
+               DBG("%s : rk312x_priv is NULL\n", __func__);
+               return -EINVAL;
+       }
+
+       if (rk312x_priv->capture_path == ucontrol->value.integer.value[0])
+               DBG("%s : capture_path is not changed!\n", __func__);
+
+       pre_path = rk312x_priv->capture_path;
+       rk312x_priv->capture_path = ucontrol->value.integer.value[0];
+
+       DBG("%s : set capture_path = %ld\n", __func__,
+           rk312x_priv->capture_path);
+
+       switch (rk312x_priv->capture_path) {
+       case MIC_OFF:
+               if (pre_path != MIC_OFF)
+                       rk312x_codec_power_down(RK312x_CODEC_CAPTURE);
+               break;
+       case Main_Mic:
+               if (pre_path == MIC_OFF)
+                       rk312x_codec_power_up(RK312x_CODEC_CAPTURE);
+               break;
+       case Hands_Free_Mic:
+               if (pre_path == MIC_OFF)
+                       rk312x_codec_power_up(RK312x_CODEC_CAPTURE);
+               break;
+       case BT_Sco_Mic:
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int rk312x_voice_call_path_get(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       if (!rk312x_priv) {
+               DBG("%s : rk312x_priv is NULL\n", __func__);
+               return -EINVAL;
+       }
+
+       DBG("%s : playback_path = %ld\n", __func__,
+           ucontrol->value.integer.value[0]);
+
+       ucontrol->value.integer.value[0] = rk312x_priv->voice_call_path;
+
+       return 0;
+}
+
+static int rk312x_voice_call_path_put(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       /* struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); */
+       long int pre_path;
+
+       if (!rk312x_priv) {
+               DBG("%s : rk312x_priv is NULL\n", __func__);
+               return -EINVAL;
+       }
+
+       if (rk312x_priv->voice_call_path == ucontrol->value.integer.value[0])
+               DBG("%s : playback_path is not changed!\n", __func__);
+
+       pre_path = rk312x_priv->voice_call_path;
+       rk312x_priv->voice_call_path = ucontrol->value.integer.value[0];
+
+       DBG("%s : set playback_path = %ld\n", __func__,
+           rk312x_priv->voice_call_path);
+
+       /* open playback route for incall route and keytone */
+       if (pre_path == OFF) {
+               if (rk312x_priv->playback_path != OFF)
+                       /* mute output for incall route pop nosie */
+                               mdelay(100);
+               else
+                       rk312x_codec_power_up(RK312x_CODEC_PLAYBACK);
+       }
+
+       switch (rk312x_priv->voice_call_path) {
+       case OFF:
+               if (pre_path != MIC_OFF)
+                       rk312x_codec_power_down(RK312x_CODEC_CAPTURE);
+               break;
+       case RCV:
+               break;
+       case SPK_PATH:
+               /* open incall route */
+               if (pre_path == OFF ||  pre_path == RCV || pre_path == BT)
+                       rk312x_codec_power_up(RK312x_CODEC_INCALL);
+
+               break;
+       case HP_PATH:
+       case HP_NO_MIC:
+               /* open incall route */
+               if (pre_path == OFF ||  pre_path == RCV || pre_path == BT)
+                       rk312x_codec_power_up(RK312x_CODEC_INCALL);
+               break;
+       case BT:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct snd_kcontrol_new rk312x_snd_path_controls[] = {
+       SOC_ENUM_EXT("Playback Path", rk312x_playback_path_type,
+                    rk312x_playback_path_get, rk312x_playback_path_put),
+       SOC_ENUM_EXT("Capture MIC Path", rk312x_capture_path_type,
+                    rk312x_capture_path_get, rk312x_capture_path_put),
+       SOC_ENUM_EXT("Voice Call Path", rk312x_voice_call_path_type,
+                    rk312x_voice_call_path_get, rk312x_voice_call_path_put),
+};
+
+static int rk312x_dacl_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol,
+                            int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_update_bits(codec, RK312x_DAC_ENABLE,
+                                   RK312x_DACL_WORK, 0);
+               snd_soc_update_bits(codec, RK312x_DAC_ENABLE,
+                                   RK312x_DACL_EN | RK312x_DACL_CLK_EN,
+                                   RK312x_DACL_EN | RK312x_DACL_CLK_EN);
+               snd_soc_update_bits(codec, RK312x_DAC_ENABLE,
+                                   RK312x_DACL_WORK, RK312x_DACL_WORK);
+               break;
+
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_update_bits(codec, RK312x_DAC_ENABLE,
+                                   RK312x_DACL_EN
+                                   | RK312x_DACL_CLK_EN, 0);
+               snd_soc_update_bits(codec, RK312x_DAC_ENABLE,
+                                   RK312x_DACL_WORK, 0);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rk312x_dacr_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol,
+                            int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_update_bits(codec, RK312x_DAC_ENABLE,
+                                   RK312x_DACR_WORK, 0);
+               snd_soc_update_bits(codec, RK312x_DAC_ENABLE,
+                                   RK312x_DACR_EN
+                                   | RK312x_DACR_CLK_EN,
+                                   RK312x_DACR_EN
+                                   | RK312x_DACR_CLK_EN);
+               snd_soc_update_bits(codec, RK312x_DAC_ENABLE,
+                                   RK312x_DACR_WORK,
+                                   RK312x_DACR_WORK);
+               break;
+
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_update_bits(codec, RK312x_DAC_ENABLE,
+                                   RK312x_DACR_EN
+                                   | RK312x_DACR_CLK_EN, 0);
+               snd_soc_update_bits(codec, RK312x_DAC_ENABLE,
+                                   RK312x_DACR_WORK, 0);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rk312x_adcl_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_update_bits(codec, RK312x_ADC_ENABLE,
+                                   RK312x_ADCL_CLK_EN_SFT
+                                   | RK312x_ADCL_AMP_EN_SFT,
+                                   RK312x_ADCL_CLK_EN
+                                   | RK312x_ADCL_AMP_EN);
+               break;
+
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_update_bits(codec, RK312x_ADC_ENABLE,
+                                   RK312x_ADCL_CLK_EN_SFT
+                                   | RK312x_ADCL_AMP_EN_SFT, 0);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rk312x_adcr_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_update_bits(codec, RK312x_ADC_ENABLE,
+                                   RK312x_ADCR_CLK_EN_SFT
+                                   | RK312x_ADCR_AMP_EN_SFT,
+                                   RK312x_ADCR_CLK_EN
+                                   | RK312x_ADCR_AMP_EN);
+               break;
+
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_update_bits(codec, RK312x_ADC_ENABLE,
+                                   RK312x_ADCR_CLK_EN_SFT
+                                   | RK312x_ADCR_AMP_EN_SFT, 0);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+/* HPmix */
+static const struct snd_kcontrol_new rk312x_hpmixl[] = {
+       SOC_DAPM_SINGLE("ALCR Switch", RK312x_HPMIX_S_SELECT,
+                       RK312x_HPMIXL_SEL_ALCR_SFT, 1, 0),
+       SOC_DAPM_SINGLE("ALCL Switch", RK312x_HPMIX_S_SELECT,
+                       RK312x_HPMIXL_SEL_ALCL_SFT, 1, 0),
+       SOC_DAPM_SINGLE("DACL Switch", RK312x_HPMIX_S_SELECT,
+                       RK312x_HPMIXL_SEL_DACL_SFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new rk312x_hpmixr[] = {
+       SOC_DAPM_SINGLE("ALCR Switch", RK312x_HPMIX_S_SELECT,
+                       RK312x_HPMIXR_SEL_ALCR_SFT, 1, 0),
+       SOC_DAPM_SINGLE("ALCL Switch", RK312x_HPMIX_S_SELECT,
+                       RK312x_HPMIXR_SEL_ALCL_SFT, 1, 0),
+       SOC_DAPM_SINGLE("DACR Switch", RK312x_HPMIX_S_SELECT,
+                       RK312x_HPMIXR_SEL_DACR_SFT, 1, 0),
+};
+
+static int rk312x_hpmixl_event(struct snd_soc_dapm_widget *w,
+                              struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_update_bits(codec, RK312x_DAC_CTL,
+                                   RK312x_ZO_DET_VOUTR_SFT,
+                                   RK312x_ZO_DET_VOUTR_EN);
+               snd_soc_update_bits(codec, RK312x_DAC_CTL,
+                                   RK312x_ZO_DET_VOUTL_SFT,
+                                   RK312x_ZO_DET_VOUTL_EN);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(codec, RK312x_DAC_CTL,
+                                   RK312x_ZO_DET_VOUTR_SFT,
+                                   RK312x_ZO_DET_VOUTR_DIS);
+               snd_soc_update_bits(codec, RK312x_DAC_CTL,
+                                   RK312x_ZO_DET_VOUTL_SFT,
+                                   RK312x_ZO_DET_VOUTL_DIS);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rk312x_hpmixr_event(struct snd_soc_dapm_widget *w,
+                              struct snd_kcontrol *kcontrol, int event)
+{
+       /* struct snd_soc_codec *codec = w->codec; */
+#if 0
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_update_bits(codec, RK312x_HPMIX_CTL,
+                                   RK312x_HPMIXR_WORK2, RK312x_HPMIXR_WORK2);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(codec, RK312x_HPMIX_CTL,
+                                   RK312x_HPMIXR_WORK2, 0);
+               break;
+
+       default:
+               return 0;
+       }
+#endif
+       return 0;
+}
+
+/* HP MUX */
+
+static const char *const hpl_sel[] = {"HPMIXL", "DACL"};
+
+static const struct soc_enum hpl_sel_enum =
+       SOC_ENUM_SINGLE(RK312x_HPMIX_S_SELECT, RK312x_HPMIXL_BYPASS_SFT,
+                       ARRAY_SIZE(hpl_sel), hpl_sel);
+
+static const struct snd_kcontrol_new hpl_sel_mux =
+       SOC_DAPM_ENUM("HPL select Mux", hpl_sel_enum);
+
+static const char *const hpr_sel[] = {"HPMIXR", "DACR"};
+
+static const struct soc_enum hpr_sel_enum =
+       SOC_ENUM_SINGLE(RK312x_HPMIX_S_SELECT, RK312x_HPMIXR_BYPASS_SFT,
+                       ARRAY_SIZE(hpr_sel), hpr_sel);
+
+static const struct snd_kcontrol_new hpr_sel_mux =
+       SOC_DAPM_ENUM("HPR select Mux", hpr_sel_enum);
+
+/* IN_L MUX */
+static const char *const lnl_sel[] = {"NO", "BSTL", "LINEL", "NOUSE"};
+
+static const struct soc_enum lnl_sel_enum =
+       SOC_ENUM_SINGLE(RK312x_ALC_MUNIN_CTL, RK312x_MUXINL_F_SHT,
+                       ARRAY_SIZE(lnl_sel), lnl_sel);
+
+static const struct snd_kcontrol_new lnl_sel_mux =
+       SOC_DAPM_ENUM("MUXIN_L select", lnl_sel_enum);
+
+/* IN_R MUX */
+static const char *const lnr_sel[] = {"NO", "BSTR", "LINER", "NOUSE"};
+
+static const struct soc_enum lnr_sel_enum =
+       SOC_ENUM_SINGLE(RK312x_ALC_MUNIN_CTL, RK312x_MUXINR_F_SHT,
+                       ARRAY_SIZE(lnr_sel), lnr_sel);
+
+static const struct snd_kcontrol_new lnr_sel_mux =
+       SOC_DAPM_ENUM("MUXIN_R select", lnr_sel_enum);
+
+
+static const struct snd_soc_dapm_widget rk312x_dapm_widgets[] = {
+       /* microphone bias */
+       SND_SOC_DAPM_MICBIAS("Mic Bias", RK312x_ADC_MIC_CTL,
+                            RK312x_MICBIAS_VOL_ENABLE, 0),
+
+       /* DACs */
+       SND_SOC_DAPM_DAC_E("DACL", NULL, SND_SOC_NOPM,
+                          0, 0, rk312x_dacl_event,
+                          SND_SOC_DAPM_POST_PMD
+                          | SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_DAC_E("DACR", NULL, SND_SOC_NOPM,
+                          0, 0, rk312x_dacr_event,
+                          SND_SOC_DAPM_POST_PMD
+                          | SND_SOC_DAPM_POST_PMU),
+
+       /* ADCs */
+       SND_SOC_DAPM_ADC_E("ADCL", NULL, SND_SOC_NOPM,
+                          0, 0, rk312x_adcl_event,
+                          SND_SOC_DAPM_POST_PMD
+                          | SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_ADC_E("ADCR", NULL, SND_SOC_NOPM,
+                          0, 0, rk312x_adcr_event,
+                          SND_SOC_DAPM_POST_PMD
+                          | SND_SOC_DAPM_POST_PMU),
+
+       /* PGA */
+       SND_SOC_DAPM_PGA("BSTL", RK312x_BST_CTL,
+                        RK312x_BSTL_PWRD_SFT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("BSTR", RK312x_BST_CTL,
+                        RK312x_BSTR_PWRD_SFT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("ALCL", RK312x_ALC_MUNIN_CTL,
+                        RK312x_ALCL_PWR_SHT , 0, NULL, 0),
+       SND_SOC_DAPM_PGA("ALCR", RK312x_ALC_MUNIN_CTL,
+                        RK312x_ALCR_PWR_SHT , 0, NULL, 0),
+       SND_SOC_DAPM_PGA("HPL", RK312x_HPOUT_CTL,
+                        RK312x_HPOUTL_PWR_SHT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("HPR", RK312x_HPOUT_CTL,
+                        RK312x_HPOUTR_PWR_SHT, 0, NULL, 0),
+
+       /* MIXER */
+       SND_SOC_DAPM_MIXER_E("HPMIXL", RK312x_HPMIX_CTL,
+                            RK312x_HPMIXL_SFT, 0,
+                            rk312x_hpmixl,
+                            ARRAY_SIZE(rk312x_hpmixl),
+                            rk312x_hpmixl_event,
+                            SND_SOC_DAPM_PRE_PMD
+                            | SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_MIXER_E("HPMIXR", RK312x_HPMIX_CTL,
+                            RK312x_HPMIXR_SFT, 0,
+                            rk312x_hpmixr,
+                            ARRAY_SIZE(rk312x_hpmixr),
+                            rk312x_hpmixr_event,
+                            SND_SOC_DAPM_PRE_PMD
+                            | SND_SOC_DAPM_POST_PMU),
+
+       /* MUX */
+       SND_SOC_DAPM_MUX("IN_R Mux", SND_SOC_NOPM, 0, 0,
+                        &lnr_sel_mux),
+       SND_SOC_DAPM_MUX("IN_L Mux", SND_SOC_NOPM, 0, 0,
+                        &lnl_sel_mux),
+       SND_SOC_DAPM_MUX("HPL Mux", SND_SOC_NOPM, 0, 0,
+                        &hpl_sel_mux),
+       SND_SOC_DAPM_MUX("HPR Mux", SND_SOC_NOPM, 0, 0,
+                        &hpr_sel_mux),
+
+       /* Audio Interface */
+       SND_SOC_DAPM_AIF_IN("I2S DAC", "HiFi Playback", 0,
+                           SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("I2S ADC", "HiFi Capture", 0,
+                            SND_SOC_NOPM, 0, 0),
+
+       /* Input */
+       SND_SOC_DAPM_INPUT("LINEL"),
+       SND_SOC_DAPM_INPUT("LINER"),
+       SND_SOC_DAPM_INPUT("MICP"),
+       SND_SOC_DAPM_INPUT("MICN"),
+
+       /* Output */
+       SND_SOC_DAPM_OUTPUT("HPOUTL"),
+       SND_SOC_DAPM_OUTPUT("HPOUTR"),
+
+};
+
+static const struct snd_soc_dapm_route rk312x_dapm_routes[] = {
+       /* Input */
+       {"BSTR", NULL, "MICP"},
+       {"BSTL", NULL, "MICP"},
+       {"BSTL", NULL, "MICN"},
+
+       {"IN_R Mux", "LINER", "LINER"},
+       {"IN_R Mux", "BSTR", "BSTR"},
+       {"IN_L Mux", "LINEL", "LINEL"},
+       {"IN_L Mux", "BSTL", "BSTL"},
+
+       {"ALCL", NULL, "IN_L Mux"},
+       {"ALCR", NULL, "IN_R Mux"},
+
+
+       {"ADCR", NULL, "ALCR"},
+       {"ADCL", NULL, "ALCL"},
+
+       {"I2S ADC", NULL, "ADCR"},
+       {"I2S ADC", NULL, "ADCL"},
+
+       /* Output */
+
+       {"DACR", NULL, "I2S DAC"},
+       {"DACL", NULL, "I2S DAC"},
+
+       {"HPMIXR", "ALCR Switch", "ALCR"},
+       {"HPMIXR", "ALCL Switch", "ALCL"},
+       {"HPMIXR", "DACR Switch", "DACR"},
+
+       {"HPMIXL", "ALCR Switch", "ALCR"},
+       {"HPMIXL", "ALCL Switch", "ALCL"},
+       {"HPMIXL", "DACL Switch", "DACL"},
+
+
+       {"HPR Mux", "DACR", "DACR"},
+       {"HPR Mux", "HPMIXR", "HPMIXR"},
+       {"HPL Mux", "DACL", "DACL"},
+       {"HPL Mux", "HPMIXL", "HPMIXL"},
+
+       {"HPR", NULL, "HPR Mux"},
+       {"HPL", NULL, "HPL Mux"},
+
+       {"HPOUTR", NULL, "HPR"},
+       {"HPOUTL", NULL, "HPL"},
+};
+
+static int rk312x_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       DBG("%s  level=%d\n", __func__, level);
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+                       writel(0x32, rk312x_priv->regbase+RK312x_DAC_INT_CTL3);
+                       snd_soc_update_bits(codec, RK312x_ADC_MIC_CTL,
+                                           RK312x_ADC_CURRENT_ENABLE,
+                                           RK312x_ADC_CURRENT_ENABLE);
+                       snd_soc_update_bits(codec, RK312x_DAC_CTL,
+                                           RK312x_CURRENT_EN,
+                                           RK312x_CURRENT_EN);
+                       /* set power */
+                       snd_soc_update_bits(codec, RK312x_ADC_ENABLE,
+                                           RK312x_ADCL_REF_VOL_EN_SFT
+                                           | RK312x_ADCR_REF_VOL_EN_SFT,
+                                           RK312x_ADCL_REF_VOL_EN
+                                           | RK312x_ADCR_REF_VOL_EN);
+
+                       snd_soc_update_bits(codec, RK312x_ADC_MIC_CTL,
+                                           RK312x_ADCL_ZERO_DET_EN_SFT
+                                           | RK312x_ADCR_ZERO_DET_EN_SFT,
+                                           RK312x_ADCL_ZERO_DET_EN
+                                           | RK312x_ADCR_ZERO_DET_EN);
+
+                       snd_soc_update_bits(codec, RK312x_DAC_CTL,
+                                           RK312x_REF_VOL_DACL_EN_SFT
+                                           | RK312x_REF_VOL_DACR_EN_SFT,
+                                           RK312x_REF_VOL_DACL_EN
+                                           | RK312x_REF_VOL_DACR_EN);
+
+                       snd_soc_update_bits(codec, RK312x_DAC_ENABLE,
+                                           RK312x_DACL_REF_VOL_EN_SFT
+                                           | RK312x_DACR_REF_VOL_EN_SFT,
+                                           RK312x_DACL_REF_VOL_EN
+                                           | RK312x_DACR_REF_VOL_EN);
+               }
+               break;
+
+       case SND_SOC_BIAS_OFF:
+                       snd_soc_update_bits(codec, RK312x_DAC_ENABLE,
+                                           RK312x_DACL_REF_VOL_EN_SFT
+                                           | RK312x_DACR_REF_VOL_EN_SFT, 0);
+                       snd_soc_update_bits(codec, RK312x_DAC_CTL,
+                                           RK312x_REF_VOL_DACL_EN_SFT
+                                           | RK312x_REF_VOL_DACR_EN_SFT, 0);
+                       snd_soc_update_bits(codec, RK312x_ADC_MIC_CTL,
+                                           RK312x_ADCL_ZERO_DET_EN_SFT
+                                           | RK312x_ADCR_ZERO_DET_EN_SFT, 0);
+                       snd_soc_update_bits(codec, RK312x_ADC_ENABLE,
+                                           RK312x_ADCL_REF_VOL_EN_SFT
+                                           | RK312x_ADCR_REF_VOL_EN_SFT, 0);
+                       snd_soc_update_bits(codec, RK312x_ADC_MIC_CTL,
+                                           RK312x_ADC_CURRENT_ENABLE, 0);
+                       snd_soc_update_bits(codec, RK312x_DAC_CTL,
+                                           RK312x_CURRENT_EN, 0);
+                       writel(0x22, rk312x_priv->regbase+RK312x_DAC_INT_CTL3);
+               break;
+       }
+       codec->dapm.bias_level = level;
+
+       return 0;
+}
+
+static int rk312x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+                                int clk_id, unsigned int freq, int dir)
+{
+       struct rk312x_codec_priv *rk312x = rk312x_priv;
+
+       if (!rk312x) {
+               DBG("%s : rk312x is NULL\n", __func__);
+               return -EINVAL;
+       }
+
+       rk312x->stereo_sysclk = freq;
+
+       return 0;
+}
+
+static int rk312x_set_dai_fmt(struct snd_soc_dai *codec_dai,
+                             unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       unsigned int adc_aif1 = 0, adc_aif2 = 0, dac_aif1 = 0, dac_aif2 = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               adc_aif2 |= RK312x_I2S_MODE_SLV;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               adc_aif2 |= RK312x_I2S_MODE_MST;
+               break;
+       default:
+               DBG("%s : set master mask failed!\n", __func__);
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_A:
+               adc_aif1 |= RK312x_ADC_DF_PCM;
+               dac_aif1 |= RK312x_DAC_DF_PCM;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               adc_aif1 |= RK312x_ADC_DF_I2S;
+               dac_aif1 |= RK312x_DAC_DF_I2S;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               adc_aif1 |= RK312x_ADC_DF_RJ;
+               dac_aif1 |= RK312x_DAC_DF_RJ;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               adc_aif1 |= RK312x_ADC_DF_LJ;
+               dac_aif1 |= RK312x_DAC_DF_LJ;
+               break;
+       default:
+               DBG("%s : set format failed!\n", __func__);
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               adc_aif1 |= RK312x_ALRCK_POL_DIS;
+               adc_aif2 |= RK312x_ABCLK_POL_DIS;
+               dac_aif1 |= RK312x_DLRCK_POL_DIS;
+               dac_aif2 |= RK312x_DBCLK_POL_DIS;
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               adc_aif1 |= RK312x_ALRCK_POL_EN;
+               adc_aif2 |= RK312x_ABCLK_POL_EN;
+               dac_aif1 |= RK312x_DLRCK_POL_EN;
+               dac_aif2 |= RK312x_DBCLK_POL_EN;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               adc_aif1 |= RK312x_ALRCK_POL_DIS;
+               adc_aif2 |= RK312x_ABCLK_POL_EN;
+               dac_aif1 |= RK312x_DLRCK_POL_DIS;
+               dac_aif2 |= RK312x_DBCLK_POL_EN;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               adc_aif1 |= RK312x_ALRCK_POL_EN;
+               adc_aif2 |= RK312x_ABCLK_POL_DIS;
+               dac_aif1 |= RK312x_DLRCK_POL_EN;
+               dac_aif2 |= RK312x_DBCLK_POL_DIS;
+               break;
+       default:
+               DBG("%s : set dai format failed!\n", __func__);
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, RK312x_ADC_INT_CTL1,
+                           RK312x_ALRCK_POL_MASK
+                           | RK312x_ADC_DF_MASK, adc_aif1);
+       snd_soc_update_bits(codec, RK312x_ADC_INT_CTL2,
+                           RK312x_ABCLK_POL_MASK
+                           | RK312x_I2S_MODE_MASK, adc_aif2);
+       snd_soc_update_bits(codec, RK312x_DAC_INT_CTL1,
+                           RK312x_DLRCK_POL_MASK
+                           | RK312x_DAC_DF_MASK, dac_aif1);
+       snd_soc_update_bits(codec, RK312x_DAC_INT_CTL2,
+                           RK312x_DBCLK_POL_MASK, dac_aif2);
+
+       return 0;
+}
+
+static int rk312x_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_codec *codec = rtd->codec;
+       struct rk312x_codec_priv *rk312x = rk312x_priv;
+       unsigned int rate = params_rate(params);
+       unsigned int div;
+       unsigned int adc_aif1 = 0, adc_aif2  = 0, dac_aif1 = 0, dac_aif2  = 0;
+
+       if (!rk312x) {
+               DBG("%s : rk312x is NULL\n", __func__);
+               return -EINVAL;
+       }
+
+       /* bclk = codec_clk / 4 */
+       /* lrck = bclk / (wl * 2) */
+       div = (((rk312x->stereo_sysclk / 4) / rate) / 2);
+
+       if ((rk312x->stereo_sysclk % (4 * rate * 2) > 0) ||
+           (div != 16 && div != 20 && div != 24 && div != 32)) {
+               DBG("%s : need PLL\n", __func__);
+               return -EINVAL;
+       }
+
+       switch (div) {
+       case 16:
+               adc_aif2 |= RK312x_ADC_WL_16;
+               dac_aif2 |= RK312x_DAC_WL_16;
+               break;
+       case 20:
+               adc_aif2 |= RK312x_ADC_WL_20;
+               dac_aif2 |= RK312x_DAC_WL_20;
+               break;
+       case 24:
+               adc_aif2 |= RK312x_ADC_WL_24;
+               dac_aif2 |= RK312x_DAC_WL_24;
+               break;
+       case 32:
+               adc_aif2 |= RK312x_ADC_WL_32;
+               dac_aif2 |= RK312x_DAC_WL_32;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+
+       DBG("%s : MCLK = %dHz, sample rate = %dHz, div = %d\n",
+           __func__, rk312x->stereo_sysclk, rate, div);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               adc_aif1 |= RK312x_ADC_VWL_16;
+               dac_aif1 |= RK312x_DAC_VWL_16;
+               DBG("SLE16\n");
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               adc_aif1 |= RK312x_ADC_VWL_20;
+               dac_aif1 |= RK312x_DAC_VWL_20;
+               DBG("S20_3LE\n");
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               adc_aif1 |= RK312x_ADC_VWL_24;
+               dac_aif1 |= RK312x_DAC_VWL_24;
+               DBG("S24LE\n");
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               adc_aif1 |= RK312x_ADC_VWL_32;
+               dac_aif1 |= RK312x_DAC_VWL_32;
+               DBG("S32LE\n");
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (params_channels(params)) {
+       case RK312x_MONO:
+               adc_aif1 |= RK312x_ADC_TYPE_MONO;
+               DBG("mono\n");
+               break;
+       case RK312x_STEREO:
+               adc_aif1 |= RK312x_ADC_TYPE_STEREO;
+               DBG("stero\n");
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       adc_aif1 |= RK312x_ADC_SWAP_DIS;
+       adc_aif2 |= RK312x_ADC_RST_DIS;
+       dac_aif1 |= RK312x_DAC_SWAP_DIS;
+       dac_aif2 |= RK312x_DAC_RST_DIS;
+
+       rk312x->rate = rate;
+
+       snd_soc_update_bits(codec, RK312x_ADC_INT_CTL1,
+                           RK312x_ADC_VWL_MASK
+                           | RK312x_ADC_SWAP_MASK
+                           | RK312x_ADC_TYPE_MASK, adc_aif1);
+       snd_soc_update_bits(codec, RK312x_ADC_INT_CTL2,
+                           RK312x_ADC_WL_MASK
+                           | RK312x_ADC_RST_MASK, adc_aif2);
+       snd_soc_update_bits(codec, RK312x_DAC_INT_CTL1,
+                           RK312x_DAC_VWL_MASK
+                           | RK312x_DAC_SWAP_MASK, dac_aif1);
+       snd_soc_update_bits(codec, RK312x_DAC_INT_CTL2,
+                           RK312x_DAC_WL_MASK
+                           | RK312x_DAC_RST_MASK, dac_aif2);
+
+       return 0;
+}
+
+static int rk312x_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       unsigned int is_hp_pd;
+
+       is_hp_pd = (RK312x_HPOUTL_MSK | RK312x_HPOUTR_MSK)
+                   & snd_soc_read(codec, RK312x_HPOUT_CTL);
+
+/*     if (mute) {
+               if (rk312x_priv && rk312x_priv->hp_ctl_gpio !=
+                       INVALID_GPIO && is_hp_pd) {
+                       DBG("%s : set hp ctl gpio LOW\n", __func__);
+                       pio_set_value(rk312x_priv->hp_ctl_gpio,
+                                     GPIO_LOW);
+                        msleep(200);//rk312x_priv->delay_time);
+               }
+
+       } else {
+               if (rk312x_priv && rk312x_priv->hp_ctl_gpio
+                   != INVALID_GPIO &&  is_hp_pd) {
+                       DBG("%s : set hp ctl gpio HIGH\n", __func__);
+                       gpio_set_value(rk312x_priv->hp_ctl_gpio,
+                                      GPIO_HIGH);
+                       msleep(100);//rk312x_priv->delay_time);
+                }
+       }
+*/
+       return 0;
+}
+
+static struct rk312x_reg_val_typ playback_power_up_list[] = {
+       {0x18, 0x32},
+       {0xa0, 0x40},
+       {0xa0, 0x62},
+       {0xa4, 0x88},
+       {0xa4, 0xcc},
+       {0xa4, 0xee},
+       {0xa8, 0x44},
+       {0xb0, 0x92},
+       {0xb0, 0xdb},
+       {0xac, 0x11}, /*DAC*/
+       {0xa8, 0x55},
+       {0xa8, 0x77},
+       {0xa4, 0xff},
+       {0xb0, 0xff},
+       {0xa0, 0x73},
+       {0xb4, OUT_VOLUME},
+       {0xb8, OUT_VOLUME},
+};
+#define RK312x_CODEC_PLAYBACK_POWER_UP_LIST_LEN ARRAY_SIZE( \
+                                       playback_power_up_list)
+
+static struct rk312x_reg_val_typ playback_power_down_list[] = {
+       {0xb0, 0xdb},
+       {0xa8, 0x44},
+       {0xac, 0x00},
+       {0xb0, 0x92},
+       {0xa0, 0x22},
+       {0xb0, 0x00},
+       {0xa8, 0x00},
+       {0xa4, 0x00},
+       {0xa0, 0x00},
+       {0x18, 0x22},
+#ifdef WITH_CAP
+       /* {0xbc, 0x08},*/
+#endif
+       {0xb4, 0x0},
+       {0xb8, 0x0},
+       {0x18, 0x22},
+};
+#define RK312x_CODEC_PLAYBACK_POWER_DOWN_LIST_LEN ARRAY_SIZE( \
+                               playback_power_down_list)
+
+static struct rk312x_reg_val_typ capture_power_up_list[] = {
+       {0x88, 0x80},
+       {0x88, 0xc0},
+       {0x88, 0xc7},
+       {0x9c, 0x88},
+       {0x8c, 0x04},
+       {0x90, 0x66},
+       {0x9c, 0xcc},
+       {0x9c, 0xee},
+       {0x8c, 0x07},
+       {0x90, 0x77},
+       {0x94, 0x20 | CAP_VOL},
+       {0x98, CAP_VOL},
+       {0x88, 0xf7},
+
+};
+#define RK312x_CODEC_CAPTURE_POWER_UP_LIST_LEN ARRAY_SIZE(capture_power_up_list)
+
+static struct rk312x_reg_val_typ capture_power_down_list[] = {
+       {0x9c, 0xcc},
+       {0x90, 0x66},
+       {0x8c, 0x44},
+       {0x9c, 0x88},
+       {0x88, 0xc7},
+       {0x88, 0xc0},
+       {0x88, 0x80},
+       {0x8c, 0x00},
+       {0X94, 0x0c},
+       {0X98, 0x0c},
+       {0x9c, 0x00},
+       {0x88, 0x00},
+       {0x90, 0x44},
+};
+#define RK312x_CODEC_CAPTURE_POWER_DOWN_LIST_LEN ARRAY_SIZE(\
+                               capture_power_down_list)
+
+static int rk312x_codec_power_up(int type)
+{
+       struct snd_soc_codec *codec = rk312x_priv->codec;
+       int i;
+
+       if (!rk312x_priv || !rk312x_priv->codec) {
+               DBG("%s : rk312x_priv or rk312x_priv->codec is NULL\n",
+                   __func__);
+               return -EINVAL;
+       }
+       DBG("%s : power up %s%s\n", __func__,
+           type == RK312x_CODEC_PLAYBACK ? "playback" : "",
+           type == RK312x_CODEC_CAPTURE ? "capture" : "");
+
+       if (type == RK312x_CODEC_PLAYBACK) {
+               for (i = 0; i < RK312x_CODEC_PLAYBACK_POWER_UP_LIST_LEN; i++) {
+                       snd_soc_write(codec, playback_power_up_list[i].reg,
+                                     playback_power_up_list[i].value);
+                       msleep(20);
+               }
+       } else if (type == RK312x_CODEC_CAPTURE) {
+               for (i = 0; i < RK312x_CODEC_CAPTURE_POWER_UP_LIST_LEN; i++) {
+                       snd_soc_write(codec, capture_power_up_list[i].reg,
+                                     capture_power_up_list[i].value);
+                       msleep(20);
+               }
+       } else if (type == RK312x_CODEC_INCALL) {
+               snd_soc_update_bits(codec, RK312x_ALC_MUNIN_CTL,
+                                   RK312x_MUXINL_F_MSK | RK312x_MUXINR_F_MSK,
+                                   RK312x_MUXINR_F_INR | RK312x_MUXINL_F_INL);
+       }
+       return 0;
+}
+
+static int rk312x_codec_power_down(int type)
+{
+       struct snd_soc_codec *codec = rk312x_priv->codec;
+       int i;
+
+       if (!rk312x_priv || !rk312x_priv->codec) {
+               DBG("%s : rk312x_priv or rk312x_priv->codec is NULL\n",
+                   __func__);
+               return -EINVAL;
+       }
+
+       DBG("%s : power down %s%s%s\n", __func__,
+           type == RK312x_CODEC_PLAYBACK ? "playback" : "",
+           type == RK312x_CODEC_CAPTURE ? "capture" : "",
+           type == RK312x_CODEC_ALL ? "all" : "");
+
+       if ((type == RK312x_CODEC_CAPTURE) || (type == RK312x_CODEC_INCALL)) {
+               for (i = 0; i < RK312x_CODEC_CAPTURE_POWER_DOWN_LIST_LEN; i++) {
+                       snd_soc_write(codec, capture_power_down_list[i].reg,
+                                     capture_power_down_list[i].value);
+               }
+       } else if (type == RK312x_CODEC_PLAYBACK) {
+               for (i = 0;
+                    i < RK312x_CODEC_PLAYBACK_POWER_DOWN_LIST_LEN;
+                    i++) {
+                       snd_soc_write(codec, playback_power_down_list[i].reg,
+                                     playback_power_down_list[i].value);
+               }
+
+       } else if (type == RK312x_CODEC_ALL) {
+               rk312x_reset(codec);
+       }
+
+       return 0;
+}
+
+static void  rk312x_codec_capture_work(struct work_struct *work)
+{
+       DBG("%s : rk312x_codec_work_capture_type = %d\n", __func__,
+           rk312x_codec_work_capture_type);
+
+       switch (rk312x_codec_work_capture_type) {
+       case RK312x_CODEC_WORK_POWER_DOWN:
+               rk312x_codec_power_down(RK312x_CODEC_CAPTURE);
+               break;
+       case RK312x_CODEC_WORK_POWER_UP:
+               rk312x_codec_power_up(RK312x_CODEC_CAPTURE);
+               break;
+       default:
+               break;
+       }
+
+       rk312x_codec_work_capture_type = RK312x_CODEC_WORK_NULL;
+}
+
+static int rk312x_startup(struct snd_pcm_substream *substream,
+                         struct snd_soc_dai *dai)
+{
+       struct rk312x_codec_priv *rk312x = rk312x_priv;
+       bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+       bool is_codec_playback_running = rk312x->playback_active > 0;
+       bool is_codec_capture_running = rk312x->capture_active > 0;
+
+       if (!rk312x_for_mid) {
+               DBG("%s immediately return for phone\n", __func__);
+               return 0;
+       }
+
+       if (!rk312x) {
+               DBG("%s : rk312x is NULL\n", __func__);
+               return -EINVAL;
+       }
+       if (playback)
+               rk312x->playback_active++;
+       else
+               rk312x->capture_active++;
+
+       if (playback) {
+               if (rk312x->playback_active > 0) {
+                       if (!is_codec_playback_running)
+                               rk312x_codec_power_up(
+                                       RK312x_CODEC_PLAYBACK);
+               }
+       } else {
+               if (rk312x->capture_active > 0 &&
+                   !is_codec_capture_running) {
+                       if (rk312x_codec_work_capture_type
+                           != RK312x_CODEC_WORK_POWER_UP) {
+                               cancel_delayed_work_sync(
+                                       &capture_delayed_work);
+                               if (rk312x_codec_work_capture_type ==
+                                       RK312x_CODEC_WORK_NULL) {
+                                       rk312x_codec_power_up(
+                                               RK312x_CODEC_CAPTURE);
+                               } else {
+                                       rk312x_codec_work_capture_type =
+                                               RK312x_CODEC_WORK_NULL;
+                               }
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static void rk312x_shutdown(struct snd_pcm_substream *substream,
+                           struct snd_soc_dai *dai)
+{
+       struct rk312x_codec_priv *rk312x = rk312x_priv;
+       bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+       bool is_codec_playback_running = rk312x->playback_active > 0;
+       bool is_codec_capture_running = rk312x->capture_active > 0;
+
+       if (!rk312x_for_mid) {
+               DBG("%s immediately return for phone\n",
+                   __func__);
+               return;
+       }
+
+       if (!rk312x) {
+               DBG("%s : rk312x is NULL\n", __func__);
+               return;
+       }
+       if (playback)
+               rk312x->playback_active--;
+       else
+               rk312x->capture_active--;
+
+       if (playback) {
+               if (rk312x->playback_active <= 0) {
+                       if (is_codec_playback_running == true)
+                               rk312x_codec_power_down(
+                                       RK312x_CODEC_PLAYBACK);
+                       else
+                               DBG(" Warning:playback closed! return !\n");
+               }
+       } else {
+               if (rk312x->capture_active <= 0) {
+                       if ((rk312x_codec_work_capture_type !=
+                            RK312x_CODEC_WORK_POWER_DOWN) &&
+                           (is_codec_capture_running == true)) {
+                               cancel_delayed_work_sync(&capture_delayed_work);
+                       /*
+                        * If rk312x_codec_work_capture_type is NULL
+                        * means codec already power down,
+                        * so power up codec.
+                        * If rk312x_codec_work_capture_type is
+                        * RK312x_CODEC_WORK_POWER_UP it means
+                        * codec haven't be powered up, so we don't
+                        * need to power down codec.
+                        * If is playback call power down,
+                        * power down immediatly, because audioflinger
+                        * already has delay 3s.
+                        */
+                               if (rk312x_codec_work_capture_type ==
+                                   RK312x_CODEC_WORK_NULL) {
+                                       rk312x_codec_work_capture_type =
+                                               RK312x_CODEC_WORK_POWER_DOWN;
+                                       queue_delayed_work(rk312x_codec_workq,
+                                                       &capture_delayed_work,
+                                                       msecs_to_jiffies(3000));
+                               } else {
+                                       rk312x_codec_work_capture_type =
+                                                       RK312x_CODEC_WORK_NULL;
+                               }
+                       }
+               }
+       }
+}
+
+#define RK312x_PLAYBACK_RATES (SNDRV_PCM_RATE_8000 |\
+                             SNDRV_PCM_RATE_16000 |    \
+                             SNDRV_PCM_RATE_32000 |    \
+                             SNDRV_PCM_RATE_44100 |    \
+                             SNDRV_PCM_RATE_48000 |    \
+                             SNDRV_PCM_RATE_96000)
+
+#define RK312x_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\
+                             SNDRV_PCM_RATE_16000 |    \
+                             SNDRV_PCM_RATE_32000 |    \
+                             SNDRV_PCM_RATE_44100 |    \
+                             SNDRV_PCM_RATE_48000 |    \
+                             SNDRV_PCM_RATE_96000)
+
+#define RK312x_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+                       SNDRV_PCM_FMTBIT_S20_3LE |\
+                       SNDRV_PCM_FMTBIT_S24_LE |\
+                       SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops rk312x_dai_ops = {
+       .hw_params      = rk312x_hw_params,
+       .set_fmt        = rk312x_set_dai_fmt,
+       .set_sysclk     = rk312x_set_dai_sysclk,
+       .digital_mute   = rk312x_digital_mute,
+       .startup        = rk312x_startup,
+       .shutdown       = rk312x_shutdown,
+};
+
+static struct snd_soc_dai_driver rk312x_dai[] = {
+       {
+               .name = "rk312x-hifi",
+               .id = RK312x_HIFI,
+               .playback = {
+                       .stream_name = "HiFi Playback",
+                       .channels_min = 2,
+                       .channels_max = 2,
+                       .rates = RK312x_PLAYBACK_RATES,
+                       .formats = RK312x_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "HiFi Capture",
+                       .channels_min = 2,
+                       .channels_max = 2,
+                       .rates = RK312x_CAPTURE_RATES,
+                       .formats = RK312x_FORMATS,
+               },
+               .ops = &rk312x_dai_ops,
+       },
+       {
+               .name = "rk312x-voice",
+               .id = RK312x_VOICE,
+               .playback = {
+                       .stream_name = "Voice Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RK312x_PLAYBACK_RATES,
+                       .formats = RK312x_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "Voice Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RK312x_CAPTURE_RATES,
+                       .formats = RK312x_FORMATS,
+               },
+               .ops = &rk312x_dai_ops,
+       },
+
+};
+
+static int rk312x_suspend(struct snd_soc_codec *codec)
+{
+       if (rk312x_for_mid) {
+               cancel_delayed_work_sync(&capture_delayed_work);
+
+               if (rk312x_codec_work_capture_type != RK312x_CODEC_WORK_NULL)
+                       rk312x_codec_work_capture_type = RK312x_CODEC_WORK_NULL;
+
+               rk312x_codec_power_down(RK312x_CODEC_PLAYBACK);
+               rk312x_codec_power_down(RK312x_CODEC_ALL);
+               snd_soc_write(codec, RK312x_SELECT_CURRENT, 0x1e);
+               snd_soc_write(codec, RK312x_SELECT_CURRENT, 0x3e);
+       } else {
+               rk312x_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       }
+       return 0;
+}
+
+static ssize_t gpio_show(struct kobject *kobj, struct kobj_attribute *attr,
+                        char *buf)
+{
+       return 0;
+}
+
+static ssize_t gpio_store(struct kobject *kobj, struct kobj_attribute *attr,
+                         const char *buf, size_t n)
+{
+       const char *buftmp = buf;
+       char cmd;
+       int ret;
+       struct rk312x_codec_priv *rk312x =
+                       snd_soc_codec_get_drvdata(rk312x_priv->codec);
+
+       ret = sscanf(buftmp, "%c ", &cmd);
+       if (ret == 0)
+               return ret;
+       DBG("--luoxt--: get cmd = %c\n", cmd);
+       switch (cmd) {
+       case 'l':
+               DBG("%s :  lxt set spk ctl gpio low\n",
+                   __func__);
+               gpio_set_value(rk312x->spk_ctl_gpio, 0);
+               break;
+       case 'h':
+                       DBG("%s :  lxt set spk ctl gpio high\n",
+                           __func__);
+                       gpio_set_value(rk312x->spk_ctl_gpio, 1);
+               break;
+       case 'o':
+                       DBG("%s: codec power up playback and capture\n",
+                           __func__);
+                       rk312x_codec_power_up(RK312x_CODEC_PLAYBACK);
+                       rk312x_codec_power_up(RK312x_CODEC_CAPTURE);
+               break;
+       default:
+                       DBG("--luoxt-- unknown cmd\n");
+       }
+       return n;
+}
+static struct kobject *gpio_kobj;
+struct gpio_attribute {
+
+       struct attribute    attr;
+
+       ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
+                       char *buf);
+       ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
+                        const char *buf, size_t n);
+};
+
+static struct gpio_attribute gpio_attrs[] = {
+       /*     node_name    permision       show_func   store_func */
+       __ATTR(gpio-ctl,  S_IRUGO | S_IWUSR,  gpio_show, gpio_store),
+};
+
+static int rk312x_resume(struct snd_soc_codec *codec)
+{
+       if (!rk312x_for_mid)
+               rk312x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       return 0;
+}
+
+static int rk312x_probe(struct snd_soc_codec *codec)
+{
+       struct rk312x_codec_priv *rk312x_codec =
+                               snd_soc_codec_get_drvdata(codec);
+       unsigned int val;
+       int ret;
+       int i = 0;
+
+       rk312x_codec->codec = codec;
+       clk_prepare_enable(rk312x_codec->pclk);
+
+       ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+       if (ret != 0)
+               goto err__;
+       codec->hw_read = rk312x_codec_read;
+       codec->hw_write = (hw_write_t)rk312x_hw_write;
+       codec->read = rk312x_codec_read;
+       codec->write = rk312x_codec_write;
+
+       if (rk312x_for_mid) {
+               rk312x_codec->playback_active = 0;
+               rk312x_codec->capture_active = 0;
+
+               rk312x_codec_workq = create_freezable_workqueue("rk312x-codec");
+
+               if (rk312x_codec_workq == NULL) {
+                       DBG("%s : rk312x_codec_workq is NULL!\n", __func__);
+                       ret = -ENOMEM;
+                       goto err__;
+               }
+       }
+
+       val = snd_soc_read(codec, RK312x_RESET);
+       if (val != rk312x_reg_defaults[RK312x_RESET]) {
+               DBG("%s : codec register 0: %x is not a 0x00000003\n",
+                   __func__, val);
+               ret = -ENODEV;
+               goto err__;
+       }
+
+       rk312x_reset(codec);
+
+       if (!rk312x_for_mid) {
+               codec->dapm.bias_level = SND_SOC_BIAS_OFF;
+               rk312x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       }
+
+#ifdef WITH_CAP
+       snd_soc_write(codec, RK312x_SELECT_CURRENT, 0x1e);
+       snd_soc_write(codec, RK312x_SELECT_CURRENT, 0x3e);
+#endif
+
+#if 1
+       gpio_kobj = kobject_create_and_add("gpio-ctl", NULL);
+
+       if (!gpio_kobj)
+               return -ENOMEM;
+       for (i = 0; i < ARRAY_SIZE(gpio_attrs); i++) {
+               ret = sysfs_create_file(gpio_kobj, &gpio_attrs[i].attr);
+               if (ret != 0) {
+                       DBG("create gpio-ctl sysfs %d error\n", i);
+                       /* return ret; */
+               }
+       }
+#endif
+
+       return 0;
+
+err__:
+       dbg_codec(2, "%s err ret=%d\n", __func__, ret);
+       return ret;
+}
+
+/* power down chip */
+static int rk312x_remove(struct snd_soc_codec *codec)
+{
+       DBG("%s\n", __func__);
+
+       if (!rk312x_priv) {
+               DBG("%s : rk312x_priv is NULL\n", __func__);
+               return 0;
+       }
+
+       /* if (rk312x_priv->spk_ctl_gpio != INVALID_GPIO) */
+               /* gpio_set_value(rk312x_priv->spk_ctl_gpio, GPIO_LOW); */
+
+       /* if (rk312x_priv->hp_ctl_gpio != INVALID_GPIO) */
+               /* gpio_set_value(rk312x_priv->hp_ctl_gpio, GPIO_LOW); */
+
+       mdelay(10);
+
+       if (rk312x_for_mid) {
+               cancel_delayed_work_sync(&capture_delayed_work);
+
+               if (rk312x_codec_work_capture_type != RK312x_CODEC_WORK_NULL)
+                       rk312x_codec_work_capture_type = RK312x_CODEC_WORK_NULL;
+       }
+       snd_soc_write(codec, RK312x_RESET, 0xfc);
+       mdelay(10);
+       snd_soc_write(codec, RK312x_RESET, 0x3);
+       mdelay(10);
+
+       /* if (rk312x_priv) */
+       kfree(rk312x_priv);
+
+       return 0;
+}
+
+#if 0
+static int rk3036_suspend(struct snd_soc_codec *codec)
+{
+       rk3036_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int rk3036_resume(struct snd_soc_codec *codec)
+{
+       rk3036_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       return 0;
+}
+
+static int rk3036_set_bias_level(struct snd_soc_codec *codec, enum
+       snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               break;
+       }
+       codec->dapm.bias_level = level;
+       return 0;
+}
+
+static int rk3036_volatile_register(struct snd_soc_codec *
+                               codec, unsigned int reg)
+{
+       switch (reg) {
+       case RK3036_CODEC_RESET:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static int rk3036_codec_register(struct snd_soc_codec *codec, unsigned int reg)
+{
+       switch (reg) {
+       case RK3036_CODEC_RESET:
+       case RK3036_CODEC_REG03:
+       case RK3036_CODEC_REG04:
+       case RK3036_CODEC_REG05:
+       case RK3036_CODEC_REG22:
+       case RK3036_CODEC_REG23:
+       case RK3036_CODEC_REG24:
+       case RK3036_CODEC_REG25:
+       case RK3036_CODEC_REG26:
+       case RK3036_CODEC_REG27:
+       case RK3036_CODEC_REG28:
+               return 1;
+       default:
+               return 0;
+       }
+}
+#endif
+
+static struct snd_soc_codec_driver soc_codec_dev_rk312x = {
+       .probe = rk312x_probe,
+       .remove = rk312x_remove,
+       .suspend = rk312x_suspend,
+       .resume = rk312x_resume,
+       .set_bias_level = rk312x_set_bias_level,
+       .reg_cache_size = ARRAY_SIZE(rk312x_reg_defaults),
+       .reg_word_size = sizeof(unsigned int),
+       .reg_cache_default = rk312x_reg_defaults,
+       .volatile_register = rk312x_volatile_register,
+       .readable_register = rk312x_codec_register,
+       .reg_cache_step = sizeof(unsigned int),
+};
+
+static int rk312x_platform_probe(struct platform_device *pdev)
+{
+       struct device_node *rk312x_np = pdev->dev.of_node;
+       struct rk312x_codec_priv *rk312x;
+       struct resource *res;
+       int ret;
+
+       rk312x = devm_kzalloc(&pdev->dev, sizeof(*rk312x), GFP_KERNEL);
+       if (!rk312x) {
+               dbg_codec(2, "%s : rk312x priv kzalloc failed!\n",
+                         __func__);
+               return -ENOMEM;
+       }
+       rk312x_priv = rk312x;
+       platform_set_drvdata(pdev, rk312x);
+       rk312x->spk_ctl_gpio = of_get_named_gpio(rk312x_np,
+                                                "spk_ctl_io", 0);
+       if (!gpio_is_valid(rk312x->spk_ctl_gpio)) {
+               dbg_codec(2, "invalid reset_gpio: %d\n",
+                         rk312x->spk_ctl_gpio);
+               ret = -ENOENT;
+               goto err__;
+       }
+
+       ret = devm_gpio_request(&pdev->dev, rk312x->spk_ctl_gpio, "spk_ctl");
+       if (ret < 0) {
+               dbg_codec(2, "rk312x_platform_probe spk_ctl_gpio fail\n");
+               goto err__;
+       }
+       gpio_direction_output(rk312x->spk_ctl_gpio, SPK_CTRL_CLOSE);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       rk312x->regbase = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(rk312x->regbase))
+               return PTR_ERR(rk312x->regbase);
+
+       rk312x->pclk = devm_clk_get(&pdev->dev, "g_pclk_acodec");
+       if (IS_ERR(rk312x->pclk)) {
+               dev_err(&pdev->dev, "Unable to get acodec hclk\n");
+               ret = -ENXIO;
+               goto err__;
+       }
+
+       return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_rk312x,
+                               rk312x_dai, ARRAY_SIZE(rk312x_dai));
+
+err__:
+       platform_set_drvdata(pdev, NULL);
+       rk312x_priv = NULL;
+       return ret;
+}
+
+static int rk312x_platform_remove(struct platform_device *pdev)
+{
+       rk312x_priv = NULL;
+       snd_soc_unregister_codec(&pdev->dev);
+       return 0;
+}
+
+void rk312x_platform_shutdown(struct platform_device *pdev)
+{
+       DBG("%s\n", __func__);
+
+       if (!rk312x_priv || !rk312x_priv->codec) {
+               DBG("%s : rk312x_priv or rk312x_priv->codec is NULL\n",
+                   __func__);
+               return;
+       }
+
+       /* if (rk312x_priv->spk_ctl_gpio != INVALID_GPIO) */
+               /* gpio_set_value(rk312x_priv->spk_ctl_gpio, GPIO_LOW); */
+
+       /* if (rk312x_priv->hp_ctl_gpio != INVALID_GPIO) */
+               /* gpio_set_value(rk312x_priv->hp_ctl_gpio, GPIO_LOW); */
+
+       mdelay(10);
+
+       if (rk312x_for_mid) {
+               cancel_delayed_work_sync(&capture_delayed_work);
+               if (rk312x_codec_work_capture_type !=
+                                       RK312x_CODEC_WORK_NULL)
+                       rk312x_codec_work_capture_type =
+                                       RK312x_CODEC_WORK_NULL;
+       }
+
+       writel(0xfc, rk312x_priv->regbase+RK312x_RESET);
+       mdelay(10);
+       writel(0x03, rk312x_priv->regbase+RK312x_RESET);
+
+       /* if (rk312x_priv) */
+       kfree(rk312x_priv);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rk3126_codec_of_match[] = {
+       { .compatible = "rk312x-codec" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rk3126_codec_of_match);
+#endif
+
+static struct platform_driver rk312x_codec_driver = {
+       .driver = {
+                  .name = "rk312x-codec",
+                  .owner = THIS_MODULE,
+                  .of_match_table = of_match_ptr(rk3126_codec_of_match),
+                  },
+       .probe = rk312x_platform_probe,
+       .remove = rk312x_platform_remove,
+       .shutdown = rk312x_platform_shutdown,
+};
+module_platform_driver(rk312x_codec_driver);
+
+/* Module information */
+MODULE_AUTHOR("rockchip");
+MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rk312x_codec.h b/sound/soc/codecs/rk312x_codec.h
new file mode 100755 (executable)
index 0000000..68f28c8
--- /dev/null
@@ -0,0 +1,564 @@
+/*
+ * rk3036.h  --  RK312x CODEC ALSA SoC audio driver
+ *
+ * Copyright 2013 Rockship
+ * Author: chenjq <chenjq@rock-chips.com>
+ *
+ */
+
+#ifndef __RK312x_CODEC_H__
+#define __RK312x_CODEC_H__
+
+
+
+/* codec register */
+#define RK312x_CODEC_BASE                      (0x0)
+
+#define RK312x_RESET                           (RK312x_CODEC_BASE + 0x00)
+#define RK312x_ADC_INT_CTL1                    (RK312x_CODEC_BASE + 0x08)
+#define RK312x_ADC_INT_CTL2                    (RK312x_CODEC_BASE + 0x0c)
+#define RK312x_DAC_INT_CTL1                    (RK312x_CODEC_BASE + 0x10)
+#define RK312x_DAC_INT_CTL2                    (RK312x_CODEC_BASE + 0x14)
+#define RK312x_DAC_INT_CTL3                    (RK312x_CODEC_BASE + 0x18)
+#define RK312x_ADC_MIC_CTL                     (RK312x_CODEC_BASE + 0x88)
+#define RK312x_BST_CTL                         (RK312x_CODEC_BASE + 0x8c)
+#define RK312x_ALC_MUNIN_CTL                   (RK312x_CODEC_BASE + 0x90)
+#define RK312x_BSTL_ALCL_CTL                   (RK312x_CODEC_BASE + 0x94)
+#define RK312x_ALCR_GAIN_CTL                   (RK312x_CODEC_BASE + 0x98)
+#define RK312x_ADC_ENABLE                      (RK312x_CODEC_BASE + 0x9c)
+#define RK312x_DAC_CTL                         (RK312x_CODEC_BASE + 0xa0)
+#define RK312x_DAC_ENABLE                      (RK312x_CODEC_BASE + 0xa4)
+#define RK312x_HPMIX_CTL                       (RK312x_CODEC_BASE + 0xa8)
+#define RK312x_HPMIX_S_SELECT                  (RK312x_CODEC_BASE + 0xac)
+#define RK312x_HPOUT_CTL                       (RK312x_CODEC_BASE + 0xB0)
+#define RK312x_HPOUTL_GAIN                     (RK312x_CODEC_BASE + 0xB4)
+#define RK312x_HPOUTR_GAIN                     (RK312x_CODEC_BASE + 0xB8)
+#define RK312x_SELECT_CURRENT                  (RK312x_CODEC_BASE + 0xBC)
+#define RK312x_PGAL_AGC_CTL1                   (RK312x_CODEC_BASE + 0x100)
+#define RK312x_PGAL_AGC_CTL2                   (RK312x_CODEC_BASE + 0x104)
+#define RK312x_PGAL_AGC_CTL3                   (RK312x_CODEC_BASE + 0x108)
+#define RK312x_PGAL_AGC_CTL4                   (RK312x_CODEC_BASE + 0x10c)
+#define RK312x_PGAL_ASR_CTL                    (RK312x_CODEC_BASE + 0x110)
+#define RK312x_PGAL_AGC_MAX_H                  (RK312x_CODEC_BASE + 0x114)
+#define RK312x_PGAL_AGC_MAX_L                  (RK312x_CODEC_BASE + 0x118)
+#define RK312x_PGAL_AGC_MIN_H                  (RK312x_CODEC_BASE + 0x11c)
+#define RK312x_PGAL_AGC_MIN_L                  (RK312x_CODEC_BASE + 0x120)
+#define RK312x_PGAL_AGC_CTL5                   (RK312x_CODEC_BASE + 0x124)
+#define RK312x_PGAR_AGC_CTL1                   (RK312x_CODEC_BASE + 0x140)
+#define RK312x_PGAR_AGC_CTL2                   (RK312x_CODEC_BASE + 0x144)
+#define RK312x_PGAR_AGC_CTL3                   (RK312x_CODEC_BASE + 0x148)
+#define RK312x_PGAR_AGC_CTL4                   (RK312x_CODEC_BASE + 0x14c)
+#define RK312x_PGAR_ASR_CTL                    (RK312x_CODEC_BASE + 0x150)
+#define RK312x_PGAR_AGC_MAX_H                  (RK312x_CODEC_BASE + 0x154)
+#define RK312x_PGAR_AGC_MAX_L                  (RK312x_CODEC_BASE + 0x158)
+#define RK312x_PGAR_AGC_MIN_H                  (RK312x_CODEC_BASE + 0x15c)
+#define RK312x_PGAR_AGC_MIN_L                  (RK312x_CODEC_BASE + 0x160)
+#define RK312x_PGAR_AGC_CTL5                   (RK312x_CODEC_BASE + 0x164)
+
+/* ADC Interface Control 1 (0x08) */
+#define RK312x_ALRCK_POL_MASK                  (0x1 << 7)
+#define RK312x_ALRCK_POL_SFT                   7
+#define RK312x_ALRCK_POL_EN                    (0x1 << 7)
+#define RK312x_ALRCK_POL_DIS                   (0x0 << 7)
+
+#define RK312x_ADC_VWL_MASK                    (0x3 << 5)
+#define RK312x_ADC_VWL_SFT                     5
+#define RK312x_ADC_VWL_32                      (0x3 << 5)
+#define RK312x_ADC_VWL_24                      (0x2 << 5)
+#define RK312x_ADC_VWL_20                      (0x1 << 5)
+#define RK312x_ADC_VWL_16                      (0x0 << 5)
+
+#define RK312x_ADC_DF_MASK                     (0x3 << 3)
+#define RK312x_ADC_DF_SFT                      3
+#define RK312x_ADC_DF_PCM                      (0x3 << 3)
+#define RK312x_ADC_DF_I2S                      (0x2 << 3)
+#define RK312x_ADC_DF_LJ                               (0x1 << 3)
+#define RK312x_ADC_DF_RJ                               (0x0 << 3)
+
+#define RK312x_ADC_SWAP_MASK                   (0x1 << 1)
+#define RK312x_ADC_SWAP_SFT                    1
+#define RK312x_ADC_SWAP_EN                     (0x1 << 1)
+#define RK312x_ADC_SWAP_DIS                    (0x0 << 1)
+
+#define RK312x_ADC_TYPE_MASK                   0x1
+#define RK312x_ADC_TYPE_SFT                    0
+#define RK312x_ADC_TYPE_MONO                   0x1
+#define RK312x_ADC_TYPE_STEREO                 0x0
+
+/* ADC Interface Control 2 (0x0c) */
+#define RK312x_I2S_MODE_MASK                   (0x1 << 4)
+#define RK312x_I2S_MODE_SFT                    (4)
+#define RK312x_I2S_MODE_MST                    (0x1 << 4)
+#define RK312x_I2S_MODE_SLV                    (0x0 << 4)
+
+#define RK312x_ADC_WL_MASK                     (0x3 << 2)
+#define RK312x_ADC_WL_SFT                      (2)
+#define RK312x_ADC_WL_32                               (0x3 << 2)
+#define RK312x_ADC_WL_24                               (0x2 << 2)
+#define RK312x_ADC_WL_20                               (0x1 << 2)
+#define RK312x_ADC_WL_16                               (0x0 << 2)
+
+#define RK312x_ADC_RST_MASK                    (0x1 << 1)
+#define RK312x_ADC_RST_SFT                     (1)
+#define RK312x_ADC_RST_DIS                     (0x1 << 1)
+#define RK312x_ADC_RST_EN                      (0x0 << 1)
+
+#define RK312x_ABCLK_POL_MASK                  0x1
+#define RK312x_ABCLK_POL_SFT                   0
+#define RK312x_ABCLK_POL_EN                    0x1
+#define RK312x_ABCLK_POL_DIS                   0x0
+
+/* DAC Interface Control 1 (0x10) */
+#define RK312x_DLRCK_POL_MASK                  (0x1 << 7)
+#define RK312x_DLRCK_POL_SFT                   7
+#define RK312x_DLRCK_POL_EN                    (0x1 << 7)
+#define RK312x_DLRCK_POL_DIS                   (0x0 << 7)
+
+#define RK312x_DAC_VWL_MASK                    (0x3 << 5)
+#define RK312x_DAC_VWL_SFT                     5
+#define RK312x_DAC_VWL_32                      (0x3 << 5)
+#define RK312x_DAC_VWL_24                      (0x2 << 5)
+#define RK312x_DAC_VWL_20                      (0x1 << 5)
+#define RK312x_DAC_VWL_16                      (0x0 << 5)
+
+#define RK312x_DAC_DF_MASK                     (0x3 << 3)
+#define RK312x_DAC_DF_SFT                      3
+#define RK312x_DAC_DF_PCM                      (0x3 << 3)
+#define RK312x_DAC_DF_I2S                      (0x2 << 3)
+#define RK312x_DAC_DF_LJ                               (0x1 << 3)
+#define RK312x_DAC_DF_RJ                               (0x0 << 3)
+
+#define RK312x_DAC_SWAP_MASK                   (0x1 << 2)
+#define RK312x_DAC_SWAP_SFT                    2
+#define RK312x_DAC_SWAP_EN                     (0x1 << 2)
+#define RK312x_DAC_SWAP_DIS                    (0x0 << 2)
+
+/* DAC Interface Control 2 (0x14) */
+#define RK312x_DAC_WL_MASK                     (0x3 << 2)
+#define RK312x_DAC_WL_SFT                      2
+#define RK312x_DAC_WL_32                               (0x3 << 2)
+#define RK312x_DAC_WL_24                               (0x2 << 2)
+#define RK312x_DAC_WL_20                               (0x1 << 2)
+#define RK312x_DAC_WL_16                               (0x0 << 2)
+
+#define RK312x_DAC_RST_MASK                    (0x1 << 1)
+#define RK312x_DAC_RST_SFT                     1
+#define RK312x_DAC_RST_DIS                     (0x1 << 1)
+#define RK312x_DAC_RST_EN                      (0x0 << 1)
+
+#define RK312x_DBCLK_POL_MASK                  0x1
+#define RK312x_DBCLK_POL_SFT                   0
+#define RK312x_DBCLK_POL_EN                    0x1
+#define RK312x_DBCLK_POL_DIS                   0x0
+
+/* ADC & MICBIAS (0x88) */
+#define  RK312x_ADC_CURRENT_ENABLE              (0x1 << 7)
+#define  RK312x_ADC_CURRENT_DISABLE             (0x0 << 7)
+
+#define  RK312x_MICBIAS_VOL_ENABLE              (6)
+
+#define  RK312x_ADCL_ZERO_DET_EN_SFT                (5)
+#define  RK312x_ADCL_ZERO_DET_EN                (0x1 << 5)
+#define  RK312x_ADCL_ZERO_DET_DIS               (0x0 << 5)
+
+#define  RK312x_ADCR_ZERO_DET_EN_SFT                (4)
+#define  RK312x_ADCR_ZERO_DET_EN                (0x1 << 4)
+#define  RK312x_ADCR_ZERO_DET_DIS               (0x0 << 4)
+
+#define  RK312x_MICBIAS_VOL_SHT                  0
+#define  RK312x_MICBIAS_VOL_MSK                  7
+#define  RK312x_MICBIAS_VOL_MIN                  (0x0 << 0)
+#define  RK312x_MICBIAS_VOL_MAX                  (0x7 << 0)
+
+/* BST_L  BST_R  CONTROL (0x8C)  */
+#define  RK312x_BSTL_PWRD_SFT              (6)
+#define  RK312x_BSTL_EN                     (0x1 << 6)
+#define  RK312x_BSTL_DIS                    (0x0 << 6)
+#define  RK312x_BSTL_GAIN_SHT               (5)
+#define  RK312x_BSTL_GAIN_20                (0x1 << 5)
+#define  RK312x_BSTL_GAIN_0                 (0x0 << 5)
+#define  RK312x_BSTL_MUTE_SHT               (4)
+
+#define  RK312x_BSTR_PWRD_SFT              (2)
+#define  RK312x_BSTR_EN                     (0x1 << 2)
+#define  RK312x_BSTR_DIS                    (0x0 << 2)
+#define  RK312x_BSTR_GAIN_SHT               (1)
+#define  RK312x_BSTR_GAIN_20                (0x1 << 1)
+#define  RK312x_BSTR_GAIN_0                 (0x0 << 1)
+#define  RK312x_BSTR_MUTE_SHT                 (0)
+
+
+/* MUXINL ALCL MUXINR ALCR  (0x90)  */
+#define  RK312x_MUXINL_F_SHT              (6)
+#define  RK312x_MUXINL_F_MSK              (0x03 << 6)
+#define  RK312x_MUXINL_F_INL                (0x02 << 6)
+#define  RK312x_MUXINL_F_BSTL               (0x01 << 6)
+#define  RK312x_ALCL_PWR_SHT                     (5)
+#define  RK312x_ALCL_EN                     (0x1 << 5)
+#define  RK312x_ALCL_DIS                    (0x0 << 5)
+#define  RK312x_ALCL_MUTE_SHT                (4)
+#define  RK312x_MUXINR_F_SHT              (2)
+#define  RK312x_MUXINR_F_MSK              (0x03 << 2)
+#define  RK312x_MUXINR_F_INR                (0x02 << 2)
+#define  RK312x_MUXINR_F_BSTR               (0x01 << 2)
+#define  RK312x_ALCR_PWR_SHT                     (1)
+#define  RK312x_ALCR_EN                     (0x1 << 1)
+#define  RK312x_ALCR_DIS                    (0x0 << 1)
+#define  RK312x_ALCR_MUTE_SHT                (0)
+
+/* BST_L MODE & ALC_L GAIN (0x94) */
+#define  RK312x_BSTL_MODE_SFT          (5)
+#define  RK312x_BSTL_MODE_SINGLE        (0x1 << 5)
+#define  RK312x_BSTL_MODE_DIFF          (0x0 << 5)
+
+#define  RK312x_ALCL_GAIN_SHT               (0)
+#define  RK312x_ALCL_GAIN_MSK               (0x1f)
+
+/* ALC_R GAIN (0x98) */
+#define  RK312x_ALCR_GAIN_SHT               (0)
+#define  RK312x_ALCR_GAIN_MSK               (0x1f)
+
+/* ADC control (0x9C) */
+#define RK312x_ADCL_REF_VOL_EN_SFT                     (3)
+#define RK312x_ADCL_REF_VOL_EN                 (0x1 << 7)
+#define RK312x_ADCL_REF_VOL_DIS                        (0x0 << 7)
+
+#define RK312x_ADCL_CLK_EN_SFT                (6)
+#define RK312x_ADCL_CLK_EN                    (0x1 << 6)
+#define RK312x_ADCL_CLK_DIS                   (0x0 << 6)
+
+#define RK312x_ADCL_AMP_EN_SFT                 (5)
+#define RK312x_ADCL_AMP_EN                     (0x1 << 5)
+#define RK312x_ADCL_AMP_DIS                    (0x0 << 5)
+
+#define  RK312x_ADCL_RST_EN                     (0x1 << 4)
+#define  RK312x_ADCL_RST_DIS                     (0x0 << 4)
+
+#define RK312x_ADCR_REF_VOL_EN_SFT                     (3)
+#define RK312x_ADCR_REF_VOL_EN                 (0x1 << 3)
+#define RK312x_ADCR_REF_VOL_DIS                        (0x0 << 3)
+
+#define RK312x_ADCR_CLK_EN_SFT                (2)
+#define RK312x_ADCR_CLK_EN                    (0x1 << 2)
+#define RK312x_ADCR_CLK_DIS                   (0x0 << 2)
+
+#define RK312x_ADCR_AMP_EN_SFT                 (1)
+#define RK312x_ADCR_AMP_EN                     (0x1 << 1)
+#define RK312x_ADCR_AMP_DIS                    (0x0 << 1)
+
+#define  RK312x_ADCR_RST_EN                     (0x1 << 0)
+#define  RK312x_ADCR_RST_DIS                     (0x0 << 0)
+
+/* DAC & VOUT Control (0xa0)  */
+#define  RK312x_CURRENT_EN                  (0x1 << 6)
+#define  RK312x_CURRENT_DIS                  (0x0 << 6)
+#define  RK312x_REF_VOL_DACL_EN_SFT                  (5)
+#define  RK312x_REF_VOL_DACL_EN                  (0x1 << 5)
+#define  RK312x_REF_VOL_DACL_DIS                 (0x0 << 5)
+#define  RK312x_ZO_DET_VOUTL_SFT                 (4)
+#define  RK312x_ZO_DET_VOUTL_EN                 (0x1 << 4)
+#define  RK312x_ZO_DET_VOUTL_DIS                  (0x0 << 4)
+#define  RK312x_DET_ERAPHONE_DIS                  (0x0 << 3)
+#define  RK312x_DET_ERAPHONE_EN                  (0x1 << 3)
+#define  RK312x_REF_VOL_DACR_EN_SFT                  (1)
+#define  RK312x_REF_VOL_DACR_EN                  (0x1 << 1)
+#define  RK312x_REF_VOL_DACR_DIS                 (0x0 << 1)
+#define  RK312x_ZO_DET_VOUTR_SFT                 (0)
+#define  RK312x_ZO_DET_VOUTR_EN                 (0x1 << 0)
+#define  RK312x_ZO_DET_VOUTR_DIS                  (0x0 << 0)
+
+/* DAC control (0xa4) */
+#define RK312x_DACL_REF_VOL_EN_SFT                     (7)
+#define RK312x_DACL_REF_VOL_EN                 (0x1 << 7)
+#define RK312x_DACL_REF_VOL_DIS                        (0x0 << 7)
+
+#define RK312x_DACL_CLK_EN                    (0x1 << 6)
+#define RK312x_DACL_CLK_DIS                   (0x0 << 6)
+
+#define RK312x_DACL_EN                 (0x1 << 5)
+#define RK312x_DACL_DIS                        (0x0 << 5)
+
+#define  RK312x_DACL_INIT                     (0x0 << 4)
+#define  RK312x_DACL_WORK                    (0x1 << 4)
+
+#define RK312x_DACR_REF_VOL_EN_SFT                     (3)
+#define RK312x_DACR_REF_VOL_EN                 (0x1 << 3)
+#define RK312x_DACR_REF_VOL_DIS                        (0x0 << 3)
+
+#define RK312x_DACR_CLK_EN                    (0x1 << 2)
+#define RK312x_DACR_CLK_DIS                   (0x0 << 2)
+
+#define RK312x_DACR_EN                 (0x1 << 1)
+#define RK312x_DACR_DIS                        (0x0 << 1)
+
+#define  RK312x_DACR_INIT                        (0x0 << 0)
+#define  RK312x_DACR_WORK                    (0x1 << 0)
+
+/* HPMIXL  HPMIXR Control (0xa8)  */
+#define RK312x_HPMIXL_SFT                         (6)
+#define RK312x_HPMIXL_EN                         (0x1 << 6)
+#define RK312x_HPMIXL_DIS                      (0x0 << 6)
+#define RK312x_HPMIXL_INIT1              (0x0 << 5)
+#define RK312x_HPMIXL_WORK1               (0x1 << 5)
+#define RK312x_HPMIXL_INIT2              (0x0 << 4)
+#define RK312x_HPMIXL_WORK2                (0x1 << 4)
+#define RK312x_HPMIXR_SFT                         (2)
+#define RK312x_HPMIXR_EN                         (0x1 << 2)
+#define RK312x_HPMIXR_DIS                      (0x0 << 2)
+#define RK312x_HPMIXR_INIT1               (0x0 << 1)
+#define RK312x_HPMIXR_WORK1               (0x1 << 1)
+#define RK312x_HPMIXR_INIT2              (0x0 << 0)
+#define RK312x_HPMIXR_WORK2                (0x1 << 0)
+
+/* HPMIXL Control  (0xac) */
+#define RK312x_HPMIXL_BYPASS_SFT             (7)
+#define RK312x_HPMIXL_SEL_ALCL_SFT              (6)
+#define RK312x_HPMIXL_SEL_ALCR_SFT              (5)
+#define RK312x_HPMIXL_SEL_DACL_SFT             (4)
+#define RK312x_HPMIXR_BYPASS_SFT             (3)
+#define RK312x_HPMIXR_SEL_ALCL_SFT              (2)
+#define RK312x_HPMIXR_SEL_ALCR_SFT              (1)
+#define RK312x_HPMIXR_SEL_DACR_SFT             (0)
+
+/* HPOUT Control  (0xb0) */
+#define RK312x_HPOUTL_PWR_SHT                  (7)
+#define RK312x_HPOUTL_MSK                      (0x1 << 7)
+#define RK312x_HPOUTL_EN                       (0x1 << 7)
+#define RK312x_HPOUTL_DIS                      (0x0 << 7)
+#define RK312x_HPOUTL_INIT_MSK                 (0x1 << 6)
+#define RK312x_HPOUTL_INIT                     (0x0 << 6)
+#define RK312x_HPOUTL_WORK                     (0x1 << 6)
+#define RK312x_HPOUTL_MUTE_SHT                 (5)
+#define RK312x_HPOUTL_MUTE_MSK                 (0x1 << 5)
+#define RK312x_HPOUTL_MUTE_EN                  (0x0 << 5)
+#define RK312x_HPOUTL_MUTE_DIS                 (0x1 << 5)
+#define RK312x_HPOUTR_PWR_SHT                  (4)
+#define RK312x_HPOUTR_MSK                      (0x1 << 4)
+#define RK312x_HPOUTR_EN                       (0x1 << 4)
+#define RK312x_HPOUTR_DIS                      (0x0 << 4)
+#define RK312x_HPOUTR_INIT_MSK                 (0x1 << 3)
+#define RK312x_HPOUTR_WORK                     (0x1 << 3)
+#define RK312x_HPOUTR_INIT                     (0x0 << 3)
+#define RK312x_HPOUTR_MUTE_SHT                 (2)
+#define RK312x_HPOUTR_MUTE_MSK                 (0x1 << 2)
+#define RK312x_HPOUTR_MUTE_EN                  (0x0 << 2)
+#define RK312x_HPOUTR_MUTE_DIS                 (0x1 << 2)
+
+#define RK312x_HPVREF_PWR_SHT                  (1)
+#define RK312x_HPVREF_EN                       (0x1 << 1)
+#define RK312x_HPVREF_DIS                      (0x0 << 1)
+#define RK312x_HPVREF_WORK                     (0x1 << 0)
+#define RK312x_HPVREF_INIT                     (0x0 << 0)
+
+/* HPOUT GAIN (0xb4 0xb8) */
+#define  RK312x_HPOUT_GAIN_SFT                 (0)
+
+/* SELECT CURR prechagrge/discharge (0xbc) */
+#define RK312x_PRE_HPOUT                       (0x1 << 5)
+#define RK312x_DIS_HPOUT                       (0x0 << 5)
+#define RK312x_CUR_10UA_EN                     (0x0 << 4)
+#define RK312x_CUR_10UA_DIS                    (0x1 << 4)
+#define RK312x_CUR_I_EN                                (0x0 << 3)
+#define RK312x_CUR_I_DIS                       (0x1 << 3)
+#define RK312x_CUR_2I_EN                       (0x0 << 2)
+#define RK312x_CUR_2I_DIS                      (0x1 << 2)
+#define RK312x_CUR_4I_EN                       (0x0 << 0)
+#define RK312x_CUR_4I_DIS                      (0x3 << 0)
+
+/* PGA AGC control 1 (0xc0 0x100) */
+#define RK312x_PGA_AGC_WAY_MASK                        (0x1 << 6)
+#define RK312x_PGA_AGC_WAY_SFT                 6
+#define RK312x_PGA_AGC_WAY_JACK                        (0x1 << 6)
+#define RK312x_PGA_AGC_WAY_NOR                 (0x0 << 6)
+
+#define RK312x_PGA_AGC_BK_WAY_SFT                      4
+#define RK312x_PGA_AGC_BK_WAY_JACK1            (0x1 << 4)
+#define RK312x_PGA_AGC_BK_WAY_NOR                      (0x0 << 4)
+#define RK312x_PGA_AGC_BK_WAY_JACK2            (0x2 << 4)
+#define RK312x_PGA_AGC_BK_WAY_JACK3            (0x3 << 4)
+
+#define RK312x_PGA_AGC_HOLD_T_MASK             0xf
+#define RK312x_PGA_AGC_HOLD_T_SFT              0
+#define RK312x_PGA_AGC_HOLD_T_1024             0xa
+#define RK312x_PGA_AGC_HOLD_T_512              0x9
+#define RK312x_PGA_AGC_HOLD_T_256              0x8
+#define RK312x_PGA_AGC_HOLD_T_128              0x7
+#define RK312x_PGA_AGC_HOLD_T_64                       0x6
+#define RK312x_PGA_AGC_HOLD_T_32                       0x5
+#define RK312x_PGA_AGC_HOLD_T_16                       0x4
+#define RK312x_PGA_AGC_HOLD_T_8                        0x3
+#define RK312x_PGA_AGC_HOLD_T_4                        0x2
+#define RK312x_PGA_AGC_HOLD_T_2                        0x1
+#define RK312x_PGA_AGC_HOLD_T_0                        0x0
+
+/* PGA AGC control 2 (0xc4 0x104) */
+#define RK312x_PGA_AGC_GRU_T_MASK              (0xf << 4)
+#define RK312x_PGA_AGC_GRU_T_SFT                       4
+#define RK312x_PGA_AGC_GRU_T_512                       (0xa << 4)
+#define RK312x_PGA_AGC_GRU_T_256                       (0x9 << 4)
+#define RK312x_PGA_AGC_GRU_T_128                       (0x8 << 4)
+#define RK312x_PGA_AGC_GRU_T_64                        (0x7 << 4)
+#define RK312x_PGA_AGC_GRU_T_32                        (0x6 << 4)
+#define RK312x_PGA_AGC_GRU_T_16                        (0x5 << 4)
+#define RK312x_PGA_AGC_GRU_T_8                 (0x4 << 4)
+#define RK312x_PGA_AGC_GRU_T_4                 (0x3 << 4)
+#define RK312x_PGA_AGC_GRU_T_2                 (0x2 << 4)
+#define RK312x_PGA_AGC_GRU_T_1                 (0x1 << 4)
+#define RK312x_PGA_AGC_GRU_T_0_5                       (0x0 << 4)
+
+#define RK312x_PGA_AGC_GRD_T_MASK              0xf
+#define RK312x_PGA_AGC_GRD_T_SFT                       0
+#define RK312x_PGA_AGC_GRD_T_128_32            0xa
+#define RK312x_PGA_AGC_GRD_T_64_16             0x9
+#define RK312x_PGA_AGC_GRD_T_32_8              0x8
+#define RK312x_PGA_AGC_GRD_T_16_4              0x7
+#define RK312x_PGA_AGC_GRD_T_8_2                       0x6
+#define RK312x_PGA_AGC_GRD_T_4_1                       0x5
+#define RK312x_PGA_AGC_GRD_T_2_0_512           0x4
+#define RK312x_PGA_AGC_GRD_T_1_0_256           0x3
+#define RK312x_PGA_AGC_GRD_T_0_500_128         0x2
+#define RK312x_PGA_AGC_GRD_T_0_250_64          0x1
+#define RK312x_PGA_AGC_GRD_T_0_125_32          0x0
+
+/* PGA AGC control 3 (0xc8 0x108) */
+#define RK312x_PGA_AGC_MODE_MASK                       (0x1 << 7)
+#define RK312x_PGA_AGC_MODE_SFT                        7
+#define RK312x_PGA_AGC_MODE_LIMIT              (0x1 << 7)
+#define RK312x_PGA_AGC_MODE_NOR                        (0x0 << 7)
+
+#define RK312x_PGA_AGC_ZO_MASK                 (0x1 << 6)
+#define RK312x_PGA_AGC_ZO_SFT                  6
+#define RK312x_PGA_AGC_ZO_EN                   (0x1 << 6)
+#define RK312x_PGA_AGC_ZO_DIS                  (0x0 << 6)
+
+#define RK312x_PGA_AGC_REC_MODE_MASK           (0x1 << 5)
+#define RK312x_PGA_AGC_REC_MODE_SFT            5
+#define RK312x_PGA_AGC_REC_MODE_AC             (0x1 << 5)
+#define RK312x_PGA_AGC_REC_MODE_RN             (0x0 << 5)
+
+#define RK312x_PGA_AGC_FAST_D_MASK             (0x1 << 4)
+#define RK312x_PGA_AGC_FAST_D_SFT              4
+#define RK312x_PGA_AGC_FAST_D_EN                       (0x1 << 4)
+#define RK312x_PGA_AGC_FAST_D_DIS              (0x0 << 4)
+
+#define RK312x_PGA_AGC_NG_MASK                 (0x1 << 3)
+#define RK312x_PGA_AGC_NG_SFT                  3
+#define RK312x_PGA_AGC_NG_EN                   (0x1 << 3)
+#define RK312x_PGA_AGC_NG_DIS                  (0x0 << 3)
+
+#define RK312x_PGA_AGC_NG_THR_MASK             0x7
+#define RK312x_PGA_AGC_NG_THR_SFT              0
+#define RK312x_PGA_AGC_NG_THR_N81DB            0x7
+#define RK312x_PGA_AGC_NG_THR_N75DB            0x6
+#define RK312x_PGA_AGC_NG_THR_N69DB            0x5
+#define RK312x_PGA_AGC_NG_THR_N63DB            0x4
+#define RK312x_PGA_AGC_NG_THR_N57DB            0x3
+#define RK312x_PGA_AGC_NG_THR_N51DB            0x2
+#define RK312x_PGA_AGC_NG_THR_N45DB            0x1
+#define RK312x_PGA_AGC_NG_THR_N39DB            0x0
+
+/* PGA AGC Control 4 (0xcc 0x10c) */
+#define RK312x_PGA_AGC_ZO_MODE_MASK            (0x1 << 5)
+#define RK312x_PGA_AGC_ZO_MODE_SFT             5
+#define RK312x_PGA_AGC_ZO_MODE_UWRC            (0x1 << 5)
+#define RK312x_PGA_AGC_ZO_MODE_UARC            (0x0 << 5)
+
+#define RK312x_PGA_AGC_VOL_MASK                        0x1f
+#define RK312x_PGA_AGC_VOL_SFT                 0
+
+/* PGA ASR Control (0xd0 0x110) */
+#define RK312x_PGA_SLOW_CLK_MASK                       (0x1 << 3)
+#define RK312x_PGA_SLOW_CLK_SFT                        3
+#define RK312x_PGA_SLOW_CLK_EN                 (0x1 << 3)
+#define RK312x_PGA_SLOW_CLK_DIS                        (0x0 << 3)
+
+#define RK312x_PGA_ASR_MASK                    0x7
+#define RK312x_PGA_ASR_SFT                     0
+#define RK312x_PGA_ASR_8KHz                    0x7
+#define RK312x_PGA_ASR_12KHz                   0x6
+#define RK312x_PGA_ASR_16KHz                   0x5
+#define RK312x_PGA_ASR_24KHz                   0x4
+#define RK312x_PGA_ASR_32KHz                   0x3
+#define RK312x_PGA_ASR_441KHz                  0x2
+#define RK312x_PGA_ASR_48KHz                   0x1
+#define RK312x_PGA_ASR_96KHz                   0x0
+
+/* PGA AGC Control 5 (0xe4 0x124) */
+#define RK312x_PGA_AGC_MASK                    (0x1 << 6)
+#define RK312x_PGA_AGC_SFT                     6
+#define RK312x_PGA_AGC_EN                      (0x1 << 6)
+#define RK312x_PGA_AGC_DIS                     (0x0 << 6)
+
+#define RK312x_PGA_AGC_MAX_G_MASK              (0x7 << 3)
+#define RK312x_PGA_AGC_MAX_G_SFT                       3
+#define RK312x_PGA_AGC_MAX_G_28_5DB            (0x7 << 3)
+#define RK312x_PGA_AGC_MAX_G_22_5DB            (0x6 << 3)
+#define RK312x_PGA_AGC_MAX_G_16_5DB            (0x5 << 3)
+#define RK312x_PGA_AGC_MAX_G_10_5DB            (0x4 << 3)
+#define RK312x_PGA_AGC_MAX_G_4_5DB             (0x3 << 3)
+#define RK312x_PGA_AGC_MAX_G_N1_5DB            (0x2 << 3)
+#define RK312x_PGA_AGC_MAX_G_N7_5DB            (0x1 << 3)
+#define RK312x_PGA_AGC_MAX_G_N13_5DB           (0x0 << 3)
+
+#define RK312x_PGA_AGC_MIN_G_MASK              0x7
+#define RK312x_PGA_AGC_MIN_G_SFT                       0
+#define RK312x_PGA_AGC_MIN_G_24DB              0x7
+#define RK312x_PGA_AGC_MIN_G_18DB              0x6
+#define RK312x_PGA_AGC_MIN_G_12DB              0x5
+#define RK312x_PGA_AGC_MIN_G_6DB                       0x4
+#define RK312x_PGA_AGC_MIN_G_0DB                       0x3
+#define RK312x_PGA_AGC_MIN_G_N6DB              0x2
+#define RK312x_PGA_AGC_MIN_G_N12DB             0x1
+#define RK312x_PGA_AGC_MIN_G_N18DB             0x0
+
+enum {
+       RK312x_HIFI,
+       RK312x_VOICE,
+};
+
+enum {
+       RK312x_MONO = 1,
+       RK312x_STEREO,
+};
+
+enum {
+       OFF,
+       RCV,
+       SPK_PATH,
+       HP_PATH,
+       HP_NO_MIC,
+       BT,
+       SPK_HP,
+       RING_SPK,
+       RING_HP,
+       RING_HP_NO_MIC,
+       RING_SPK_HP,
+};
+
+enum {
+       MIC_OFF,
+       Main_Mic,
+       Hands_Free_Mic,
+       BT_Sco_Mic,
+};
+
+struct rk312x_reg_val_typ {
+       unsigned int reg;
+       unsigned int value;
+};
+
+struct rk312x_init_bit_typ {
+       unsigned int reg;
+       unsigned int power_bit;
+       unsigned int init2_bit;
+       unsigned int init1_bit;
+       unsigned int init0_bit;
+};
+
+struct rk312x_codec_pdata {
+       int spk_ctl_gpio;
+       int hp_ctl_gpio;
+       int delay_time;
+};
+
+#endif /* __RK312x_CODEC_H__ */
index 5e08ecadf44fdf3200bd15f56818356341b78219..14236fd321a0b0109b6dc6f0b7654182df3cdf46 100644 (file)
@@ -249,6 +249,15 @@ config SND_RK_SOC_RK3036
          Say Y if you want to add support for SoC audio on rockchip
          with the RK3036 s40.
 
+config SND_RK_SOC_RK312X
+        tristate "SoC I2S Audio support for rockchip - RK312X"
+        depends on SND_RK_SOC
+        select SND_RK_SOC_I2S
+        select SND_SOC_RK312X
+        help
+          Say Y if you want to add support for SoC audio on rockchip
+          with the RK3036 s40.
+
 config SND_RK_SOC_RK610
        tristate "SoC I2S Audio support for rockchip - RK610"
        depends on SND_RK_SOC && MFD_RK610
index 16c7e03069b774ded544cc934e1965ce521d8076..9fbecd9db5e3e3e3663caf7bf91360cdfca1c4c0 100644 (file)
@@ -28,6 +28,7 @@ snd-soc-aic3111-objs := rk_aic3111.o
 snd-soc-wm8988-objs := rk_wm8988.o
 snd-soc-rk1000-objs := rk_rk1000codec.o
 snd-soc-rk3036-objs := rk_rk3036.o
+snd-soc-rk312x-objs := rk_rk312x.o
 snd-soc-wm8994-objs := rk_wm8994.o
 snd-soc-rk610-objs := rk_jetta_codec.o
 snd-soc-rk616-objs := rk_rk616.o
@@ -56,6 +57,7 @@ obj-$(CONFIG_SND_RK_SOC_RT5639) += snd-soc-rt5639.o
 obj-$(CONFIG_SND_RK_SOC_RT5616) += snd-soc-rt5616.o
 obj-$(CONFIG_SND_RK_SOC_RK1000) += snd-soc-rk1000.o
 obj-$(CONFIG_SND_RK_SOC_RK3036) += snd-soc-rk3036.o
+obj-$(CONFIG_SND_RK_SOC_RK312X) += snd-soc-rk312x.o
 obj-$(CONFIG_SND_RK_SOC_CS42L52) += snd-soc-cs42l52.o
 obj-$(CONFIG_SND_RK_SOC_AIC3111) += snd-soc-aic3111.o
 obj-$(CONFIG_SND_RK_SOC_AIC3262) += snd-soc-aic3262.o
diff --git a/sound/soc/rockchip/rk_rk312x.c b/sound/soc/rockchip/rk_rk312x.c
new file mode 100755 (executable)
index 0000000..1ae38cd
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * rk_rk312x.c  --  SoC audio for rockchip
+ *
+ * Driver for rockchip rk312x 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 <linux/of.h>
+#include <linux/of_gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "../codecs/rk312x_codec.h"
+#include "card_info.h"
+#include "rk_pcm.h"
+#include "rk_i2s.h"
+
+#if 1
+#define        DBG(x...)       pr_info("rk_rk312x" x)
+#else
+#define        DBG(x...)
+#endif
+
+static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
+       SND_SOC_DAPM_MIC("Mic Jack", NULL),
+       SND_SOC_DAPM_MIC("Headset Jack", NULL),
+       SND_SOC_DAPM_SPK("Ext Spk", NULL),
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route rk_audio_map[] = {
+       /* Mic Jack --> MIC_IN*/
+       {"Mic Bias", NULL, "Mic Jack"},
+       {"MICP", NULL, "Mic Bias"},
+       {"MICN", NULL, "Mic Bias"},
+
+       /* HP MIC */
+       {"Mic Bias", NULL, "Headset Jack"},
+
+       {"Ext Spk", NULL, "HPOUTR"},
+       {"Ext Spk", NULL, "HPOUTL"},
+
+       {"Headphone Jack", NULL, "HPOUTR"},
+       {"Headphone Jack", NULL, "HPOUTL"},
+};
+
+static const struct snd_kcontrol_new rk_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Mic Jack"),
+       SOC_DAPM_PIN_SWITCH("Headset Jack"),
+       SOC_DAPM_PIN_SWITCH("Ext Spk"),
+       SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+};
+
+static int rk312x_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_codec *codec = rtd->codec;
+       struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+       DBG("Enter::%s----%d\n", __func__, __LINE__);
+
+       mutex_lock(&dapm->card->dapm_mutex);
+
+       snd_soc_dapm_enable_pin(dapm, "Mic Jack");
+       snd_soc_dapm_enable_pin(dapm, "Headset Jack");
+       snd_soc_dapm_enable_pin(dapm, "Ext Spk");
+       snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
+
+       mutex_unlock(&dapm->card->dapm_mutex);
+
+       snd_soc_dapm_sync(dapm);
+
+       return 0;
+}
+
+static int rk_hifi_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, dai_fmt = rtd->dai_link->dai_fmt;
+       int ret;
+
+       DBG("Enter::%s----%d\n", __func__, __LINE__);
+
+       /* set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
+       if (ret < 0) {
+               DBG("%s():failed to set the format for codec side\n",
+                   __func__);
+               return ret;
+       }
+
+       /* set cpu DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt);
+       if (ret < 0) {
+               DBG("%s():failed to set the format for cpu side\n",
+                   __func__);
+               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",
+                   __func__, __LINE__, params_rate(params));
+               return -EINVAL;
+               break;
+       }
+
+       DBG("Enter:%s, %d, rate=%d\n",
+           __func__, __LINE__, params_rate(params));
+
+       /*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("rk_hifi_hw_params:failed to set the sysclk for codec\n");
+               return ret;
+       }
+
+       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);
+       DBG("Enter:%s, %d, pll_out/4/params_rate(params) = %d \n",
+           __func__, __LINE__, (pll_out/4)/params_rate(params));
+
+       return 0;
+}
+
+static int rk_voice_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, dai_fmt = rtd->dai_link->dai_fmt;
+       int ret;
+
+       DBG("Enter::%s----%d\n", __func__, __LINE__);
+
+       /* set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
+       if (ret < 0) {
+               DBG("%s():failed to set the format for codec side\n",
+                   __func__);
+               return ret;
+       }
+
+       /* set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
+                               SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBS_CFS);
+
+       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",
+                   __func__, __LINE__,
+               params_rate(params));
+               return -EINVAL;
+               break;
+       }
+
+       /*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("rk_voice_hw_params:failed to set codec side\n");
+               return ret;
+       }
+
+       ret = snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
+
+       return 0;
+}
+
+static struct snd_soc_ops rk312x_hifi_ops = {
+       .hw_params = rk_hifi_hw_params,
+};
+
+static struct snd_soc_ops rk312x_voice_ops = {
+       .hw_params = rk_voice_hw_params,
+};
+
+static struct snd_soc_dai_link rk_dai[] = {
+       {
+               .name = "RK312X I2S1",
+               .stream_name = "RK312X PCM",
+               .codec_dai_name = "rk312x-hifi",
+               .init = rk312x_init,
+               .ops = &rk312x_hifi_ops,
+       },
+       {
+               .name = "RK312X I2S2",
+               .stream_name = "RK312X PCM",
+               .codec_dai_name = "rk312x-voice",
+               .ops = &rk312x_voice_ops,
+       },
+};
+
+static struct snd_soc_card rockchip_rk312x_snd_card = {
+       .name = "RK_RK312X",
+       .dai_link = rk_dai,
+       .num_links = 2,
+       .controls = rk_controls,
+       .num_controls = ARRAY_SIZE(rk_controls),
+       .dapm_widgets    = rk_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets),
+       .dapm_routes    = rk_audio_map,
+       .num_dapm_routes = ARRAY_SIZE(rk_audio_map),
+};
+
+static int rockchip_rk312x_audio_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct snd_soc_card *card = &rockchip_rk312x_snd_card;
+
+       card->dev = &pdev->dev;
+       ret = rockchip_of_get_sound_card_info(card);
+       if (ret) {
+               DBG("%s() get sound card info failed:%d\n", __func__, ret);
+               return ret;
+       }
+
+       ret = snd_soc_register_card(card);
+       if (ret)
+               DBG("%s() register card failed:%d\n", __func__, ret);
+
+       return ret;
+}
+
+static int rockchip_rk312x_audio_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+       snd_soc_unregister_card(card);
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rockchip_rk312x_of_match[] = {
+       { .compatible = "audio-rk312x", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rockchip_rk312x_of_match);
+#endif /* CONFIG_OF */
+
+static struct platform_driver rockchip_rk312x_audio_driver = {
+       .driver         = {
+               .name   = "audio-rk312x",
+               .owner  = THIS_MODULE,
+               .pm = &snd_soc_pm_ops,
+               .of_match_table = of_match_ptr(rockchip_rk312x_of_match),
+       },
+       .probe          = rockchip_rk312x_audio_probe,
+       .remove         = rockchip_rk312x_audio_remove,
+};
+
+module_platform_driver(rockchip_rk312x_audio_driver);
+
+/* Module information */
+MODULE_AUTHOR("rockchip");
+MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface");
+MODULE_LICENSE("GPL");
old mode 100644 (file)
new mode 100755 (executable)
index 7680946..755c596
@@ -43,7 +43,7 @@
 #include "rk_pcm.h"
 
 #undef  DEBUG_SPDIF
-#define DEBUG_SPDIF 0
+#define DEBUG_SPDIF 1
 
 #if DEBUG_SPDIF
 #define RK_SPDIF_DBG(x...) pr_info("rk_spdif:"x)
@@ -140,6 +140,7 @@ struct rockchip_spdif_info {
        spinlock_t      lock;/*lock parmeter setting.*/
        void __iomem    *regs;
        unsigned long   clk_rate;
+       struct clk      *hclk;
        struct clk      *clk;
        struct snd_dmaengine_dai_dma_data       dma_playback;
 };
@@ -453,6 +454,14 @@ static int spdif_probe(struct platform_device *pdev)
                goto err_;
        }
 
+       /* get spdif clock and init. */
+       spdif->hclk = devm_clk_get(&pdev->dev, "spdif_hclk");
+       if (IS_ERR(spdif->hclk)) {
+               dev_err(&pdev->dev, "Can't retrieve spdif hclock\n");
+               spdif->hclk = NULL;
+       }
+       clk_prepare_enable(spdif->hclk);
+
        /* get spdif clock and init. */
        spdif->clk = devm_clk_get(&pdev->dev, "spdif_mclk");
        if (IS_ERR(spdif->clk)) {
@@ -507,7 +516,7 @@ static int spdif_remove(struct platform_device *pdev)
 
 #ifdef CONFIG_OF
 static const struct of_device_id exynos_spdif_match[] = {
-       { .compatible = "rockchip-spdif"},
+       { .compatible = "rk312x-spdif"},
        {},
 };
 MODULE_DEVICE_TABLE(of, exynos_spdif_match);
@@ -517,7 +526,7 @@ static struct platform_driver rockchip_spdif_driver = {
        .probe  = spdif_probe,
        .remove = spdif_remove,
        .driver = {
-               .name   = "rockchip-spdif",
+               .name   = "rk312x-spdif",
                .owner  = THIS_MODULE,
                .of_match_table = of_match_ptr(exynos_spdif_match),
        },