audio: rk3036 codec driver
authordgl <“dgl@rock-chips.com”>
Mon, 28 Jul 2014 02:07:40 +0000 (10:07 +0800)
committerdgl <“dgl@rock-chips.com”>
Mon, 28 Jul 2014 02:07:40 +0000 (10:07 +0800)
add rk3036 audio codec driver program, include DTS node.

Signed-off-by: dgl@rock-chips.com
Documentation/devicetree/bindings/sound/rockchip-audio.txt [new file with mode: 0644]
arch/arm/boot/dts/rk3036-sdk.dts [changed mode: 0755->0644]
arch/arm/boot/dts/rk3036.dtsi [changed mode: 0755->0644]
arch/arm/configs/rk3036_defconfig
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile [changed mode: 0755->0644]
sound/soc/codecs/rk3036_codec.c [new file with mode: 0644]
sound/soc/codecs/rk3036_codec.h [new file with mode: 0644]
sound/soc/rockchip/Kconfig [changed mode: 0755->0644]
sound/soc/rockchip/Makefile [changed mode: 0755->0644]
sound/soc/rockchip/rk_rk3036.c [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/sound/rockchip-audio.txt b/Documentation/devicetree/bindings/sound/rockchip-audio.txt
new file mode 100644 (file)
index 0000000..a4d728a
--- /dev/null
@@ -0,0 +1,36 @@
+* Rockchip Audio controller
+
+Required SoC Specific Properties:
+
+- compatible : "rk3036-audio"
+- 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 = "rk3036-audio";
+       dais {
+               dai0 {
+                       audio-codec = <&codec>;
+                       i2s-controller = <&i2s>;
+                       format = "i2s";
+                       //continuous-clock;
+                       //bitclock-inversion;
+                       //frame-inversion;
+                       //bitclock-master;
+                       //frame-master;
+               };
+       };
+};
old mode 100755 (executable)
new mode 100644 (file)
index d83cb97..adfbc43
                        };
                };
        };
+
+       rockchip-audio {
+               compatible = "rk3036-audio";
+               dais {
+                       dai0 {
+                               audio-codec = <&codec>;
+                               i2s-controller = <&i2s>;
+                               format = "i2s";
+                               //continuous-clock;
+                               //bitclock-inversion;
+                               //frame-inversion;
+                               //bitclock-master;
+                               //frame-master;
+                       };
+               };
+       };
 };
 
 &uart0{
old mode 100755 (executable)
new mode 100644 (file)
index 30c9557..d9f420b
                //pinctrl-1 = <&i2s_gpio>;
        };
 
