ASoC: codecs: add support for rk3328
authorSugar Zhang <sugar.zhang@rock-chips.com>
Fri, 31 Mar 2017 02:56:10 +0000 (10:56 +0800)
committerHuang, Tao <huangtao@rock-chips.com>
Mon, 17 Apr 2017 07:08:47 +0000 (15:08 +0800)
Change-Id: I66ff61c18fe70135fd7ac0569954263743263a3a
Signed-off-by: Sugar Zhang <sugar.zhang@rock-chips.com>
Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.txt [new file with mode: 0644]
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/rk3328_codec.c [new file with mode: 0644]
sound/soc/codecs/rk3328_codec.h [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.txt b/Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.txt
new file mode 100644 (file)
index 0000000..2469588
--- /dev/null
@@ -0,0 +1,23 @@
+* Rockchip Rk3328 internal codec
+
+Required properties:
+
+- compatible: "rockchip,rk3328-codec"
+- reg: physical base address of the controller and length of memory mapped
+  region.
+- rockchip,grf: the phandle of the syscon node for GRF register.
+- clocks: a list of phandle + clock-specifer pairs, one for each entry in clock-names.
+- clock-names: should be "pclk".
+- spk-depop-time-ms: speak depop time msec.
+
+Example for rk3328 internal codec:
+
+codec: codec@ff410000 {
+       compatible = "rockchip,rk3328-codec";
+       reg = <0x0 0xff410000 0x0 0x1000>;
+       rockchip,grf = <&grf>;
+       clocks = <&cru PCLK_ACODEC>;
+       clock-names = "pclk";
+       spk-depop-time-ms = 100;
+       status = "disabled";
+};
index c47ea699a54728aa790219c5324ddb16d3d8b478..73396e2acacef5b2c26d7d6103581356d5c68fbd 100644 (file)
@@ -91,6 +91,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_PCM3008
        select SND_SOC_PCM512x_I2C if I2C
        select SND_SOC_PCM512x_SPI if SPI_MASTER
+       select SND_SOC_RK3328
        select SND_SOC_RT286 if I2C
        select SND_SOC_RT298 if I2C
        select SND_SOC_RT5616 if I2C
@@ -548,6 +549,10 @@ config SND_SOC_PCM512x_SPI
        select SND_SOC_PCM512x
        select REGMAP_SPI
 
+config SND_SOC_RK3328
+       select REGMAP_MMIO
+       tristate "Rockchip RK3328 CODEC"
+
 config SND_SOC_RL6231
        tristate
        default y if SND_SOC_RT5616=y
index e4baa257aad2497bb73cb78a01be34824d824596..313eca65fb42d3d5de8f15cffc9b075795091a1b 100644 (file)
@@ -86,6 +86,7 @@ snd-soc-pcm3008-objs := pcm3008.o
 snd-soc-pcm512x-objs := pcm512x.o
 snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
 snd-soc-pcm512x-spi-objs := pcm512x-spi.o
+snd-soc-rk3328-objs := rk3328_codec.o
 snd-soc-rl6231-objs := rl6231.o
 snd-soc-rl6347a-objs := rl6347a.o
 snd-soc-rt286-objs := rt286.o
@@ -288,6 +289,7 @@ obj-$(CONFIG_SND_SOC_PCM3008)       += snd-soc-pcm3008.o
 obj-$(CONFIG_SND_SOC_PCM512x)  += snd-soc-pcm512x.o
 obj-$(CONFIG_SND_SOC_PCM512x_I2C)      += snd-soc-pcm512x-i2c.o
 obj-$(CONFIG_SND_SOC_PCM512x_SPI)      += snd-soc-pcm512x-spi.o
+obj-$(CONFIG_SND_SOC_RK3328)   += snd-soc-rk3328.o
 obj-$(CONFIG_SND_SOC_RL6231)   += snd-soc-rl6231.o
 obj-$(CONFIG_SND_SOC_RL6347A)  += snd-soc-rl6347a.o
 obj-$(CONFIG_SND_SOC_RT286)    += snd-soc-rt286.o
diff --git a/sound/soc/codecs/rk3328_codec.c b/sound/soc/codecs/rk3328_codec.c
new file mode 100644 (file)
index 0000000..ceb2eb6
--- /dev/null
@@ -0,0 +1,519 @@
+/*
+ * rk3328_codec.c  --  rk3328 ALSA Soc Audio driver
+ *
+ * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+#include "rk3328_codec.h"
+
+/*
+ * volume setting
+ * 0: -39dB
+ * 26: 0dB
+ * 31: 6dB
+ * Step: 1.5dB
+ */
+#define OUT_VOLUME     (0x18)
+#define RK3328_GRF_SOC_CON2    (0x0408)
+#define RK3328_GRF_SOC_CON10   (0x0428)
+
+struct rk3328_codec_priv {
+       struct regmap *regmap;
+       struct regmap *grf;
+       struct clk *pclk;
+       unsigned int sclk;
+       int spk_depop_time; /* msec */
+};
+
+static const struct reg_default rk3328_codec_reg_defaults[] = {
+       { CODEC_RESET, 0x03 },
+       { DAC_INIT_CTRL1, 0x00 },
+       { DAC_INIT_CTRL2, 0x50 },
+       { DAC_INIT_CTRL3, 0x0e },
+       { DAC_PRECHARGE_CTRL, 0x01 },
+       { DAC_PWR_CTRL, 0x00 },
+       { DAC_CLK_CTRL, 0x00 },
+       { HPMIX_CTRL, 0x00 },
+       { HPOUT_CTRL, 0x00 },
+       { HPOUTL_GAIN_CTRL, 0x00 },
+       { HPOUTR_GAIN_CTRL, 0x00 },
+       { HPOUT_POP_CTRL, 0x11 },
+};
+
+static int rk3328_codec_reset(struct snd_soc_codec *codec)
+{
+       struct rk3328_codec_priv *rk3328 = snd_soc_codec_get_drvdata(codec);
+
+       regmap_write(rk3328->regmap, CODEC_RESET, 0);
+       mdelay(10);
+       regmap_write(rk3328->regmap, CODEC_RESET, 0x03);
+
+       return 0;
+}
+
+static int rk3328_set_dai_fmt(struct snd_soc_dai *codec_dai,
+                             unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct rk3328_codec_priv *rk3328 = snd_soc_codec_get_drvdata(codec);
+       unsigned int val = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               val |= PIN_DIRECTION_IN | DAC_I2S_MODE_SLAVE;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               val |= PIN_DIRECTION_OUT | DAC_I2S_MODE_MASTER;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL1,
+                          PIN_DIRECTION_MASK | DAC_I2S_MODE_MASK, val);
+
+       val = 0;
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_A:
+       case SND_SOC_DAIFMT_DSP_B:
+               val |= DAC_MODE_PCM;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               val |= DAC_MODE_I2S;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               val |= DAC_MODE_RJM;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               val |= DAC_MODE_LJM;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL2,
+                          DAC_MODE_MASK, val);
+       return 0;
+}
+
+static void rk3328_analog_output(struct rk3328_codec_priv *rk3328, int mute)
+{
+       regmap_write(rk3328->grf, RK3328_GRF_SOC_CON10,
+                    (BIT(1) << 16) | (mute << 1));
+}
+
+static int rk3328_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct rk3328_codec_priv *rk3328 = snd_soc_codec_get_drvdata(codec);
+       unsigned int val = 0;
+
+       if (mute)
+               val = HPOUTL_MUTE | HPOUTR_MUTE;
+       else
+               val = HPOUTL_UNMUTE | HPOUTR_UNMUTE;
+
+       regmap_update_bits(rk3328->regmap, HPOUT_CTRL,
+                          HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK, val);
+       return 0;
+}
+
+static int rk3328_codec_power_on(struct snd_soc_codec *codec, int wait_ms)
+{
+       struct rk3328_codec_priv *rk3328 = snd_soc_codec_get_drvdata(codec);
+
+       regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+                          DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_PRECHARGE);
+       mdelay(10);
+       regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+                          DAC_CHARGE_CURRENT_ALL_MASK,
+                          DAC_CHARGE_CURRENT_ALL_ON);
+
+       mdelay(wait_ms);
+
+       return 0;
+}
+
+static int rk3328_codec_power_off(struct snd_soc_codec *codec, int wait_ms)
+{
+       struct rk3328_codec_priv *rk3328 = snd_soc_codec_get_drvdata(codec);
+
+       regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+                          DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_DISCHARGE);
+       mdelay(10);
+       regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+                          DAC_CHARGE_CURRENT_ALL_MASK,
+                          DAC_CHARGE_CURRENT_ALL_ON);
+
+       mdelay(wait_ms);
+
+       return 0;
+}
+
+static struct rk3328_reg_msk_val playback_open_list[] = {
+       { DAC_PWR_CTRL, DAC_PWR_MASK, DAC_PWR_ON },
+       { DAC_PWR_CTRL, DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK,
+         DACL_PATH_REFV_ON | DACR_PATH_REFV_ON },
+       { DAC_PWR_CTRL, HPOUTL_ZERO_CROSSING_ON | HPOUTR_ZERO_CROSSING_ON,
+         HPOUTL_ZERO_CROSSING_ON | HPOUTR_ZERO_CROSSING_ON },
+       { HPOUT_POP_CTRL, HPOUTR_POP_MASK | HPOUTL_POP_MASK,
+         HPOUTR_POP_WORK | HPOUTL_POP_WORK },
+       { HPMIX_CTRL, HPMIXL_MASK | HPMIXR_MASK, HPMIXL_EN | HPMIXR_EN },
+       { HPMIX_CTRL, HPMIXL_INIT_MASK | HPMIXR_INIT_MASK,
+         HPMIXL_INIT_EN | HPMIXR_INIT_EN },
+       { HPOUT_CTRL, HPOUTL_MASK | HPOUTR_MASK, HPOUTL_EN | HPOUTR_EN },
+       { HPOUT_CTRL, HPOUTL_INIT_MASK | HPOUTR_INIT_MASK,
+         HPOUTL_INIT_EN | HPOUTR_INIT_EN },
+       { DAC_CLK_CTRL, DACL_REFV_MASK | DACR_REFV_MASK,
+         DACL_REFV_ON | DACR_REFV_ON },
+       { DAC_CLK_CTRL, DACL_CLK_MASK | DACR_CLK_MASK,
+         DACL_CLK_ON | DACR_CLK_ON },
+       { DAC_CLK_CTRL, DACL_MASK | DACR_MASK, DACL_ON | DACR_ON },
+       { DAC_CLK_CTRL, DACL_INIT_MASK | DACR_INIT_MASK,
+         DACL_INIT_ON | DACR_INIT_ON },
+       { DAC_SELECT, DACL_SELECT_MASK | DACR_SELECT_MASK,
+         DACL_SELECT | DACR_SELECT },
+       { HPMIX_CTRL, HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK,
+         HPMIXL_INIT2_EN | HPMIXR_INIT2_EN },
+       { HPOUT_CTRL, HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK,
+         HPOUTL_UNMUTE | HPOUTR_UNMUTE },
+};
+
+#define PLAYBACK_OPEN_LIST_LEN ARRAY_SIZE(playback_open_list)
+
+static int rk3328_codec_open_playback(struct snd_soc_codec *codec)
+{
+       struct rk3328_codec_priv *rk3328 = snd_soc_codec_get_drvdata(codec);
+       int i = 0;
+
+       regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+                          DAC_CHARGE_CURRENT_ALL_MASK,
+                          DAC_CHARGE_CURRENT_I);
+
+       for (i = 0; i < PLAYBACK_OPEN_LIST_LEN; i++) {
+               regmap_update_bits(rk3328->regmap,
+                                  playback_open_list[i].reg,
+                                  playback_open_list[i].msk,
+                                  playback_open_list[i].val);
+               mdelay(1);
+       }
+
+       msleep(rk3328->spk_depop_time);
+       rk3328_analog_output(rk3328, 1);
+
+       regmap_update_bits(rk3328->regmap, HPOUTL_GAIN_CTRL,
+                          HPOUTL_GAIN_MASK, OUT_VOLUME);
+       regmap_update_bits(rk3328->regmap, HPOUTR_GAIN_CTRL,
+                          HPOUTR_GAIN_MASK, OUT_VOLUME);
+       return 0;
+}
+
+static struct rk3328_reg_msk_val playback_close_list[] = {
+       { HPMIX_CTRL, HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK,
+         HPMIXL_INIT2_DIS | HPMIXR_INIT2_DIS },
+       { DAC_SELECT, DACL_SELECT_MASK | DACR_SELECT_MASK,
+         DACL_UNSELECT | DACR_UNSELECT },
+       { HPOUT_CTRL, HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK,
+         HPOUTL_MUTE | HPOUTR_MUTE },
+       { HPOUT_CTRL, HPOUTL_INIT_MASK | HPOUTR_INIT_MASK,
+         HPOUTL_INIT_DIS | HPOUTR_INIT_DIS },
+       { HPOUT_CTRL, HPOUTL_MASK | HPOUTR_MASK, HPOUTL_DIS | HPOUTR_DIS },
+       { HPMIX_CTRL, HPMIXL_MASK | HPMIXR_MASK, HPMIXL_DIS | HPMIXR_DIS },
+       { DAC_CLK_CTRL, DACL_MASK | DACR_MASK, DACL_OFF | DACR_OFF },
+       { DAC_CLK_CTRL, DACL_CLK_MASK | DACR_CLK_MASK,
+         DACL_CLK_OFF | DACR_CLK_OFF },
+       { DAC_CLK_CTRL, DACL_REFV_MASK | DACR_REFV_MASK,
+         DACL_REFV_OFF | DACR_REFV_OFF },
+       { HPOUT_POP_CTRL, HPOUTR_POP_MASK | HPOUTL_POP_MASK,
+         HPOUTR_POP_XCHARGE | HPOUTL_POP_XCHARGE },
+       { DAC_PWR_CTRL, DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK,
+         DACL_PATH_REFV_OFF | DACR_PATH_REFV_OFF },
+       { DAC_PWR_CTRL, DAC_PWR_MASK, DAC_PWR_OFF },
+       { HPMIX_CTRL, HPMIXL_INIT_MASK | HPMIXR_INIT_MASK,
+         HPMIXL_INIT_DIS | HPMIXR_INIT_DIS },
+       { DAC_CLK_CTRL, DACL_INIT_MASK | DACR_INIT_MASK,
+         DACL_INIT_OFF | DACR_INIT_OFF },
+};
+
+#define PLAYBACK_CLOSE_LIST_LEN ARRAY_SIZE(playback_close_list)
+
+static int rk3328_codec_close_playback(struct snd_soc_codec *codec)
+{
+       struct rk3328_codec_priv *rk3328 = snd_soc_codec_get_drvdata(codec);
+       int i = 0;
+
+       rk3328_analog_output(rk3328, 0);
+
+       regmap_update_bits(rk3328->regmap, HPOUTL_GAIN_CTRL,
+                          HPOUTL_GAIN_MASK, 0);
+       regmap_update_bits(rk3328->regmap, HPOUTR_GAIN_CTRL,
+                          HPOUTR_GAIN_MASK, 0);
+
+       for (i = 0; i < PLAYBACK_CLOSE_LIST_LEN; i++) {
+               regmap_update_bits(rk3328->regmap,
+                                  playback_close_list[i].reg,
+                                  playback_close_list[i].msk,
+                                  playback_close_list[i].val);
+               mdelay(1);
+       }
+
+       regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL,
+                          DAC_CHARGE_CURRENT_ALL_MASK,
+                          DAC_CHARGE_CURRENT_I);
+       return 0;
+}
+
+static int rk3328_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct rk3328_codec_priv *rk3328 = snd_soc_codec_get_drvdata(codec);
+       unsigned int val = 0;
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               val |= DAC_VDL_16BITS;
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               val |= DAC_VDL_20BITS;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               val |= DAC_VDL_24BITS;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               val |= DAC_VDL_32BITS;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL2, DAC_VDL_MASK, val);
+       val = DAC_WL_32BITS | DAC_RST_DIS;
+       regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL3,
+                          DAC_WL_MASK | DAC_RST_MASK, val);
+
+       return 0;
+}
+
+static int rk3328_pcm_startup(struct snd_pcm_substream *substream,
+                             struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+
+       return rk3328_codec_open_playback(codec);
+}
+
+static void rk3328_pcm_shutdown(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+
+       rk3328_codec_close_playback(codec);
+}
+
+static struct snd_soc_dai_ops rk3328_dai_ops = {
+       .hw_params = rk3328_hw_params,
+       .set_fmt = rk3328_set_dai_fmt,
+       .digital_mute = rk3328_digital_mute,
+       .startup = rk3328_pcm_startup,
+       .shutdown = rk3328_pcm_shutdown,
+};
+
+static struct snd_soc_dai_driver rk3328_dai[] = {
+       {
+               .name = "rk3328-hifi",
+               .id = RK3328_HIFI,
+               .playback = {
+                       .stream_name = "HIFI Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_96000,
+                       .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+                                   SNDRV_PCM_FMTBIT_S20_3LE |
+                                   SNDRV_PCM_FMTBIT_S24_LE |
+                                   SNDRV_PCM_FMTBIT_S32_LE),
+               },
+               .ops = &rk3328_dai_ops,
+       },
+};
+
+static int rk3328_codec_probe(struct snd_soc_codec *codec)
+{
+       rk3328_codec_reset(codec);
+       rk3328_codec_power_on(codec, 0);
+
+       return 0;
+}
+
+static int rk3328_codec_remove(struct snd_soc_codec *codec)
+{
+       rk3328_codec_close_playback(codec);
+       rk3328_codec_power_off(codec, 0);
+
+       return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_rk3328 = {
+       .probe = rk3328_codec_probe,
+       .remove = rk3328_codec_remove,
+};
+
+static bool rk3328_codec_write_read_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CODEC_RESET:
+       case DAC_INIT_CTRL1:
+       case DAC_INIT_CTRL2:
+       case DAC_INIT_CTRL3:
+       case DAC_PRECHARGE_CTRL:
+       case DAC_PWR_CTRL:
+       case DAC_CLK_CTRL:
+       case HPMIX_CTRL:
+       case DAC_SELECT:
+       case HPOUT_CTRL:
+       case HPOUTL_GAIN_CTRL:
+       case HPOUTR_GAIN_CTRL:
+       case HPOUT_POP_CTRL:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool rk3328_codec_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CODEC_RESET:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static const struct regmap_config rk3328_codec_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+       .max_register = HPOUT_POP_CTRL,
+       .writeable_reg = rk3328_codec_write_read_reg,
+       .readable_reg = rk3328_codec_write_read_reg,
+       .volatile_reg = rk3328_codec_volatile_reg,
+       .reg_defaults = rk3328_codec_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(rk3328_codec_reg_defaults),
+       .cache_type = REGCACHE_FLAT,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id rk3328codec_of_match[] = {
+               { .compatible = "rockchip,rk3328-codec", },
+               {},
+};
+MODULE_DEVICE_TABLE(of, rk3328codec_of_match);
+#endif
+
+static int rk3328_platform_probe(struct platform_device *pdev)
+{
+       struct device_node *rk3328_np = pdev->dev.of_node;
+       struct rk3328_codec_priv *rk3328;
+       struct resource *res;
+       struct regmap *grf;
+       void __iomem *base;
+       int ret = 0;
+
+       rk3328 = devm_kzalloc(&pdev->dev, sizeof(*rk3328), GFP_KERNEL);
+       if (!rk3328)
+               return -ENOMEM;
+
+       grf = syscon_regmap_lookup_by_phandle(rk3328_np,
+                                             "rockchip,grf");
+       if (IS_ERR(grf)) {
+               dev_err(&pdev->dev, "missing 'rockchip,grf'\n");
+               return PTR_ERR(grf);
+       }
+       rk3328->grf = grf;
+       /* enable i2s_acodec_en */
+       regmap_write(grf, RK3328_GRF_SOC_CON2,
+                    (BIT(14) << 16 | BIT(14)));
+
+       ret = of_property_read_u32(rk3328_np, "spk-depop-time-ms",
+                                  &rk3328->spk_depop_time);
+       if (ret < 0) {
+               dev_info(&pdev->dev, "spk_depop_time use default value.\n");
+               rk3328->spk_depop_time = 200;
+       }
+
+       rk3328_analog_output(rk3328, 0);
+
+       rk3328->pclk = devm_clk_get(&pdev->dev, "pclk");
+       if (IS_ERR(rk3328->pclk)) {
+               dev_err(&pdev->dev, "can't get acodec pclk\n");
+               return PTR_ERR(rk3328->pclk);
+       }
+
+       ret = clk_prepare_enable(rk3328->pclk);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to enable acodec pclk\n");
+               return ret;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       rk3328->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+                                              &rk3328_codec_regmap_config);
+       if (IS_ERR(rk3328->regmap))
+               return PTR_ERR(rk3328->regmap);
+
+       platform_set_drvdata(pdev, rk3328);
+
+       return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_rk3328,
+                                     rk3328_dai, ARRAY_SIZE(rk3328_dai));
+}
+
+static int rk3328_platform_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_codec(&pdev->dev);
+       return 0;
+}
+
+static struct platform_driver rk3328_codec_driver = {
+       .driver = {
+                  .name = "rk3328-codec",
+                  .owner = THIS_MODULE,
+                  .of_match_table = of_match_ptr(rk3328codec_of_match),
+       },
+       .probe = rk3328_platform_probe,
+       .remove = rk3328_platform_remove,
+};
+module_platform_driver(rk3328_codec_driver);
+
+MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>");
+MODULE_DESCRIPTION("ASoC rk3328 codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rk3328_codec.h b/sound/soc/codecs/rk3328_codec.h
new file mode 100644 (file)
index 0000000..232b2fe
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * rk3328_codec.h  --  rk3328 ALSA Soc Audio driver
+ *
+ * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _RK3328_CODEC_H
+#define _RK3328_CODEC_H
+
+/* codec register */
+#define CODEC_RESET                    (0x00 << 2)
+#define DAC_INIT_CTRL1                 (0x03 << 2)
+#define DAC_INIT_CTRL2                 (0x04 << 2)
+#define DAC_INIT_CTRL3                 (0x05 << 2)
+#define DAC_PRECHARGE_CTRL             (0x22 << 2)
+#define DAC_PWR_CTRL                   (0x23 << 2)
+#define DAC_CLK_CTRL                   (0x24 << 2)
+#define HPMIX_CTRL                     (0x25 << 2)
+#define DAC_SELECT                     (0x26 << 2)
+#define HPOUT_CTRL                     (0x27 << 2)
+#define HPOUTL_GAIN_CTRL               (0x28 << 2)
+#define HPOUTR_GAIN_CTRL               (0x29 << 2)
+#define HPOUT_POP_CTRL                 (0x2a << 2)
+
+/* REG00: CODEC_RESET */
+#define PWR_RST_BYPASS_DIS             BIT(6)
+#define PWR_RST_BYPASS_EN              BIT(6)
+#define DIG_CORE_RST                   (0 << 1)
+#define DIG_CORE_WORK                  BIT(1)
+#define SYS_RST                                (0)
+#define SYS_WORK                       BIT(0)
+
+/* REG03: DAC_INIT_CTRL1 */
+#define PIN_DIRECTION_MASK             BIT(5)
+#define PIN_DIRECTION_IN               (0 << 5)
+#define PIN_DIRECTION_OUT              BIT(5)
+#define DAC_I2S_MODE_MASK              BIT(4)
+#define DAC_I2S_MODE_SLAVE             (0 << 4)
+#define DAC_I2S_MODE_MASTER            BIT(4)
+
+/* REG04: DAC_INIT_CTRL2 */
+#define DAC_I2S_LRP_MASK               BIT(7)
+#define DAC_I2S_LRP_NORMAL             (0 << 7)
+#define DAC_I2S_LRP_REVERSAL           BIT(7)
+#define DAC_VDL_MASK                   (3 << 5)
+#define DAC_VDL_16BITS                 (0 << 5)
+#define DAC_VDL_20BITS                 BIT(5)
+#define DAC_VDL_24BITS                 (2 << 5)
+#define DAC_VDL_32BITS                 (3 << 5)
+#define DAC_MODE_MASK                  (3 << 3)
+#define DAC_MODE_RJM                   (0 << 3)
+#define DAC_MODE_LJM                   BIT(3)
+#define DAC_MODE_I2S                   (2 << 3)
+#define DAC_MODE_PCM                   (3 << 3)
+#define DAC_LR_SWAP_MASK               BIT(2)
+#define DAC_LR_SWAP_DIS                        (0 << 2)
+#define DAC_LR_SWAP_EN                 BIT(2)
+
+/* REG05: DAC_INIT_CTRL3 */
+#define DAC_WL_MASK                    (3 << 2)
+#define DAC_WL_16BITS                  (0 << 2)
+#define DAC_WL_20BITS                  BIT(2)
+#define DAC_WL_24BITS                  (2 << 2)
+#define DAC_WL_32BITS                  (3 << 2)
+#define DAC_RST_MASK                   BIT(1)
+#define DAC_RST_EN                     (0 << 1)
+#define DAC_RST_DIS                    BIT(1)
+#define DAC_BCP_MASK                   BIT(0)
+#define DAC_BCP_NORMAL                 (0 << 0)
+#define DAC_BCP_REVERSAL               BIT(0)
+
+/* REG22: DAC_PRECHARGE_CTRL */
+#define DAC_CHARGE_PRECHARGE           BIT(7)
+#define DAC_CHARGE_DISCHARGE           (0 << 7)
+#define DAC_CHARGE_XCHARGE_MASK                BIT(7)
+#define DAC_CHARGE_CURRENT_64I         BIT(6)
+#define DAC_CHARGE_CURRENT_64I_MASK    BIT(6)
+#define DAC_CHARGE_CURRENT_32I         BIT(5)
+#define DAC_CHARGE_CURRENT_32I_MASK    BIT(5)
+#define DAC_CHARGE_CURRENT_16I         BIT(4)
+#define DAC_CHARGE_CURRENT_16I_MASK    BIT(4)
+#define DAC_CHARGE_CURRENT_08I         BIT(3)
+#define DAC_CHARGE_CURRENT_08I_MASK    BIT(3)
+#define DAC_CHARGE_CURRENT_04I         BIT(2)
+#define DAC_CHARGE_CURRENT_04I_MASK    BIT(2)
+#define DAC_CHARGE_CURRENT_02I         BIT(1)
+#define DAC_CHARGE_CURRENT_02I_MASK    BIT(1)
+#define DAC_CHARGE_CURRENT_I           BIT(0)
+#define DAC_CHARGE_CURRENT_I_MASK      BIT(0)
+#define DAC_CHARGE_CURRENT_ALL_MASK    (0x7f)
+#define DAC_CHARGE_CURRENT_ALL_OFF     (0x0)
+#define DAC_CHARGE_CURRENT_ALL_ON      (0x7f)
+
+/* REG23: DAC_PWR_CTRL */
+#define DAC_PWR_OFF                    (0 << 6)
+#define DAC_PWR_ON                     BIT(6)
+#define DAC_PWR_MASK                   BIT(6)
+#define DACL_PATH_REFV_OFF             (0 << 5)
+#define DACL_PATH_REFV_ON              BIT(5)
+#define DACL_PATH_REFV_MASK            BIT(5)
+#define HPOUTL_ZERO_CROSSING_OFF       (0 << 4)
+#define HPOUTL_ZERO_CROSSING_ON                BIT(4)
+#define DACR_PATH_REFV_OFF             (0 << 1)
+#define DACR_PATH_REFV_ON              BIT(1)
+#define DACR_PATH_REFV_MASK            BIT(1)
+#define HPOUTR_ZERO_CROSSING_OFF       (0 << 0)
+#define HPOUTR_ZERO_CROSSING_ON                BIT(0)
+
+/* REG24: DAC_CLK_CTRL */
+#define DACL_REFV_OFF                  (0 << 7)
+#define DACL_REFV_ON                   BIT(7)
+#define DACL_REFV_MASK                 BIT(7)
+#define DACL_CLK_OFF                   (0 << 6)
+#define DACL_CLK_ON                    BIT(6)
+#define DACL_CLK_MASK                  BIT(6)
+#define DACL_OFF                       (0 << 5)
+#define DACL_ON                                BIT(5)
+#define DACL_MASK                      BIT(5)
+#define DACL_INIT_OFF                  (0 << 4)
+#define DACL_INIT_ON                   BIT(4)
+#define DACL_INIT_MASK                 BIT(4)
+#define DACR_REFV_OFF                  (0 << 3)
+#define DACR_REFV_ON                   BIT(3)
+#define DACR_REFV_MASK                 BIT(3)
+#define DACR_CLK_OFF                   (0 << 2)
+#define DACR_CLK_ON                    BIT(2)
+#define DACR_CLK_MASK                  BIT(2)
+#define DACR_OFF                       (0 << 1)
+#define DACR_ON                                BIT(1)
+#define DACR_MASK                      BIT(1)
+#define DACR_INIT_OFF                  (0 << 0)
+#define DACR_INIT_ON                   BIT(0)
+#define DACR_INIT_MASK                 BIT(0)
+
+/* REG25: HPMIX_CTRL*/
+#define HPMIXL_DIS                     (0 << 6)
+#define HPMIXL_EN                      BIT(6)
+#define HPMIXL_MASK                    BIT(6)
+#define HPMIXL_INIT_DIS                        (0 << 5)
+#define HPMIXL_INIT_EN                 BIT(5)
+#define HPMIXL_INIT_MASK               BIT(5)
+#define HPMIXL_INIT2_DIS               (0 << 4)
+#define HPMIXL_INIT2_EN                        BIT(4)
+#define HPMIXL_INIT2_MASK              BIT(4)
+#define HPMIXR_DIS                     (0 << 2)
+#define HPMIXR_EN                      BIT(2)
+#define HPMIXR_MASK                    BIT(2)
+#define HPMIXR_INIT_DIS                        (0 << 1)
+#define HPMIXR_INIT_EN                 BIT(1)
+#define HPMIXR_INIT_MASK               BIT(1)
+#define HPMIXR_INIT2_DIS               (0 << 0)
+#define HPMIXR_INIT2_EN                        BIT(0)
+#define HPMIXR_INIT2_MASK              BIT(0)
+
+/* REG26: DAC_SELECT */
+#define DACL_SELECT                    BIT(4)
+#define DACL_SELECT_MASK               BIT(4)
+#define DACL_UNSELECT                  (0 << 4)
+#define DACR_SELECT                    BIT(0)
+#define DACR_SELECT_MASK               BIT(0)
+#define DACR_UNSELECT                  (0 << 0)
+
+/* REG27: HPOUT_CTRL */
+#define HPOUTL_DIS                     (0 << 7)
+#define HPOUTL_EN                      BIT(7)
+#define HPOUTL_MASK                    BIT(7)
+#define HPOUTL_INIT_DIS                        (0 << 6)
+#define HPOUTL_INIT_EN                 BIT(6)
+#define HPOUTL_INIT_MASK               BIT(6)
+#define HPOUTL_MUTE                    (0 << 5)
+#define HPOUTL_UNMUTE                  BIT(5)
+#define HPOUTL_MUTE_MASK               BIT(5)
+#define HPOUTR_DIS                     (0 << 4)
+#define HPOUTR_EN                      BIT(4)
+#define HPOUTR_MASK                    BIT(4)
+#define HPOUTR_INIT_DIS                        (0 << 3)
+#define HPOUTR_INIT_EN                 BIT(3)
+#define HPOUTR_INIT_MASK               BIT(3)
+#define HPOUTR_MUTE                    (0 << 2)
+#define HPOUTR_UNMUTE                  BIT(2)
+#define HPOUTR_MUTE_MASK               BIT(2)
+
+/* REG28: HPOUTL_GAIN_CTRL */
+#define HPOUTL_GAIN_MASK               (0X1f << 0)
+
+/* REG29: HPOUTR_GAIN_CTRL */
+#define HPOUTR_GAIN_MASK               (0X1f << 0)
+
+/* REG2a: HPOUT_POP_CTRL */
+#define HPOUTR_POP_XCHARGE             BIT(4)
+#define HPOUTR_POP_WORK                        (2 << 4)
+#define HPOUTR_POP_MASK                        (3 << 4)
+#define HPOUTL_POP_XCHARGE             BIT(0)
+#define HPOUTL_POP_WORK                        (2 << 0)
+#define HPOUTL_POP_MASK                        (3 << 0)
+
+#define RK3328_HIFI                    (0)
+
+struct rk3328_reg_msk_val {
+       unsigned int reg;
+       unsigned int msk;
+       unsigned int val;
+};
+
+#endif