From f9b136f50bbc129e681a1895939ac3c79ba427c0 Mon Sep 17 00:00:00 2001 From: Xing Zheng Date: Thu, 17 May 2018 11:05:01 +0800 Subject: [PATCH] ASoC: rockchip: i2s-tdm: add support calibrate mclk src MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Usually, there are 2 groups of sample rates on audio system: a) 8000/16000/32000/48000/64000/96000/192000 b) 11025/22050/44100/88200/176400 If the platform provides two suitable multiples of the PLLs(e.g VPLL0@1179648000, VPLL1@903168000 on RK3308), we can switch the mclk src to VPLL0 if sample rate belongs a) group, otherwise, switch the mclk src to VPLL1. Also, we need to ensure that the numerator of fractional division is equal to 1. And we need to set a suitable reference frequency for the parent of mclk: group a) is 192000*256=49152000Hz group b) is 176400*256=45158400Hz The following is the calibrated i2s_8ch_2 on RK3308 EVB: ======== // VPLL0=1179648000Hz, VPLL1=903168000Hz // @8000Hz, root is VPLL0,mclk=2048000Hz, FRAC division, numerator is 1 ff5001f0: 00000417 00010018 00000013 00000000 // @11025Hz, root is VPLL1,mclk=2822400Hz, FRAC division, numerator is 1 ff5001f0: 00000513 00010010 00000013 00000000 // @16000Hz, root is VPLL0,mclk=4096000Hz, FRAC division, numerator is 1 ff5001f0: 00000417 0001000c 00000013 00000000 // @22050Hz, root is VPLL1,mclk=5644800Hz, FRAC division, numerator is 1 ff5001f0: 00000513 00010008 00000013 00000000 // @32000Hz, root is VPLL0, mclk=8192000Hz, FRAC division, numerator is 1 ff5001f0: 00000417 00010006 00000013 00000000 // @44100Hz, root is VPLL1, mclk=11289600Hz, Integer division direclty ff5001f0: 0000014f 00010006 00000013 00000000 // @48000Hz, root is VPLL0, mclk=12288000Hz, Integer division direclty ff5001f0: 0000005f 00010006 00000013 00000000 // @64000Hz, root is VPLL0, mclk=16384000Hz, Integer division direclty ff5001f0: 00000047 00010006 00000013 00000000 // @88200Hz, root is VPLL1 mclk=22579200Hz, Integer division direclty ff5001f0: 00000127 00010006 00000013 00000000 // @96000Hz, root is VPLL0, mclk=24576000Hz, Integer division direclty ff5001f0: 0000002f 00010006 00000013 00000000 // @176400Hz, root is VPLL1, mclk=45158400Hz, Integer division direclty ff5001f0: 00000113 00010006 00000013 00000000 // @192000Hz, root is VPLL0, mclk=49152000Hz, Integer division direclty ff5001f0: 00000017 00010006 00000013 00000000 ======== Change-Id: I6776f96e92790ba83a7d0f5caeeaf917dff5e5c5 Signed-off-by: Xing Zheng --- sound/soc/rockchip/rockchip_i2s_tdm.c | 104 ++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 10380e05efc9..25b8c479f034 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -29,6 +29,8 @@ #define DRV_NAME "rockchip-i2s-tdm" +#define DEFAULT_MCLK_FS 256 + struct rk_i2s_soc_data { u32 softrst_offset; int tx_reset_id; @@ -40,6 +42,20 @@ struct rk_i2s_tdm_dev { struct clk *hclk; struct clk *mclk_tx; struct clk *mclk_rx; + /* The mclk_tx_src is parent of mclk_tx */ + struct clk *mclk_tx_src; + /* The mclk_rx_src is parent of mclk_rx */ + struct clk *mclk_rx_src; + /* + * The mclk_root0 and mclk_root1 are root parent and supplies for + * the different FS. + * + * e.g: + * mclk_root0 is VPLL0, used for FS=48000Hz + * mclk_root0 is VPLL1, used for FS=44100Hz + */ + struct clk *mclk_root0; + struct clk *mclk_root1; struct regmap *regmap; struct snd_dmaengine_dai_dma_data capture_dma_data; struct snd_dmaengine_dai_dma_data playback_dma_data; @@ -48,6 +64,7 @@ struct rk_i2s_tdm_dev { struct rk_i2s_soc_data *soc_data; void __iomem *cru_base; bool is_master_mode; + bool mclk_calibrate; unsigned int mclk_rx_freq; unsigned int mclk_tx_freq; unsigned int bclk_fs; @@ -524,6 +541,69 @@ static void rockchip_i2s_tdm_xfer_resume(struct snd_pcm_substream *substream, I2S_XFER_RXS_START); } +static int rockchip_i2s_tdm_calibrate_mclk(struct rk_i2s_tdm_dev *i2s_tdm, + struct snd_pcm_substream *substream, + unsigned int lrck_freq) +{ + struct clk *mclk_root; + struct clk *mclk_parent; + /* It's 256 times higher than a high sample rate */ + unsigned int mclk_parent_freq; + int ret; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + mclk_parent = i2s_tdm->mclk_tx_src; + else + mclk_parent = i2s_tdm->mclk_rx_src; + + switch (lrck_freq) { + case 8000: + case 16000: + case 24000: + case 32000: + case 48000: + case 64000: + case 96000: + case 192000: + mclk_root = i2s_tdm->mclk_root0; + mclk_parent_freq = DEFAULT_MCLK_FS * 192000; + break; + case 11025: + case 22050: + case 44100: + case 88200: + case 176400: + mclk_root = i2s_tdm->mclk_root1; + mclk_parent_freq = DEFAULT_MCLK_FS * 176400; + break; + default: + dev_err(i2s_tdm->dev, "Invalid LRCK freq: %u Hz\n", + lrck_freq); + return -EINVAL; + } + + ret = clk_set_parent(mclk_parent, mclk_root); + if (ret < 0) { + dev_err(i2s_tdm->dev, "parent: %s set root: %s failed: %d\n", + __clk_get_name(mclk_parent), + __clk_get_name(mclk_root), + ret); + goto out; + } + + ret = clk_set_rate(mclk_parent, mclk_parent_freq); + if (ret < 0) { + dev_err(i2s_tdm->dev, "parent: %s set freq: %d failed: %d\n", + __clk_get_name(mclk_parent), + mclk_parent_freq, + ret); + goto out; + } + +out: + return ret; +} + static int rockchip_i2s_tdm_set_mclk(struct rk_i2s_tdm_dev *i2s_tdm, struct snd_pcm_substream *substream, struct clk **mclk) @@ -595,6 +675,10 @@ 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; + if (i2s_tdm->mclk_calibrate) + rockchip_i2s_tdm_calibrate_mclk(i2s_tdm, substream, + params_rate(params)); + ret = rockchip_i2s_tdm_set_mclk(i2s_tdm, substream, &mclk); if (ret) return ret; @@ -1024,6 +1108,26 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) if (IS_ERR(i2s_tdm->mclk_rx)) return PTR_ERR(i2s_tdm->mclk_rx); + i2s_tdm->mclk_calibrate = + of_property_read_bool(node, "rockchip,mclk-calibrate"); + if (i2s_tdm->mclk_calibrate) { + i2s_tdm->mclk_tx_src = devm_clk_get(&pdev->dev, "mclk_tx_src"); + if (IS_ERR(i2s_tdm->mclk_tx_src)) + return PTR_ERR(i2s_tdm->mclk_tx_src); + + i2s_tdm->mclk_rx_src = devm_clk_get(&pdev->dev, "mclk_rx_src"); + if (IS_ERR(i2s_tdm->mclk_rx_src)) + return PTR_ERR(i2s_tdm->mclk_rx_src); + + i2s_tdm->mclk_root0 = devm_clk_get(&pdev->dev, "mclk_root0"); + if (IS_ERR(i2s_tdm->mclk_root0)) + return PTR_ERR(i2s_tdm->mclk_root0); + + i2s_tdm->mclk_root1 = devm_clk_get(&pdev->dev, "mclk_root1"); + if (IS_ERR(i2s_tdm->mclk_root1)) + return PTR_ERR(i2s_tdm->mclk_root1); + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(regs))