From 1f6bb2da545f9caad290d2b525d74fc8896dbe86 Mon Sep 17 00:00:00 2001 From: Xing Zheng Date: Tue, 8 May 2018 09:31:25 +0800 Subject: [PATCH] ASoC: rockchip: i2s-tdm: set mclk tx/rx rate independently By default the i2s-tdm supports asymmetric sample rate, we just set mclk tx or rx during playback or capture. Change-Id: I1a9786a64b956c68bb5ba8796d2742f5b142b124 Signed-off-by: Xing Zheng --- sound/soc/rockchip/rockchip_i2s_tdm.c | 96 +++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 13 deletions(-) diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 0f03c4179104..10380e05efc9 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,8 @@ struct rk_i2s_tdm_dev { struct rk_i2s_soc_data *soc_data; void __iomem *cru_base; bool is_master_mode; + unsigned int mclk_rx_freq; + unsigned int mclk_tx_freq; unsigned int bclk_fs; unsigned int clk_trcm; atomic_t refcount; @@ -521,6 +524,67 @@ static void rockchip_i2s_tdm_xfer_resume(struct snd_pcm_substream *substream, I2S_XFER_RXS_START); } +static int rockchip_i2s_tdm_set_mclk(struct rk_i2s_tdm_dev *i2s_tdm, + struct snd_pcm_substream *substream, + struct clk **mclk) +{ + unsigned int mclk_freq; + int ret; + + if (i2s_tdm->clk_trcm) { + if (i2s_tdm->mclk_tx_freq != i2s_tdm->mclk_rx_freq) { + dev_err(i2s_tdm->dev, + "clk_trcm, tx: %d and rx: %d should be same\n", + i2s_tdm->mclk_tx_freq, + i2s_tdm->mclk_rx_freq); + ret = -EINVAL; + goto err; + } + + ret = clk_set_rate(i2s_tdm->mclk_tx, i2s_tdm->mclk_tx_freq); + if (ret < 0) { + dev_err(i2s_tdm->dev, + "Set mclk_tx: %s freq: %d failed: %d\n", + __clk_get_name(i2s_tdm->mclk_tx), + i2s_tdm->mclk_tx_freq, ret); + goto err; + } + + ret = clk_set_rate(i2s_tdm->mclk_rx, i2s_tdm->mclk_rx_freq); + if (ret < 0) { + dev_err(i2s_tdm->dev, + "Set mclk_rx: %s freq: %d failed: %d\n", + __clk_get_name(i2s_tdm->mclk_rx), + i2s_tdm->mclk_rx_freq, ret); + goto err; + } + + /* Using mclk_rx is ok. */ + *mclk = i2s_tdm->mclk_tx; + } else { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + *mclk = i2s_tdm->mclk_tx; + mclk_freq = i2s_tdm->mclk_tx_freq; + } else { + *mclk = i2s_tdm->mclk_rx; + mclk_freq = i2s_tdm->mclk_rx_freq; + } + + ret = clk_set_rate(*mclk, mclk_freq); + if (ret < 0) { + dev_err(i2s_tdm->dev, "Set mclk_%s: %s freq: %d failed: %d\n", + substream->stream ? "rx" : "tx", + __clk_get_name(*mclk), mclk_freq, ret); + goto err; + } + } + + return 0; + +err: + return ret; +} + static int rockchip_i2s_tdm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -531,17 +595,16 @@ static int rockchip_i2s_tdm_hw_params(struct snd_pcm_substream *substream, unsigned int val = 0; unsigned int mclk_rate, bclk_rate, div_bclk, div_lrck; + ret = rockchip_i2s_tdm_set_mclk(i2s_tdm, substream, &mclk); + if (ret) + return ret; + if (i2s_tdm->clk_trcm) { spin_lock(&i2s_tdm->lock); if (atomic_read(&i2s_tdm->refcount)) rockchip_i2s_tdm_xfer_pause(substream, i2s_tdm); } - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - mclk = i2s_tdm->mclk_tx; - else - mclk = i2s_tdm->mclk_rx; - if (i2s_tdm->is_master_mode) { mclk_rate = clk_get_rate(mclk); bclk_rate = i2s_tdm->bclk_fs * params_rate(params); @@ -679,19 +742,26 @@ static int rockchip_i2s_tdm_trigger(struct snd_pcm_substream *substream, return ret; } -static int rockchip_i2s_tdm_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, +static int rockchip_i2s_tdm_set_sysclk(struct snd_soc_dai *cpu_dai, int stream, unsigned int freq, int dir) { struct rk_i2s_tdm_dev *i2s_tdm = to_info(cpu_dai); - int ret; - ret = clk_set_rate(i2s_tdm->mclk_tx, freq); - if (ret) - dev_err(i2s_tdm->dev, "Fail to set mclk_tx %d\n", ret); + /* Put set mclk rate into rockchip_i2s_tdm_set_mclk() */ + if (i2s_tdm->clk_trcm) { + i2s_tdm->mclk_tx_freq = freq; + i2s_tdm->mclk_rx_freq = freq; + } else { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + i2s_tdm->mclk_tx_freq = freq; + else + i2s_tdm->mclk_rx_freq = freq; + } - if (!IS_ERR(i2s_tdm->mclk_rx)) - ret = clk_set_rate(i2s_tdm->mclk_rx, freq); - return ret; + dev_dbg(i2s_tdm->dev, "The target mclk_%s freq is: %d\n", + stream ? "rx" : "tx", freq); + + return 0; } static int rockchip_i2s_tdm_dai_probe(struct snd_soc_dai *dai)