diff options
author | Sumit Bhattacharya <sumitb@nvidia.com> | 2011-12-13 21:07:56 +0530 |
---|---|---|
committer | Varun Wadekar <vwadekar@nvidia.com> | 2011-12-15 12:12:35 +0530 |
commit | 7d2988bcb3b28c3cb3c2c027fc1ae6f230db5941 (patch) | |
tree | 5d683c7c82d35c4f0d17b9d2e476773ca25df4bf /sound/soc/tegra/tegra30_i2s.c | |
parent | 0906482aa3e33187e3430c1c517fbdbccd1843f2 (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.c | 116 |
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); |