summaryrefslogtreecommitdiff
path: root/sound/soc/tegra/tegra30_i2s.c
diff options
context:
space:
mode:
authorSumit Bhattacharya <sumitb@nvidia.com>2011-12-13 21:07:56 +0530
committerVarun Wadekar <vwadekar@nvidia.com>2011-12-15 12:12:35 +0530
commit7d2988bcb3b28c3cb3c2c027fc1ae6f230db5941 (patch)
tree5d683c7c82d35c4f0d17b9d2e476773ca25df4bf /sound/soc/tegra/tegra30_i2s.c
parent0906482aa3e33187e3430c1c517fbdbccd1843f2 (diff)
ASoC: Tegra: Add support for AP slave mode for Tegra30
If AP is set as slave set audio_sync clock as source of i2s controller clock and use pll_a_out0 as i2s controller source in AP master mode. This change is needed to support AP slave mode reliably on Tegra30. Bug 911332 Change-Id: I91e54d1d297c58ad65baac86831bccfbaadf732c Signed-off-by: Sumit Bhattacharya <sumitb@nvidia.com> Reviewed-on: http://git-master/r/69777 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Lokesh Pathak <lpathak@nvidia.com> Tested-by: Lokesh Pathak <lpathak@nvidia.com>
Diffstat (limited to 'sound/soc/tegra/tegra30_i2s.c')
-rw-r--r--sound/soc/tegra/tegra30_i2s.c116
1 files changed, 88 insertions, 28 deletions
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
index 29ada9943450..d8edcfba096d 100644
--- a/sound/soc/tegra/tegra30_i2s.c
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -302,37 +302,67 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream,
srate = params_rate(params);
- /* Final "* 2" required by Tegra hardware */
- i2sclock = srate * params_channels(params) * sample_size * 2;
+ if (i2s->reg_ctrl & TEGRA30_I2S_CTRL_MASTER_ENABLE) {
+ /* Final "* 2" required by Tegra hardware */
+ i2sclock = srate * params_channels(params) * sample_size * 2;
+
+ /* Additional "* 2" is needed for FSYNC mode */
+ if (i2s->reg_ctrl & TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC)
+ i2sclock *= 2;
+
+ ret = clk_set_parent(i2s->clk_i2s, i2s->clk_pll_a_out0);
+ if (ret) {
+ dev_err(dev, "Can't set parent of I2S clock\n");
+ return ret;
+ }
+
+ ret = clk_set_rate(i2s->clk_i2s, i2sclock);
+ if (ret) {
+ dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
+ return ret;
+ }
+
+ if (i2s->reg_ctrl & TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC) {
+ bitcnt = (i2sclock / srate) - 1;
+ sym_bitclk = !(i2sclock % srate);
+ } else {
+ bitcnt = (i2sclock / (2 * srate)) - 1;
+ sym_bitclk = !(i2sclock % (2 * srate));
+ }
+ val = bitcnt << TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
- /* Additional "* 2" is needed for FSYNC mode */
- if (i2s->reg_ctrl & TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC)
- i2sclock *= 2;
+ if (!sym_bitclk)
+ val |= TEGRA30_I2S_TIMING_NON_SYM_ENABLE;
- ret = clk_set_rate(i2s->clk_i2s, i2sclock);
- if (ret) {
- dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
- return ret;
- }
+ tegra30_i2s_enable_clocks(i2s);
- if (i2s->reg_ctrl & TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC) {
- bitcnt = (i2sclock / srate) - 1;
- sym_bitclk = !(i2sclock % srate);
- i2s_client_ch = params_channels(params);
+ tegra30_i2s_write(i2s, TEGRA30_I2S_TIMING, val);
} else {
- bitcnt = (i2sclock / (2 * srate)) - 1;
- sym_bitclk = !(i2sclock % (2 * srate));
- i2s_client_ch = 2;
- }
-
- tegra30_i2s_enable_clocks(i2s);
-
- val = bitcnt << TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
+ i2sclock = srate * params_channels(params) * sample_size;
+
+ ret = clk_set_rate(i2s->clk_i2s_sync, i2sclock);
+ if (ret) {
+ dev_err(dev, "Can't set I2S sync clock rate\n");
+ return ret;
+ }
+
+ ret = clk_set_rate(i2s->clk_audio_2x, i2sclock);
+ if (ret) {
+ dev_err(dev, "Can't set I2S sync clock rate\n");
+ return ret;
+ }
+
+ ret = clk_set_parent(i2s->clk_i2s, i2s->clk_audio_2x);
+ if (ret) {
+ dev_err(dev, "Can't set parent of audio2x clock\n");
+ return ret;
+ }
- if (!sym_bitclk)
- val |= TEGRA30_I2S_TIMING_NON_SYM_ENABLE;
+ tegra30_i2s_enable_clocks(i2s);
+ }
- tegra30_i2s_write(i2s, TEGRA30_I2S_TIMING, val);
+ i2s_client_ch = (i2s->reg_ctrl & TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC) ?
+ params_channels(params) : 2;
val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) |
((params_channels(params) - 1) <<
@@ -788,11 +818,32 @@ static __devinit int tegra30_i2s_platform_probe(struct platform_device *pdev)
goto exit;
}
+ i2s->clk_i2s_sync = clk_get(&pdev->dev, "ext_audio_sync");
+ if (IS_ERR(i2s->clk_i2s_sync)) {
+ dev_err(&pdev->dev, "Can't retrieve i2s_sync clock\n");
+ ret = PTR_ERR(i2s->clk_i2s_sync);
+ goto err_i2s_clk_put;
+ }
+
+ i2s->clk_audio_2x = clk_get(&pdev->dev, "audio_sync_2x");
+ if (IS_ERR(i2s->clk_audio_2x)) {
+ dev_err(&pdev->dev, "Can't retrieve audio 2x clock\n");
+ ret = PTR_ERR(i2s->clk_audio_2x);
+ goto err_i2s_sync_clk_put;
+ }
+
+ i2s->clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0");
+ if (IS_ERR(i2s->clk_pll_a_out0)) {
+ dev_err(&pdev->dev, "Can't retrieve pll_a_out0 clock\n");
+ ret = PTR_ERR(i2s->clk_pll_a_out0);
+ goto err_audio_2x_clk_put;
+ }
+
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
dev_err(&pdev->dev, "No memory resource\n");
ret = -ENODEV;
- goto err_clk_put;
+ goto err_pll_a_out0_clk_put;
}
memregion = request_mem_region(mem->start, resource_size(mem),
@@ -800,7 +851,7 @@ static __devinit int tegra30_i2s_platform_probe(struct platform_device *pdev)
if (!memregion) {
dev_err(&pdev->dev, "Memory region already claimed\n");
ret = -EBUSY;
- goto err_clk_put;
+ goto err_pll_a_out0_clk_put;
}
i2s->regs = ioremap(mem->start, resource_size(mem));
@@ -825,7 +876,13 @@ err_unmap:
iounmap(i2s->regs);
err_release:
release_mem_region(mem->start, resource_size(mem));
-err_clk_put:
+err_pll_a_out0_clk_put:
+ clk_put(i2s->clk_pll_a_out0);
+err_audio_2x_clk_put:
+ clk_put(i2s->clk_audio_2x);
+err_i2s_sync_clk_put:
+ clk_put(i2s->clk_i2s_sync);
+err_i2s_clk_put:
clk_put(i2s->clk_i2s);
exit:
return ret;
@@ -845,6 +902,9 @@ static int __devexit tegra30_i2s_platform_remove(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, resource_size(res));
+ clk_put(i2s->clk_pll_a_out0);
+ clk_put(i2s->clk_audio_2x);
+ clk_put(i2s->clk_i2s_sync);
clk_put(i2s->clk_i2s);
kfree(i2s);