From 3644caf8debbb3ce22db9190f405fc9951fb7bb7 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Tue, 2 Aug 2022 09:08:53 +0800 Subject: [PATCH] ASoC: rockchip: i2s-tdm: Add support for clk always-on This patch introduce function always-on which allow controller to keep clk (BCLK/SYNC) always-on. In HDMI audio situation, we found some SINK devices need SOURCE to keep xfer ACR, AUDS packet always on to fix sound compatibility. And the AUDS packet is drived by audio source (e.g. I2S). Signed-off-by: Sugar Zhang Change-Id: I4c7aceb504e3b0820c329b398812d04f6a88993d --- sound/soc/rockchip/rockchip_i2s_tdm.c | 61 ++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 8610e87bbc81..4a1ef92116ee 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -36,11 +36,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) + struct txrx_config { u32 addr; u32 reg; @@ -103,11 +107,22 @@ struct rk_i2s_tdm_dev { 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, + }, +}; + static int to_ch_num(unsigned int val) { int chs; @@ -445,10 +460,13 @@ static void rockchip_i2s_tdm_xfer_start(struct rk_i2s_tdm_dev *i2s_tdm, } static void rockchip_i2s_tdm_xfer_stop(struct rk_i2s_tdm_dev *i2s_tdm, - int stream) + 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; @@ -487,7 +505,7 @@ static void rockchip_i2s_tdm_xfer_trcm_stop(struct rk_i2s_tdm_dev *i2s_tdm) spin_lock_irqsave(&i2s_tdm->lock, flags); if (atomic_dec_and_test(&i2s_tdm->refcount)) - rockchip_i2s_tdm_xfer_stop(i2s_tdm, 0); + rockchip_i2s_tdm_xfer_stop(i2s_tdm, 0, false); spin_unlock_irqrestore(&i2s_tdm->lock, flags); } @@ -500,7 +518,7 @@ static void rockchip_i2s_tdm_trcm_pause(struct snd_pcm_substream *substream, /* 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); + rockchip_i2s_tdm_xfer_stop(i2s_tdm, bstream, true); } static void rockchip_i2s_tdm_trcm_resume(struct snd_pcm_substream *substream, @@ -533,7 +551,7 @@ static void rockchip_i2s_tdm_stop(struct rk_i2s_tdm_dev *i2s_tdm, int stream) if (i2s_tdm->clk_trcm) rockchip_i2s_tdm_xfer_trcm_stop(i2s_tdm); else - rockchip_i2s_tdm_xfer_stop(i2s_tdm, stream); + rockchip_i2s_tdm_xfer_stop(i2s_tdm, stream, false); } static int rockchip_i2s_tdm_set_fmt(struct snd_soc_dai *cpu_dai, @@ -1030,7 +1048,7 @@ static int rockchip_i2s_tdm_params(struct snd_pcm_substream *substream, int stream = substream->stream; if (is_stream_active(i2s_tdm, stream)) - rockchip_i2s_tdm_stop(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, @@ -1878,8 +1896,7 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) #ifdef HAVE_SYNC_RESET bool sync; #endif - int ret; - int val; + int ret, val, i; ret = rockchip_i2s_tdm_dai_prepare(pdev, &soc_dai); if (ret) @@ -1898,6 +1915,10 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) 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)) { if ((val >= 32) && (val % 2 == 0)) @@ -2038,6 +2059,32 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) 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); + + 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,