Commit 2a919800 authored by Mark Brown's avatar Mark Brown

ASoC: nau8xxx: Implement hw constraint for rates

Merge series from Takashi Iwai <tiwai@suse.de>:

This is a series of patches to address the issues on nau8xxx codecs
I've stumbled upon while dealing with a bug report for Steam Deck.
Most of them are to implement the missing hw constraint for rate
restrictions while one patch is to fix the semaphore unbalance in
nau8824 driver.
parents 13e575de be919239
......@@ -357,17 +357,32 @@ static const struct snd_soc_dapm_route nau8540_dapm_routes[] = {
{"AIFTX", NULL, "Digital CH4 Mux"},
};
static int nau8540_clock_check(struct nau8540 *nau8540, int rate, int osr)
static const struct nau8540_osr_attr *
nau8540_get_osr(struct nau8540 *nau8540)
{
unsigned int osr;
regmap_read(nau8540->regmap, NAU8540_REG_ADC_SAMPLE_RATE, &osr);
osr &= NAU8540_ADC_OSR_MASK;
if (osr >= ARRAY_SIZE(osr_adc_sel))
return -EINVAL;
return NULL;
return &osr_adc_sel[osr];
}
static int nau8540_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
const struct nau8540_osr_attr *osr;
if (rate * osr > CLK_ADC_MAX) {
dev_err(nau8540->dev, "exceed the maximum frequency of CLK_ADC\n");
osr = nau8540_get_osr(nau8540);
if (!osr || !osr->osr)
return -EINVAL;
}
return 0;
return snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
0, CLK_ADC_MAX / osr->osr);
}
static int nau8540_hw_params(struct snd_pcm_substream *substream,
......@@ -375,7 +390,8 @@ static int nau8540_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
unsigned int val_len = 0, osr;
unsigned int val_len = 0;
const struct nau8540_osr_attr *osr;
/* CLK_ADC = OSR * FS
* ADC clock frequency is defined as Over Sampling Rate (OSR)
......@@ -383,13 +399,14 @@ static int nau8540_hw_params(struct snd_pcm_substream *substream,
* values must be selected such that the maximum frequency is less
* than 6.144 MHz.
*/
regmap_read(nau8540->regmap, NAU8540_REG_ADC_SAMPLE_RATE, &osr);
osr &= NAU8540_ADC_OSR_MASK;
if (nau8540_clock_check(nau8540, params_rate(params), osr))
osr = nau8540_get_osr(nau8540);
if (!osr || !osr->osr)
return -EINVAL;
if (params_rate(params) * osr->osr > CLK_ADC_MAX)
return -EINVAL;
regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
NAU8540_CLK_ADC_SRC_MASK,
osr_adc_sel[osr].clk_src << NAU8540_CLK_ADC_SRC_SFT);
osr->clk_src << NAU8540_CLK_ADC_SRC_SFT);
switch (params_width(params)) {
case 16:
......@@ -515,6 +532,7 @@ static int nau8540_set_tdm_slot(struct snd_soc_dai *dai,
static const struct snd_soc_dai_ops nau8540_dai_ops = {
.startup = nau8540_dai_startup,
.hw_params = nau8540_hw_params,
.set_fmt = nau8540_set_fmt,
.set_tdm_slot = nau8540_set_tdm_slot,
......
......@@ -670,28 +670,40 @@ static const struct snd_soc_dapm_route nau8821_dapm_routes[] = {
{"HPOR", NULL, "Class G"},
};
static int nau8821_clock_check(struct nau8821 *nau8821,
int stream, int rate, int osr)
static const struct nau8821_osr_attr *
nau8821_get_osr(struct nau8821 *nau8821, int stream)
{
int osrate = 0;
unsigned int osr;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
regmap_read(nau8821->regmap, NAU8821_R2C_DAC_CTRL1, &osr);
osr &= NAU8821_DAC_OVERSAMPLE_MASK;
if (osr >= ARRAY_SIZE(osr_dac_sel))
return -EINVAL;
osrate = osr_dac_sel[osr].osr;
return NULL;
return &osr_dac_sel[osr];
} else {
regmap_read(nau8821->regmap, NAU8821_R2B_ADC_RATE, &osr);
osr &= NAU8821_ADC_SYNC_DOWN_MASK;
if (osr >= ARRAY_SIZE(osr_adc_sel))
return -EINVAL;
osrate = osr_adc_sel[osr].osr;
return NULL;
return &osr_adc_sel[osr];
}
}
if (!osrate || rate * osrate > CLK_DA_AD_MAX) {
dev_err(nau8821->dev,
"exceed the maximum frequency of CLK_ADC or CLK_DAC");
static int nau8821_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
const struct nau8821_osr_attr *osr;
osr = nau8821_get_osr(nau8821, substream->stream);
if (!osr || !osr->osr)
return -EINVAL;
}
return 0;
return snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
0, CLK_DA_AD_MAX / osr->osr);
}
static int nau8821_hw_params(struct snd_pcm_substream *substream,
......@@ -699,7 +711,8 @@ static int nau8821_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
unsigned int val_len = 0, osr, ctrl_val, bclk_fs, clk_div;
unsigned int val_len = 0, ctrl_val, bclk_fs, clk_div;
const struct nau8821_osr_attr *osr;
nau8821->fs = params_rate(params);
/* CLK_DAC or CLK_ADC = OSR * FS
......@@ -708,27 +721,19 @@ static int nau8821_hw_params(struct snd_pcm_substream *substream,
* values must be selected such that the maximum frequency is less
* than 6.144 MHz.
*/
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
regmap_read(nau8821->regmap, NAU8821_R2C_DAC_CTRL1, &osr);
osr &= NAU8821_DAC_OVERSAMPLE_MASK;
if (nau8821_clock_check(nau8821, substream->stream,
nau8821->fs, osr)) {
return -EINVAL;
}
osr = nau8821_get_osr(nau8821, substream->stream);
if (!osr || !osr->osr)
return -EINVAL;
if (nau8821->fs * osr->osr > CLK_DA_AD_MAX)
return -EINVAL;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
regmap_update_bits(nau8821->regmap, NAU8821_R03_CLK_DIVIDER,
NAU8821_CLK_DAC_SRC_MASK,
osr_dac_sel[osr].clk_src << NAU8821_CLK_DAC_SRC_SFT);
} else {
regmap_read(nau8821->regmap, NAU8821_R2B_ADC_RATE, &osr);
osr &= NAU8821_ADC_SYNC_DOWN_MASK;
if (nau8821_clock_check(nau8821, substream->stream,
nau8821->fs, osr)) {
return -EINVAL;
}
osr->clk_src << NAU8821_CLK_DAC_SRC_SFT);
else
regmap_update_bits(nau8821->regmap, NAU8821_R03_CLK_DIVIDER,
NAU8821_CLK_ADC_SRC_MASK,
osr_adc_sel[osr].clk_src << NAU8821_CLK_ADC_SRC_SFT);
}
osr->clk_src << NAU8821_CLK_ADC_SRC_SFT);
/* make BCLK and LRC divde configuration if the codec as master. */
regmap_read(nau8821->regmap, NAU8821_R1D_I2S_PCM_CTRL2, &ctrl_val);
......@@ -843,6 +848,7 @@ static int nau8821_digital_mute(struct snd_soc_dai *dai, int mute,
}
static const struct snd_soc_dai_ops nau8821_dai_ops = {
.startup = nau8821_dai_startup,
.hw_params = nau8821_hw_params,
.set_fmt = nau8821_set_dai_fmt,
.mute_stream = nau8821_digital_mute,
......
......@@ -1014,27 +1014,42 @@ static irqreturn_t nau8824_interrupt(int irq, void *data)
return IRQ_HANDLED;
}
static int nau8824_clock_check(struct nau8824 *nau8824,
int stream, int rate, int osr)
static const struct nau8824_osr_attr *
nau8824_get_osr(struct nau8824 *nau8824, int stream)
{
int osrate;
unsigned int osr;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
regmap_read(nau8824->regmap,
NAU8824_REG_DAC_FILTER_CTRL_1, &osr);
osr &= NAU8824_DAC_OVERSAMPLE_MASK;
if (osr >= ARRAY_SIZE(osr_dac_sel))
return -EINVAL;
osrate = osr_dac_sel[osr].osr;
return NULL;
return &osr_dac_sel[osr];
} else {
regmap_read(nau8824->regmap,
NAU8824_REG_ADC_FILTER_CTRL, &osr);
osr &= NAU8824_ADC_SYNC_DOWN_MASK;
if (osr >= ARRAY_SIZE(osr_adc_sel))
return -EINVAL;
osrate = osr_adc_sel[osr].osr;
return NULL;
return &osr_adc_sel[osr];
}
}
static int nau8824_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
const struct nau8824_osr_attr *osr;
if (!osrate || rate * osr > CLK_DA_AD_MAX) {
dev_err(nau8824->dev, "exceed the maximum frequency of CLK_ADC or CLK_DAC\n");
osr = nau8824_get_osr(nau8824, substream->stream);
if (!osr || !osr->osr)
return -EINVAL;
}
return 0;
return snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
0, CLK_DA_AD_MAX / osr->osr);
}
static int nau8824_hw_params(struct snd_pcm_substream *substream,
......@@ -1042,7 +1057,9 @@ static int nau8824_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
unsigned int val_len = 0, osr, ctrl_val, bclk_fs, bclk_div;
unsigned int val_len = 0, ctrl_val, bclk_fs, bclk_div;
const struct nau8824_osr_attr *osr;
int err = -EINVAL;
nau8824_sema_acquire(nau8824, HZ);
......@@ -1053,27 +1070,19 @@ static int nau8824_hw_params(struct snd_pcm_substream *substream,
* than 6.144 MHz.
*/
nau8824->fs = params_rate(params);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
regmap_read(nau8824->regmap,
NAU8824_REG_DAC_FILTER_CTRL_1, &osr);
osr &= NAU8824_DAC_OVERSAMPLE_MASK;
if (nau8824_clock_check(nau8824, substream->stream,
nau8824->fs, osr))
return -EINVAL;
osr = nau8824_get_osr(nau8824, substream->stream);
if (!osr || !osr->osr)
goto error;
if (nau8824->fs * osr->osr > CLK_DA_AD_MAX)
goto error;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER,
NAU8824_CLK_DAC_SRC_MASK,
osr_dac_sel[osr].clk_src << NAU8824_CLK_DAC_SRC_SFT);
} else {
regmap_read(nau8824->regmap,
NAU8824_REG_ADC_FILTER_CTRL, &osr);
osr &= NAU8824_ADC_SYNC_DOWN_MASK;
if (nau8824_clock_check(nau8824, substream->stream,
nau8824->fs, osr))
return -EINVAL;
osr->clk_src << NAU8824_CLK_DAC_SRC_SFT);
else
regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER,
NAU8824_CLK_ADC_SRC_MASK,
osr_adc_sel[osr].clk_src << NAU8824_CLK_ADC_SRC_SFT);
}
osr->clk_src << NAU8824_CLK_ADC_SRC_SFT);
/* make BCLK and LRC divde configuration if the codec as master. */
regmap_read(nau8824->regmap,
......@@ -1090,7 +1099,7 @@ static int nau8824_hw_params(struct snd_pcm_substream *substream,
else if (bclk_fs <= 256)
bclk_div = 0;
else
return -EINVAL;
goto error;
regmap_update_bits(nau8824->regmap,
NAU8824_REG_PORT0_I2S_PCM_CTRL_2,
NAU8824_I2S_LRC_DIV_MASK | NAU8824_I2S_BLK_DIV_MASK,
......@@ -1111,15 +1120,17 @@ static int nau8824_hw_params(struct snd_pcm_substream *substream,
val_len |= NAU8824_I2S_DL_32;
break;
default:
return -EINVAL;
goto error;
}
regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_I2S_PCM_CTRL_1,
NAU8824_I2S_DL_MASK, val_len);
err = 0;
error:
nau8824_sema_release(nau8824);
return 0;
return err;
}
static int nau8824_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
......@@ -1128,8 +1139,6 @@ static int nau8824_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component);
unsigned int ctrl1_val = 0, ctrl2_val = 0;
nau8824_sema_acquire(nau8824, HZ);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
ctrl2_val |= NAU8824_I2S_MS_MASTER;
......@@ -1171,6 +1180,8 @@ static int nau8824_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
nau8824_sema_acquire(nau8824, HZ);
regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_I2S_PCM_CTRL_1,
NAU8824_I2S_DF_MASK | NAU8824_I2S_BP_MASK |
NAU8824_I2S_PCMB_EN, ctrl1_val);
......@@ -1547,6 +1558,7 @@ static const struct snd_soc_component_driver nau8824_component_driver = {
};
static const struct snd_soc_dai_ops nau8824_dai_ops = {
.startup = nau8824_dai_startup,
.hw_params = nau8824_hw_params,
.set_fmt = nau8824_set_fmt,
.set_tdm_slot = nau8824_set_tdm_slot,
......
......@@ -1247,27 +1247,42 @@ static const struct snd_soc_dapm_route nau8825_dapm_routes[] = {
{"HPOR", NULL, "Class G"},
};
static int nau8825_clock_check(struct nau8825 *nau8825,
int stream, int rate, int osr)
static const struct nau8825_osr_attr *
nau8825_get_osr(struct nau8825 *nau8825, int stream)
{
int osrate;
unsigned int osr;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
regmap_read(nau8825->regmap,
NAU8825_REG_DAC_CTRL1, &osr);
osr &= NAU8825_DAC_OVERSAMPLE_MASK;
if (osr >= ARRAY_SIZE(osr_dac_sel))
return -EINVAL;
osrate = osr_dac_sel[osr].osr;
return NULL;
return &osr_dac_sel[osr];
} else {
regmap_read(nau8825->regmap,
NAU8825_REG_ADC_RATE, &osr);
osr &= NAU8825_ADC_SYNC_DOWN_MASK;
if (osr >= ARRAY_SIZE(osr_adc_sel))
return -EINVAL;
osrate = osr_adc_sel[osr].osr;
return NULL;
return &osr_adc_sel[osr];
}
}
if (!osrate || rate * osr > CLK_DA_AD_MAX) {
dev_err(nau8825->dev, "exceed the maximum frequency of CLK_ADC or CLK_DAC\n");
static int nau8825_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
const struct nau8825_osr_attr *osr;
osr = nau8825_get_osr(nau8825, substream->stream);
if (!osr || !osr->osr)
return -EINVAL;
}
return 0;
return snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
0, CLK_DA_AD_MAX / osr->osr);
}
static int nau8825_hw_params(struct snd_pcm_substream *substream,
......@@ -1276,7 +1291,9 @@ static int nau8825_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
unsigned int val_len = 0, osr, ctrl_val, bclk_fs, bclk_div;
unsigned int val_len = 0, ctrl_val, bclk_fs, bclk_div;
const struct nau8825_osr_attr *osr;
int err = -EINVAL;
nau8825_sema_acquire(nau8825, 3 * HZ);
......@@ -1286,29 +1303,19 @@ static int nau8825_hw_params(struct snd_pcm_substream *substream,
* values must be selected such that the maximum frequency is less
* than 6.144 MHz.
*/
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
regmap_read(nau8825->regmap, NAU8825_REG_DAC_CTRL1, &osr);
osr &= NAU8825_DAC_OVERSAMPLE_MASK;
if (nau8825_clock_check(nau8825, substream->stream,
params_rate(params), osr)) {
nau8825_sema_release(nau8825);
return -EINVAL;
}
osr = nau8825_get_osr(nau8825, substream->stream);
if (!osr || !osr->osr)
goto error;
if (params_rate(params) * osr->osr > CLK_DA_AD_MAX)
goto error;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
NAU8825_CLK_DAC_SRC_MASK,
osr_dac_sel[osr].clk_src << NAU8825_CLK_DAC_SRC_SFT);
} else {
regmap_read(nau8825->regmap, NAU8825_REG_ADC_RATE, &osr);
osr &= NAU8825_ADC_SYNC_DOWN_MASK;
if (nau8825_clock_check(nau8825, substream->stream,
params_rate(params), osr)) {
nau8825_sema_release(nau8825);
return -EINVAL;
}
osr->clk_src << NAU8825_CLK_DAC_SRC_SFT);
else
regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
NAU8825_CLK_ADC_SRC_MASK,
osr_adc_sel[osr].clk_src << NAU8825_CLK_ADC_SRC_SFT);
}
osr->clk_src << NAU8825_CLK_ADC_SRC_SFT);
/* make BCLK and LRC divde configuration if the codec as master. */
regmap_read(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2, &ctrl_val);
......@@ -1321,10 +1328,8 @@ static int nau8825_hw_params(struct snd_pcm_substream *substream,
bclk_div = 1;
else if (bclk_fs <= 128)
bclk_div = 0;
else {
nau8825_sema_release(nau8825);
return -EINVAL;
}
else
goto error;
regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2,
NAU8825_I2S_LRC_DIV_MASK | NAU8825_I2S_BLK_DIV_MASK,
((bclk_div + 1) << NAU8825_I2S_LRC_DIV_SFT) | bclk_div);
......@@ -1344,17 +1349,18 @@ static int nau8825_hw_params(struct snd_pcm_substream *substream,
val_len |= NAU8825_I2S_DL_32;
break;
default:
nau8825_sema_release(nau8825);
return -EINVAL;
goto error;
}
regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1,
NAU8825_I2S_DL_MASK, val_len);
err = 0;
error:
/* Release the semaphore. */
nau8825_sema_release(nau8825);
return 0;
return err;
}
static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
......@@ -1420,6 +1426,7 @@ static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
}
static const struct snd_soc_dai_ops nau8825_dai_ops = {
.startup = nau8825_dai_startup,
.hw_params = nau8825_hw_params,
.set_fmt = nau8825_set_dai_fmt,
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment