ASoC: rockchip: i2s-tdm: add support calibrate mclk src

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 <zhengxing@rock-chips.com>
This commit is contained in:
Xing Zheng
2018-05-17 11:05:01 +08:00
committed by Tao Huang
parent 1f6bb2da54
commit f9b136f50b

View File

@@ -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))