mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
ASoC: fsl-asoc-card: Add some improvements
Shengjiu Wang <shengjiu.wang@nxp.com> says: This patch series addresses several issues in the Freescale Generic ASoC Sound Card driver related to hardware limitations, DPCM path switching, and codec-specific constraints. The fsl-asoc-card driver provides a generic machine driver for i.MX SoCs, supporting various codecs and optional ASRC (Asynchronous Sample Rate Converter) for sample rate conversion. During testing several issues were identified: 1. Missing channel constraint propagation in DPCM mode 2. DPCM path switching causing audio dropouts 3. Hardware channel alignment requirements 4. Clock generation limitations preventing certain audio formats 5. Codec-specific PLL frequency violations
This commit is contained in:
commit
b772d5b485
|
|
@ -40,6 +40,33 @@
|
|||
/* Default DAI format without Master and Slave flag */
|
||||
#define DAI_FMT_BASE (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF)
|
||||
|
||||
static const u32 cs42888_rates_48k[] = {
|
||||
48000, 96000, 192000,
|
||||
};
|
||||
|
||||
static const u32 cs42888_rates_44k[] = {
|
||||
44100, 88200, 176400,
|
||||
};
|
||||
|
||||
static const u32 cs42888_channels[] = {
|
||||
1, 2, 4, 6, 8,
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hw_constraint_list cs42888_rate_48k_constraints = {
|
||||
.list = cs42888_rates_48k,
|
||||
.count = ARRAY_SIZE(cs42888_rates_48k),
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hw_constraint_list cs42888_rate_44k_constraints = {
|
||||
.list = cs42888_rates_44k,
|
||||
.count = ARRAY_SIZE(cs42888_rates_44k),
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hw_constraint_list cs42888_channel_constraints = {
|
||||
.list = cs42888_channels,
|
||||
.count = ARRAY_SIZE(cs42888_channels),
|
||||
};
|
||||
|
||||
/**
|
||||
* struct codec_priv - CODEC private data
|
||||
* @mclk: Main clock of the CODEC
|
||||
|
|
@ -48,6 +75,9 @@
|
|||
* @mclk_id: MCLK (or main clock) id for set_sysclk()
|
||||
* @fll_id: FLL (or secordary clock) id for set_sysclk()
|
||||
* @pll_id: PLL id for set_pll()
|
||||
* @pll_ratio_s24: PLL output ratio for S24_LE format (PLL_freq = sample_rate × ratio)
|
||||
* Default is 384, but some codecs (e.g., WM8904) require lower values
|
||||
* to stay within PLL frequency limits
|
||||
*/
|
||||
struct codec_priv {
|
||||
struct clk *mclk;
|
||||
|
|
@ -56,6 +86,7 @@ struct codec_priv {
|
|||
u32 mclk_id;
|
||||
int fll_id;
|
||||
int pll_id;
|
||||
int pll_ratio_s24;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -87,12 +118,15 @@ struct cpu_priv {
|
|||
* @codec_priv: CODEC private data
|
||||
* @cpu_priv: CPU private data
|
||||
* @card: ASoC card structure
|
||||
* @constraint_rates: array of supported rates
|
||||
* @constraint_channels: array of supported channels
|
||||
* @streams: Mask of current active streams
|
||||
* @sample_rate: Current sample rate
|
||||
* @sample_format: Current sample format
|
||||
* @asrc_rate: ASRC sample rate used by Back-Ends
|
||||
* @asrc_format: ASRC sample format used by Back-Ends
|
||||
* @dai_fmt: DAI format between CPU and CODEC
|
||||
* @exclude_format: excluded format;
|
||||
* @name: Card name
|
||||
*/
|
||||
|
||||
|
|
@ -104,12 +138,15 @@ struct fsl_asoc_card_priv {
|
|||
struct codec_priv codec_priv[2];
|
||||
struct cpu_priv cpu_priv;
|
||||
struct snd_soc_card card;
|
||||
const struct snd_pcm_hw_constraint_list *constraint_rates;
|
||||
const struct snd_pcm_hw_constraint_list *constraint_channels;
|
||||
u8 streams;
|
||||
u32 sample_rate;
|
||||
snd_pcm_format_t sample_format;
|
||||
u32 asrc_rate;
|
||||
snd_pcm_format_t asrc_format;
|
||||
u32 dai_fmt;
|
||||
u64 exclude_format;
|
||||
char name[32];
|
||||
};
|
||||
|
||||
|
|
@ -222,7 +259,7 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
|
|||
|
||||
if (codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) {
|
||||
if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE)
|
||||
pll_out = priv->sample_rate * 384;
|
||||
pll_out = priv->sample_rate * codec_priv->pll_ratio_s24;
|
||||
else
|
||||
pll_out = priv->sample_rate * 256;
|
||||
|
||||
|
|
@ -291,7 +328,47 @@ static int fsl_asoc_card_hw_free(struct snd_pcm_substream *substream)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asoc_card_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int ret;
|
||||
|
||||
if (priv->exclude_format && !rtd->dai_link->no_pcm) {
|
||||
ret = snd_pcm_hw_constraint_mask64(runtime,
|
||||
SNDRV_PCM_HW_PARAM_FORMAT,
|
||||
~priv->exclude_format);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (priv->constraint_channels) {
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
priv->constraint_channels);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply rate constraints only to frontend DAI links (no_pcm = 0).
|
||||
* Skip DPCM backend (no_pcm = 1) as rate is fixed by be_hw_params_fixup()
|
||||
* and ASRC frontend handles rate conversion.
|
||||
*/
|
||||
if (priv->constraint_rates && !rtd->dai_link->no_pcm) {
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
priv->constraint_rates);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_ops fsl_asoc_card_ops = {
|
||||
.startup = fsl_asoc_card_startup,
|
||||
.hw_params = fsl_asoc_card_hw_params,
|
||||
.hw_free = fsl_asoc_card_hw_free,
|
||||
};
|
||||
|
|
@ -742,6 +819,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
|
|||
for (codec_idx = 0; codec_idx < 2; codec_idx++) {
|
||||
priv->codec_priv[codec_idx].fll_id = -1;
|
||||
priv->codec_priv[codec_idx].pll_id = -1;
|
||||
priv->codec_priv[codec_idx].pll_ratio_s24 = 384;
|
||||
}
|
||||
|
||||
/* Diversify the card configurations */
|
||||
|
|
@ -753,6 +831,14 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
|
|||
priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT;
|
||||
priv->cpu_priv.slot_width = 32;
|
||||
priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC;
|
||||
priv->constraint_channels = &cs42888_channel_constraints;
|
||||
if (priv->codec_priv[0].mclk_freq % 12288000 == 0)
|
||||
priv->constraint_rates = &cs42888_rate_48k_constraints;
|
||||
else if (priv->codec_priv[0].mclk_freq % 11289600 == 0)
|
||||
priv->constraint_rates = &cs42888_rate_44k_constraints;
|
||||
else
|
||||
dev_warn(&pdev->dev, "Unknown MCLK frequency %lu, no rate constraints\n",
|
||||
priv->codec_priv[0].mclk_freq);
|
||||
} else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) {
|
||||
codec_dai_name[0] = "cs4271-hifi";
|
||||
priv->codec_priv[0].mclk_id = CS427x_SYSCLK_MCLK;
|
||||
|
|
@ -779,11 +865,30 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
|
|||
priv->codec_priv[0].fll_id = WM8962_SYSCLK_FLL;
|
||||
priv->codec_priv[0].pll_id = WM8962_FLL;
|
||||
priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
|
||||
/*
|
||||
* WM8962 has same BCLK generation limitations as WM8960.
|
||||
* See WM8960 section for detailed explanation.
|
||||
*/
|
||||
if (of_node_name_eq(cpu_np, "sai"))
|
||||
priv->exclude_format = SNDRV_PCM_FMTBIT_S20_3LE;
|
||||
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8960")) {
|
||||
codec_dai_name[0] = "wm8960-hifi";
|
||||
priv->codec_priv[0].fll_id = WM8960_SYSCLK_AUTO;
|
||||
priv->codec_priv[0].pll_id = WM8960_SYSCLK_AUTO;
|
||||
priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
|
||||
/*
|
||||
* WM8960 in master mode cannot generate exact 1.92 MHz BCLK
|
||||
* required for S20_3LE (48kHz × 2ch × 20bit). Closest available
|
||||
* is 2.048 MHz (SYSCLK/6), which causes right channel corruption.
|
||||
*
|
||||
* In SAI master mode, SAI derive BCLK from MCLK using integer
|
||||
* dividers only. S20_3LE requires non-integer divider ratios
|
||||
* with standard MCLK frequencies. For example, 48kHz stereo
|
||||
* needs 1.920 MHz BCLK, which requires a divider of 6.4 from
|
||||
* 12.288 MHz MCLK (not an integer).
|
||||
*/
|
||||
if (of_node_name_eq(cpu_np, "sai"))
|
||||
priv->exclude_format = SNDRV_PCM_FMTBIT_S20_3LE;
|
||||
} else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) {
|
||||
codec_dai_name[0] = "ac97-hifi";
|
||||
priv->dai_fmt = SND_SOC_DAIFMT_AC97;
|
||||
|
|
@ -835,6 +940,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
|
|||
priv->codec_priv[0].mclk_id = WM8904_FLL_MCLK;
|
||||
priv->codec_priv[0].fll_id = WM8904_CLK_FLL;
|
||||
priv->codec_priv[0].pll_id = WM8904_FLL_MCLK;
|
||||
priv->codec_priv[0].pll_ratio_s24 = 192;
|
||||
priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
|
||||
} else if (of_device_is_compatible(np, "fsl,imx-audio-spdif")) {
|
||||
ret = fsl_asoc_card_spdif_init(codec_np, cpu_np, codec_dai_name, priv);
|
||||
|
|
@ -989,6 +1095,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
|
|||
|
||||
if (asrc_pdev) {
|
||||
/* DPCM DAI Links only if ASRC exists */
|
||||
priv->dai_link[1].dpcm_merged_chan = 1;
|
||||
priv->dai_link[1].ignore_pmdown_time = 1;
|
||||
priv->dai_link[1].cpus->of_node = asrc_np;
|
||||
priv->dai_link[1].platforms->of_node = asrc_np;
|
||||
for_each_link_codecs((&(priv->dai_link[2])), codec_idx, codec_comp) {
|
||||
|
|
@ -998,6 +1106,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
|
|||
}
|
||||
priv->dai_link[2].cpus->of_node = cpu_np;
|
||||
priv->dai_link[2].dai_fmt = priv->dai_fmt;
|
||||
priv->dai_link[2].ignore_pmdown_time = 1;
|
||||
priv->card.num_links = 3;
|
||||
|
||||
ret = of_property_read_u32(asrc_np, "fsl,asrc-rate",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user