ASoC: rockchip: i2s: increase dma maxburst to 16
[firefly-linux-kernel-4.4.55.git] / sound / soc / rockchip / rockchip_i2s.c
index 652e8c5ea1667185f6bb191a2cc8a3714cd74c1b..d7aa635e3693b684f32e1909d1bf5df7ed6fe52f 100644 (file)
@@ -51,12 +51,14 @@ struct rk_i2s_dev {
        bool rx_start;
        bool is_master_mode;
        const struct rk_i2s_pins *pins;
+       unsigned int bclk_fs;
 };
 
 static int i2s_runtime_suspend(struct device *dev)
 {
        struct rk_i2s_dev *i2s = dev_get_drvdata(dev);
 
+       regcache_cache_only(i2s->regmap, true);
        clk_disable_unprepare(i2s->mclk);
 
        return 0;
@@ -73,7 +75,14 @@ static int i2s_runtime_resume(struct device *dev)
                return ret;
        }
 
-       return 0;
+       regcache_cache_only(i2s->regmap, false);
+       regcache_mark_dirty(i2s->regmap);
+
+       ret = regcache_sync(i2s->regmap);
+       if (ret)
+               clk_disable_unprepare(i2s->mclk);
+
+       return ret;
 }
 
 static inline struct rk_i2s_dev *to_info(struct snd_soc_dai *dai)
@@ -108,6 +117,7 @@ static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on)
                                           I2S_XFER_TXS_STOP |
                                           I2S_XFER_RXS_STOP);
 
+                       udelay(150);
                        regmap_update_bits(i2s->regmap, I2S_CLR,
                                           I2S_CLR_TXC | I2S_CLR_RXC,
                                           I2S_CLR_TXC | I2S_CLR_RXC);
@@ -154,6 +164,7 @@ static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on)
                                           I2S_XFER_TXS_STOP |
                                           I2S_XFER_RXS_STOP);
 
+                       udelay(150);
                        regmap_update_bits(i2s->regmap, I2S_CLR,
                                           I2S_CLR_TXC | I2S_CLR_RXC,
                                           I2S_CLR_TXC | I2S_CLR_RXC);
@@ -196,7 +207,21 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
 
        regmap_update_bits(i2s->regmap, I2S_CKR, mask, val);
 
-       mask = I2S_TXCR_IBM_MASK;
+       mask = I2S_CKR_CKP_MASK;
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               val = I2S_CKR_CKP_NEG;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               val = I2S_CKR_CKP_POS;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(i2s->regmap, I2S_CKR, mask, val);
+
+       mask = I2S_TXCR_IBM_MASK | I2S_TXCR_TFS_MASK | I2S_TXCR_PBM_MASK;
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_RIGHT_J:
                val = I2S_TXCR_IBM_RSJM;
@@ -207,13 +232,19 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
        case SND_SOC_DAIFMT_I2S:
                val = I2S_TXCR_IBM_NORMAL;
                break;
+       case SND_SOC_DAIFMT_DSP_A: /* PCM no delay mode */
+               val = I2S_TXCR_TFS_PCM;
+               break;
+       case SND_SOC_DAIFMT_DSP_B: /* PCM delay 1 mode */
+               val = I2S_TXCR_TFS_PCM | I2S_TXCR_PBM_MODE(1);
+               break;
        default:
                return -EINVAL;
        }
 
        regmap_update_bits(i2s->regmap, I2S_TXCR, mask, val);
 
-       mask = I2S_RXCR_IBM_MASK;
+       mask = I2S_RXCR_IBM_MASK | I2S_RXCR_TFS_MASK | I2S_RXCR_PBM_MASK;
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_RIGHT_J:
                val = I2S_RXCR_IBM_RSJM;
@@ -224,6 +255,12 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
        case SND_SOC_DAIFMT_I2S:
                val = I2S_RXCR_IBM_NORMAL;
                break;
+       case SND_SOC_DAIFMT_DSP_A: /* PCM no delay mode */
+               val = I2S_RXCR_TFS_PCM;
+               break;
+       case SND_SOC_DAIFMT_DSP_B: /* PCM delay 1 mode */
+               val = I2S_RXCR_TFS_PCM | I2S_RXCR_PBM_MODE(1);
+               break;
        default:
                return -EINVAL;
        }
@@ -244,7 +281,7 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
 
        if (i2s->is_master_mode) {
                mclk_rate = clk_get_rate(i2s->mclk);
-               bclk_rate = 2 * 32 * params_rate(params);
+               bclk_rate = i2s->bclk_fs * params_rate(params);
                if (bclk_rate && mclk_rate % bclk_rate)
                        return -EINVAL;
 
@@ -592,11 +629,11 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
 
        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 = 4;
+       i2s->playback_dma_data.maxburst = 16;
 
        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 = 4;
+       i2s->capture_dma_data.maxburst = 16;
 
        dev_set_drvdata(&pdev->dev, i2s);
 
@@ -623,6 +660,12 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
                        soc_dai->capture.channels_max = val;
        }
 
+       i2s->bclk_fs = 64;
+       if (!of_property_read_u32(node, "rockchip,bclk-fs", &val)) {
+               if ((val >= 32) && (val % 2 == 0))
+                       i2s->bclk_fs = val;
+       }
+
        ret = devm_snd_soc_register_component(&pdev->dev,
                                              &rockchip_i2s_component,
                                              soc_dai, 1);
@@ -663,9 +706,35 @@ static int rockchip_i2s_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int rockchip_i2s_suspend(struct device *dev)
+{
+       struct rk_i2s_dev *i2s = dev_get_drvdata(dev);
+
+       regcache_mark_dirty(i2s->regmap);
+
+       return 0;
+}
+
+static int rockchip_i2s_resume(struct device *dev)
+{
+       struct rk_i2s_dev *i2s = dev_get_drvdata(dev);
+       int ret;
+
+       ret = pm_runtime_get_sync(dev);
+       if (ret < 0)
+               return ret;
+       ret = regcache_sync(i2s->regmap);
+       pm_runtime_put(dev);
+
+       return ret;
+}
+#endif
+
 static const struct dev_pm_ops rockchip_i2s_pm_ops = {
        SET_RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume,
                           NULL)
+       SET_SYSTEM_SLEEP_PM_OPS(rockchip_i2s_suspend, rockchip_i2s_resume)
 };
 
 static struct platform_driver rockchip_i2s_driver = {