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
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 */
5282struct 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+
294370static 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