mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 04:34:03 +02:00
ASoC: ux500: Validate MSP DAI configuration
Installing channel constraints from hw_params is too late to affect the
parameters being committed. The driver consequently accepts channel
counts which disagree with the I2S or TDM setup. It also silently
truncates out-of-range slot masks and accepts inverted bit clock formats
which prepare then rejects.
Validate the selected channel count directly, reject invalid masks
before changing cached TDM state, and implement all four standard clock
and frame inversion combinations. Use the requested format in
validation diagnostics.
Fixes: 3592b7f69a ("ASoC: Ux500: Add MSP I2S-driver")
Assisted-by: LLM
Signed-off-by: Linus Walleij <linusw@kernel.org>
Link: https://patch.msgid.link/20260902-ux500-msp-fixes-v2-4-4b60b002d55a@kernel.org
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
94c18cea65
commit
9ccbacf5a0
|
|
@ -130,7 +130,16 @@ static int setup_clocking(struct snd_soc_dai *dai,
|
|||
case SND_SOC_DAIFMT_NB_IF:
|
||||
msp_config->tx_fsync_pol ^= 1 << TFSPOL_SHIFT;
|
||||
msp_config->rx_fsync_pol ^= 1 << RFSPOL_SHIFT;
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
msp_config->bclk_inverted = true;
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
msp_config->bclk_inverted = true;
|
||||
msp_config->tx_fsync_pol ^= 1 << TFSPOL_SHIFT;
|
||||
msp_config->rx_fsync_pol ^= 1 << RFSPOL_SHIFT;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -453,7 +462,6 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream,
|
|||
struct snd_soc_dai *dai)
|
||||
{
|
||||
unsigned int mask, slots_active;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
|
||||
|
||||
dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n",
|
||||
|
|
@ -461,9 +469,8 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream,
|
|||
|
||||
switch (drvdata->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
snd_pcm_hw_constraint_minmax(runtime,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
1, 2);
|
||||
if (params_channels(params) < 1 || params_channels(params) > 2)
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
|
|
@ -475,9 +482,8 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream,
|
|||
slots_active = hweight32(mask);
|
||||
dev_dbg(dai->dev, "TDM-slots active: %d", slots_active);
|
||||
|
||||
snd_pcm_hw_constraint_single(runtime,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
slots_active);
|
||||
if (!slots_active || params_channels(params) != slots_active)
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -510,20 +516,21 @@ static int ux500_msp_dai_set_dai_fmt(struct snd_soc_dai *dai,
|
|||
default:
|
||||
dev_err(dai->dev,
|
||||
"%s: Error: Unsupported protocol/master (fmt = 0x%x)!\n",
|
||||
__func__, drvdata->fmt);
|
||||
__func__, fmt);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(dai->dev,
|
||||
"%s: Error: Unsupported inversion (fmt = 0x%x)!\n",
|
||||
__func__, drvdata->fmt);
|
||||
__func__, fmt);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -557,17 +564,23 @@ static int ux500_msp_dai_set_tdm_slot(struct snd_soc_dai *dai,
|
|||
__func__, slots);
|
||||
return -EINVAL;
|
||||
}
|
||||
drvdata->slots = slots;
|
||||
|
||||
if (!(slot_width == 16)) {
|
||||
if (slot_width != 16) {
|
||||
dev_err(dai->dev, "%s: Error: Unsupported slot-width (%d)!\n",
|
||||
__func__, slot_width);
|
||||
return -EINVAL;
|
||||
}
|
||||
drvdata->slot_width = slot_width;
|
||||
|
||||
drvdata->tx_mask = tx_mask & cap;
|
||||
drvdata->rx_mask = rx_mask & cap;
|
||||
if ((tx_mask | rx_mask) & ~cap) {
|
||||
dev_err(dai->dev, "%s: Slot mask exceeds %d slots\n",
|
||||
__func__, slots);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
drvdata->slots = slots;
|
||||
drvdata->slot_width = slot_width;
|
||||
drvdata->tx_mask = tx_mask;
|
||||
drvdata->rx_mask = rx_mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -201,10 +201,12 @@ static int configure_protocol(struct ux500_msp *msp,
|
|||
|
||||
/* The code below should not be separated. */
|
||||
temp_reg = readl(msp->registers + MSP_GCR) & ~TX_CLK_POL_RISING;
|
||||
temp_reg |= MSP_TX_CLKPOL_BIT(~protdesc->tx_clk_pol);
|
||||
temp_reg |= MSP_TX_CLKPOL_BIT(!protdesc->tx_clk_pol ^
|
||||
config->bclk_inverted);
|
||||
writel(temp_reg, msp->registers + MSP_GCR);
|
||||
temp_reg = readl(msp->registers + MSP_GCR) & ~RX_CLK_POL_RISING;
|
||||
temp_reg |= MSP_RX_CLKPOL_BIT(protdesc->rx_clk_pol);
|
||||
temp_reg |= MSP_RX_CLKPOL_BIT(protdesc->rx_clk_pol ^
|
||||
config->bclk_inverted);
|
||||
writel(temp_reg, msp->registers + MSP_GCR);
|
||||
|
||||
return 0;
|
||||
|
|
@ -441,6 +443,7 @@ static bool ux500_msp_config_compatible(struct ux500_msp *msp,
|
|||
active->data_size == config->data_size &&
|
||||
active->def_elem_len == config->def_elem_len &&
|
||||
active->clock_provider == config->clock_provider &&
|
||||
active->bclk_inverted == config->bclk_inverted &&
|
||||
!memcmp(&active->protdesc, &config->protdesc,
|
||||
sizeof(active->protdesc));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -459,6 +459,7 @@ struct ux500_msp_config {
|
|||
unsigned int def_elem_len;
|
||||
unsigned int iodelay;
|
||||
bool clock_provider;
|
||||
bool bclk_inverted;
|
||||
};
|
||||
|
||||
struct ux500_msp {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user