From f45e756958099c35d6afb746df1d40a1c6302cfc Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Tue, 07 Nov 2023 06:20:23 +0000
Subject: [PATCH] enable wifi gpio
---
kernel/sound/soc/rockchip/rockchip_i2s_tdm.c | 951 +++++++++++++++++++++++++++++++++++++++-------------------
1 files changed, 635 insertions(+), 316 deletions(-)
diff --git a/kernel/sound/soc/rockchip/rockchip_i2s_tdm.c b/kernel/sound/soc/rockchip/rockchip_i2s_tdm.c
index bcc2b76..1824ea5 100644
--- a/kernel/sound/soc/rockchip/rockchip_i2s_tdm.c
+++ b/kernel/sound/soc/rockchip/rockchip_i2s_tdm.c
@@ -35,10 +35,15 @@
#endif
#define DEFAULT_MCLK_FS 256
+#define DEFAULT_FS 48000
#define CH_GRP_MAX 4 /* The max channel 8 / 2 */
#define MULTIPLEX_CH_MAX 10
#define CLK_PPM_MIN (-1000)
#define CLK_PPM_MAX (1000)
+#define MAXBURST_PER_FIFO 8
+
+#define QUIRK_ALWAYS_ON BIT(0)
+#define QUIRK_HDMI_PATH BIT(1)
struct txrx_config {
u32 addr;
@@ -79,6 +84,7 @@
struct regmap *grf;
struct snd_dmaengine_dai_dma_data capture_dma_data;
struct snd_dmaengine_dai_dma_data playback_dma_data;
+ struct snd_pcm_substream *substreams[SNDRV_PCM_STREAM_LAST + 1];
struct reset_control *tx_reset;
struct reset_control *rx_reset;
const struct rk_i2s_soc_data *soc_data;
@@ -102,9 +108,24 @@
unsigned int clk_trcm;
unsigned int i2s_sdis[CH_GRP_MAX];
unsigned int i2s_sdos[CH_GRP_MAX];
+ unsigned int quirks;
int clk_ppm;
atomic_t refcount;
spinlock_t lock; /* xfer lock */
+};
+
+static struct i2s_of_quirks {
+ char *quirk;
+ int id;
+} of_quirks[] = {
+ {
+ .quirk = "rockchip,always-on",
+ .id = QUIRK_ALWAYS_ON,
+ },
+ {
+ .quirk = "rockchip,hdmi-path",
+ .id = QUIRK_HDMI_PATH,
+ },
};
static int to_ch_num(unsigned int val)
@@ -134,10 +155,9 @@
struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
regcache_cache_only(i2s_tdm->regmap, true);
- if (!IS_ERR(i2s_tdm->mclk_tx))
- clk_disable_unprepare(i2s_tdm->mclk_tx);
- if (!IS_ERR(i2s_tdm->mclk_rx))
- clk_disable_unprepare(i2s_tdm->mclk_rx);
+
+ clk_disable_unprepare(i2s_tdm->mclk_tx);
+ clk_disable_unprepare(i2s_tdm->mclk_rx);
return 0;
}
@@ -147,28 +167,45 @@
struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
int ret;
- if (!IS_ERR(i2s_tdm->mclk_tx))
- clk_prepare_enable(i2s_tdm->mclk_tx);
- if (!IS_ERR(i2s_tdm->mclk_rx))
- clk_prepare_enable(i2s_tdm->mclk_rx);
+ ret = clk_prepare_enable(i2s_tdm->mclk_tx);
+ if (ret)
+ goto err_mclk_tx;
+
+ ret = clk_prepare_enable(i2s_tdm->mclk_rx);
+ if (ret)
+ goto err_mclk_rx;
regcache_cache_only(i2s_tdm->regmap, false);
regcache_mark_dirty(i2s_tdm->regmap);
-
ret = regcache_sync(i2s_tdm->regmap);
- if (ret) {
- if (!IS_ERR(i2s_tdm->mclk_tx))
- clk_disable_unprepare(i2s_tdm->mclk_tx);
- if (!IS_ERR(i2s_tdm->mclk_rx))
- clk_disable_unprepare(i2s_tdm->mclk_rx);
- }
+ if (ret)
+ goto err_regmap;
+ return 0;
+
+err_regmap:
+ clk_disable_unprepare(i2s_tdm->mclk_rx);
+err_mclk_rx:
+ clk_disable_unprepare(i2s_tdm->mclk_tx);
+err_mclk_tx:
return ret;
}
static inline struct rk_i2s_tdm_dev *to_info(struct snd_soc_dai *dai)
{
return snd_soc_dai_get_drvdata(dai);
+}
+
+static inline bool is_stream_active(struct rk_i2s_tdm_dev *i2s_tdm, int stream)
+{
+ unsigned int val;
+
+ regmap_read(i2s_tdm->regmap, I2S_XFER, &val);
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return (val & I2S_XFER_TXS_START);
+ else
+ return (val & I2S_XFER_RXS_START);
}
#ifdef HAVE_SYNC_RESET
@@ -180,7 +217,7 @@
#define writeq(v,c) ({ __iowmb(); __raw_writeq((__force u64) cpu_to_le64(v), c); })
#endif
-static void rockchip_snd_xfer_reset_assert(struct rk_i2s_tdm_dev *i2s_tdm)
+static void rockchip_i2s_tdm_reset_assert(struct rk_i2s_tdm_dev *i2s_tdm)
{
int tx_bank, rx_bank, tx_offset, rx_offset, tx_id, rx_id;
void __iomem *cru_reset, *addr;
@@ -229,7 +266,7 @@
writeq(val, addr);
break;
}
- /* fall through */
+ /* fallthrough */
default:
local_irq_save(flags);
writel(BIT(tx_offset) | (BIT(tx_offset) << 16),
@@ -243,7 +280,7 @@
udelay(10);
}
-static void rockchip_snd_xfer_reset_deassert(struct rk_i2s_tdm_dev *i2s_tdm)
+static void rockchip_i2s_tdm_reset_deassert(struct rk_i2s_tdm_dev *i2s_tdm)
{
int tx_bank, rx_bank, tx_offset, rx_offset, tx_id, rx_id;
void __iomem *cru_reset, *addr;
@@ -291,7 +328,7 @@
writeq(val, addr);
break;
}
- /* fall through */
+ /* fallthrough */
default:
local_irq_save(flags);
writel((BIT(tx_offset) << 16),
@@ -309,94 +346,26 @@
* make sure both tx and rx are reset at the same time for sync lrck
* when clk_trcm > 0
*/
-static void rockchip_snd_xfer_sync_reset(struct rk_i2s_tdm_dev *i2s_tdm)
+static void rockchip_i2s_tdm_sync_reset(struct rk_i2s_tdm_dev *i2s_tdm)
{
- rockchip_snd_xfer_reset_assert(i2s_tdm);
- rockchip_snd_xfer_reset_deassert(i2s_tdm);
+ rockchip_i2s_tdm_reset_assert(i2s_tdm);
+ rockchip_i2s_tdm_reset_deassert(i2s_tdm);
}
#else
-static inline void rockchip_snd_xfer_reset_assert(struct rk_i2s_tdm_dev *i2s_tdm)
+static inline void rockchip_i2s_tdm_reset_assert(struct rk_i2s_tdm_dev *i2s_tdm)
{
}
-static inline void rockchip_snd_xfer_reset_deassert(struct rk_i2s_tdm_dev *i2s_tdm)
+static inline void rockchip_i2s_tdm_reset_deassert(struct rk_i2s_tdm_dev *i2s_tdm)
{
}
-static inline void rockchip_snd_xfer_sync_reset(struct rk_i2s_tdm_dev *i2s_tdm)
+static inline void rockchip_i2s_tdm_sync_reset(struct rk_i2s_tdm_dev *i2s_tdm)
{
}
#endif
-/* only used when clk_trcm > 0 */
-static void rockchip_snd_txrxctrl(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai, int on)
+static void rockchip_i2s_tdm_reset(struct reset_control *rc)
{
- struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai);
- unsigned int val = 0;
- unsigned long flags;
- int retry = 10;
-
- spin_lock_irqsave(&i2s_tdm->lock, flags);
- if (on) {
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- regmap_update_bits(i2s_tdm->regmap, I2S_DMACR,
- I2S_DMACR_TDE_ENABLE,
- I2S_DMACR_TDE_ENABLE);
- else
- regmap_update_bits(i2s_tdm->regmap, I2S_DMACR,
- I2S_DMACR_RDE_ENABLE,
- I2S_DMACR_RDE_ENABLE);
-
- if (atomic_inc_return(&i2s_tdm->refcount) == 1) {
- rockchip_snd_xfer_reset_assert(i2s_tdm);
- regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
- I2S_XFER_TXS_START |
- I2S_XFER_RXS_START,
- I2S_XFER_TXS_START |
- I2S_XFER_RXS_START);
- rockchip_snd_xfer_reset_deassert(i2s_tdm);
- }
- } else {
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- regmap_update_bits(i2s_tdm->regmap, I2S_DMACR,
- I2S_DMACR_TDE_ENABLE,
- I2S_DMACR_TDE_DISABLE);
- else
- regmap_update_bits(i2s_tdm->regmap, I2S_DMACR,
- I2S_DMACR_RDE_ENABLE,
- I2S_DMACR_RDE_DISABLE);
-
- if (atomic_dec_and_test(&i2s_tdm->refcount)) {
- regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
- I2S_XFER_TXS_START |
- I2S_XFER_RXS_START,
- I2S_XFER_TXS_STOP |
- I2S_XFER_RXS_STOP);
-
- udelay(150);
- regmap_update_bits(i2s_tdm->regmap, I2S_CLR,
- I2S_CLR_TXC | I2S_CLR_RXC,
- I2S_CLR_TXC | I2S_CLR_RXC);
-
- regmap_read(i2s_tdm->regmap, I2S_CLR, &val);
-
- /* Should wait for clear operation to finish */
- while (val) {
- regmap_read(i2s_tdm->regmap, I2S_CLR, &val);
- retry--;
- if (!retry) {
- dev_info(i2s_tdm->dev, "reset txrx\n");
- rockchip_snd_xfer_sync_reset(i2s_tdm);
- break;
- }
- }
- }
- }
- spin_unlock_irqrestore(&i2s_tdm->lock, flags);
-}
-
-static void rockchip_snd_reset(struct reset_control *rc)
-{
- if (IS_ERR(rc))
+ if (IS_ERR_OR_NULL(rc))
return;
reset_control_assert(rc);
@@ -407,92 +376,264 @@
udelay(10);
}
-static void rockchip_snd_txctrl(struct rk_i2s_tdm_dev *i2s_tdm, int on)
+static int rockchip_i2s_tdm_clear(struct rk_i2s_tdm_dev *i2s_tdm,
+ unsigned int clr)
{
+ struct reset_control *rst = NULL;
unsigned int val = 0;
- int retry = 10;
+ int ret = 0;
- if (on) {
- regmap_update_bits(i2s_tdm->regmap, I2S_DMACR,
- I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_ENABLE);
+ switch (clr) {
+ case I2S_CLR_TXC:
+ rst = i2s_tdm->tx_reset;
+ break;
+ case I2S_CLR_RXC:
+ rst = i2s_tdm->rx_reset;
+ break;
+ case I2S_CLR_TXC | I2S_CLR_RXC:
+ break;
+ default:
+ return -EINVAL;
+ }
- regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
- I2S_XFER_TXS_START,
- I2S_XFER_TXS_START);
+ /*
+ * Workaround for FIFO clear on SLAVE mode:
+ *
+ * A Suggest to do reset hclk domain and then do mclk
+ * domain, especially for SLAVE mode without CLK in.
+ * at last, recovery regmap config.
+ *
+ * B Suggest to switch to MASTER, and then do FIFO clr,
+ * at last, bring back to SLAVE.
+ *
+ * Now we choose plan B here.
+ */
+ if (!i2s_tdm->is_master_mode)
+ regmap_update_bits(i2s_tdm->regmap, I2S_CKR,
+ I2S_CKR_MSS_MASK, I2S_CKR_MSS_MASTER);
+ regmap_update_bits(i2s_tdm->regmap, I2S_CLR, clr, clr);
+ ret = regmap_read_poll_timeout_atomic(i2s_tdm->regmap, I2S_CLR, val,
+ !(val & clr), 10, 100);
+ if (!i2s_tdm->is_master_mode)
+ regmap_update_bits(i2s_tdm->regmap, I2S_CKR,
+ I2S_CKR_MSS_MASK, I2S_CKR_MSS_SLAVE);
+
+ if (ret < 0) {
+ dev_warn(i2s_tdm->dev, "failed to clear %u on %s mode\n",
+ clr, i2s_tdm->is_master_mode ? "master" : "slave");
+ goto reset;
+ }
+
+ return 0;
+
+reset:
+ if (i2s_tdm->clk_trcm)
+ rockchip_i2s_tdm_sync_reset(i2s_tdm);
+ else
+ rockchip_i2s_tdm_reset(rst);
+
+ return 0;
+}
+
+/*
+ * HDMI controller ignores the first FRAME_SYNC cycle, Lost one frame is no big deal
+ * for LPCM, but it does matter for Bitstream (NLPCM/HBR), So, padding one frame
+ * before xfer the real data to fix it.
+ */
+static void rockchip_i2s_tdm_tx_fifo_padding(struct rk_i2s_tdm_dev *i2s_tdm, bool en)
+{
+ unsigned int val, w, c, i;
+
+ if (!en)
+ return;
+
+ regmap_read(i2s_tdm->regmap, I2S_TXCR, &val);
+ w = ((val & I2S_TXCR_VDW_MASK) >> I2S_TXCR_VDW_SHIFT) + 1;
+ c = to_ch_num(val & I2S_TXCR_CSR_MASK) * w / 32;
+
+ for (i = 0; i < c; i++)
+ regmap_write(i2s_tdm->regmap, I2S_TXDR, 0x0);
+}
+
+static void rockchip_i2s_tdm_fifo_xrun_detect(struct rk_i2s_tdm_dev *i2s_tdm,
+ int stream, bool en)
+{
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* clear irq status which was asserted before TXUIE enabled */
+ regmap_update_bits(i2s_tdm->regmap, I2S_INTCR,
+ I2S_INTCR_TXUIC, I2S_INTCR_TXUIC);
+ regmap_update_bits(i2s_tdm->regmap, I2S_INTCR,
+ I2S_INTCR_TXUIE_MASK,
+ I2S_INTCR_TXUIE(en));
} else {
- regmap_update_bits(i2s_tdm->regmap, I2S_DMACR,
- I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_DISABLE);
-
- regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
- I2S_XFER_TXS_START,
- I2S_XFER_TXS_STOP);
-
- udelay(150);
- if (i2s_tdm->is_master_mode) {
- regmap_update_bits(i2s_tdm->regmap, I2S_CLR,
- I2S_CLR_TXC,
- I2S_CLR_TXC);
-
- regmap_read(i2s_tdm->regmap, I2S_CLR, &val);
-
- /* Should wait for clear operation to finish */
- while (val) {
- regmap_read(i2s_tdm->regmap, I2S_CLR, &val);
- retry--;
- if (!retry) {
- dev_warn(i2s_tdm->dev, "reset tx\n");
- rockchip_snd_reset(i2s_tdm->tx_reset);
- break;
- }
- }
- } else {
- rockchip_snd_reset(i2s_tdm->tx_reset);
- }
+ /* clear irq status which was asserted before RXOIE enabled */
+ regmap_update_bits(i2s_tdm->regmap, I2S_INTCR,
+ I2S_INTCR_RXOIC, I2S_INTCR_RXOIC);
+ regmap_update_bits(i2s_tdm->regmap, I2S_INTCR,
+ I2S_INTCR_RXOIE_MASK,
+ I2S_INTCR_RXOIE(en));
}
}
-static void rockchip_snd_rxctrl(struct rk_i2s_tdm_dev *i2s_tdm, int on)
+static void rockchip_i2s_tdm_dma_ctrl(struct rk_i2s_tdm_dev *i2s_tdm,
+ int stream, bool en)
{
- unsigned int val = 0;
- int retry = 10;
+ if (!en)
+ rockchip_i2s_tdm_fifo_xrun_detect(i2s_tdm, stream, 0);
- if (on) {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (i2s_tdm->quirks & QUIRK_HDMI_PATH)
+ rockchip_i2s_tdm_tx_fifo_padding(i2s_tdm, en);
+
regmap_update_bits(i2s_tdm->regmap, I2S_DMACR,
- I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_ENABLE);
-
- regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
- I2S_XFER_RXS_START,
- I2S_XFER_RXS_START);
+ I2S_DMACR_TDE_MASK,
+ I2S_DMACR_TDE(en));
+ /*
+ * Explicitly delay 1 usec for dma to fill FIFO,
+ * though there was a implied HW delay that around
+ * half LRCK cycle (e.g. 2.6us@192k) from XFER-start
+ * to FIFO-pop.
+ *
+ * 1 usec is enough to fill at lease 4 entry each FIFO
+ * @192k 8ch 32bit situation.
+ */
+ udelay(1);
} else {
regmap_update_bits(i2s_tdm->regmap, I2S_DMACR,
- I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_DISABLE);
-
- regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
- I2S_XFER_RXS_START,
- I2S_XFER_RXS_STOP);
-
- udelay(150);
- if (i2s_tdm->is_master_mode) {
- regmap_update_bits(i2s_tdm->regmap, I2S_CLR,
- I2S_CLR_RXC,
- I2S_CLR_RXC);
-
- regmap_read(i2s_tdm->regmap, I2S_CLR, &val);
-
- /* Should wait for clear operation to finish */
- while (val) {
- regmap_read(i2s_tdm->regmap, I2S_CLR, &val);
- retry--;
- if (!retry) {
- dev_warn(i2s_tdm->dev, "reset rx\n");
- rockchip_snd_reset(i2s_tdm->rx_reset);
- break;
- }
- }
- } else {
- rockchip_snd_reset(i2s_tdm->rx_reset);
- }
+ I2S_DMACR_RDE_MASK,
+ I2S_DMACR_RDE(en));
}
+
+ if (en)
+ rockchip_i2s_tdm_fifo_xrun_detect(i2s_tdm, stream, 1);
+}
+
+static void rockchip_i2s_tdm_xfer_start(struct rk_i2s_tdm_dev *i2s_tdm,
+ int stream)
+{
+ if (i2s_tdm->clk_trcm) {
+ rockchip_i2s_tdm_reset_assert(i2s_tdm);
+ regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
+ I2S_XFER_TXS_MASK |
+ I2S_XFER_RXS_MASK,
+ I2S_XFER_TXS_START |
+ I2S_XFER_RXS_START);
+ rockchip_i2s_tdm_reset_deassert(i2s_tdm);
+ } else if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
+ I2S_XFER_TXS_MASK,
+ I2S_XFER_TXS_START);
+ } else {
+ regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
+ I2S_XFER_RXS_MASK,
+ I2S_XFER_RXS_START);
+ }
+}
+
+static void rockchip_i2s_tdm_xfer_stop(struct rk_i2s_tdm_dev *i2s_tdm,
+ int stream, bool force)
+{
+ unsigned int msk, val, clr;
+
+ if (i2s_tdm->quirks & QUIRK_ALWAYS_ON && !force)
+ return;
+
+ if (i2s_tdm->clk_trcm) {
+ msk = I2S_XFER_TXS_MASK | I2S_XFER_RXS_MASK;
+ val = I2S_XFER_TXS_STOP | I2S_XFER_RXS_STOP;
+ clr = I2S_CLR_TXC | I2S_CLR_RXC;
+ } else if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msk = I2S_XFER_TXS_MASK;
+ val = I2S_XFER_TXS_STOP;
+ clr = I2S_CLR_TXC;
+ } else {
+ msk = I2S_XFER_RXS_MASK;
+ val = I2S_XFER_RXS_STOP;
+ clr = I2S_CLR_RXC;
+ }
+
+ regmap_update_bits(i2s_tdm->regmap, I2S_XFER, msk, val);
+
+ /* delay for LRCK signal integrity */
+ udelay(150);
+
+ rockchip_i2s_tdm_clear(i2s_tdm, clr);
+}
+
+static void rockchip_i2s_tdm_xfer_trcm_start(struct rk_i2s_tdm_dev *i2s_tdm)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&i2s_tdm->lock, flags);
+ if (atomic_inc_return(&i2s_tdm->refcount) == 1)
+ rockchip_i2s_tdm_xfer_start(i2s_tdm, 0);
+ spin_unlock_irqrestore(&i2s_tdm->lock, flags);
+}
+
+static void rockchip_i2s_tdm_xfer_trcm_stop(struct rk_i2s_tdm_dev *i2s_tdm)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&i2s_tdm->lock, flags);
+ if (atomic_dec_and_test(&i2s_tdm->refcount))
+ rockchip_i2s_tdm_xfer_stop(i2s_tdm, 0, false);
+ spin_unlock_irqrestore(&i2s_tdm->lock, flags);
+}
+
+static void rockchip_i2s_tdm_trcm_pause(struct snd_pcm_substream *substream,
+ struct rk_i2s_tdm_dev *i2s_tdm)
+{
+ int stream = substream->stream;
+ int bstream = SNDRV_PCM_STREAM_LAST - stream;
+
+ /* disable dma for both tx and rx */
+ rockchip_i2s_tdm_dma_ctrl(i2s_tdm, stream, 0);
+ rockchip_i2s_tdm_dma_ctrl(i2s_tdm, bstream, 0);
+ rockchip_i2s_tdm_xfer_stop(i2s_tdm, bstream, true);
+}
+
+static void rockchip_i2s_tdm_trcm_resume(struct snd_pcm_substream *substream,
+ struct rk_i2s_tdm_dev *i2s_tdm)
+{
+ int bstream = SNDRV_PCM_STREAM_LAST - substream->stream;
+
+ /*
+ * just resume bstream, because current stream will be
+ * startup in the trigger-cmd-START
+ */
+ rockchip_i2s_tdm_dma_ctrl(i2s_tdm, bstream, 1);
+ rockchip_i2s_tdm_xfer_start(i2s_tdm, bstream);
+}
+
+static void rockchip_i2s_tdm_start(struct rk_i2s_tdm_dev *i2s_tdm, int stream)
+{
+ /*
+ * On HDMI-PATH-ALWAYS-ON situation, we almost keep XFER always on,
+ * so, for new data start, suggested to STOP-CLEAR-START to make sure
+ * data aligned.
+ */
+ if ((i2s_tdm->quirks & QUIRK_HDMI_PATH) &&
+ (i2s_tdm->quirks & QUIRK_ALWAYS_ON) &&
+ (stream == SNDRV_PCM_STREAM_PLAYBACK)) {
+ rockchip_i2s_tdm_xfer_stop(i2s_tdm, stream, true);
+ }
+
+ rockchip_i2s_tdm_dma_ctrl(i2s_tdm, stream, 1);
+
+ if (i2s_tdm->clk_trcm)
+ rockchip_i2s_tdm_xfer_trcm_start(i2s_tdm);
+ else
+ rockchip_i2s_tdm_xfer_start(i2s_tdm, stream);
+}
+
+static void rockchip_i2s_tdm_stop(struct rk_i2s_tdm_dev *i2s_tdm, int stream)
+{
+ rockchip_i2s_tdm_dma_ctrl(i2s_tdm, stream, 0);
+
+ if (i2s_tdm->clk_trcm)
+ rockchip_i2s_tdm_xfer_trcm_stop(i2s_tdm);
+ else
+ rockchip_i2s_tdm_xfer_stop(i2s_tdm, stream, false);
}
static int rockchip_i2s_tdm_set_fmt(struct snd_soc_dai *cpu_dai,
@@ -659,72 +800,6 @@
return ret;
}
-static void rockchip_i2s_tdm_xfer_pause(struct snd_pcm_substream *substream,
- struct rk_i2s_tdm_dev *i2s_tdm)
-{
- int stream;
- unsigned int val = 0;
- int retry = 10;
-
- stream = SNDRV_PCM_STREAM_LAST - substream->stream;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- regmap_update_bits(i2s_tdm->regmap, I2S_DMACR,
- I2S_DMACR_TDE_ENABLE,
- I2S_DMACR_TDE_DISABLE);
- else
- regmap_update_bits(i2s_tdm->regmap, I2S_DMACR,
- I2S_DMACR_RDE_ENABLE,
- I2S_DMACR_RDE_DISABLE);
-
- regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
- I2S_XFER_TXS_START |
- I2S_XFER_RXS_START,
- I2S_XFER_TXS_STOP |
- I2S_XFER_RXS_STOP);
-
- udelay(150);
- regmap_update_bits(i2s_tdm->regmap, I2S_CLR,
- I2S_CLR_TXC | I2S_CLR_RXC,
- I2S_CLR_TXC | I2S_CLR_RXC);
-
- regmap_read(i2s_tdm->regmap, I2S_CLR, &val);
-
- /* Should wait for clear operation to finish */
- while (val) {
- regmap_read(i2s_tdm->regmap, I2S_CLR, &val);
- retry--;
- if (!retry) {
- dev_info(i2s_tdm->dev, "reset txrx\n");
- rockchip_snd_xfer_sync_reset(i2s_tdm);
- break;
- }
- }
-}
-
-static void rockchip_i2s_tdm_xfer_resume(struct snd_pcm_substream *substream,
- struct rk_i2s_tdm_dev *i2s_tdm)
-{
- int stream;
-
- stream = SNDRV_PCM_STREAM_LAST - substream->stream;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- regmap_update_bits(i2s_tdm->regmap, I2S_DMACR,
- I2S_DMACR_TDE_ENABLE,
- I2S_DMACR_TDE_ENABLE);
- else
- regmap_update_bits(i2s_tdm->regmap, I2S_DMACR,
- I2S_DMACR_RDE_ENABLE,
- I2S_DMACR_RDE_ENABLE);
-
- rockchip_snd_xfer_reset_assert(i2s_tdm);
- regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
- I2S_XFER_TXS_START |
- I2S_XFER_RXS_START,
- I2S_XFER_TXS_START |
- I2S_XFER_RXS_START);
- rockchip_snd_xfer_reset_deassert(i2s_tdm);
-}
-
static int rockchip_i2s_tdm_clk_set_rate(struct rk_i2s_tdm_dev *i2s_tdm,
struct clk *clk, unsigned long rate,
int ppm)
@@ -840,6 +915,28 @@
return ret;
}
+static int rockchip_i2s_tdm_mclk_reparent(struct rk_i2s_tdm_dev *i2s_tdm)
+{
+ struct clk *parent;
+ int ret = 0;
+
+ /* reparent to the same clk on TRCM mode */
+ switch (i2s_tdm->clk_trcm) {
+ case I2S_CKR_TRCM_TXONLY:
+ parent = clk_get_parent(i2s_tdm->mclk_tx);
+ if (clk_has_parent(i2s_tdm->mclk_rx, parent))
+ ret = clk_set_parent(i2s_tdm->mclk_rx, parent);
+ break;
+ case I2S_CKR_TRCM_RXONLY:
+ parent = clk_get_parent(i2s_tdm->mclk_rx);
+ if (clk_has_parent(i2s_tdm->mclk_tx, parent))
+ ret = clk_set_parent(i2s_tdm->mclk_tx, parent);
+ break;
+ }
+
+ return ret;
+}
+
static int rockchip_i2s_tdm_set_mclk(struct rk_i2s_tdm_dev *i2s_tdm,
struct snd_pcm_substream *substream,
struct clk **mclk)
@@ -862,6 +959,10 @@
goto err;
ret = clk_set_rate(i2s_tdm->mclk_rx, i2s_tdm->mclk_rx_freq);
+ if (ret)
+ goto err;
+
+ ret = rockchip_i2s_tdm_mclk_reparent(i2s_tdm);
if (ret)
goto err;
@@ -895,6 +996,9 @@
unsigned int val = 0;
if (!i2s_tdm->io_multiplex)
+ return 0;
+
+ if (IS_ERR(i2s_tdm->grf))
return 0;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
@@ -1006,24 +1110,18 @@
return false;
}
-static int rockchip_i2s_trcm_mode(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai,
- unsigned int div_bclk,
- unsigned int div_lrck,
- unsigned int fmt)
+static int rockchip_i2s_tdm_params_trcm(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai,
+ unsigned int div_bclk,
+ unsigned int div_lrck,
+ unsigned int fmt)
{
struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai);
unsigned long flags;
- if (!i2s_tdm->clk_trcm)
- return 0;
-
- if (!is_params_dirty(substream, dai, div_bclk, div_lrck, fmt))
- return 0;
-
spin_lock_irqsave(&i2s_tdm->lock, flags);
if (atomic_read(&i2s_tdm->refcount))
- rockchip_i2s_tdm_xfer_pause(substream, i2s_tdm);
+ rockchip_i2s_tdm_trcm_pause(substream, i2s_tdm);
regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV,
I2S_CLKDIV_TXM_MASK | I2S_CLKDIV_RXM_MASK,
@@ -1042,8 +1140,56 @@
fmt);
if (atomic_read(&i2s_tdm->refcount))
- rockchip_i2s_tdm_xfer_resume(substream, i2s_tdm);
+ rockchip_i2s_tdm_trcm_resume(substream, i2s_tdm);
spin_unlock_irqrestore(&i2s_tdm->lock, flags);
+
+ return 0;
+}
+
+static int rockchip_i2s_tdm_params(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai,
+ unsigned int div_bclk,
+ unsigned int div_lrck,
+ unsigned int fmt)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai);
+ int stream = substream->stream;
+
+ if (is_stream_active(i2s_tdm, stream))
+ rockchip_i2s_tdm_xfer_stop(i2s_tdm, stream, true);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV,
+ I2S_CLKDIV_TXM_MASK,
+ I2S_CLKDIV_TXM(div_bclk));
+ regmap_update_bits(i2s_tdm->regmap, I2S_CKR,
+ I2S_CKR_TSD_MASK,
+ I2S_CKR_TSD(div_lrck));
+ regmap_update_bits(i2s_tdm->regmap, I2S_TXCR,
+ I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK,
+ fmt);
+ } else {
+ regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV,
+ I2S_CLKDIV_RXM_MASK,
+ I2S_CLKDIV_RXM(div_bclk));
+ regmap_update_bits(i2s_tdm->regmap, I2S_CKR,
+ I2S_CKR_RSD_MASK,
+ I2S_CKR_RSD(div_lrck));
+ regmap_update_bits(i2s_tdm->regmap, I2S_RXCR,
+ I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK,
+ fmt);
+ }
+
+ /*
+ * Bring back CLK ASAP after cfg changed to make SINK devices active
+ * on HDMI-PATH-ALWAYS-ON situation, this workaround for some TVs no
+ * sound issue. at the moment, it's 8K@60Hz display situation.
+ */
+ if ((i2s_tdm->quirks & QUIRK_HDMI_PATH) &&
+ (i2s_tdm->quirks & QUIRK_ALWAYS_ON) &&
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) {
+ rockchip_i2s_tdm_xfer_start(i2s_tdm, SNDRV_PCM_STREAM_PLAYBACK);
+ }
return 0;
}
@@ -1110,29 +1256,31 @@
struct snd_soc_dai *dai)
{
struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai);
+ struct snd_dmaengine_dai_dma_data *dma_data;
struct clk *mclk;
int ret = 0;
unsigned int val = 0;
unsigned int mclk_rate, bclk_rate, div_bclk = 4, div_lrck = 64;
- if (i2s_tdm->is_master_mode) {
- if (i2s_tdm->mclk_calibrate)
- rockchip_i2s_tdm_calibrate_mclk(i2s_tdm, substream,
- params_rate(params));
+ dma_data = snd_soc_dai_get_dma_data(dai, substream);
+ dma_data->maxburst = MAXBURST_PER_FIFO * params_channels(params) / 2;
- ret = rockchip_i2s_tdm_set_mclk(i2s_tdm, substream, &mclk);
- if (ret)
- goto err;
+ if (i2s_tdm->mclk_calibrate)
+ rockchip_i2s_tdm_calibrate_mclk(i2s_tdm, substream,
+ params_rate(params));
- mclk_rate = clk_get_rate(mclk);
- bclk_rate = i2s_tdm->bclk_fs * params_rate(params);
- if (!bclk_rate) {
- ret = -EINVAL;
- goto err;
- }
- div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate);
- div_lrck = bclk_rate / params_rate(params);
+ ret = rockchip_i2s_tdm_set_mclk(i2s_tdm, substream, &mclk);
+ if (ret)
+ goto err;
+
+ mclk_rate = clk_get_rate(mclk);
+ bclk_rate = i2s_tdm->bclk_fs * params_rate(params);
+ if (!bclk_rate) {
+ ret = -EINVAL;
+ goto err;
}
+ div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate);
+ div_lrck = bclk_rate / params_rate(params);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
@@ -1148,6 +1296,7 @@
val |= I2S_TXCR_VDW(24);
break;
case SNDRV_PCM_FORMAT_S32_LE:
+ case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
val |= I2S_TXCR_VDW(32);
break;
default:
@@ -1160,29 +1309,13 @@
goto err;
val |= ret;
- if (i2s_tdm->clk_trcm) {
- rockchip_i2s_trcm_mode(substream, dai, div_bclk, div_lrck, val);
- } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV,
- I2S_CLKDIV_TXM_MASK,
- I2S_CLKDIV_TXM(div_bclk));
- regmap_update_bits(i2s_tdm->regmap, I2S_CKR,
- I2S_CKR_TSD_MASK,
- I2S_CKR_TSD(div_lrck));
- regmap_update_bits(i2s_tdm->regmap, I2S_TXCR,
- I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK,
- val);
- } else {
- regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV,
- I2S_CLKDIV_RXM_MASK,
- I2S_CLKDIV_RXM(div_bclk));
- regmap_update_bits(i2s_tdm->regmap, I2S_CKR,
- I2S_CKR_RSD_MASK,
- I2S_CKR_RSD(div_lrck));
- regmap_update_bits(i2s_tdm->regmap, I2S_RXCR,
- I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK,
- val);
- }
+ if (!is_params_dirty(substream, dai, div_bclk, div_lrck, val))
+ return 0;
+
+ if (i2s_tdm->clk_trcm)
+ rockchip_i2s_tdm_params_trcm(substream, dai, div_bclk, div_lrck, val);
+ else
+ rockchip_i2s_tdm_params(substream, dai, div_bclk, div_lrck, val);
ret = rockchip_i2s_io_multiplex(substream, dai);
@@ -1200,22 +1333,12 @@
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (i2s_tdm->clk_trcm)
- rockchip_snd_txrxctrl(substream, dai, 1);
- else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- rockchip_snd_rxctrl(i2s_tdm, 1);
- else
- rockchip_snd_txctrl(i2s_tdm, 1);
+ rockchip_i2s_tdm_start(i2s_tdm, substream->stream);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (i2s_tdm->clk_trcm)
- rockchip_snd_txrxctrl(substream, dai, 0);
- else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- rockchip_snd_rxctrl(i2s_tdm, 0);
- else
- rockchip_snd_txctrl(i2s_tdm, 0);
+ rockchip_i2s_tdm_stop(i2s_tdm, substream->stream);
break;
default:
ret = -EINVAL;
@@ -1305,6 +1428,93 @@
.put = rockchip_i2s_tdm_clk_compensation_put,
};
+/* loopback mode select */
+enum {
+ LOOPBACK_MODE_DIS = 0,
+ LOOPBACK_MODE_1,
+ LOOPBACK_MODE_2,
+ LOOPBACK_MODE_2_SWAP,
+};
+
+static const char *const loopback_text[] = {
+ "Disabled",
+ "Mode1",
+ "Mode2",
+ "Mode2 Swap",
+};
+
+static SOC_ENUM_SINGLE_EXT_DECL(loopback_mode, loopback_text);
+
+static int rockchip_i2s_tdm_loopback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_component_get_drvdata(component);
+ unsigned int reg = 0, mode = 0;
+
+ pm_runtime_get_sync(component->dev);
+ regmap_read(i2s_tdm->regmap, I2S_XFER, ®);
+ pm_runtime_put(component->dev);
+
+ switch (reg & I2S_XFER_LP_MODE_MASK) {
+ case I2S_XFER_LP_MODE_2_SWAP:
+ mode = LOOPBACK_MODE_2_SWAP;
+ break;
+ case I2S_XFER_LP_MODE_2:
+ mode = LOOPBACK_MODE_2;
+ break;
+ case I2S_XFER_LP_MODE_1:
+ mode = LOOPBACK_MODE_1;
+ break;
+ default:
+ mode = LOOPBACK_MODE_DIS;
+ break;
+ }
+
+ ucontrol->value.enumerated.item[0] = mode;
+
+ return 0;
+}
+
+static int rockchip_i2s_tdm_loopback_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0, mode = ucontrol->value.enumerated.item[0];
+
+ if (mode < LOOPBACK_MODE_DIS ||
+ mode > LOOPBACK_MODE_2_SWAP)
+ return -EINVAL;
+
+ switch (mode) {
+ case LOOPBACK_MODE_2_SWAP:
+ val = I2S_XFER_LP_MODE_2_SWAP;
+ break;
+ case LOOPBACK_MODE_2:
+ val = I2S_XFER_LP_MODE_2;
+ break;
+ case LOOPBACK_MODE_1:
+ val = I2S_XFER_LP_MODE_1;
+ break;
+ default:
+ val = I2S_XFER_LP_MODE_DIS;
+ break;
+ }
+
+ pm_runtime_get_sync(component->dev);
+ regmap_update_bits(i2s_tdm->regmap, I2S_XFER, I2S_XFER_LP_MODE_MASK, val);
+ pm_runtime_put(component->dev);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new rockchip_i2s_tdm_snd_controls[] = {
+ SOC_ENUM_EXT("I2STDM Digital Loopback Mode", loopback_mode,
+ rockchip_i2s_tdm_loopback_get,
+ rockchip_i2s_tdm_loopback_put),
+};
+
static int rockchip_i2s_tdm_dai_probe(struct snd_soc_dai *dai)
{
struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai);
@@ -1340,7 +1550,30 @@
return 0;
}
+static int rockchip_i2s_tdm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai);
+
+ if (i2s_tdm->substreams[substream->stream])
+ return -EBUSY;
+
+ i2s_tdm->substreams[substream->stream] = substream;
+
+ return 0;
+}
+
+static void rockchip_i2s_tdm_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai);
+
+ i2s_tdm->substreams[substream->stream] = NULL;
+}
+
static const struct snd_soc_dai_ops rockchip_i2s_tdm_dai_ops = {
+ .startup = rockchip_i2s_tdm_startup,
+ .shutdown = rockchip_i2s_tdm_shutdown,
.hw_params = rockchip_i2s_tdm_hw_params,
.set_sysclk = rockchip_i2s_tdm_set_sysclk,
.set_fmt = rockchip_i2s_tdm_set_fmt,
@@ -1350,6 +1583,8 @@
static const struct snd_soc_component_driver rockchip_i2s_tdm_component = {
.name = DRV_NAME,
+ .controls = rockchip_i2s_tdm_snd_controls,
+ .num_controls = ARRAY_SIZE(rockchip_i2s_tdm_snd_controls),
};
static bool rockchip_i2s_tdm_wr_reg(struct device *dev, unsigned int reg)
@@ -1400,6 +1635,7 @@
{
switch (reg) {
case I2S_TXFIFOLR:
+ case I2S_INTCR:
case I2S_INTSR:
case I2S_CLR:
case I2S_TXDR:
@@ -1453,9 +1689,11 @@
u32 reg = 0, val = 0, trcm = i2s_tdm->clk_trcm;
int i;
+ if (IS_ERR(i2s_tdm->grf))
+ return 0;
+
switch (trcm) {
case I2S_CKR_TRCM_TXONLY:
- /* fall through */
case I2S_CKR_TRCM_RXONLY:
break;
default:
@@ -1591,7 +1829,8 @@
SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE),
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE),
},
.capture = {
.stream_name = "Capture",
@@ -1602,7 +1841,8 @@
SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE),
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE),
},
.ops = &rockchip_i2s_tdm_dai_ops,
};
@@ -1758,6 +1998,34 @@
return rockchip_i2s_tdm_path_prepare(i2s_tdm, np, 1);
}
+static irqreturn_t rockchip_i2s_tdm_isr(int irq, void *devid)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = (struct rk_i2s_tdm_dev *)devid;
+ struct snd_pcm_substream *substream;
+ u32 val;
+
+ regmap_read(i2s_tdm->regmap, I2S_INTSR, &val);
+ if (val & I2S_INTSR_TXUI_ACT) {
+ dev_warn_ratelimited(i2s_tdm->dev, "TX FIFO Underrun\n");
+ regmap_update_bits(i2s_tdm->regmap, I2S_INTCR,
+ I2S_INTCR_TXUIC, I2S_INTCR_TXUIC);
+ substream = i2s_tdm->substreams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream)
+ snd_pcm_stop_xrun(substream);
+ }
+
+ if (val & I2S_INTSR_RXOI_ACT) {
+ dev_warn_ratelimited(i2s_tdm->dev, "RX FIFO Overrun\n");
+ regmap_update_bits(i2s_tdm->regmap, I2S_INTCR,
+ I2S_INTCR_RXOIC, I2S_INTCR_RXOIC);
+ substream = i2s_tdm->substreams[SNDRV_PCM_STREAM_CAPTURE];
+ if (substream)
+ snd_pcm_stop_xrun(substream);
+ }
+
+ return IRQ_HANDLED;
+}
+
static int rockchip_i2s_tdm_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
@@ -1769,8 +2037,7 @@
#ifdef HAVE_SYNC_RESET
bool sync;
#endif
- int ret;
- int val;
+ int ret, val, i, irq;
ret = rockchip_i2s_tdm_dai_prepare(pdev, &soc_dai);
if (ret)
@@ -1788,6 +2055,10 @@
spin_lock_init(&i2s_tdm->lock);
i2s_tdm->soc_data = (const struct rk_i2s_soc_data *)of_id->data;
+
+ for (i = 0; i < ARRAY_SIZE(of_quirks); i++)
+ if (of_property_read_bool(node, of_quirks[i].quirk))
+ i2s_tdm->quirks |= of_quirks[i].id;
i2s_tdm->bclk_fs = 64;
if (!of_property_read_u32(node, "rockchip,bclk-fs", &val)) {
@@ -1813,8 +2084,6 @@
soc_dai->playback.channels_min = 0;
i2s_tdm->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf");
- if (IS_ERR(i2s_tdm->grf))
- return PTR_ERR(i2s_tdm->grf);
#ifdef HAVE_SYNC_RESET
sync = of_device_is_compatible(node, "rockchip,px30-i2s-tdm") ||
@@ -1892,23 +2161,32 @@
i2s_tdm->mclk_root1_freq = i2s_tdm->mclk_root1_initial_freq;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- regs = devm_ioremap_resource(&pdev->dev, res);
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(regs))
return PTR_ERR(regs);
i2s_tdm->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
- &rockchip_i2s_tdm_regmap_config);
+ &rockchip_i2s_tdm_regmap_config);
if (IS_ERR(i2s_tdm->regmap))
return PTR_ERR(i2s_tdm->regmap);
+ irq = platform_get_irq(pdev, 0);
+ if (irq > 0) {
+ ret = devm_request_irq(&pdev->dev, irq, rockchip_i2s_tdm_isr,
+ IRQF_SHARED, node->name, i2s_tdm);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq %u\n", irq);
+ return ret;
+ }
+ }
+
i2s_tdm->playback_dma_data.addr = res->start + I2S_TXDR;
i2s_tdm->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- i2s_tdm->playback_dma_data.maxburst = 8;
+ i2s_tdm->playback_dma_data.maxburst = MAXBURST_PER_FIFO;
i2s_tdm->capture_dma_data.addr = res->start + I2S_RXDR;
i2s_tdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- i2s_tdm->capture_dma_data.maxburst = 8;
+ i2s_tdm->capture_dma_data.maxburst = MAXBURST_PER_FIFO;
ret = rockchip_i2s_tdm_tx_path_prepare(i2s_tdm, node);
if (ret < 0) {
@@ -1932,6 +2210,36 @@
goto err_pm_disable;
}
+ if (i2s_tdm->quirks & QUIRK_ALWAYS_ON) {
+ unsigned int rate = DEFAULT_FS * DEFAULT_MCLK_FS;
+ unsigned int div_bclk = DEFAULT_FS * DEFAULT_MCLK_FS;
+ unsigned int div_lrck = i2s_tdm->bclk_fs;
+
+ div_bclk = DIV_ROUND_CLOSEST(rate, div_lrck * DEFAULT_FS);
+
+ /* assign generic freq */
+ clk_set_rate(i2s_tdm->mclk_rx, rate);
+ clk_set_rate(i2s_tdm->mclk_tx, rate);
+
+ ret = rockchip_i2s_tdm_mclk_reparent(i2s_tdm);
+ if (ret)
+ goto err_pm_disable;
+
+ regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV,
+ I2S_CLKDIV_RXM_MASK | I2S_CLKDIV_TXM_MASK,
+ I2S_CLKDIV_RXM(div_bclk) | I2S_CLKDIV_TXM(div_bclk));
+ regmap_update_bits(i2s_tdm->regmap, I2S_CKR,
+ I2S_CKR_RSD_MASK | I2S_CKR_TSD_MASK,
+ I2S_CKR_RSD(div_lrck) | I2S_CKR_TSD(div_lrck));
+
+ if (i2s_tdm->clk_trcm)
+ rockchip_i2s_tdm_xfer_trcm_start(i2s_tdm);
+ else
+ rockchip_i2s_tdm_xfer_start(i2s_tdm, SNDRV_PCM_STREAM_PLAYBACK);
+
+ pm_runtime_forbid(&pdev->dev);
+ }
+
regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK,
I2S_DMACR_TDL(16));
regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK,
@@ -1951,8 +2259,11 @@
goto err_suspend;
}
- if (of_property_read_bool(node, "rockchip,no-dmaengine"))
- return ret;
+ if (of_property_read_bool(node, "rockchip,no-dmaengine")) {
+ dev_info(&pdev->dev, "Used for Multi-DAI\n");
+ return 0;
+ }
+
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (ret) {
dev_err(&pdev->dev, "Could not register PCM\n");
@@ -1978,14 +2289,21 @@
if (!pm_runtime_status_suspended(&pdev->dev))
i2s_tdm_runtime_suspend(&pdev->dev);
- if (!IS_ERR(i2s_tdm->mclk_tx))
- clk_prepare_enable(i2s_tdm->mclk_tx);
- if (!IS_ERR(i2s_tdm->mclk_rx))
- clk_prepare_enable(i2s_tdm->mclk_rx);
- if (!IS_ERR(i2s_tdm->hclk))
- clk_disable_unprepare(i2s_tdm->hclk);
+ clk_disable_unprepare(i2s_tdm->mclk_tx);
+ clk_disable_unprepare(i2s_tdm->mclk_rx);
+ clk_disable_unprepare(i2s_tdm->hclk);
return 0;
+}
+
+static void rockchip_i2s_tdm_platform_shutdown(struct platform_device *pdev)
+{
+ struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(&pdev->dev);
+
+ pm_runtime_get_sync(i2s_tdm->dev);
+ rockchip_i2s_tdm_stop(i2s_tdm, SNDRV_PCM_STREAM_PLAYBACK);
+ rockchip_i2s_tdm_stop(i2s_tdm, SNDRV_PCM_STREAM_CAPTURE);
+ pm_runtime_put(i2s_tdm->dev);
}
#ifdef CONFIG_PM_SLEEP
@@ -2023,6 +2341,7 @@
static struct platform_driver rockchip_i2s_tdm_driver = {
.probe = rockchip_i2s_tdm_probe,
.remove = rockchip_i2s_tdm_remove,
+ .shutdown = rockchip_i2s_tdm_platform_shutdown,
.driver = {
.name = DRV_NAME,
.of_match_table = of_match_ptr(rockchip_i2s_tdm_match),
--
Gitblit v1.6.2