Skip to content

Commit 0d90ca0

Browse files
author
Fox Snowpatch
committed
1 parent 182544a commit 0d90ca0

1 file changed

Lines changed: 110 additions & 1 deletion

File tree

sound/soc/fsl/fsl-asoc-card.c

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,33 @@
4040
/* Default DAI format without Master and Slave flag */
4141
#define DAI_FMT_BASE (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF)
4242

43+
static const u32 cs42888_rates_48k[] = {
44+
48000, 96000, 192000,
45+
};
46+
47+
static const u32 cs42888_rates_44k[] = {
48+
44100, 88200, 176400,
49+
};
50+
51+
static const u32 cs42888_channels[] = {
52+
1, 2, 4, 6, 8,
53+
};
54+
55+
static const struct snd_pcm_hw_constraint_list cs42888_rate_48k_constraints = {
56+
.list = cs42888_rates_48k,
57+
.count = ARRAY_SIZE(cs42888_rates_48k),
58+
};
59+
60+
static const struct snd_pcm_hw_constraint_list cs42888_rate_44k_constraints = {
61+
.list = cs42888_rates_44k,
62+
.count = ARRAY_SIZE(cs42888_rates_44k),
63+
};
64+
65+
static const struct snd_pcm_hw_constraint_list cs42888_channel_constraints = {
66+
.list = cs42888_channels,
67+
.count = ARRAY_SIZE(cs42888_channels),
68+
};
69+
4370
/**
4471
* struct codec_priv - CODEC private data
4572
* @mclk: Main clock of the CODEC
@@ -48,6 +75,9 @@
4875
* @mclk_id: MCLK (or main clock) id for set_sysclk()
4976
* @fll_id: FLL (or secordary clock) id for set_sysclk()
5077
* @pll_id: PLL id for set_pll()
78+
* @pll_ratio_s24: PLL output ratio for S24_LE format (PLL_freq = sample_rate × ratio)
79+
* Default is 384, but some codecs (e.g., WM8904) require lower values
80+
* to stay within PLL frequency limits
5181
*/
5282
struct codec_priv {
5383
struct clk *mclk;
@@ -56,6 +86,7 @@ struct codec_priv {
5686
u32 mclk_id;
5787
int fll_id;
5888
int pll_id;
89+
int pll_ratio_s24;
5990
};
6091

6192
/**
@@ -87,12 +118,15 @@ struct cpu_priv {
87118
* @codec_priv: CODEC private data
88119
* @cpu_priv: CPU private data
89120
* @card: ASoC card structure
121+
* @constraint_rates: array of supported rates
122+
* @constraint_channels: array of supported channels
90123
* @streams: Mask of current active streams
91124
* @sample_rate: Current sample rate
92125
* @sample_format: Current sample format
93126
* @asrc_rate: ASRC sample rate used by Back-Ends
94127
* @asrc_format: ASRC sample format used by Back-Ends
95128
* @dai_fmt: DAI format between CPU and CODEC
129+
* @exclude_format: excluded format;
96130
* @name: Card name
97131
*/
98132

@@ -104,12 +138,15 @@ struct fsl_asoc_card_priv {
104138
struct codec_priv codec_priv[2];
105139
struct cpu_priv cpu_priv;
106140
struct snd_soc_card card;
141+
const struct snd_pcm_hw_constraint_list *constraint_rates;
142+
const struct snd_pcm_hw_constraint_list *constraint_channels;
107143
u8 streams;
108144
u32 sample_rate;
109145
snd_pcm_format_t sample_format;
110146
u32 asrc_rate;
111147
snd_pcm_format_t asrc_format;
112148
u32 dai_fmt;
149+
u64 exclude_format;
113150
char name[32];
114151
};
115152

@@ -222,7 +259,7 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
222259

223260
if (codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) {
224261
if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE)
225-
pll_out = priv->sample_rate * 384;
262+
pll_out = priv->sample_rate * codec_priv->pll_ratio_s24;
226263
else
227264
pll_out = priv->sample_rate * 256;
228265

@@ -291,7 +328,47 @@ static int fsl_asoc_card_hw_free(struct snd_pcm_substream *substream)
291328
return 0;
292329
}
293330

