ASoC: rockchip: rename rk30_i2s.c to rk_i2s.c
authorSugar Zhang <sugar.zhang@rock-chips.com>
Thu, 2 Jul 2015 12:02:31 +0000 (20:02 +0800)
committerSugar Zhang <sugar.zhang@rock-chips.com>
Thu, 2 Jul 2015 12:09:01 +0000 (20:09 +0800)
Signed-off-by: Sugar Zhang <sugar.zhang@rock-chips.com>
sound/soc/rockchip/Makefile
sound/soc/rockchip/rk30_i2s.c [deleted file]
sound/soc/rockchip/rk_i2s.c [new file with mode: 0644]

index eff5607a4347129153ef1141810aaafd7d6735ae..cea9f66cf05d3187528df32ee3eaf959512ddc37 100755 (executable)
@@ -3,7 +3,7 @@ snd-soc-rockchip-objs := rk_pcm.o card_info.o
 ifdef CONFIG_ARCH_RK29
 snd-soc-rockchip-i2s-objs := rk29_i2s.o
 else
-snd-soc-rockchip-i2s-objs := rk30_i2s.o
+snd-soc-rockchip-i2s-objs := rk_i2s.o
 endif
 snd-soc-rockchip-spdif-objs := rk_spdif.o
 
diff --git a/sound/soc/rockchip/rk30_i2s.c b/sound/soc/rockchip/rk30_i2s.c
deleted file mode 100644 (file)
index d551860..0000000
+++ /dev/null
@@ -1,785 +0,0 @@
-/*
- * Rockchip I2S ALSA SoC Digital Audio Interface(DAI)  driver
- *
- * Copyright (C) 2015 Fuzhou Rockchip Electronics Co., Ltd
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that 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.
- *
- */
-
-#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/io.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/regmap.h>
-#include <linux/rockchip/cpu.h>
-#include <linux/rockchip/cru.h>
-#include <linux/rockchip/grf.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/spinlock.h>
-#include <linux/workqueue.h>
-
-#include "rk_pcm.h"
-#include "rk_i2s.h"
-
-#define CLK_SET_LATER
-#define I2S_DEFAULT_FREQ       (11289600)
-#define I2S_DMA_BURST_SIZE     (16) /* size * width: 16*4 = 64 bytes */
-static DEFINE_SPINLOCK(lock);
-
-#if defined(CONFIG_RK_HDMI) && defined(CONFIG_SND_RK_SOC_HDMI_I2S)
-extern int snd_config_hdmi_audio(struct snd_pcm_hw_params *params);
-#endif
-
-struct rk_i2s_dev {
-       struct device *dev;
-       struct clk *clk; /* bclk */
-       struct clk *mclk; /*mclk output only */
-       struct clk *hclk; /*ahb clk */
-       struct snd_dmaengine_dai_dma_data capture_dma_data;
-       struct snd_dmaengine_dai_dma_data playback_dma_data;
-       struct regmap *regmap;
-       bool tx_start;
-       bool rx_start;
-#ifdef CLK_SET_LATER
-       struct delayed_work clk_delayed_work;
-#endif
-};
-
-static inline struct rk_i2s_dev *to_info(struct snd_soc_dai *dai)
-{
-       return snd_soc_dai_get_drvdata(dai);
-}
-
-static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on)
-{
-       unsigned long flags;
-       unsigned int val = 0;
-       int retry = 10;
-
-       spin_lock_irqsave(&lock, flags);
-
-       dev_dbg(i2s->dev, "%s: %d: on: %d\n", __func__, __LINE__, on);
-
-       if (on) {
-               regmap_update_bits(i2s->regmap, I2S_DMACR,
-                                  I2S_DMACR_TDE_MASK, I2S_DMACR_TDE_ENABLE);
-
-               regmap_update_bits(i2s->regmap, I2S_XFER,
-                                  I2S_XFER_TXS_MASK | I2S_XFER_RXS_MASK,
-                                  I2S_XFER_TXS_START | I2S_XFER_RXS_START);
-
-               i2s->tx_start = true;
-       } else {
-               i2s->tx_start = false;
-
-               regmap_update_bits(i2s->regmap, I2S_DMACR,
-                                  I2S_DMACR_TDE_MASK, I2S_DMACR_TDE_DISABLE);
-
-
-               if (!i2s->rx_start) {
-                       regmap_update_bits(i2s->regmap, I2S_XFER,
-                                          I2S_XFER_TXS_MASK |
-                                          I2S_XFER_RXS_MASK,
-                                          I2S_XFER_TXS_STOP |
-                                          I2S_XFER_RXS_STOP);
-
-                       regmap_update_bits(i2s->regmap, I2S_CLR,
-                                          I2S_CLR_TXC_MASK | I2S_CLR_RXC_MASK,
-                                          I2S_CLR_TXC | I2S_CLR_RXC);
-
-                       regmap_read(i2s->regmap, I2S_CLR, &val);
-
-                       /* Should wait for clear operation to finish */
-                       while (val) {
-                               regmap_read(i2s->regmap, I2S_CLR, &val);
-                               retry--;
-                               if (!retry) {
-                                       dev_warn(i2s->dev, "fail to clear\n");
-                                       break;
-                               }
-                       }
-                       dev_dbg(i2s->dev, "%s: %d: stop xfer\n",
-                               __func__, __LINE__);
-               }
-       }
-
-       spin_unlock_irqrestore(&lock, flags);
-}
-
-static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on)
-{
-       unsigned long flags;
-       unsigned int val = 0;
-       int retry = 10;
-
-       spin_lock_irqsave(&lock, flags);
-
-       dev_dbg(i2s->dev, "%s: %d: on: %d\n", __func__, __LINE__, on);
-
-       if (on) {
-               regmap_update_bits(i2s->regmap, I2S_DMACR,
-                                  I2S_DMACR_RDE_MASK, I2S_DMACR_RDE_ENABLE);
-
-               regmap_update_bits(i2s->regmap, I2S_XFER,
-                                  I2S_XFER_TXS_MASK | I2S_XFER_RXS_MASK,
-                                  I2S_XFER_TXS_START | I2S_XFER_RXS_START);
-
-               i2s->rx_start = true;
-       } else {
-               i2s->rx_start = false;
-
-               regmap_update_bits(i2s->regmap, I2S_DMACR,
-                                  I2S_DMACR_RDE_MASK, I2S_DMACR_RDE_DISABLE);
-
-               if (!i2s->tx_start) {
-                       regmap_update_bits(i2s->regmap, I2S_XFER,
-                                          I2S_XFER_TXS_MASK |
-                                          I2S_XFER_RXS_MASK,
-                                          I2S_XFER_TXS_STOP |
-                                          I2S_XFER_RXS_STOP);
-
-                       regmap_update_bits(i2s->regmap, I2S_CLR,
-                                          I2S_CLR_TXC_MASK | I2S_CLR_RXC_MASK,
-                                          I2S_CLR_TXC | I2S_CLR_RXC);
-
-                       regmap_read(i2s->regmap, I2S_CLR, &val);
-
-                       /* Should wait for clear operation to finish */
-                       while (val) {
-                               regmap_read(i2s->regmap, I2S_CLR, &val);
-                               retry--;
-                               if (!retry) {
-                                       dev_warn(i2s->dev, "fail to clear\n");
-                                       break;
-                               }
-                       }
-                       dev_dbg(i2s->dev, "%s: %d: stop xfer\n",
-                               __func__, __LINE__);
-               }
-       }
-
-       spin_unlock_irqrestore(&lock, flags);
-}
-
-static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
-                               unsigned int fmt)
-{
-       struct rk_i2s_dev *i2s = to_info(cpu_dai);
-       unsigned int mask = 0, val = 0;
-       int ret = 0;
-       unsigned long flags;
-
-       spin_lock_irqsave(&lock, flags);
-
-       mask = I2S_CKR_MSS_MASK;
-       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-       case SND_SOC_DAIFMT_CBS_CFS:
-               /* Codec is slave, so set cpu master */
-               val = I2S_CKR_MSS_MASTER;
-               break;
-       case SND_SOC_DAIFMT_CBM_CFM:
-               /* Codec is master, so set cpu slave */
-               val = I2S_CKR_MSS_SLAVE;
-               break;
-       default:
-               ret = -EINVAL;
-               goto err_fmt;
-       }
-
-       regmap_update_bits(i2s->regmap, I2S_CKR, mask, val);
-
-       mask = I2S_TXCR_IBM_MASK;
-       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-       case SND_SOC_DAIFMT_RIGHT_J:
-               val = I2S_TXCR_IBM_RSJM;
-               break;
-       case SND_SOC_DAIFMT_LEFT_J:
-               val = I2S_TXCR_IBM_LSJM;
-               break;
-       case SND_SOC_DAIFMT_I2S:
-               val = I2S_TXCR_IBM_NORMAL;
-               break;
-       default:
-               ret = -EINVAL;
-               goto err_fmt;
-       }
-
-       regmap_update_bits(i2s->regmap, I2S_TXCR, mask, val);
-
-       mask = I2S_RXCR_IBM_MASK;
-       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-       case SND_SOC_DAIFMT_RIGHT_J:
-               val = I2S_RXCR_IBM_RSJM;
-               break;
-       case SND_SOC_DAIFMT_LEFT_J:
-               val = I2S_RXCR_IBM_LSJM;
-               break;
-       case SND_SOC_DAIFMT_I2S:
-               val = I2S_RXCR_IBM_NORMAL;
-               break;
-       default:
-               ret = -EINVAL;
-               goto err_fmt;
-       }
-
-       regmap_update_bits(i2s->regmap, I2S_RXCR, mask, val);
-
-err_fmt:
-
-       spin_unlock_irqrestore(&lock, flags);
-       return ret;
-}
-
-static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
-                                 struct snd_pcm_hw_params *params,
-                                 struct snd_soc_dai *dai)
-{
-       struct rk_i2s_dev *i2s = to_info(dai);
-       unsigned int val = 0;
-       unsigned long flags;
-
-       spin_lock_irqsave(&lock, flags);
-
-       dev_dbg(i2s->dev, "%s: %d\n", __func__, __LINE__);
-
-       switch (params_format(params)) {
-       case SNDRV_PCM_FORMAT_S8:
-               val |= I2S_TXCR_VDW(8);
-               break;
-       case SNDRV_PCM_FORMAT_S16_LE:
-               val |= I2S_TXCR_VDW(16);
-               break;
-       case SNDRV_PCM_FORMAT_S20_3LE:
-               val |= I2S_TXCR_VDW(20);
-               break;
-       case SNDRV_PCM_FORMAT_S24_LE:
-       case SNDRV_PCM_FORMAT_S24_3LE:
-               val |= I2S_TXCR_VDW(24);
-               break;
-       case SNDRV_PCM_FORMAT_S32_LE:
-               val |= I2S_TXCR_VDW(32);
-               break;
-       default:
-               dev_err(i2s->dev, "invalid fmt: %d\n", params_format(params));
-               spin_unlock_irqrestore(&lock, flags);
-               return -EINVAL;
-       }
-
-       switch (params_channels(params)) {
-       case I2S_CHANNEL_8:
-               val |= I2S_TXCR_CHN_8;
-               break;
-       case I2S_CHANNEL_6:
-               val |= I2S_TXCR_CHN_6;
-               break;
-       case I2S_CHANNEL_4:
-               val |= I2S_TXCR_CHN_4;
-               break;
-       case I2S_CHANNEL_2:
-               val |= I2S_TXCR_CHN_2;
-               break;
-       default:
-               dev_err(i2s->dev, "invalid channel: %d\n",
-                       params_channels(params));
-               spin_unlock_irqrestore(&lock, flags);
-               return -EINVAL;
-       }
-
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               regmap_update_bits(i2s->regmap, I2S_TXCR,
-                                  I2S_TXCR_VDW_MASK |
-                                  I2S_TXCR_CSR_MASK,
-                                  val);
-       } else {
-               regmap_update_bits(i2s->regmap, I2S_RXCR,
-                                  I2S_RXCR_VDW_MASK, val);
-       }
-
-       regmap_update_bits(i2s->regmap, I2S_DMACR,
-                          I2S_DMACR_TDL_MASK | I2S_DMACR_RDL_MASK,
-                          I2S_DMACR_TDL(16) | I2S_DMACR_RDL(16));
-
-#if defined(CONFIG_RK_HDMI) && defined(CONFIG_SND_RK_SOC_HDMI_I2S)
-       snd_config_hdmi_audio(params);
-#endif
-       spin_unlock_irqrestore(&lock, flags);
-
-       return 0;
-}
-
-static int rockchip_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
-                               struct snd_soc_dai *dai)
-{
-       struct rk_i2s_dev *i2s = to_info(dai);
-       int ret = 0;
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-       case SNDRV_PCM_TRIGGER_RESUME:
-       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
-                       rockchip_snd_rxctrl(i2s, 1);
-               else
-                       rockchip_snd_txctrl(i2s, 1);
-               break;
-       case SNDRV_PCM_TRIGGER_SUSPEND:
-       case SNDRV_PCM_TRIGGER_STOP:
-       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
-                       rockchip_snd_rxctrl(i2s, 0);
-               else
-                       rockchip_snd_txctrl(i2s, 0);
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-
-       return ret;
-}
-
-static int rockchip_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
-                                  int clk_id, unsigned int freq, int dir)
-{
-       struct rk_i2s_dev *i2s = to_info(cpu_dai);
-       int ret;
-
-       ret = clk_set_rate(i2s->clk, freq);
-       if (ret)
-               dev_err(i2s->dev, "fail set clk: freq: %d\n", freq);
-
-       return ret;
-}
-
-static int rockchip_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
-                                  int div_id, int div)
-{
-       struct rk_i2s_dev *i2s = to_info(cpu_dai);
-       unsigned int val = 0;
-       unsigned long flags;
-
-       spin_lock_irqsave(&lock, flags);
-
-       dev_dbg(i2s->dev, "%s: div_id=%d, div=%d\n", __func__, div_id, div);
-
-       switch (div_id) {
-       case ROCKCHIP_DIV_BCLK:
-               val |= I2S_CKR_TSD(div);
-               val |= I2S_CKR_RSD(div);
-               regmap_update_bits(i2s->regmap, I2S_CKR,
-                                  I2S_CKR_TSD_MASK | I2S_CKR_RSD_MASK,
-                                  val);
-               break;
-       case ROCKCHIP_DIV_MCLK:
-               val |= I2S_CKR_MDIV(div);
-               regmap_update_bits(i2s->regmap, I2S_CKR,
-                                  I2S_CKR_MDIV_MASK, val);
-               break;
-       default:
-               spin_unlock_irqrestore(&lock, flags);
-               return -EINVAL;
-       }
-
-       spin_unlock_irqrestore(&lock, flags);
-
-       return 0;
-}
-
-static int rockchip_i2s_dai_probe(struct snd_soc_dai *dai)
-{
-       struct rk_i2s_dev *i2s = to_info(dai);
-
-       dai->capture_dma_data = &i2s->capture_dma_data;
-       dai->playback_dma_data = &i2s->playback_dma_data;
-
-       return 0;
-}
-
-static struct snd_soc_dai_ops rockchip_i2s_dai_ops = {
-       .trigger = rockchip_i2s_trigger,
-       .hw_params = rockchip_i2s_hw_params,
-       .set_fmt = rockchip_i2s_set_fmt,
-       .set_clkdiv = rockchip_i2s_set_clkdiv,
-       .set_sysclk = rockchip_i2s_set_sysclk,
-};
-
-#define ROCKCHIP_I2S_RATES SNDRV_PCM_RATE_8000_192000
-#define ROCKCHIP_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
-                             SNDRV_PCM_FMTBIT_S20_3LE | \
-                             SNDRV_PCM_FMTBIT_S24_LE | \
-                             SNDRV_PCM_FORMAT_S32_LE)
-
-struct snd_soc_dai_driver rockchip_i2s_dai[] = {
-       {
-               .probe = rockchip_i2s_dai_probe,
-               .name = "rockchip-i2s.0",
-               .id = 0,
-               .playback = {
-                       .channels_min = 2,
-                       .channels_max = 8,
-                       .rates = ROCKCHIP_I2S_RATES,
-                       .formats = ROCKCHIP_I2S_FORMATS,
-               },
-               .capture = {
-                       .channels_min = 2,
-                       .channels_max = 2,
-                       .rates = ROCKCHIP_I2S_RATES,
-                       .formats = ROCKCHIP_I2S_FORMATS,
-               },
-               .ops = &rockchip_i2s_dai_ops,
-               .symmetric_rates = 1,
-       },
-       {
-               .probe = rockchip_i2s_dai_probe,
-               .name = "rockchip-i2s.1",
-               .id = 1,
-               .playback = {
-                       .channels_min = 2,
-                       .channels_max = 2,
-                       .rates = ROCKCHIP_I2S_RATES,
-                       .formats = ROCKCHIP_I2S_FORMATS,
-               },
-               .capture = {
-                       .channels_min = 2,
-                       .channels_max = 2,
-                       .rates = ROCKCHIP_I2S_RATES,
-                       .formats = ROCKCHIP_I2S_FORMATS,
-               },
-               .ops = &rockchip_i2s_dai_ops,
-               .symmetric_rates = 1,
-       },
-};
-
-static const struct snd_soc_component_driver rockchip_i2s_component = {
-       .name = "rockchip-i2s",
-};
-
-#ifdef CONFIG_PM
-static int i2s_runtime_suspend(struct device *dev)
-{
-       struct rk_i2s_dev *i2s = dev_get_drvdata(dev);
-
-       dev_dbg(i2s->dev, "%s\n", __func__);
-       return pinctrl_pm_select_sleep_state(dev);
-}
-
-static int i2s_runtime_resume(struct device *dev)
-{
-       struct rk_i2s_dev *i2s = dev_get_drvdata(dev);
-
-       dev_dbg(i2s->dev, "%s\n", __func__);
-       return pinctrl_pm_select_default_state(dev);
-}
-#else
-#define i2s_runtime_suspend NULL
-#define i2s_runtime_resume NULL
-#endif
-
-#ifdef CLK_SET_LATER
-static void set_clk_later_work(struct work_struct *work)
-{
-       struct rk_i2s_dev *i2s = container_of(work, struct rk_i2s_dev,
-                                                clk_delayed_work.work);
-
-       clk_set_rate(i2s->clk, I2S_DEFAULT_FREQ);
-       if (!IS_ERR(i2s->mclk))
-               clk_set_rate(i2s->mclk, I2S_DEFAULT_FREQ);
-}
-#endif
-
-static bool rockchip_i2s_wr_reg(struct device *dev, unsigned int reg)
-{
-       switch (reg) {
-       case I2S_TXCR:
-       case I2S_RXCR:
-       case I2S_CKR:
-       case I2S_DMACR:
-       case I2S_INTCR:
-       case I2S_XFER:
-       case I2S_CLR:
-       case I2S_TXDR:
-               return true;
-       default:
-               return false;
-       }
-}
-
-static bool rockchip_i2s_rd_reg(struct device *dev, unsigned int reg)
-{
-       switch (reg) {
-       case I2S_TXCR:
-       case I2S_RXCR:
-       case I2S_CKR:
-       case I2S_DMACR:
-       case I2S_INTCR:
-       case I2S_XFER:
-       case I2S_CLR:
-       case I2S_RXDR:
-       case I2S_FIFOLR:
-       case I2S_INTSR:
-               return true;
-       default:
-               return false;
-       }
-}
-
-static bool rockchip_i2s_volatile_reg(struct device *dev, unsigned int reg)
-{
-       switch (reg) {
-       case I2S_INTSR:
-       case I2S_CLR:
-               return true;
-       default:
-               return false;
-       }
-}
-
-static bool rockchip_i2s_precious_reg(struct device *dev, unsigned int reg)
-{
-       switch (reg) {
-       default:
-               return false;
-       }
-}
-
-static const struct regmap_config rockchip_i2s_regmap_config = {
-       .reg_bits = 32,
-       .reg_stride = 4,
-       .val_bits = 32,
-       .max_register = I2S_RXDR,
-       .writeable_reg = rockchip_i2s_wr_reg,
-       .readable_reg = rockchip_i2s_rd_reg,
-       .volatile_reg = rockchip_i2s_volatile_reg,
-       .precious_reg = rockchip_i2s_precious_reg,
-       .cache_type = REGCACHE_FLAT,
-};
-
-static int rockchip_i2s_probe(struct platform_device *pdev)
-{
-       struct device_node *node = pdev->dev.of_node;
-       struct rk_i2s_dev *i2s;
-       struct resource *res;
-       void __iomem *regs;
-       int ret;
-
-       ret = of_property_read_u32(node, "i2s-id", &pdev->id);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "Property 'i2s-id' missing or invalid\n");
-               ret = -EINVAL;
-               goto err;
-       }
-
-       if (soc_is_rk3126b()) {
-               int sdi_src = 0;
-
-               /* rk3126b has no i2s1 controller(i2s_8ch) */
-               if (1 == pdev->id) {
-                       pr_info("rk3126b has no i2s1 controller\n");
-                       ret = -ENODEV;
-                       goto err;
-               }
-
-               ret = of_property_read_u32(node, "sdi_source",
-                                          &sdi_src);
-               if (ret < 0)
-                       sdi_src = 0;
-
-               if (1 == sdi_src) {
-                       int val;
-
-                       /*GRF_SOC_CON*/
-                       val = readl_relaxed(RK_GRF_VIRT + 0x0140);
-                       val = val | 0x04000400;
-                       writel_relaxed(val, RK_GRF_VIRT + 0x0140);
-               }
-       }
-
-       i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
-       if (!i2s) {
-               dev_err(&pdev->dev, "Can't allocate rk_i2s_dev\n");
-               ret = -ENOMEM;
-               goto err;
-       }
-
-       i2s->hclk = devm_clk_get(&pdev->dev, "i2s_hclk");
-       if (IS_ERR(i2s->hclk)) {
-               dev_err(&pdev->dev, "Can't retrieve i2s bus clock\n");
-               ret = PTR_ERR(i2s->hclk);
-               goto err;
-       } else {
-               clk_prepare_enable(i2s->hclk);
-       }
-
-       i2s->clk = devm_clk_get(&pdev->dev, "i2s_clk");
-       if (IS_ERR(i2s->clk)) {
-               dev_err(&pdev->dev, "Can't retrieve i2s clock\n");
-               ret = PTR_ERR(i2s->clk);
-               goto err;
-       }
-#ifdef CLK_SET_LATER
-       INIT_DELAYED_WORK(&i2s->clk_delayed_work, set_clk_later_work);
-       schedule_delayed_work(&i2s->clk_delayed_work, msecs_to_jiffies(10));
-#else
-       clk_set_rate(i2s->clk, I2S_DEFAULT_FREQ);
-#endif
-       clk_prepare_enable(i2s->clk);
-
-       i2s->mclk = devm_clk_get(&pdev->dev, "i2s_mclk");
-       if (IS_ERR(i2s->mclk)) {
-               dev_info(&pdev->dev, "i2s%d has no mclk\n", pdev->id);
-       } else {
-       #ifndef CLK_SET_LATER
-               clk_set_rate(i2s->mclk, I2S_DEFAULT_FREQ);
-       #endif
-               clk_prepare_enable(i2s->mclk);
-       }
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       regs = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(regs)) {
-               ret = PTR_ERR(regs);
-               goto err;
-       }
-
-       i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
-                                           &rockchip_i2s_regmap_config);
-       if (IS_ERR(i2s->regmap)) {
-               dev_err(&pdev->dev,
-                       "Failed to initialise managed register map\n");
-               ret = PTR_ERR(i2s->regmap);
-               goto err;
-       }
-
-       i2s->playback_dma_data.addr = res->start + I2S_TXDR;
-       i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-       i2s->playback_dma_data.maxburst = I2S_DMA_BURST_SIZE;
-
-       i2s->capture_dma_data.addr = res->start + I2S_RXDR;
-       i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-       i2s->capture_dma_data.maxburst = I2S_DMA_BURST_SIZE;
-
-       i2s->tx_start = false;
-       i2s->rx_start = false;
-
-       i2s->dev = &pdev->dev;
-       dev_set_drvdata(&pdev->dev, i2s);
-
-       pm_runtime_enable(&pdev->dev);
-       if (!pm_runtime_enabled(&pdev->dev)) {
-               ret = i2s_runtime_resume(&pdev->dev);
-               if (ret)
-                       goto err_pm_disable;
-       }
-
-       ret = snd_soc_register_component(&pdev->dev, &rockchip_i2s_component,
-                                        &rockchip_i2s_dai[pdev->id], 1);
-
-       if (ret) {
-               dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
-               ret = -ENOMEM;
-               goto err_suspend;
-       }
-
-       ret = rockchip_pcm_platform_register(&pdev->dev);
-       if (ret) {
-               dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
-               goto err_unregister_component;
-       }
-
-       rockchip_snd_txctrl(i2s, 0);
-       rockchip_snd_rxctrl(i2s, 0);
-
-       return 0;
-
-err_unregister_component:
-       snd_soc_unregister_component(&pdev->dev);
-err_suspend:
-       if (!pm_runtime_status_suspended(&pdev->dev))
-               i2s_runtime_suspend(&pdev->dev);
-err_pm_disable:
-       pm_runtime_disable(&pdev->dev);
-err:
-       return ret;
-}
-
-static int rockchip_i2s_remove(struct platform_device *pdev)
-{
-       struct rk_i2s_dev *i2s = dev_get_drvdata(&pdev->dev);
-
-       pm_runtime_disable(&pdev->dev);
-       if (!pm_runtime_status_suspended(&pdev->dev))
-               i2s_runtime_suspend(&pdev->dev);
-
-       if (!IS_ERR(i2s->mclk))
-               clk_disable_unprepare(i2s->mclk);
-
-       clk_disable_unprepare(i2s->clk);
-       clk_disable_unprepare(i2s->hclk);
-       rockchip_pcm_platform_unregister(&pdev->dev);
-       snd_soc_unregister_component(&pdev->dev);
-
-       return 0;
-}
-
-#ifdef CONFIG_OF
-static const struct of_device_id rockchip_i2s_match[] = {
-       { .compatible = "rockchip-i2s", },
-       {},
-};
-MODULE_DEVICE_TABLE(of, rockchip_i2s_match);
-#endif
-
-static const struct dev_pm_ops rockchip_i2s_pm_ops = {
-       SET_RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume,
-                          NULL)
-};
-
-static struct platform_driver rockchip_i2s_driver = {
-       .probe  = rockchip_i2s_probe,
-       .remove = rockchip_i2s_remove,
-       .driver = {
-               .name   = "rockchip-i2s",
-               .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(rockchip_i2s_match),
-               .pm     = &rockchip_i2s_pm_ops,
-       },
-};
-
-static int __init rockchip_i2s_init(void)
-{
-       return platform_driver_register(&rockchip_i2s_driver);
-}
-subsys_initcall_sync(rockchip_i2s_init);
-
-static void __exit rockchip_i2s_exit(void)
-{
-       platform_driver_unregister(&rockchip_i2s_driver);
-}
-module_exit(rockchip_i2s_exit);
-
-MODULE_AUTHOR("Sugar <sugar.zhang@rock-chips.com>");
-MODULE_DESCRIPTION("Rockchip I2S Controller Driver");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/rockchip/rk_i2s.c b/sound/soc/rockchip/rk_i2s.c
new file mode 100644 (file)
index 0000000..d551860
--- /dev/null
@@ -0,0 +1,785 @@
+/*
+ * Rockchip I2S ALSA SoC Digital Audio Interface(DAI)  driver
+ *
+ * Copyright (C) 2015 Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ */
+
+#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/io.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/rockchip/cpu.h>
+#include <linux/rockchip/cru.h>
+#include <linux/rockchip/grf.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/spinlock.h>
+#include <linux/workqueue.h>
+
+#include "rk_pcm.h"
+#include "rk_i2s.h"
+
+#define CLK_SET_LATER
+#define I2S_DEFAULT_FREQ       (11289600)
+#define I2S_DMA_BURST_SIZE     (16) /* size * width: 16*4 = 64 bytes */
+static DEFINE_SPINLOCK(lock);
+
+#if defined(CONFIG_RK_HDMI) && defined(CONFIG_SND_RK_SOC_HDMI_I2S)
+extern int snd_config_hdmi_audio(struct snd_pcm_hw_params *params);
+#endif
+
+struct rk_i2s_dev {
+       struct device *dev;
+       struct clk *clk; /* bclk */
+       struct clk *mclk; /*mclk output only */
+       struct clk *hclk; /*ahb clk */
+       struct snd_dmaengine_dai_dma_data capture_dma_data;
+       struct snd_dmaengine_dai_dma_data playback_dma_data;
+       struct regmap *regmap;
+       bool tx_start;
+       bool rx_start;
+#ifdef CLK_SET_LATER
+       struct delayed_work clk_delayed_work;
+#endif
+};
+
+static inline struct rk_i2s_dev *to_info(struct snd_soc_dai *dai)
+{
+       return snd_soc_dai_get_drvdata(dai);
+}
+
+static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on)
+{
+       unsigned long flags;
+       unsigned int val = 0;
+       int retry = 10;
+
+       spin_lock_irqsave(&lock, flags);
+
+       dev_dbg(i2s->dev, "%s: %d: on: %d\n", __func__, __LINE__, on);
+
+       if (on) {
+               regmap_update_bits(i2s->regmap, I2S_DMACR,
+                                  I2S_DMACR_TDE_MASK, I2S_DMACR_TDE_ENABLE);
+
+               regmap_update_bits(i2s->regmap, I2S_XFER,
+                                  I2S_XFER_TXS_MASK | I2S_XFER_RXS_MASK,
+                                  I2S_XFER_TXS_START | I2S_XFER_RXS_START);
+
+               i2s->tx_start = true;
+       } else {
+               i2s->tx_start = false;
+
+               regmap_update_bits(i2s->regmap, I2S_DMACR,
+                                  I2S_DMACR_TDE_MASK, I2S_DMACR_TDE_DISABLE);
+
+
+               if (!i2s->rx_start) {
+                       regmap_update_bits(i2s->regmap, I2S_XFER,
+                                          I2S_XFER_TXS_MASK |
+                                          I2S_XFER_RXS_MASK,
+                                          I2S_XFER_TXS_STOP |
+                                          I2S_XFER_RXS_STOP);
+
+                       regmap_update_bits(i2s->regmap, I2S_CLR,
+                                          I2S_CLR_TXC_MASK | I2S_CLR_RXC_MASK,
+                                          I2S_CLR_TXC | I2S_CLR_RXC);
+
+                       regmap_read(i2s->regmap, I2S_CLR, &val);
+
+                       /* Should wait for clear operation to finish */
+                       while (val) {
+                               regmap_read(i2s->regmap, I2S_CLR, &val);
+                               retry--;
+                               if (!retry) {
+                                       dev_warn(i2s->dev, "fail to clear\n");
+                                       break;
+                               }
+                       }
+                       dev_dbg(i2s->dev, "%s: %d: stop xfer\n",
+                               __func__, __LINE__);
+               }
+       }
+
+       spin_unlock_irqrestore(&lock, flags);
+}
+
+static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on)
+{
+       unsigned long flags;
+       unsigned int val = 0;
+       int retry = 10;
+
+       spin_lock_irqsave(&lock, flags);
+
+       dev_dbg(i2s->dev, "%s: %d: on: %d\n", __func__, __LINE__, on);
+
+       if (on) {
+               regmap_update_bits(i2s->regmap, I2S_DMACR,
+                                  I2S_DMACR_RDE_MASK, I2S_DMACR_RDE_ENABLE);
+
+               regmap_update_bits(i2s->regmap, I2S_XFER,
+                                  I2S_XFER_TXS_MASK | I2S_XFER_RXS_MASK,
+                                  I2S_XFER_TXS_START | I2S_XFER_RXS_START);
+
+               i2s->rx_start = true;
+       } else {
+               i2s->rx_start = false;
+
+               regmap_update_bits(i2s->regmap, I2S_DMACR,
+                                  I2S_DMACR_RDE_MASK, I2S_DMACR_RDE_DISABLE);
+
+               if (!i2s->tx_start) {
+                       regmap_update_bits(i2s->regmap, I2S_XFER,
+                                          I2S_XFER_TXS_MASK |
+                                          I2S_XFER_RXS_MASK,
+                                          I2S_XFER_TXS_STOP |
+                                          I2S_XFER_RXS_STOP);
+
+                       regmap_update_bits(i2s->regmap, I2S_CLR,
+                                          I2S_CLR_TXC_MASK | I2S_CLR_RXC_MASK,
+                                          I2S_CLR_TXC | I2S_CLR_RXC);
+
+                       regmap_read(i2s->regmap, I2S_CLR, &val);
+
+                       /* Should wait for clear operation to finish */
+                       while (val) {
+                               regmap_read(i2s->regmap, I2S_CLR, &val);
+                               retry--;
+                               if (!retry) {
+                                       dev_warn(i2s->dev, "fail to clear\n");
+                                       break;
+                               }
+                       }
+                       dev_dbg(i2s->dev, "%s: %d: stop xfer\n",
+                               __func__, __LINE__);
+               }
+       }
+
+       spin_unlock_irqrestore(&lock, flags);
+}
+
+static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
+                               unsigned int fmt)
+{
+       struct rk_i2s_dev *i2s = to_info(cpu_dai);
+       unsigned int mask = 0, val = 0;
+       int ret = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&lock, flags);
+
+       mask = I2S_CKR_MSS_MASK;
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               /* Codec is slave, so set cpu master */
+               val = I2S_CKR_MSS_MASTER;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               /* Codec is master, so set cpu slave */
+               val = I2S_CKR_MSS_SLAVE;
+               break;
+       default:
+               ret = -EINVAL;
+               goto err_fmt;
+       }
+
+       regmap_update_bits(i2s->regmap, I2S_CKR, mask, val);
+
+       mask = I2S_TXCR_IBM_MASK;
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_RIGHT_J:
+               val = I2S_TXCR_IBM_RSJM;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               val = I2S_TXCR_IBM_LSJM;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               val = I2S_TXCR_IBM_NORMAL;
+               break;
+       default:
+               ret = -EINVAL;
+               goto err_fmt;
+       }
+
+       regmap_update_bits(i2s->regmap, I2S_TXCR, mask, val);
+
+       mask = I2S_RXCR_IBM_MASK;
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_RIGHT_J:
+               val = I2S_RXCR_IBM_RSJM;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               val = I2S_RXCR_IBM_LSJM;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               val = I2S_RXCR_IBM_NORMAL;
+               break;
+       default:
+               ret = -EINVAL;
+               goto err_fmt;
+       }
+
+       regmap_update_bits(i2s->regmap, I2S_RXCR, mask, val);
+
+err_fmt:
+
+       spin_unlock_irqrestore(&lock, flags);
+       return ret;
+}
+
+static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
+                                 struct snd_pcm_hw_params *params,
+                                 struct snd_soc_dai *dai)
+{
+       struct rk_i2s_dev *i2s = to_info(dai);
+       unsigned int val = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&lock, flags);
+
+       dev_dbg(i2s->dev, "%s: %d\n", __func__, __LINE__);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S8:
+               val |= I2S_TXCR_VDW(8);
+               break;
+       case SNDRV_PCM_FORMAT_S16_LE:
+               val |= I2S_TXCR_VDW(16);
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               val |= I2S_TXCR_VDW(20);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+       case SNDRV_PCM_FORMAT_S24_3LE:
+               val |= I2S_TXCR_VDW(24);
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               val |= I2S_TXCR_VDW(32);
+               break;
+       default:
+               dev_err(i2s->dev, "invalid fmt: %d\n", params_format(params));
+               spin_unlock_irqrestore(&lock, flags);
+               return -EINVAL;
+       }
+
+       switch (params_channels(params)) {
+       case I2S_CHANNEL_8:
+               val |= I2S_TXCR_CHN_8;
+               break;
+       case I2S_CHANNEL_6:
+               val |= I2S_TXCR_CHN_6;
+               break;
+       case I2S_CHANNEL_4:
+               val |= I2S_TXCR_CHN_4;
+               break;
+       case I2S_CHANNEL_2:
+               val |= I2S_TXCR_CHN_2;
+               break;
+       default:
+               dev_err(i2s->dev, "invalid channel: %d\n",
+                       params_channels(params));
+               spin_unlock_irqrestore(&lock, flags);
+               return -EINVAL;
+       }
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               regmap_update_bits(i2s->regmap, I2S_TXCR,
+                                  I2S_TXCR_VDW_MASK |
+                                  I2S_TXCR_CSR_MASK,
+                                  val);
+       } else {
+               regmap_update_bits(i2s->regmap, I2S_RXCR,
+                                  I2S_RXCR_VDW_MASK, val);
+       }
+
+       regmap_update_bits(i2s->regmap, I2S_DMACR,
+                          I2S_DMACR_TDL_MASK | I2S_DMACR_RDL_MASK,
+                          I2S_DMACR_TDL(16) | I2S_DMACR_RDL(16));
+
+#if defined(CONFIG_RK_HDMI) && defined(CONFIG_SND_RK_SOC_HDMI_I2S)
+       snd_config_hdmi_audio(params);
+#endif
+       spin_unlock_irqrestore(&lock, flags);
+
+       return 0;
+}
+
+static int rockchip_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+                               struct snd_soc_dai *dai)
+{
+       struct rk_i2s_dev *i2s = to_info(dai);
+       int ret = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+                       rockchip_snd_rxctrl(i2s, 1);
+               else
+                       rockchip_snd_txctrl(i2s, 1);
+               break;
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+                       rockchip_snd_rxctrl(i2s, 0);
+               else
+                       rockchip_snd_txctrl(i2s, 0);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static int rockchip_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
+                                  int clk_id, unsigned int freq, int dir)
+{
+       struct rk_i2s_dev *i2s = to_info(cpu_dai);
+       int ret;
+
+       ret = clk_set_rate(i2s->clk, freq);
+       if (ret)
+               dev_err(i2s->dev, "fail set clk: freq: %d\n", freq);
+
+       return ret;
+}
+
+static int rockchip_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
+                                  int div_id, int div)
+{
+       struct rk_i2s_dev *i2s = to_info(cpu_dai);
+       unsigned int val = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&lock, flags);
+
+       dev_dbg(i2s->dev, "%s: div_id=%d, div=%d\n", __func__, div_id, div);
+
+       switch (div_id) {
+       case ROCKCHIP_DIV_BCLK:
+               val |= I2S_CKR_TSD(div);
+               val |= I2S_CKR_RSD(div);
+               regmap_update_bits(i2s->regmap, I2S_CKR,
+                                  I2S_CKR_TSD_MASK | I2S_CKR_RSD_MASK,
+                                  val);
+               break;
+       case ROCKCHIP_DIV_MCLK:
+               val |= I2S_CKR_MDIV(div);
+               regmap_update_bits(i2s->regmap, I2S_CKR,
+                                  I2S_CKR_MDIV_MASK, val);
+               break;
+       default:
+               spin_unlock_irqrestore(&lock, flags);
+               return -EINVAL;
+       }
+
+       spin_unlock_irqrestore(&lock, flags);
+
+       return 0;
+}
+
+static int rockchip_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+       struct rk_i2s_dev *i2s = to_info(dai);
+
+       dai->capture_dma_data = &i2s->capture_dma_data;
+       dai->playback_dma_data = &i2s->playback_dma_data;
+
+       return 0;
+}
+
+static struct snd_soc_dai_ops rockchip_i2s_dai_ops = {
+       .trigger = rockchip_i2s_trigger,
+       .hw_params = rockchip_i2s_hw_params,
+       .set_fmt = rockchip_i2s_set_fmt,
+       .set_clkdiv = rockchip_i2s_set_clkdiv,
+       .set_sysclk = rockchip_i2s_set_sysclk,
+};
+
+#define ROCKCHIP_I2S_RATES SNDRV_PCM_RATE_8000_192000
+#define ROCKCHIP_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+                             SNDRV_PCM_FMTBIT_S20_3LE | \
+                             SNDRV_PCM_FMTBIT_S24_LE | \
+                             SNDRV_PCM_FORMAT_S32_LE)
+
+struct snd_soc_dai_driver rockchip_i2s_dai[] = {
+       {
+               .probe = rockchip_i2s_dai_probe,
+               .name = "rockchip-i2s.0",
+               .id = 0,
+               .playback = {
+                       .channels_min = 2,
+                       .channels_max = 8,
+                       .rates = ROCKCHIP_I2S_RATES,
+                       .formats = ROCKCHIP_I2S_FORMATS,
+               },
+               .capture = {
+                       .channels_min = 2,
+                       .channels_max = 2,
+                       .rates = ROCKCHIP_I2S_RATES,
+                       .formats = ROCKCHIP_I2S_FORMATS,
+               },
+               .ops = &rockchip_i2s_dai_ops,
+               .symmetric_rates = 1,
+       },
+       {
+               .probe = rockchip_i2s_dai_probe,
+               .name = "rockchip-i2s.1",
+               .id = 1,
+               .playback = {
+                       .channels_min = 2,
+                       .channels_max = 2,
+                       .rates = ROCKCHIP_I2S_RATES,
+                       .formats = ROCKCHIP_I2S_FORMATS,
+               },
+               .capture = {
+                       .channels_min = 2,
+                       .channels_max = 2,
+                       .rates = ROCKCHIP_I2S_RATES,
+                       .formats = ROCKCHIP_I2S_FORMATS,
+               },
+               .ops = &rockchip_i2s_dai_ops,
+               .symmetric_rates = 1,
+       },
+};
+
+static const struct snd_soc_component_driver rockchip_i2s_component = {
+       .name = "rockchip-i2s",
+};
+
+#ifdef CONFIG_PM
+static int i2s_runtime_suspend(struct device *dev)
+{
+       struct rk_i2s_dev *i2s = dev_get_drvdata(dev);
+
+       dev_dbg(i2s->dev, "%s\n", __func__);
+       return pinctrl_pm_select_sleep_state(dev);
+}
+
+static int i2s_runtime_resume(struct device *dev)
+{
+       struct rk_i2s_dev *i2s = dev_get_drvdata(dev);
+
+       dev_dbg(i2s->dev, "%s\n", __func__);
+       return pinctrl_pm_select_default_state(dev);
+}
+#else
+#define i2s_runtime_suspend NULL
+#define i2s_runtime_resume NULL
+#endif
+
+#ifdef CLK_SET_LATER
+static void set_clk_later_work(struct work_struct *work)
+{
+       struct rk_i2s_dev *i2s = container_of(work, struct rk_i2s_dev,
+                                                clk_delayed_work.work);
+
+       clk_set_rate(i2s->clk, I2S_DEFAULT_FREQ);
+       if (!IS_ERR(i2s->mclk))
+               clk_set_rate(i2s->mclk, I2S_DEFAULT_FREQ);
+}
+#endif
+
+static bool rockchip_i2s_wr_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case I2S_TXCR:
+       case I2S_RXCR:
+       case I2S_CKR:
+       case I2S_DMACR:
+       case I2S_INTCR:
+       case I2S_XFER:
+       case I2S_CLR:
+       case I2S_TXDR:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool rockchip_i2s_rd_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case I2S_TXCR:
+       case I2S_RXCR:
+       case I2S_CKR:
+       case I2S_DMACR:
+       case I2S_INTCR:
+       case I2S_XFER:
+       case I2S_CLR:
+       case I2S_RXDR:
+       case I2S_FIFOLR:
+       case I2S_INTSR:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool rockchip_i2s_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case I2S_INTSR:
+       case I2S_CLR:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool rockchip_i2s_precious_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       default:
+               return false;
+       }
+}
+
+static const struct regmap_config rockchip_i2s_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+       .max_register = I2S_RXDR,
+       .writeable_reg = rockchip_i2s_wr_reg,
+       .readable_reg = rockchip_i2s_rd_reg,
+       .volatile_reg = rockchip_i2s_volatile_reg,
+       .precious_reg = rockchip_i2s_precious_reg,
+       .cache_type = REGCACHE_FLAT,
+};
+
+static int rockchip_i2s_probe(struct platform_device *pdev)
+{
+       struct device_node *node = pdev->dev.of_node;
+       struct rk_i2s_dev *i2s;
+       struct resource *res;
+       void __iomem *regs;
+       int ret;
+
+       ret = of_property_read_u32(node, "i2s-id", &pdev->id);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Property 'i2s-id' missing or invalid\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       if (soc_is_rk3126b()) {
+               int sdi_src = 0;
+
+               /* rk3126b has no i2s1 controller(i2s_8ch) */
+               if (1 == pdev->id) {
+                       pr_info("rk3126b has no i2s1 controller\n");
+                       ret = -ENODEV;
+                       goto err;
+               }
+
+               ret = of_property_read_u32(node, "sdi_source",
+                                          &sdi_src);
+               if (ret < 0)
+                       sdi_src = 0;
+
+               if (1 == sdi_src) {
+                       int val;
+
+                       /*GRF_SOC_CON*/
+                       val = readl_relaxed(RK_GRF_VIRT + 0x0140);
+                       val = val | 0x04000400;
+                       writel_relaxed(val, RK_GRF_VIRT + 0x0140);
+               }
+       }
+
+       i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
+       if (!i2s) {
+               dev_err(&pdev->dev, "Can't allocate rk_i2s_dev\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       i2s->hclk = devm_clk_get(&pdev->dev, "i2s_hclk");
+       if (IS_ERR(i2s->hclk)) {
+               dev_err(&pdev->dev, "Can't retrieve i2s bus clock\n");
+               ret = PTR_ERR(i2s->hclk);
+               goto err;
+       } else {
+               clk_prepare_enable(i2s->hclk);
+       }
+
+       i2s->clk = devm_clk_get(&pdev->dev, "i2s_clk");
+       if (IS_ERR(i2s->clk)) {
+               dev_err(&pdev->dev, "Can't retrieve i2s clock\n");
+               ret = PTR_ERR(i2s->clk);
+               goto err;
+       }
+#ifdef CLK_SET_LATER
+       INIT_DELAYED_WORK(&i2s->clk_delayed_work, set_clk_later_work);
+       schedule_delayed_work(&i2s->clk_delayed_work, msecs_to_jiffies(10));
+#else
+       clk_set_rate(i2s->clk, I2S_DEFAULT_FREQ);
+#endif
+       clk_prepare_enable(i2s->clk);
+
+       i2s->mclk = devm_clk_get(&pdev->dev, "i2s_mclk");
+       if (IS_ERR(i2s->mclk)) {
+               dev_info(&pdev->dev, "i2s%d has no mclk\n", pdev->id);
+       } else {
+       #ifndef CLK_SET_LATER
+               clk_set_rate(i2s->mclk, I2S_DEFAULT_FREQ);
+       #endif
+               clk_prepare_enable(i2s->mclk);
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(regs)) {
+               ret = PTR_ERR(regs);
+               goto err;
+       }
+
+       i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+                                           &rockchip_i2s_regmap_config);
+       if (IS_ERR(i2s->regmap)) {
+               dev_err(&pdev->dev,
+                       "Failed to initialise managed register map\n");
+               ret = PTR_ERR(i2s->regmap);
+               goto err;
+       }
+
+       i2s->playback_dma_data.addr = res->start + I2S_TXDR;
+       i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       i2s->playback_dma_data.maxburst = I2S_DMA_BURST_SIZE;
+
+       i2s->capture_dma_data.addr = res->start + I2S_RXDR;
+       i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       i2s->capture_dma_data.maxburst = I2S_DMA_BURST_SIZE;
+
+       i2s->tx_start = false;
+       i2s->rx_start = false;
+
+       i2s->dev = &pdev->dev;
+       dev_set_drvdata(&pdev->dev, i2s);
+
+       pm_runtime_enable(&pdev->dev);
+       if (!pm_runtime_enabled(&pdev->dev)) {
+               ret = i2s_runtime_resume(&pdev->dev);
+               if (ret)
+                       goto err_pm_disable;
+       }
+
+       ret = snd_soc_register_component(&pdev->dev, &rockchip_i2s_component,
+                                        &rockchip_i2s_dai[pdev->id], 1);
+
+       if (ret) {
+               dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
+               ret = -ENOMEM;
+               goto err_suspend;
+       }
+
+       ret = rockchip_pcm_platform_register(&pdev->dev);
+       if (ret) {
+               dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
+               goto err_unregister_component;
+       }
+
+       rockchip_snd_txctrl(i2s, 0);
+       rockchip_snd_rxctrl(i2s, 0);
+
+       return 0;
+
+err_unregister_component:
+       snd_soc_unregister_component(&pdev->dev);
+err_suspend:
+       if (!pm_runtime_status_suspended(&pdev->dev))
+               i2s_runtime_suspend(&pdev->dev);
+err_pm_disable:
+       pm_runtime_disable(&pdev->dev);
+err:
+       return ret;
+}
+
+static int rockchip_i2s_remove(struct platform_device *pdev)
+{
+       struct rk_i2s_dev *i2s = dev_get_drvdata(&pdev->dev);
+
+       pm_runtime_disable(&pdev->dev);
+       if (!pm_runtime_status_suspended(&pdev->dev))
+               i2s_runtime_suspend(&pdev->dev);
+
+       if (!IS_ERR(i2s->mclk))
+               clk_disable_unprepare(i2s->mclk);
+
+       clk_disable_unprepare(i2s->clk);
+       clk_disable_unprepare(i2s->hclk);
+       rockchip_pcm_platform_unregister(&pdev->dev);
+       snd_soc_unregister_component(&pdev->dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rockchip_i2s_match[] = {
+       { .compatible = "rockchip-i2s", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rockchip_i2s_match);
+#endif
+
+static const struct dev_pm_ops rockchip_i2s_pm_ops = {
+       SET_RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume,
+                          NULL)
+};
+
+static struct platform_driver rockchip_i2s_driver = {
+       .probe  = rockchip_i2s_probe,
+       .remove = rockchip_i2s_remove,
+       .driver = {
+               .name   = "rockchip-i2s",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(rockchip_i2s_match),
+               .pm     = &rockchip_i2s_pm_ops,
+       },
+};
+
+static int __init rockchip_i2s_init(void)
+{
+       return platform_driver_register(&rockchip_i2s_driver);
+}
+subsys_initcall_sync(rockchip_i2s_init);
+
+static void __exit rockchip_i2s_exit(void)
+{
+       platform_driver_unregister(&rockchip_i2s_driver);
+}
+module_exit(rockchip_i2s_exit);
+
+MODULE_AUTHOR("Sugar <sugar.zhang@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip I2S Controller Driver");
+MODULE_LICENSE("GPL v2");