diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c index e0e83ad0..238cd7ce 100644 --- a/sound/soc/tegra/tegra_asoc_utils.c +++ b/sound/soc/tegra/tegra_asoc_utils.c @@ -15,6 +15,7 @@ #include "tegra_asoc_utils.h" +#define MAX(X, Y) ((X > Y) ? (X) : (Y)) /* * this will be used for platforms from Tegra210 onwards. * odd rates: sample rates multiple of 11.025kHz @@ -26,8 +27,8 @@ enum rate_type { NUM_RATE_TYPE, }; unsigned int tegra210_pll_base_rate[NUM_RATE_TYPE] = {338688000, 368640000}; -unsigned int tegra186_pll_base_rate[NUM_RATE_TYPE] = {270950400, 294912000}; -unsigned int default_pll_out_rate[NUM_RATE_TYPE] = {45158400, 49152000}; +unsigned int tegra186_pll_stereo_base_rate[NUM_RATE_TYPE] = {270950400, 294912000}; +unsigned int default_pll_out_stereo_rate[NUM_RATE_TYPE] = {45158400, 49152000}; int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, int mclk) @@ -170,10 +171,64 @@ int tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data *data) } EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_ac97_rate); -int tegra_asoc_utils_set_tegra210_rate(struct tegra_asoc_utils_data *data, - unsigned int sample_rate) +static int modify_parent_clk_base_rates(unsigned int *new_pll_base, + unsigned int *pll_out, + unsigned int req_bclk, + struct tegra_asoc_utils_data *data) { - unsigned int new_pll_base, pll_out, aud_mclk = 0; + unsigned int bclk_div, pll_div; + bool pll_out_halved = false; + + if (req_bclk == 0) + return 0; + + if (req_bclk > *pll_out) + return -EOPNOTSUPP; + + if ((*pll_out / req_bclk) > 128) { + /* reduce pll_out rate to support lower sampling rates */ + *pll_out >>= 1; + pll_out_halved = true; + } + + /* Modify base rates on chips >= T186 if fractional dividier is seen */ + if (data->soc >= TEGRA_ASOC_UTILS_SOC_TEGRA186 && + (*pll_out % req_bclk)) { + + /* Below logic is added to reduce dynamic range + * of PLLA (~37MHz). Min and max plla for chips >= t186 + * are 258.048 MHz and 294.912 MHz respectively. PLLA dynamic + * range is kept minimal to avoid clk ramp up/down issues + * and avoid halving plla if already halved + */ + if (!pll_out_halved && req_bclk <= (*pll_out >> 1)) + *pll_out >>= 1; + + *new_pll_base = MAX(data->pll_base_rate[EVEN_RATE], + data->pll_base_rate[ODD_RATE]); + + /* Modifying base rates for i2s parent and grand parent + * clocks so that i2s rate can be derived with integer division + * as fractional divider is not supported in HW + */ + + bclk_div = *pll_out / req_bclk; + *pll_out = req_bclk * bclk_div; + pll_div = *new_pll_base / *pll_out; + *new_pll_base = pll_div * (*pll_out); + /* TODO: Make sure that the dynamic range is not violated + * by having chip specific lower and upper limits of PLLA + */ + } + return 0; +} + +int tegra_asoc_utils_set_tegra210_rate(struct tegra_asoc_utils_data *data, + unsigned int sample_rate, + unsigned int channels, + unsigned int sample_size) +{ + unsigned int new_pll_base, pll_out, aud_mclk = 0, req_bclk; int err; if (data->fixed_pll) @@ -186,7 +241,7 @@ int tegra_asoc_utils_set_tegra210_rate(struct tegra_asoc_utils_data *data, case 88200: case 176400: new_pll_base = data->pll_base_rate[ODD_RATE]; - pll_out = default_pll_out_rate[ODD_RATE]; + pll_out = default_pll_out_stereo_rate[ODD_RATE]; break; case 8000: case 16000: @@ -197,15 +252,21 @@ int tegra_asoc_utils_set_tegra210_rate(struct tegra_asoc_utils_data *data, case 96000: case 192000: new_pll_base = data->pll_base_rate[EVEN_RATE]; - pll_out = default_pll_out_rate[EVEN_RATE]; + pll_out = default_pll_out_stereo_rate[EVEN_RATE]; break; default: return -EINVAL; } - /* reduce pll_out rate to support lower sampling rates */ - if (sample_rate <= 11025) - pll_out = pll_out >> 1; + req_bclk = sample_rate * channels * sample_size; + + err = modify_parent_clk_base_rates(&new_pll_base, + &pll_out, req_bclk, data); + if (err) { + dev_err(data->dev, "Clk rate %d not supported\n", + req_bclk); + return err; + } if (data->set_baseclock != new_pll_base) { err = clk_set_rate(data->clk_pll_a, new_pll_base); @@ -322,7 +383,7 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, if (data->soc < TEGRA_ASOC_UTILS_SOC_TEGRA186) data->pll_base_rate = tegra210_pll_base_rate; else - data->pll_base_rate = tegra186_pll_base_rate; + data->pll_base_rate = tegra186_pll_stereo_base_rate; /* * If clock parents are not set in DT, configure here to use clk_out_1 * as mclk and extern1 as parent for Tegra30 and higher. diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h index 83e43376..42ec5677 100644 --- a/sound/soc/tegra/tegra_asoc_utils.h +++ b/sound/soc/tegra/tegra_asoc_utils.h @@ -41,7 +41,9 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, int mclk); int tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data *data); int tegra_asoc_utils_set_tegra210_rate(struct tegra_asoc_utils_data *data, - unsigned int sample_rate); + unsigned int sample_rate, + unsigned int channels, + unsigned int sample_size); int tegra_asoc_utils_clk_enable(struct tegra_asoc_utils_data *data); void tegra_asoc_utils_clk_disable(struct tegra_asoc_utils_data *data); int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, diff --git a/sound/soc/tegra/tegra_machine_driver.c b/sound/soc/tegra/tegra_machine_driver.c index dc3bd860..f51a4c88 100644 --- a/sound/soc/tegra/tegra_machine_driver.c +++ b/sound/soc/tegra/tegra_machine_driver.c @@ -131,16 +131,40 @@ static int tegra_machine_dai_init(struct snd_soc_pcm_runtime *runtime, struct snd_soc_pcm_stream *dai_params; unsigned int aud_mclk, srate; u64 format_k, fmt; - int err; + int err, sample_size; struct snd_soc_pcm_runtime *rtd; srate = (machine->rate_via_kcontrol) ? tegra_machine_srate_values[machine->rate_via_kcontrol] : rate; format_k = (machine->fmt_via_kcontrol == 2) ? - (1ULL << SNDRV_PCM_FORMAT_S32_LE) : formats; + (1ULL << SNDRV_PCM_FORMAT_S32_LE) : (1 << formats); + switch (formats) { + case SNDRV_PCM_FORMAT_S8: + sample_size = 8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + sample_size = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + /* + * I2S bit clock is derived from PLLA_OUT0 and size of + * 24 bits results in fractional value and the clock + * is not accurate with this. To have integer clock + * division below is used. It means there are additional + * bit clocks (8 cycles) which are ignored. Codec picks + * up data for other channel when LRCK signal toggles. + */ + case SNDRV_PCM_FORMAT_S32_LE: + sample_size = 32; + break; + default: + pr_err("Wrong format!\n"); + return -EINVAL; + } - err = tegra_asoc_utils_set_tegra210_rate(&machine->audio_clock, srate); + err = tegra_asoc_utils_set_tegra210_rate(&machine->audio_clock, srate, + channels, sample_size); if (err < 0) { dev_err(card->dev, "Can't configure clocks\n"); return err; @@ -187,7 +211,7 @@ static int tegra_machine_pcm_hw_params(struct snd_pcm_substream *substream, err = tegra_machine_dai_init(rtd, params_rate(params), params_channels(params), - 1ULL << params_format(params)); + params_format(params)); if (err < 0) { dev_err(card->dev, "Failed dai init\n"); return err; @@ -254,7 +278,7 @@ static int tegra_machine_compr_set_params(struct snd_compr_stream *cstream) err = tegra_machine_dai_init(rtd, codec_params.sample_rate, codec_params.ch_out, - SNDRV_PCM_FMTBIT_S16_LE); + SNDRV_PCM_FORMAT_S16_LE); if (err < 0) { dev_err(card->dev, "Failed dai init\n"); return err;