From 2e3c724c167ec5da8b1ed5ab07cda55232ecdf13 Mon Sep 17 00:00:00 2001 From: ScottPeterson Date: Tue, 17 Jan 2012 17:32:11 -0800 Subject: asoc:tegra: Support I2S slave mode Support I2S slave mode. Disable pll_p_out1 and pll_a to reduce power when in slave mode. Slave mode disabled by default. Reviewed-on: http://git-master/r/76046 Change-Id: I873a11d54f1e037d99c86ff4cec06ee83064902a Signed-off-by: ScottPeterson Signed-off-by: Varun Wadekar Reviewed-on: http://git-master/r/77765 Reviewed-by: Automatic_Commit_Validation_User --- sound/soc/tegra/tegra30_ahub.c | 6 +++ sound/soc/tegra/tegra30_dam.c | 6 +++ sound/soc/tegra/tegra30_i2s.c | 8 ++- sound/soc/tegra/tegra_aic326x.c | 29 ++++++++-- sound/soc/tegra/tegra_asoc_utils.c | 107 ++++++++++++++++++++----------------- sound/soc/tegra/tegra_asoc_utils.h | 5 ++ sound/soc/tegra/tegra_max98088.c | 41 ++++++++++---- sound/soc/tegra/tegra_wm8903.c | 60 +++++++++++++++++++-- 8 files changed, 192 insertions(+), 70 deletions(-) (limited to 'sound/soc/tegra') diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c index 3fceda143da8..710d9465b4b0 100644 --- a/sound/soc/tegra/tegra30_ahub.c +++ b/sound/soc/tegra/tegra30_ahub.c @@ -487,6 +487,7 @@ static int __devinit tegra30_ahub_probe(struct platform_device *pdev) #ifdef CONFIG_PM int i = 0, cache_idx_rsvd; #endif + int clkm_rate; if (ahub) return -ENODEV; @@ -505,6 +506,11 @@ static int __devinit tegra30_ahub_probe(struct platform_device *pdev) ret = PTR_ERR(ahub->clk_d_audio); goto err_free; } + clkm_rate = clk_get_rate(clk_get_parent(ahub->clk_d_audio)); + while (clkm_rate > 12000000) + clkm_rate >>= 1; + + clk_set_rate(ahub->clk_d_audio,clkm_rate); ahub->clk_apbif = clk_get(&pdev->dev, "apbif"); if (IS_ERR(ahub->clk_apbif)) { diff --git a/sound/soc/tegra/tegra30_dam.c b/sound/soc/tegra/tegra30_dam.c index 4ac81266e7cf..d308179110c9 100644 --- a/sound/soc/tegra/tegra30_dam.c +++ b/sound/soc/tegra/tegra30_dam.c @@ -529,6 +529,7 @@ static int __devinit tegra30_dam_probe(struct platform_device *pdev) #ifdef CONFIG_PM int i; #endif + int clkm_rate; if ((pdev->id < 0) || (pdev->id >= TEGRA30_NR_DAM_IFC)) { @@ -552,6 +553,11 @@ static int __devinit tegra30_dam_probe(struct platform_device *pdev) ret = PTR_ERR(dam->dam_clk); goto err_free; } + clkm_rate = clk_get_rate(clk_get_parent(dam->dam_clk)); + while (clkm_rate > 12000000) + clkm_rate >>= 1; + + clk_set_rate(dam->dam_clk,clkm_rate); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c index c1de635765a5..9a0ed5a762ed 100644 --- a/sound/soc/tegra/tegra30_i2s.c +++ b/sound/soc/tegra/tegra30_i2s.c @@ -303,13 +303,11 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream, srate = params_rate(params); if (i2s->reg_ctrl & TEGRA30_I2S_CTRL_MASTER_ENABLE) { - /* Final "* 2" required by Tegra hardware */ - i2sclock = srate * params_channels(params) * sample_size * 2; + i2sclock = srate * params_channels(params) * sample_size; - /* Additional "* 2" is needed for FSYNC mode */ + /* Additional "* 4" is needed for FSYNC mode */ if (i2s->reg_ctrl & TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC) - i2sclock *= 2; - + i2sclock *= 4; 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"); diff --git a/sound/soc/tegra/tegra_aic326x.c b/sound/soc/tegra/tegra_aic326x.c index a7297a54233a..31afb907a653 100644 --- a/sound/soc/tegra/tegra_aic326x.c +++ b/sound/soc/tegra/tegra_aic326x.c @@ -237,6 +237,32 @@ static int tegra_aic326x_hw_params(struct snd_pcm_substream *substream, if (mclk < 0) return mclk; + +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) + clk = clk_get_sys(NULL, "cdev1"); +#else + clk = clk_get_sys("extern1", NULL); +#endif + if (IS_ERR(clk)) { + dev_err(card->dev, "Can't retrieve clk cdev1\n"); + err = PTR_ERR(clk); + return err; + } + + rate = clk_get_rate(clk); + printk("extern1 rate=%d\n",rate); + +#if TEGRA30_I2S_MASTER_PLAYBACK + daifmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS; +#else + daifmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; + mclk = rate; +#endif + err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); if (err < 0) { if (!(machine->util_data.set_mclk % mclk)) @@ -249,9 +275,6 @@ static int tegra_aic326x_hw_params(struct snd_pcm_substream *substream, tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1); - daifmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS; - err = snd_soc_dai_set_fmt(codec_dai, daifmt); if (err < 0) { dev_err(card->dev, "codec_dai fmt not set\n"); diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c index c517bd04e2da..1f336d82bfe3 100644 --- a/sound/soc/tegra/tegra_asoc_utils.c +++ b/sound/soc/tegra/tegra_asoc_utils.c @@ -25,6 +25,8 @@ #include #include +#include + #include "tegra_asoc_utils.h" int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, @@ -33,6 +35,7 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, int new_baseclock; bool clk_change; int err; + bool reenable_clock; switch (srate) { case 11025: @@ -73,41 +76,32 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, data->set_baseclock = 0; data->set_mclk = 0; - clk_disable(data->clk_cdev1); - clk_disable(data->clk_pll_a_out0); - clk_disable(data->clk_pll_a); - + reenable_clock = false; + if(tegra_is_clk_enabled(data->clk_pll_a)) { + clk_disable(data->clk_pll_a); + reenable_clock = true; + } err = clk_set_rate(data->clk_pll_a, new_baseclock); if (err) { dev_err(data->dev, "Can't set pll_a rate: %d\n", err); return err; } + if(reenable_clock) + clk_enable(data->clk_pll_a); - err = clk_set_rate(data->clk_pll_a_out0, mclk); - if (err) { - dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err); - return err; + reenable_clock = false; + if(tegra_is_clk_enabled(data->clk_pll_a_out0)) { + clk_disable(data->clk_pll_a_out0); + reenable_clock = true; } - - /* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */ - - err = clk_enable(data->clk_pll_a); - if (err) { - dev_err(data->dev, "Can't enable pll_a: %d\n", err); - return err; - } - - err = clk_enable(data->clk_pll_a_out0); + err = clk_set_rate(data->clk_pll_a_out0, mclk); if (err) { - dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err); + dev_err(data->dev, "Can't set clk_pll_a_out0 rate: %d\n", err); return err; } + if(reenable_clock) + clk_enable(data->clk_pll_a_out0); - err = clk_enable(data->clk_cdev1); - if (err) { - dev_err(data->dev, "Can't enable cdev1: %d\n", err); - return err; - } data->set_baseclock = new_baseclock; data->set_mclk = mclk; @@ -130,18 +124,6 @@ int tegra_asoc_utils_clk_enable(struct tegra_asoc_utils_data *data) { int err; - err = clk_enable(data->clk_pll_a); - if (err) { - dev_err(data->dev, "Can't enable pll_a: %d\n", err); - return err; - } - - err = clk_enable(data->clk_pll_a_out0); - if (err) { - dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err); - return err; - } - err = clk_enable(data->clk_cdev1); if (err) { dev_err(data->dev, "Can't enable cdev1: %d\n", err); @@ -155,8 +137,6 @@ EXPORT_SYMBOL_GPL(tegra_asoc_utils_clk_enable); int tegra_asoc_utils_clk_disable(struct tegra_asoc_utils_data *data) { clk_disable(data->clk_cdev1); - clk_disable(data->clk_pll_a_out0); - clk_disable(data->clk_pll_a); return 0; } EXPORT_SYMBOL_GPL(tegra_asoc_utils_clk_disable); @@ -165,14 +145,22 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, struct device *dev) { int ret; + int rate; data->dev = dev; + data->clk_pll_p_out1 = clk_get_sys(NULL, "pll_p_out1"); + if (IS_ERR(data->clk_pll_p_out1)) { + dev_err(data->dev, "Can't retrieve clk pll_p_out1\n"); + ret = PTR_ERR(data->clk_pll_p_out1); + goto err; + } + data->clk_pll_a = clk_get_sys(NULL, "pll_a"); if (IS_ERR(data->clk_pll_a)) { dev_err(data->dev, "Can't retrieve clk pll_a\n"); ret = PTR_ERR(data->clk_pll_a); - goto err; + goto err_put_pll_p_out1; } data->clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0"); @@ -182,6 +170,13 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, goto err_put_pll_a; } + data->clk_m = clk_get_sys(NULL, "clk_m"); + if (IS_ERR(data->clk_m)) { + dev_err(data->dev, "Can't retrieve clk clk_m\n"); + ret = PTR_ERR(data->clk_m); + goto err; + } + #if defined(CONFIG_ARCH_TEGRA_2x_SOC) data->clk_cdev1 = clk_get_sys(NULL, "cdev1"); #else @@ -204,24 +199,26 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, } #endif - ret = clk_enable(data->clk_pll_a); +#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) +#if TEGRA30_I2S_MASTER_PLAYBACK + ret = clk_set_parent(data->clk_cdev1, data->clk_pll_a_out0); if (ret) { - dev_err(data->dev, "Can't enable clk pll_a"); + dev_err(data->dev, "Can't set clk cdev1/extern1 parent"); goto err_put_out1; } +#else + rate = clk_get_rate(data->clk_m); - ret = clk_enable(data->clk_pll_a_out0); - if (ret) { - dev_err(data->dev, "Can't enable clk pll_a_out0"); - goto err_put_out1; - } + if(rate == 26000000) + clk_set_rate(data->clk_cdev1, 13000000); -#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) - ret = clk_set_parent(data->clk_cdev1, data->clk_pll_a_out0); + ret = clk_set_parent(data->clk_cdev1, data->clk_m); if (ret) { dev_err(data->dev, "Can't set clk cdev1/extern1 parent"); goto err_put_out1; } +#endif + #endif ret = clk_enable(data->clk_cdev1); @@ -255,6 +252,8 @@ err_put_pll_a_out0: clk_put(data->clk_pll_a_out0); err_put_pll_a: clk_put(data->clk_pll_a); +err_put_pll_p_out1: + clk_put(data->clk_pll_p_out1); err: return ret; } @@ -264,9 +263,17 @@ void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data) { if (!IS_ERR(data->clk_out1)) clk_put(data->clk_out1); + clk_put(data->clk_cdev1); - clk_put(data->clk_pll_a_out0); - clk_put(data->clk_pll_a); + + if (!IS_ERR(data->clk_pll_a_out0)) + clk_put(data->clk_pll_a_out0); + + if (!IS_ERR(data->clk_pll_a)) + clk_put(data->clk_pll_a); + + if (!IS_ERR(data->clk_pll_p_out1)) + clk_put(data->clk_pll_p_out1); } EXPORT_SYMBOL_GPL(tegra_asoc_utils_fini); diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h index 72d3994a935f..1c4e521cb4ba 100644 --- a/sound/soc/tegra/tegra_asoc_utils.h +++ b/sound/soc/tegra/tegra_asoc_utils.h @@ -23,6 +23,9 @@ #ifndef __TEGRA_ASOC_UTILS_H__ #define __TEGRA_ASOC_UTILS_H_ + +#define TEGRA30_I2S_MASTER_PLAYBACK 1 + struct clk; struct device; @@ -32,6 +35,8 @@ struct tegra_asoc_utils_data { struct clk *clk_pll_a_out0; struct clk *clk_cdev1; struct clk *clk_out1; + struct clk *clk_m; + struct clk *clk_pll_p_out1; int set_baseclock; int set_mclk; int lock_count; diff --git a/sound/soc/tegra/tegra_max98088.c b/sound/soc/tegra/tegra_max98088.c index d666a953a394..61f45936e440 100644 --- a/sound/soc/tegra/tegra_max98088.c +++ b/sound/soc/tegra/tegra_max98088.c @@ -30,6 +30,7 @@ #include +#include #include #include #include @@ -223,8 +224,10 @@ static int tegra_max98088_hw_params(struct snd_pcm_substream *substream, #ifndef CONFIG_ARCH_TEGRA_2x_SOC struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai); #endif - int srate, mclk, sample_size; + int srate, mclk, sample_size, i2s_daifmt; int err; + struct clk *clk; + int rate; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -256,6 +259,32 @@ static int tegra_max98088_hw_params(struct snd_pcm_substream *substream, break; } + +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) + clk = clk_get_sys(NULL, "cdev1"); +#else + clk = clk_get_sys("extern1", NULL); +#endif + if (IS_ERR(clk)) { + dev_err(card->dev, "Can't retrieve clk cdev1\n"); + err = PTR_ERR(clk); + return err; + } + + rate = clk_get_rate(clk); + printk("extern1 rate=%d\n",rate); + +#if TEGRA30_I2S_MASTER_PLAYBACK + i2s_daifmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS; +#else + i2s_daifmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; + mclk = rate; +#endif + err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); if (err < 0) { if (!(machine->util_data.set_mclk % mclk)) @@ -268,19 +297,13 @@ static int tegra_max98088_hw_params(struct snd_pcm_substream *substream, tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1); - err = snd_soc_dai_set_fmt(codec_dai, - SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); + err = snd_soc_dai_set_fmt(codec_dai,i2s_daifmt); if (err < 0) { dev_err(card->dev, "codec_dai fmt not set\n"); return err; } - err = snd_soc_dai_set_fmt(cpu_dai, - SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); + err = snd_soc_dai_set_fmt(cpu_dai, i2s_daifmt); if (err < 0) { dev_err(card->dev, "cpu_dai fmt not set\n"); return err; diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index bdd355574066..80b05dcd654c 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c @@ -30,6 +30,7 @@ #include +#include #include #include #include @@ -73,6 +74,7 @@ struct tegra_wm8903 { #ifdef CONFIG_SWITCH int jack_status; #endif + enum snd_soc_bias_level bias_level; }; static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream, @@ -86,6 +88,8 @@ static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream, struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); int srate, mclk, i2s_daifmt; int err; + struct clk *clk_m; + int rate; srate = params_rate(params); switch (srate) { @@ -98,10 +102,33 @@ static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream, mclk = 256 * srate; break; } + + + + clk_m = clk_get_sys(NULL, "clk_m"); + if (IS_ERR(clk_m)) { + dev_err(card->dev, "Can't retrieve clk clk_m\n"); + err = PTR_ERR(clk_m); + return err; + } + rate = clk_get_rate(clk_m); + printk("extern1 rate=%d\n",rate); + +#if TEGRA30_I2S_MASTER_PLAYBACK /* FIXME: Codec only requires >= 3MHz if OSR==0 */ while (mclk < 6000000) mclk *= 2; + i2s_daifmt = SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS; +#else + mclk = rate; + + i2s_daifmt = SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; +#endif + + err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); if (err < 0) { if (!(machine->util_data.set_mclk % mclk)) @@ -114,9 +141,6 @@ static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream, tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1); - i2s_daifmt = SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS; - /* Use DSP mode for mono on Tegra20 */ if ((params_channels(params) != 2) && (machine_is_ventana() || machine_is_harmony() || @@ -557,6 +581,8 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd) struct tegra_wm8903_platform_data *pdata = machine->pdata; int ret; + machine->bias_level = SND_SOC_BIAS_STANDBY; + if (gpio_is_valid(pdata->gpio_spkr_en)) { ret = gpio_request(pdata->gpio_spkr_en, "spkr_en"); if (ret) { @@ -664,6 +690,32 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd) return 0; } +static int tegra30_soc_set_bias_level(struct snd_soc_card *card, + enum snd_soc_bias_level level) +{ + struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); + + if (machine->bias_level == SND_SOC_BIAS_OFF && + level != SND_SOC_BIAS_OFF) + tegra_asoc_utils_clk_enable(&machine->util_data); + + return 0; +} + +static int tegra30_soc_set_bias_level_post(struct snd_soc_card *card, + enum snd_soc_bias_level level) +{ + struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); + + if (machine->bias_level != SND_SOC_BIAS_OFF && + level == SND_SOC_BIAS_OFF) + tegra_asoc_utils_clk_disable(&machine->util_data); + + machine->bias_level = level; + + return 0 ; +} + static struct snd_soc_dai_link tegra_wm8903_dai[] = { { .name = "WM8903", @@ -699,6 +751,8 @@ static struct snd_soc_card snd_soc_tegra_wm8903 = { .name = "tegra-wm8903", .dai_link = tegra_wm8903_dai, .num_links = ARRAY_SIZE(tegra_wm8903_dai), + //.set_bias_level = tegra30_soc_set_bias_level, + //.set_bias_level_post = tegra30_soc_set_bias_level_post, }; static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) -- cgit v1.2.3