331+
static int fsl_asoc_card_startup(struct snd_pcm_substream *substream)
332+
{
333+
struct snd_soc_pcm_runtime *rtd = substream->private_data;
334+
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
335+
struct snd_pcm_runtime *runtime = substream->runtime;
336+
int ret;
337+
338+
if (priv->exclude_format && !rtd->dai_link->no_pcm) {
339+
ret = snd_pcm_hw_constraint_mask64(runtime,
340+
SNDRV_PCM_HW_PARAM_FORMAT,
341+
~priv->exclude_format);
342+
if (ret)
343+
return ret;
344+
}
345+
346+
if (priv->constraint_channels) {
347+
ret = snd_pcm_hw_constraint_list(runtime, 0,
348+
SNDRV_PCM_HW_PARAM_CHANNELS,
349+
priv->constraint_channels);
350+
if (ret)
351+
return ret;
352+
}
353+
354+
/*
355+
* Apply rate constraints only to frontend DAI links (no_pcm = 0).
356+
* Skip DPCM backend (no_pcm = 1) as rate is fixed by be_hw_params_fixup()
357+
* and ASRC frontend handles rate conversion.
358+
*/
359+
if (priv->constraint_rates && !rtd->dai_link->no_pcm) {
360+
ret = snd_pcm_hw_constraint_list(runtime, 0,
361+
SNDRV_PCM_HW_PARAM_RATE,
362+
priv->constraint_rates);
363+
if (ret)
364+
return ret;
365+
}
366+
367+
return 0;
368+
}
369+
294370
static const struct snd_soc_ops fsl_asoc_card_ops = {
371+
.startup = fsl_asoc_card_startup,
295372
.hw_params = fsl_asoc_card_hw_params,
296373
.hw_free = fsl_asoc_card_hw_free,
297374
};
@@ -742,6 +819,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
742819
for (codec_idx = 0; codec_idx < 2; codec_idx++) {
743820
priv->codec_priv[codec_idx].fll_id = -1;
744821
priv->codec_priv[codec_idx].pll_id = -1;
822+
priv->codec_priv[codec_idx].pll_ratio_s24 = 384;
745823
}
746824

747825
/* Diversify the card configurations */
@@ -753,6 +831,14 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
753831
priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT;
754832
priv->cpu_priv.slot_width = 32;
755833
priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC;
834+
priv->constraint_channels = &cs42888_channel_constraints;
835+
if (priv->codec_priv[0].mclk_freq % 12288000 == 0)
836+
priv->constraint_rates = &cs42888_rate_48k_constraints;
837+
else if (priv->codec_priv[0].mclk_freq % 11289600 == 0)
838+
priv->constraint_rates = &cs42888_rate_44k_constraints;
839+
else
840+
dev_warn(&pdev->dev, "Unknown MCLK frequency %lu, no rate constraints\n",
841+
priv->codec_priv[0].mclk_freq);
756842
} else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) {
757843
codec_dai_name[0] = "cs4271-hifi";
758844
priv->codec_priv[0].mclk_id = CS427x_SYSCLK_MCLK;
@@ -779,11 +865,30 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
779865
priv->codec_priv[0].fll_id = WM8962_SYSCLK_FLL;
780866
priv->codec_priv[0].pll_id = WM8962_FLL;
781867
priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
868+
/*
869+
* WM8962 has same BCLK generation limitations as WM8960.
870+
* See WM8960 section for detailed explanation.
871+
*/
872+
if (of_node_name_eq(cpu_np, "sai"))
873+
priv->exclude_format = SNDRV_PCM_FMTBIT_S20_3LE;
782874
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8960")) {
783875
codec_dai_name[0] = "wm8960-hifi";
784876
priv->codec_priv[0].fll_id = WM8960_SYSCLK_AUTO;
785877
priv->codec_priv[0].pll_id = WM8960_SYSCLK_AUTO;
786878
priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
879+
/*
880+
* WM8960 in master mode cannot generate exact 1.92 MHz BCLK
881+
* required for S20_3LE (48kHz × 2ch × 20bit). Closest available
882+
* is 2.048 MHz (SYSCLK/6), which causes right channel corruption.
883+
*
884+
* In SAI master mode, SAI derive BCLK from MCLK using integer
885+
* dividers only. S20_3LE requires non-integer divider ratios
886+
* with standard MCLK frequencies. For example, 48kHz stereo
887+
* needs 1.920 MHz BCLK, which requires a divider of 6.4 from
888+
* 12.288 MHz MCLK (not an integer).
889+
*/
890+
if (of_node_name_eq(cpu_np, "sai"))
891+
priv->exclude_format = SNDRV_PCM_FMTBIT_S20_3LE;
787892
} else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) {
788893
codec_dai_name[0] = "ac97-hifi";
789894
priv->dai_fmt = SND_SOC_DAIFMT_AC97;
@@ -835,6 +940,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
835940
priv->codec_priv[0].mclk_id = WM8904_FLL_MCLK;
836941
priv->codec_priv[0].fll_id = WM8904_CLK_FLL;
837942
priv->codec_priv[0].pll_id = WM8904_FLL_MCLK;
943+
priv->codec_priv[0].pll_ratio_s24 = 192;
838944
priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
839945
} else if (of_device_is_compatible(np, "fsl,imx-audio-spdif")) {
840946
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)
9891095

9901096
if (asrc_pdev) {
9911097
/* DPCM DAI Links only if ASRC exists */
1098+
priv->dai_link[1].dpcm_merged_chan = 1;
1099+
priv->dai_link[1].ignore_pmdown_time = 1;
9921100
priv->dai_link[1].cpus->of_node = asrc_np;
9931101
priv->dai_link[1].platforms->of_node = asrc_np;
9941102
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)
9981106
}
9991107
priv->dai_link[2].cpus->of_node = cpu_np;
10001108
priv->dai_link[2].dai_fmt = priv->dai_fmt;
1109+
priv->dai_link[2].ignore_pmdown_time = 1;
10011110
priv->card.num_links = 3;
10021111

10031112
ret = of_property_read_u32(asrc_np, "fsl,asrc-rate",

0 commit comments

Comments
 (0)