+       codec: codec@20030000 {
+               compatible = "rk3036-codec";
+               reg = <0x20030000 0x1000>;
+               spk_ctl_io = <&gpio1 GPIO_A0 0>;
+               pinctrl-names = "default";
+               pinctrl-0 = <&i2s0_gpio>;
+
+               boot_depop = <1>;
+               pa_enable_time = <1000>;
+               clocks = <&clk_gates5 14>;
+               clock-names = "g_pclk_acodec";
+       };
        spdif: spdif@10204000 {
                compatible = "rockchip-spdif";
                reg = <0x10204000 0x1000>;
index 93672bac6c9297905eb924e7f23a09c3f8ef4aa6..d4554a48dccf64302c1593ed8195a4e11cebe914 100644 (file)
@@ -375,8 +375,7 @@ CONFIG_SND_USB_AUDIO=y
 CONFIG_SND_SOC=y
 CONFIG_SND_RK_SOC=y
 CONFIG_SND_RK_SOC_HDMI_I2S=y
-CONFIG_SND_RK_SOC_RT5631=y
-CONFIG_SND_RK_SOC_RT3224=y
+CONFIG_SND_RK_SOC_RK3036=y
 CONFIG_HID_BATTERY_STRENGTH=y
 CONFIG_HIDRAW=y
 CONFIG_UHID=y
index 998c628c479d7cc64cc06e089d6591191506316c..a11ae2b7dcb22dacb0a04c08080d83518c487618 100644 (file)
@@ -314,6 +314,9 @@ config SND_SOC_OMAP_HDMI_CODEC
 config SND_SOC_PCM3008
        tristate
 
+config SND_SOC_RK3036
+       tristate
+
 #Freescale sgtl5000 codec
 config SND_SOC_SGTL5000
        tristate
old mode 100755 (executable)
new mode 100644 (file)
index f02b0c1..7389cb5
@@ -129,12 +129,13 @@ snd-soc-rt5616-objs := rt5616.o
 snd-soc-rt5631-phone-objs := rt5631_phone.o
 snd-soc-rt5625-objs := rt5625.o
 obj-y := rt56xx_ioctl.o
-snd-soc-rt5639-objs := rt5639.o rt5639_ioctl.o 
+snd-soc-rt5639-objs := rt5639.o rt5639_ioctl.o
 snd-soc-rt5640-objs := rt5640.o rt5640-dsp.o rt5640_ioctl.o
 snd-soc-rt3224-objs := rt3261.o rt3261_ioctl.o rt_codec_ioctl.o
 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-rk610-objs := rk610_codec.o
 snd-soc-rk616-objs := rk616_codec.o
 snd-soc-rk2928-objs := rk2928_codec.o
@@ -282,6 +283,7 @@ obj-$(CONFIG_SND_SOC_RT5639)    += snd-soc-rt5639.o
 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_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/rk3036_codec.c b/sound/soc/codecs/rk3036_codec.c
new file mode 100644 (file)
index 0000000..81c84e9
--- /dev/null
@@ -0,0 +1,762 @@
+/*
+ * rk3036_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/dmaengine_pcm.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include "rk3036_codec.h"
+
+#define DEBUGOPEN 0
+
+#if DEBUGOPEN
+#define dbg_codec(level, fmt, arg...) do {     \
+       if (debug >= level)                                     \
+               printk(fmt , ## arg); }                         \
+       while (0)
+
+#else
+#define        dbg_codec(level, fmt, arg...)
+#endif
+
+#define INVALID_GPIO -1
+#define SPK_CTRL_OPEN  1
+#define SPK_CTRL_CLOSE 0
+
+/* volume setting
+ *  0: -39dB
+ *  26: 0dB
+ *  31: 6dB
+ *  Step: 1.5dB
+*/
+#define  OUT_VOLUME    31
+
+/*with capacity or not*/
+#define WITH_CAP
+
+struct rk3036_codec_priv {
+       void __iomem    *regbase;
+       struct snd_soc_codec *codec;
+
+       unsigned int stereo_sysclk;
+       unsigned int rate;
+
+       struct delayed_work codec_delayed_work;
+       int spk_ctl_gpio;
+       int delay_time;
+
+       struct clk      *pclk;
+};
+static struct rk3036_codec_priv *rk3036_priv;
+
+static const unsigned int rk3036_reg_defaults[RK3036_CODEC_REG28+1] = {
+       [RK3036_CODEC_RESET] = 0x03,
+       [RK3036_CODEC_REG03] = 0x00,
+       [RK3036_CODEC_REG04] = 0x50,
+       [RK3036_CODEC_REG05] = 0x0E,
+       [RK3036_CODEC_REG22] = 0x00,
+       [RK3036_CODEC_REG23] = 0x00,
+       [RK3036_CODEC_REG24] = 0x00,
+       [RK3036_CODEC_REG25] = 0x00,
+       [RK3036_CODEC_REG26] = 0x00,
+       [RK3036_CODEC_REG27] = 0x05,
+       [RK3036_CODEC_REG28] = 0x00,
+};
+
+/* function declare: */
+static int rk3036_codec_register(
+       struct snd_soc_codec *codec, unsigned int reg);
+static int rk3036_volatile_register(
+       struct snd_soc_codec *codec, unsigned int reg);
+static int rk3036_set_bias_level(
+       struct snd_soc_codec *codec, enum snd_soc_bias_level level);
+static unsigned int rk3036_codec_read(
+       struct snd_soc_codec *codec, unsigned int reg);
+static inline void rk3036_write_reg_cache(
+       struct snd_soc_codec *codec, unsigned int reg, unsigned int value);
+
+static inline unsigned int rk3036_read_reg_cache(struct snd_soc_codec *
+       codec, unsigned int reg)
+{
+       unsigned int *cache = codec->reg_cache;
+
+       if (rk3036_codec_register(codec, reg))
+               return  cache[reg];
+
+       dbg_codec(2, "%s : reg error!\n", __func__);
+
+       return -EINVAL;
+}
+
+static inline void rk3036_write_reg_cache(struct snd_soc_codec *
+       codec, unsigned int reg, unsigned int value)
+{
+       unsigned int *cache = codec->reg_cache;
+
+       if (rk3036_codec_register(codec, reg)) {
+               cache[reg] = value;
+               return;
+       }
+
+       dbg_codec(2, "%s : reg error!\n", __func__);
+}
+
+static int rk3036_reset(struct snd_soc_codec *codec)
+{
+       writel(0x00, rk3036_priv->regbase+RK3036_CODEC_RESET);
+       mdelay(10);
+       writel(0x03, rk3036_priv->regbase+RK3036_CODEC_RESET);
+       mdelay(10);
+       memcpy(codec->
+               reg_cache, rk3036_reg_defaults,
+               sizeof(rk3036_reg_defaults));
+       return 0;
+}
+
+static int rk3036_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 rk3036_codec_priv *rk3036 = rk3036_priv;
+       unsigned int dac_aif1 = 0, dac_aif2  = 0;
+
+       if (!rk3036) {
+               dbg_codec(2, "%s : rk3036 is NULL\n", __func__);
+               return -EINVAL;
+       }
+       dac_aif2 |= RK3036_CR05_FRAMEH_32BITS;
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               dac_aif1 |= RK3036_CR04_HFVALID_16BITS;
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               dac_aif1 |= RK3036_CR04_HFVALID_20BITS;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               dac_aif1 |= RK3036_CR04_HFVALID_24BITS;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               dac_aif1 |= RK3036_CR04_HFVALID_32BITS;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       dac_aif1 |= RK3036_CR04_LR_SWAP_DIS;
+       dac_aif2 |= RK3036_CR05_DAC_RESET_DIS;
+
+       snd_soc_update_bits(
+               codec, RK3036_CODEC_REG04,
+               RK3036_CR04_HFVALID_MASK
+               |RK3036_CR04_LR_SWAP_MASK, dac_aif1);
+       snd_soc_update_bits(
+               codec, RK3036_CODEC_REG05,
+               RK3036_CR05_FRAMEH_MASK
+               |RK3036_CR05_DAC_RESET_MASK, dac_aif2);
+
+       return 0;
+}
+
+static int rk3036_set_dai_fmt(struct snd_soc_dai *codec_dai,
+                             unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       unsigned int fmt_ms = 0,  dac_aif1 = 0, dac_aif2 = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               fmt_ms |= RK3036_CR03_DIRECTION_IN|RK3036_CR03_I2SMODE_SLAVE;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               fmt_ms |= RK3036_CR03_DIRECTION_IOUT|RK3036_CR03_I2SMODE_MASTER;
+               break;
+       default:
+               dbg_codec(2, "%s : set master mask failed!\n", __func__);
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_A:
+               dac_aif1 |= RK3036_CR04_MODE_PCM;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               dac_aif1 |= RK3036_CR04_MODE_I2S;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               dac_aif1 |= RK3036_CR04_MODE_RIGHT;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               dac_aif1 |= RK3036_CR04_MODE_LEFT;
+               break;
+       default:
+               dbg_codec(2, "%s : set format failed!\n", __func__);
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               dac_aif1 |= RK3036_CR04_I2SLRC_NORMAL;
+               dac_aif2 |= RK3036_CR05_BCLKPOL_NORMAL;
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               dac_aif1 |= RK3036_CR04_I2SLRC_REVERSAL;
+               dac_aif2 |= RK3036_CR05_BCLKPOL_REVERSAL;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               dac_aif1 |= RK3036_CR04_I2SLRC_REVERSAL;
+               dac_aif2 |= RK3036_CR05_BCLKPOL_NORMAL;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               dac_aif1 |= RK3036_CR04_I2SLRC_NORMAL;
+               dac_aif2 |= RK3036_CR05_BCLKPOL_REVERSAL;
+               break;
+       default:
+               dbg_codec(2, "%s : set dai format failed!\n", __func__);
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(
+               codec, RK3036_CODEC_REG03,
+               RK3036_CR03_DIRECTION_MASK
+               |RK3036_CR03_I2SMODE_MASK, fmt_ms);
+       snd_soc_update_bits(
+               codec, RK3036_CODEC_REG04,
+               RK3036_CR04_I2SLRC_MASK
+               | RK3036_CR04_MODE_MASK, dac_aif1);
+       snd_soc_update_bits(
+               codec, RK3036_CODEC_REG05,
+               RK3036_CR05_BCLKPOL_MASK, dac_aif2);
+       return 0;
+}
+
+static int rk3036_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+                                int clk_id, unsigned int freq, int dir)
+{
+       struct rk3036_codec_priv *rk3036 = rk3036_priv;
+
+       if (!rk3036) {
+               dbg_codec(2, "%s : rk3036 is NULL\n", __func__);
+               return -EINVAL;
+       }
+
+       rk3036->stereo_sysclk = freq;
+
+       return 0;
+}
+
+static int rk3036_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       unsigned int val;
+
+       if (mute) {
+               val = snd_soc_read(codec, RK3036_CODEC_REG27);
+               if (val & (RK3036_CR27_HPOUTL_G_WORK
+                       |RK3036_CR27_HPOUTR_G_WORK)) {
+                       val &= ~RK3036_CR27_HPOUTL_G_WORK;
+                       val &= ~RK3036_CR27_HPOUTR_G_WORK;
+                       snd_soc_write(codec, RK3036_CODEC_REG27, val);
+               }
+       } else {
+               val = snd_soc_read(codec, RK3036_CODEC_REG27);
+               if ((val | ~RK3036_CR27_HPOUTL_G_WORK) || (val
+                       | ~RK3036_CR27_HPOUTR_G_WORK)) {
+                       val |= RK3036_CR27_HPOUTL_G_WORK
+                               |RK3036_CR27_HPOUTR_G_WORK;
+                       snd_soc_write(codec, RK3036_CODEC_REG27, val);
+               }
+       }
+       return 0;
+}
+
+static struct rk3036_reg_val_typ playback_power_up_list[] = {
+       /*01*/{RK3036_CODEC_REG24,
+               RK3036_CR24_DAC_SOURCE_WORK},
+       /* open current source. */
+
+       /*02*/{RK3036_CODEC_REG22,
+       RK3036_CR22_DACL_PATH_REFV_WORK|RK3036_CR22_DACR_PATH_REFV_WORK},
+       /* power on dac path reference voltage. */
+
+       /*03*/{RK3036_CODEC_REG27,
+       RK3036_CR27_HPOUTL_POP_WORK|RK3036_CR27_HPOUTR_POP_WORK},
+       /* pop precharge work. */
+
+       /*04*/{RK3036_CODEC_REG23,
+       RK3036_CR23_HPOUTL_EN_WORK|RK3036_CR23_HPOUTR_EN_WORK},
+       /* start-up HPOUTL HPOUTR */
+
+       /*05*/{RK3036_CODEC_REG23,
+       RK3036_CR23_HPOUTL_EN_WORK|RK3036_CR23_HPOUTR_EN_WORK
+       |RK3036_CR23_HPOUTL_WORK|RK3036_CR23_HPOUTR_WORK},
+       /* end the init state of HPOUTL HPOUTR */
+
+       /*06*/{RK3036_CODEC_REG24,
+       RK3036_CR24_DAC_SOURCE_WORK
+       |RK3036_CR24_DAC_PRECHARGE
+       |RK3036_CR24_DACL_REFV_WORK|RK3036_CR24_DACR_REFV_WORK},
+       /* start-up special ref_v of DACL DACR */
+
+       /*07*/{RK3036_CODEC_REG22,
+       RK3036_CR22_DACL_PATH_REFV_WORK|RK3036_CR22_DACR_PATH_REFV_WORK
+       |RK3036_CR22_DACL_CLK_WORK|RK3036_CR22_DACR_CLK_WORK},
+       /* start-up clock modul of LR channel */
+
+       /*08*/{RK3036_CODEC_REG22,
+       RK3036_CR22_DACL_PATH_REFV_WORK|RK3036_CR22_DACR_PATH_REFV_WORK
+       |RK3036_CR22_DACL_CLK_WORK|RK3036_CR22_DACR_CLK_WORK
+       |RK3036_CR22_DACL_WORK|RK3036_CR22_DACR_WORK},
+       /* start-up DACL DACR module */
+
+       /*09*/{RK3036_CODEC_REG27,
+       RK3036_CR27_HPOUTL_POP_WORK|RK3036_CR27_HPOUTR_POP_WORK
+       |RK3036_CR27_DACL_WORK|RK3036_CR27_DACR_WORK},
+       /* end the init state of DACL DACR */
+
+       /*10*/{RK3036_CODEC_REG25, OUT_VOLUME},
+       /*11*/{RK3036_CODEC_REG26, OUT_VOLUME},
+
+       /*12*/{RK3036_CODEC_REG24,
+       RK3036_CR24_DAC_SOURCE_WORK
+       |RK3036_CR24_DAC_PRECHARGE
+       |RK3036_CR24_DACL_REFV_WORK|RK3036_CR24_DACR_REFV_WORK
+       |RK3036_CR24_VOUTL_ZEROD_WORK|RK3036_CR24_VOUTR_ZEROD_WORK},
+       /* according to the need, open the zero-crossing detection function. */
+
+       /*13*/{RK3036_CODEC_REG27,
+       RK3036_CR27_HPOUTL_POP_WORK|RK3036_CR27_HPOUTR_POP_WORK
+       |RK3036_CR27_DACL_WORK|RK3036_CR27_DACR_WORK
+       |RK3036_CR27_HPOUTL_G_WORK|RK3036_CR27_HPOUTR_G_WORK},
+       /* end initial status of HPOUTL HPOUTR module,start normal output. */
+};
+
+#define PLAYBACK_POWER_UP_LIST_LEN ARRAY_SIZE(playback_power_up_list)
+
+static int rk3036_codec_power_up(void)
+{
+       struct snd_soc_codec *codec = rk3036_priv->codec;
+       int i, volume = 0;
+
+       if (!rk3036_priv || !rk3036_priv->codec)
+               return -EINVAL;
+
+       for (i = 0; i < PLAYBACK_POWER_UP_LIST_LEN; i++) {
+               if ((playback_power_up_list[i].reg ==
+                       RK3036_CODEC_REG25) && (volume < OUT_VOLUME)) {
+                       snd_soc_write(codec, RK3036_CODEC_REG25, volume);
+                       snd_soc_write(codec, RK3036_CODEC_REG26, volume);
+                       volume++;
+                       mdelay(10);
+                       i--;
+               } else {
+                       snd_soc_write(codec, playback_power_up_list[i].
+                               reg, playback_power_up_list[i].value);
+                       mdelay(1);
+               }
+       }
+
+       return 0;
+}
+
+#define RK3036_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 RK3036_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 rk3036_dai_ops = {
+       .hw_params      = rk3036_hw_params,
+       .set_fmt        = rk3036_set_dai_fmt,
+       .set_sysclk     = rk3036_set_dai_sysclk,
+       .digital_mute   = rk3036_digital_mute,
+};
+
+static struct snd_soc_dai_driver rk3036_dai[] = {
+       {
+               .name = "rk3036-voice",
+               .id = RK3036_VOICE,
+               .playback = {
+                       .stream_name = "RK3036 CODEC PCM",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RK3036_PLAYBACK_RATES,
+                       .formats = RK3036_FORMATS,
+               },
+               .ops = &rk3036_dai_ops,
+       },
+};
+
+static unsigned int rk3036_codec_read(struct snd_soc_codec *
+       codec, unsigned int reg)
+{
+       unsigned int value;
+
+       if (!rk3036_priv) {
+               dbg_codec(2, "%s : rk3036 is NULL\n", __func__);
+               return -EINVAL;
+       }
+
+       if (!rk3036_codec_register(codec, reg)) {
+               dbg_codec(2, "%s : reg error!\n", __func__);
+               return -EINVAL;
+       }
+
+       value = readl_relaxed(rk3036_priv->regbase+reg);
+       dbg_codec(2, "%s : reg = 0x%x, val= 0x%x\n", __func__, reg, value);
+
+       return value;
+}
+
+static int rk3036_hw_write(const struct i2c_client *
+       client, const char *buf, int count)
+{
+       unsigned int reg, value;
+
+       if (!rk3036_priv || !rk3036_priv->codec)
+               return -EINVAL;
+
+       if (count == 2) {
+               reg = (unsigned int)buf[0];
+               value = (unsigned int)buf[1];
+               writel(value, rk3036_priv->regbase+reg);
+       } else {
+               dbg_codec(2, "%s : i2c len error\n", __func__);
+       }
+
+       return  count;
+}
+
+static int rk3036_codec_write(struct snd_soc_codec *
+       codec, unsigned int reg, unsigned int value)
+{
+       int new_value = -1;
+
+       if (!rk3036_priv) {
+               dbg_codec(2, "%s : rk3036 is NULL\n", __func__);
+               return -EINVAL;
+       } else if (!rk3036_codec_register(codec, reg)) {
+               dbg_codec(2, "%s : reg error!\n", __func__);
+               return -EINVAL;
+       }
+
+       /*new_value = rk3036_set_init_value(codec, reg, value);*/
+       if (new_value == -1) {
+               writel(value, rk3036_priv->regbase+reg);
+               rk3036_write_reg_cache(codec, reg, value);
+       }
+
+       return 0;
+}
+
+static void spk_ctrl_fun(int status)
+{
+       if (rk3036_priv == NULL)
+               return;
+
+       if (rk3036_priv->spk_ctl_gpio != INVALID_GPIO)
+               gpio_set_value(rk3036_priv->spk_ctl_gpio, status);
+}
+
+static void rk3036_delayedwork_fun(struct work_struct *work)
+{
+       if (rk3036_priv == NULL)
+               return;
+       dbg_codec(2, "rk3036_delayedwork_fun\n");
+       spk_ctrl_fun(SPK_CTRL_OPEN);
+}
+
+static int rk3036_probe(struct snd_soc_codec *codec)
+{
+       struct rk3036_codec_priv *rk3036_codec
+               = snd_soc_codec_get_drvdata(codec);
+       unsigned int val;
+       int ret;
+
+       rk3036_codec->codec = codec;
+
+       clk_prepare_enable(rk3036_codec->pclk);
+
+       ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+       if (ret != 0)
+               goto err__;
+
+       codec->hw_read = rk3036_codec_read;
+       codec->hw_write = (hw_write_t)rk3036_hw_write;
+       codec->read = rk3036_codec_read;
+       codec->write = rk3036_codec_write;
+
+       INIT_DELAYED_WORK(&rk3036_codec->
+               codec_delayed_work, rk3036_delayedwork_fun);
+
+       val = snd_soc_read(codec, RK3036_CODEC_RESET);
+       if (val != rk3036_reg_defaults[RK3036_CODEC_RESET]) {
+               ret = -ENODEV;
+               goto err__;
+       }
+
+       /* config i2s output to acodec module. */
+       val = readl_relaxed(RK_GRF_VIRT + RK3036_GRF_SOC_CON0);
+       writel_relaxed(val | 0x04000400, RK_GRF_VIRT + RK3036_GRF_SOC_CON0);
+
+       rk3036_reset(codec);
+
+       snd_soc_write(codec, RK3036_CODEC_REG24, RK3036_CR24_DAC_SOURCE_STOP
+                                               | RK3036_CR24_DAC_PRECHARGE
+                                               | RK3036_CR24_DACL_REFV_STOP
+                                               | RK3036_CR24_DACR_REFV_STOP
+                                               | RK3036_CR24_VOUTL_ZEROD_STOP
+                                               | RK3036_CR24_VOUTR_ZEROD_STOP);
+#ifdef WITH_CAP
+       /*set for capacity output,clear up noise*/
+       snd_soc_write(codec, RK3036_CODEC_REG28, 0x37);
+       mdelay(50);
+       /*snd_soc_write(codec, 0xbc,0x28);*/
+#endif
+
+       rk3036_codec_power_up();
+
+       schedule_delayed_work(&rk3036_codec->
+               codec_delayed_work, msecs_to_jiffies(5000));
+       codec->dapm.bias_level = SND_SOC_BIAS_PREPARE;
+
+       return 0;
+
+err__:
+       dbg_codec(2, "%s err ret=%d\n", __func__, ret);
+       return ret;
+}
+
+static int rk3036_remove(struct snd_soc_codec *codec)
+{
+       if (!rk3036_priv) {
+               dbg_codec(2, "%s : rk3036_priv is NULL\n", __func__);
+               return 0;
+       }
+
+       spk_ctrl_fun(SPK_CTRL_CLOSE);
+
+       mdelay(10);
+       snd_soc_write(codec, RK3036_CODEC_RESET, 0xfc);
+       mdelay(10);
+       snd_soc_write(codec, RK3036_CODEC_RESET, 0x3);
+       mdelay(10);
+
+       return 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;
+       }
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_rk3036 = {
+       .probe = rk3036_probe,
+       .remove = rk3036_remove,
+       .suspend = rk3036_suspend,
+       .resume = rk3036_resume,
+       .set_bias_level = rk3036_set_bias_level,
+       .reg_cache_size = ARRAY_SIZE(rk3036_reg_defaults),
+       .reg_word_size = sizeof(unsigned int),
+       .reg_cache_default = rk3036_reg_defaults,
+       .volatile_register = rk3036_volatile_register,
+       .readable_register = rk3036_codec_register,
+       .reg_cache_step = sizeof(unsigned int),
+};
+
+static int rk3036_platform_probe(struct platform_device *pdev)
+{
+       struct device_node *rk3036_np = pdev->dev.of_node;
+       struct rk3036_codec_priv *rk3036;
+       struct resource *res;
+       int ret;
+
+       rk3036 = devm_kzalloc(&pdev->dev, sizeof(*rk3036), GFP_KERNEL);
+       if (!rk3036) {
+               dbg_codec(2, "%s : rk3036 priv kzalloc failed!\n", __func__);
+               return -ENOMEM;
+       }
+       rk3036_priv = rk3036;
+       platform_set_drvdata(pdev, rk3036);
+
+       rk3036->spk_ctl_gpio = of_get_named_gpio(rk3036_np, "spk_ctl_io", 0);
+       if (!gpio_is_valid(rk3036->spk_ctl_gpio)) {
+               dbg_codec(2, "invalid reset_gpio: %d\n", rk3036->spk_ctl_gpio);
+               ret = -ENOENT;
+               goto err__;
+       }
+
+       ret = devm_gpio_request(&pdev->dev, rk3036->spk_ctl_gpio, "spk_ctl");
+       if (ret < 0) {
+               dbg_codec(2, "rk3036_platform_probe spk_ctl_gpio fail\n");
+               goto err__;
+       }
+
+       gpio_direction_output(rk3036->spk_ctl_gpio, SPK_CTRL_CLOSE);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       rk3036->regbase = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(rk3036->regbase))
+               return PTR_ERR(rk3036->regbase);
+
+       rk3036->pclk = devm_clk_get(&pdev->dev, "g_pclk_acodec");
+       if (IS_ERR(rk3036->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_rk3036,
+                               rk3036_dai, ARRAY_SIZE(rk3036_dai));
+
+err__:
+       platform_set_drvdata(pdev, NULL);
+       rk3036_priv = NULL;
+       return ret;
+}
+
+static int rk3036_platform_remove(struct platform_device *pdev)
+{
+       rk3036_priv = NULL;
+       snd_soc_unregister_codec(&pdev->dev);
+       return 0;
+}
+
+void rk3036_platform_shutdown(struct platform_device *pdev)
+{
+       if (!rk3036_priv || !rk3036_priv->codec)
+               return;
+
+       spk_ctrl_fun(SPK_CTRL_CLOSE);
+
+       mdelay(10);
+       writel(0xfc, rk3036_priv->regbase+RK3036_CODEC_RESET);
+       mdelay(10);
+       writel(0x03, rk3036_priv->regbase+RK3036_CODEC_RESET);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rk3036codec_of_match[] = {
+               { .compatible = "rk3036-codec"},
+               {},
+};
+MODULE_DEVICE_TABLE(of, rk3036codec_of_match);
+#endif
+
+static struct platform_driver rk3036_codec_driver = {
+       .driver = {
+                  .name = "rk3036-codec",
+                  .owner = THIS_MODULE,
+                  .of_match_table = of_match_ptr(rk3036codec_of_match),
+                  },
+       .probe = rk3036_platform_probe,
+       .remove = rk3036_platform_remove,
+       .shutdown = rk3036_platform_shutdown,
+};
+module_platform_driver(rk3036_codec_driver);
+
+/* Module information */
+MODULE_AUTHOR("rockchip");
+MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rk3036_codec.h b/sound/soc/codecs/rk3036_codec.h
new file mode 100644 (file)
index 0000000..f2bd981
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ *
+ * Copyright (C) 2014 rockchip
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef _RK3036_CODEC_H
+#define _RK3036_CODEC_H
+
+/* codec register */
+#define RK3036_CODEC_RESET                     (0x00 << 2)
+#define RK3036_CODEC_REG03                     (0x03 << 2)
+#define RK3036_CODEC_REG04                     (0x04 << 2)
+#define RK3036_CODEC_REG05                     (0x05 << 2)
+
+#define RK3036_CODEC_REG22                     (0x22 << 2)
+#define RK3036_CODEC_REG23                     (0x23 << 2)
+#define RK3036_CODEC_REG24                     (0x24 << 2)
+#define RK3036_CODEC_REG25                     (0x25 << 2)
+#define RK3036_CODEC_REG26                     (0x26 << 2)
+#define RK3036_CODEC_REG27                     (0x27 << 2)
+#define RK3036_CODEC_REG28                     (0x28 << 2)
+
+/* RK3036_CODEC_RESET */
+#define RK3036_CR00_DIGITAL_RESET           (0 << 1)
+#define RK3036_CR00_DIGITAL_WORK            (1 << 1)
+#define RK3036_CR00_SYSTEM_RESET            (0 << 0)
+#define RK3036_CR00_SYSTEM_WORK             (1 << 0)
+
+/*RK3036_CODEC_REG03*/
+#define RK3036_CR03_DIRECTION_MASK          (1 << 5)
+#define RK3036_CR03_DIRECTION_IN            (0 << 5)
+#define RK3036_CR03_DIRECTION_IOUT          (1 << 5)
+#define RK3036_CR03_I2SMODE_MASK            (1 << 4)
+#define RK3036_CR03_I2SMODE_SLAVE           (0 << 4)
+#define RK3036_CR03_I2SMODE_MASTER          (1 << 4)
+
+/*RK3036_CODEC_REG04*/
+#define RK3036_CR04_I2SLRC_MASK             (1 << 7)
+#define RK3036_CR04_I2SLRC_NORMAL           (0 << 7)
+#define RK3036_CR04_I2SLRC_REVERSAL         (1 << 7)
+#define RK3036_CR04_HFVALID_MASK            (3 << 5)
+#define RK3036_CR04_HFVALID_16BITS          (0 << 5)
+#define RK3036_CR04_HFVALID_20BITS          (1 << 5)
+#define RK3036_CR04_HFVALID_24BITS          (2 << 5)
+#define RK3036_CR04_HFVALID_32BITS          (3 << 5)
+#define RK3036_CR04_MODE_MASK               (3 << 3)
+#define RK3036_CR04_MODE_RIGHT              (0 << 3)
+#define RK3036_CR04_MODE_LEFT               (1 << 3)
+#define RK3036_CR04_MODE_I2S                (2 << 3)
+#define RK3036_CR04_MODE_PCM                (3 << 3)
+#define RK3036_CR04_LR_SWAP_MASK            (1 << 2)
+#define RK3036_CR04_LR_SWAP_DIS             (0 << 2)
+#define RK3036_CR04_LR_SWAP_EN              (1 << 2)
+
+/*RK3036_CODEC_REG05*/
+#define RK3036_CR05_FRAMEH_MASK             (3 << 2)
+#define RK3036_CR05_FRAMEH_16BITS           (0 << 2)
+#define RK3036_CR05_FRAMEH_20BITS           (1 << 2)
+#define RK3036_CR05_FRAMEH_24BITS           (2 << 2)
+#define RK3036_CR05_FRAMEH_32BITS           (3 << 2)
+#define RK3036_CR05_DAC_RESET_MASK          (1 << 1)
+#define RK3036_CR05_DAC_RESET_EN            (0 << 1)
+#define RK3036_CR05_DAC_RESET_DIS           (1 << 1)
+#define RK3036_CR05_BCLKPOL_MASK            (1 << 0)
+#define RK3036_CR05_BCLKPOL_NORMAL          (0 << 0)
+#define RK3036_CR05_BCLKPOL_REVERSAL        (1 << 0)
+
+/*RK3036_CODEC_REG22*/
+#define RK3036_CR22_DACL_PATH_REFV_MASK     (1 << 5)
+#define RK3036_CR22_DACL_PATH_REFV_STOP     (0 << 5)
+#define RK3036_CR22_DACL_PATH_REFV_WORK     (1 << 5)
+#define RK3036_CR22_DACR_PATH_REFV_MASK     (1 << 4)
+#define RK3036_CR22_DACR_PATH_REFV_STOP     (0 << 4)
+#define RK3036_CR22_DACR_PATH_REFV_WORK     (1 << 4)
+#define RK3036_CR22_DACL_CLK_STOP           (0 << 3)
+#define RK3036_CR22_DACL_CLK_WORK           (1 << 3)
+#define RK3036_CR22_DACR_CLK_STOP           (0 << 2)
+#define RK3036_CR22_DACR_CLK_WORK           (1 << 2)
+#define RK3036_CR22_DACL_STOP               (0 << 1)
+#define RK3036_CR22_DACL_WORK               (1 << 1)
+#define RK3036_CR22_DACR_STOP               (0 << 0)
+#define RK3036_CR22_DACR_WORK               (1 << 0)
+
+/*RK3036_CODEC_REG23*/
+#define RK3036_CR23_HPOUTL_INIT             (0 << 3)
+#define RK3036_CR23_HPOUTL_WORK             (1 << 3)
+#define RK3036_CR23_HPOUTR_INIT             (0 << 2)
+#define RK3036_CR23_HPOUTR_WORK             (1 << 2)
+#define RK3036_CR23_HPOUTL_EN_STOP          (0 << 1)
+#define RK3036_CR23_HPOUTL_EN_WORK          (1 << 1)
+#define RK3036_CR23_HPOUTR_EN_STOP          (0 << 0)
+#define RK3036_CR23_HPOUTR_EN_WORK          (1 << 0)
+
+/*RK3036_CODEC_REG24*/
+#define RK3036_CR24_DAC_SOURCE_STOP         (0 << 5)
+#define RK3036_CR24_DAC_SOURCE_WORK         (1 << 5)
+#define RK3036_CR24_DAC_PRECHARGE           (0 << 4)
+#define RK3036_CR24_DAC_DISCHARGE           (1 << 4)
+#define RK3036_CR24_DACL_REFV_STOP          (0 << 3)
+#define RK3036_CR24_DACL_REFV_WORK          (1 << 3)
+#define RK3036_CR24_DACR_REFV_STOP          (0 << 2)
+#define RK3036_CR24_DACR_REFV_WORK          (1 << 2)
+#define RK3036_CR24_VOUTL_ZEROD_STOP        (0 << 1)
+#define RK3036_CR24_VOUTL_ZEROD_WORK        (1 << 1)
+#define RK3036_CR24_VOUTR_ZEROD_STOP        (0 << 0)
+#define RK3036_CR24_VOUTR_ZEROD_WORK        (1 << 0)
+
+/*RK3036_CODEC_REG27*/
+#define RK3036_CR27_DACL_INIT               (0 << 7)
+#define RK3036_CR27_DACL_WORK               (1 << 7)
+#define RK3036_CR27_DACR_INIT               (0 << 6)
+#define RK3036_CR27_DACR_WORK               (1 << 6)
+#define RK3036_CR27_HPOUTL_G_MUTE           (0 << 5)
+#define RK3036_CR27_HPOUTL_G_WORK           (1 << 5)
+#define RK3036_CR27_HPOUTR_G_MUTE           (0 << 4)
+#define RK3036_CR27_HPOUTR_G_WORK           (1 << 4)
+#define RK3036_CR27_HPOUTL_POP_PRECHARGE    (1 << 2)
+#define RK3036_CR27_HPOUTL_POP_WORK         (2 << 2)
+#define RK3036_CR27_HPOUTR_POP_PRECHARGE    (1 << 0)
+#define RK3036_CR27_HPOUTR_POP_WORK         (2 << 0)
+
+/*RK3036_CODEC_REG28*/
+#define RK3036_CR28_YES_027I                (0 << 5)
+#define RK3036_CR28_NON_027I                (1 << 5)
+#define RK3036_CR28_YES_050I                (0 << 4)
+#define RK3036_CR28_NON_050I                (1 << 4)
+#define RK3036_CR28_YES_100I                (0 << 3)
+#define RK3036_CR28_NON_100I                (1 << 3)
+#define RK3036_CR28_YES_130I                (0 << 2)
+#define RK3036_CR28_NON_130I                (1 << 2)
+#define RK3036_CR28_YES_260I                (0 << 1)
+#define RK3036_CR28_NON_260I                (1 << 1)
+#define RK3036_CR28_YES_400I                (0 << 0)
+#define RK3036_CR28_NON_400I                (1 << 0)
+
+enum {
+       RK3036_HIFI,
+       RK3036_VOICE,
+};
+
+struct rk3036_reg_val_typ {
+       unsigned int reg;
+       unsigned int value;
+};
+
+struct rk3036_init_bit_typ {
+       unsigned int reg;
+       unsigned int power_bit;
+       unsigned int init2_bit;
+       unsigned int init1_bit;
+       unsigned int init0_bit;
+};
+
+#endif
old mode 100755 (executable)
new mode 100644 (file)
index 50f1edf..df792f1
@@ -20,7 +20,7 @@ choice
   bool "Set audio support for HDMI"
   default SND_RK_SOC_HDMI_I2S
        config SND_RK_SOC_HDMI_I2S
-               depends on !RK616_USE_MCLK_12M || !SND_RK_SOC_RK616
+               depends on (!RK616_USE_MCLK_12M || !SND_RK_SOC_RK616) && !SND_RK_SOC_RK3036
                select SND_RK_SOC_I2S
                select SND_SOC_HDMI_I2S
                tristate  "HDMI use I2S"
@@ -240,6 +240,15 @@ config SND_RK_SOC_RK1000
          Say Y if you want to add support for SoC audio on rockchip
          with the RK1000.
 
+config SND_RK_SOC_RK3036
+       tristate "SoC I2S Audio support for rockchip - RK3036"
+       depends on SND_RK_SOC
+       select SND_RK_SOC_I2S
+       select SND_SOC_RK3036
+       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
old mode 100755 (executable)
new mode 100644 (file)
index 2c731cc..16c7e03
@@ -27,6 +27,7 @@ snd-soc-cs42l52-objs := rk_cs42l52.o
 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-wm8994-objs := rk_wm8994.o
 snd-soc-rk610-objs := rk_jetta_codec.o
 snd-soc-rk616-objs := rk_rk616.o
@@ -54,6 +55,7 @@ obj-$(CONFIG_SND_RK_SOC_RT3224) += snd-soc-rt3224.o
 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_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_rk3036.c b/sound/soc/rockchip/rk_rk3036.c
new file mode 100644 (file)
index 0000000..ae413af
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+* rk_rk3036codec.c-- SoC audio for rockchip
+*
+* Driver for rockchip internal rk_rk3036codec 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/rk3036_codec.h"
+#include "card_info.h"
+#include "rk_pcm.h"
+#include "rk_i2s.h"
+
+static int rk30_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 dai_fmt = rtd->dai_link->dai_fmt;
+       int ret;
+
+       /* set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
+       if (ret < 0)
+               return ret;
+
+       /* set cpu DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+/*
+ * Logic for a rk3036 codec as connected on a rockchip board.
+ */
+static int rk30_rk3036_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+       return 0;
+}
+
+static struct snd_soc_ops rk30_ops = {
+         .hw_params = rk30_hw_params,
+};
+
+static struct snd_soc_dai_link rk30_dai[] = {
+       {
+               .name = "RK3036",
+               .stream_name = "RK3036 CODEC PCM",
+               .codec_dai_name = "rk3036-voice",
+               .init = rk30_rk3036_codec_init,
+               .ops = &rk30_ops,
+       }
+};
+
+static struct snd_soc_card rockchip_rk3036_snd_card = {
+       .name = "RK_RK3036",
+       .dai_link = rk30_dai,
+       .num_links = 1,
+};
+
+static int rockchip_rk3036_audio_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct snd_soc_card *card = &rockchip_rk3036_snd_card;
+
+       card->dev = &pdev->dev;
+
+       ret = rockchip_of_get_sound_card_info(card);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_register_card(card);
+
+       return ret;
+}
+
+static int rockchip_rk3036_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_rk3036_of_match[] = {
+               { .compatible = "rk3036-audio", },
+               {},
+};
+MODULE_DEVICE_TABLE(of, rockchip_rk3036_of_match);
+#endif /* CONFIG_OF */
+
+static struct platform_driver rockchip_rk3036_audio_driver = {
+       .driver = {
+               .name   = "rk3036-audio",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(rockchip_rk3036_of_match),
+       },
+       .probe      = rockchip_rk3036_audio_probe,
+       .remove     = rockchip_rk3036_audio_remove,
+};
+module_platform_driver(rockchip_rk3036_audio_driver);
+
+/* Module information */
+MODULE_AUTHOR("rockchip");
+MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface");
+MODULE_LICENSE("GPL");