mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
AsoC: tegra-alt: Update i2s rate setting logic
Earlier hardcoded pll base rates were leading to fractional divider values for i2s multichannel config while deriving i2s bclk. Hence updated clock rates and logic for >2 channel configs for t186ref and higher boards such that the dynamic range between max plla and min plla is < 35 MHz. Also, if desired bclk is above limit, the case will be declared as not supported. Note that new facility will be used only in l4t machine driver. Bug 200702569 Change-Id: I83aba425f6dde30a1f29f85b16a1bbbebba14198 Signed-off-by: Asha Talambedu <atalambedu@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2491744 (cherry picked from commit e0fe51d5e7ced073eb618e19836f88a023f70bdc) Reviewed-on: https://git-master.nvidia.com/r/c/linux-5.10/+/2613507 Tested-by: mobile promotions <svcmobile_promotions@nvidia.com> Reviewed-by: svc_kernel_abi <svc_kernel_abi@nvidia.com> Reviewed-by: Mohan Kumar D <mkumard@nvidia.com> Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com> Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com> GVS: Gerrit_Virtual_Submit
This commit is contained in:
committed by
Sameer Pujar
parent
39cc4081e3
commit
719434d072
@@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include "tegra_asoc_utils.h"
|
#include "tegra_asoc_utils.h"
|
||||||
|
|
||||||
|
#define MAX(X, Y) ((X > Y) ? (X) : (Y))
|
||||||
/*
|
/*
|
||||||
* this will be used for platforms from Tegra210 onwards.
|
* this will be used for platforms from Tegra210 onwards.
|
||||||
* odd rates: sample rates multiple of 11.025kHz
|
* odd rates: sample rates multiple of 11.025kHz
|
||||||
@@ -26,8 +27,8 @@ enum rate_type {
|
|||||||
NUM_RATE_TYPE,
|
NUM_RATE_TYPE,
|
||||||
};
|
};
|
||||||
unsigned int tegra210_pll_base_rate[NUM_RATE_TYPE] = {338688000, 368640000};
|
unsigned int tegra210_pll_base_rate[NUM_RATE_TYPE] = {338688000, 368640000};
|
||||||
unsigned int tegra186_pll_base_rate[NUM_RATE_TYPE] = {270950400, 294912000};
|
unsigned int tegra186_pll_stereo_base_rate[NUM_RATE_TYPE] = {270950400, 294912000};
|
||||||
unsigned int default_pll_out_rate[NUM_RATE_TYPE] = {45158400, 49152000};
|
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 tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
|
||||||
int mclk)
|
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);
|
EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_ac97_rate);
|
||||||
|
|
||||||
int tegra_asoc_utils_set_tegra210_rate(struct tegra_asoc_utils_data *data,
|
static int modify_parent_clk_base_rates(unsigned int *new_pll_base,
|
||||||
unsigned int sample_rate)
|
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;
|
int err;
|
||||||
|
|
||||||
if (data->fixed_pll)
|
if (data->fixed_pll)
|
||||||
@@ -186,7 +241,7 @@ int tegra_asoc_utils_set_tegra210_rate(struct tegra_asoc_utils_data *data,
|
|||||||
case 88200:
|
case 88200:
|
||||||
case 176400:
|
case 176400:
|
||||||
new_pll_base = data->pll_base_rate[ODD_RATE];
|
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;
|
break;
|
||||||
case 8000:
|
case 8000:
|
||||||
case 16000:
|
case 16000:
|
||||||
@@ -197,15 +252,21 @@ int tegra_asoc_utils_set_tegra210_rate(struct tegra_asoc_utils_data *data,
|
|||||||
case 96000:
|
case 96000:
|
||||||
case 192000:
|
case 192000:
|
||||||
new_pll_base = data->pll_base_rate[EVEN_RATE];
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* reduce pll_out rate to support lower sampling rates */
|
req_bclk = sample_rate * channels * sample_size;
|
||||||
if (sample_rate <= 11025)
|
|
||||||
pll_out = pll_out >> 1;
|
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) {
|
if (data->set_baseclock != new_pll_base) {
|
||||||
err = clk_set_rate(data->clk_pll_a, 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)
|
if (data->soc < TEGRA_ASOC_UTILS_SOC_TEGRA186)
|
||||||
data->pll_base_rate = tegra210_pll_base_rate;
|
data->pll_base_rate = tegra210_pll_base_rate;
|
||||||
else
|
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
|
* 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.
|
* as mclk and extern1 as parent for Tegra30 and higher.
|
||||||
|
|||||||
@@ -41,7 +41,9 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
|
|||||||
int mclk);
|
int mclk);
|
||||||
int tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data *data);
|
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,
|
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);
|
int tegra_asoc_utils_clk_enable(struct tegra_asoc_utils_data *data);
|
||||||
void tegra_asoc_utils_clk_disable(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,
|
int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
|
||||||
|
|||||||
@@ -131,16 +131,40 @@ static int tegra_machine_dai_init(struct snd_soc_pcm_runtime *runtime,
|
|||||||
struct snd_soc_pcm_stream *dai_params;
|
struct snd_soc_pcm_stream *dai_params;
|
||||||
unsigned int aud_mclk, srate;
|
unsigned int aud_mclk, srate;
|
||||||
u64 format_k, fmt;
|
u64 format_k, fmt;
|
||||||
int err;
|
int err, sample_size;
|
||||||
struct snd_soc_pcm_runtime *rtd;
|
struct snd_soc_pcm_runtime *rtd;
|
||||||
|
|
||||||
srate = (machine->rate_via_kcontrol) ?
|
srate = (machine->rate_via_kcontrol) ?
|
||||||
tegra_machine_srate_values[machine->rate_via_kcontrol] :
|
tegra_machine_srate_values[machine->rate_via_kcontrol] :
|
||||||
rate;
|
rate;
|
||||||
format_k = (machine->fmt_via_kcontrol == 2) ?
|
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) {
|
if (err < 0) {
|
||||||
dev_err(card->dev, "Can't configure clocks\n");
|
dev_err(card->dev, "Can't configure clocks\n");
|
||||||
return err;
|
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),
|
err = tegra_machine_dai_init(rtd, params_rate(params),
|
||||||
params_channels(params),
|
params_channels(params),
|
||||||
1ULL << params_format(params));
|
params_format(params));
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(card->dev, "Failed dai init\n");
|
dev_err(card->dev, "Failed dai init\n");
|
||||||
return err;
|
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,
|
err = tegra_machine_dai_init(rtd, codec_params.sample_rate,
|
||||||
codec_params.ch_out,
|
codec_params.ch_out,
|
||||||
SNDRV_PCM_FMTBIT_S16_LE);
|
SNDRV_PCM_FORMAT_S16_LE);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(card->dev, "Failed dai init\n");
|
dev_err(card->dev, "Failed dai init\n");
|
||||||
return err;
|
return err;
|
||||||
|
|||||||
Reference in New Issue
Block a user