Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: cs42xx8: Fix MFREQ selection issue for async mode

When sample rate of TX is different with sample rate of RX in
async mode, the MFreq selection will be wrong.

For example, sysclk = 24.576MHz, TX rate = 96000Hz, RX rate = 48000Hz.
Then ratio of TX = 256, ratio of RX = 512, For MFreq is shared by TX
and RX instance, the correct value of MFreq is 2 for both TX and RX.

But original method will cause MFreq = 0 for TX, MFreq = 2 for RX.
If TX is started after RX, RX will be impacted, RX work abnormal with
MFreq = 0.

This patch is to select proper MFreq value according to TX rate and
RX rate.

Fixes: 0c516b4ff85c ("ASoC: cs42xx8: Add codec driver support for CS42448/CS42888")
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Link: https://lore.kernel.org/r/20190716094547.46787-1-shengjiu.wang@nxp.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Shengjiu Wang and committed by
Mark Brown
48dfd37a 8dd26dff

+97 -19
+97 -19
sound/soc/codecs/cs42xx8.c
··· 47 47 unsigned long sysclk; 48 48 u32 tx_channels; 49 49 struct gpio_desc *gpiod_reset; 50 + u32 rate[2]; 50 51 }; 51 52 52 53 /* -127.5dB to 0dB with step of 0.5dB */ ··· 177 176 }; 178 177 179 178 struct cs42xx8_ratios { 180 - unsigned int ratio; 181 - unsigned char speed; 182 - unsigned char mclk; 179 + unsigned int mfreq; 180 + unsigned int min_mclk; 181 + unsigned int max_mclk; 182 + unsigned int ratio[3]; 183 183 }; 184 184 185 + /* 186 + * According to reference mannual, define the cs42xx8_ratio struct 187 + * MFreq2 | MFreq1 | MFreq0 | Description | SSM | DSM | QSM | 188 + * 0 | 0 | 0 |1.029MHz to 12.8MHz | 256 | 128 | 64 | 189 + * 0 | 0 | 1 |1.536MHz to 19.2MHz | 384 | 192 | 96 | 190 + * 0 | 1 | 0 |2.048MHz to 25.6MHz | 512 | 256 | 128 | 191 + * 0 | 1 | 1 |3.072MHz to 38.4MHz | 768 | 384 | 192 | 192 + * 1 | x | x |4.096MHz to 51.2MHz |1024 | 512 | 256 | 193 + */ 185 194 static const struct cs42xx8_ratios cs42xx8_ratios[] = { 186 - { 64, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_256(4) }, 187 - { 96, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_384(4) }, 188 - { 128, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_512(4) }, 189 - { 192, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_768(4) }, 190 - { 256, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_256(1) }, 191 - { 384, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_384(1) }, 192 - { 512, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_512(1) }, 193 - { 768, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_768(1) }, 194 - { 1024, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_1024(1) } 195 + { 0, 1029000, 12800000, {256, 128, 64} }, 196 + { 2, 1536000, 19200000, {384, 192, 96} }, 197 + { 4, 2048000, 25600000, {512, 256, 128} }, 198 + { 6, 3072000, 38400000, {768, 384, 192} }, 199 + { 8, 4096000, 51200000, {1024, 512, 256} }, 195 200 }; 196 201 197 202 static int cs42xx8_set_dai_sysclk(struct snd_soc_dai *codec_dai, ··· 264 257 struct snd_soc_component *component = dai->component; 265 258 struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); 266 259 bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 267 - u32 ratio = cs42xx8->sysclk / params_rate(params); 268 - u32 i, fm, val, mask; 260 + u32 ratio[2]; 261 + u32 rate[2]; 262 + u32 fm[2]; 263 + u32 i, val, mask; 264 + bool condition1, condition2; 269 265 270 266 if (tx) 271 267 cs42xx8->tx_channels = params_channels(params); 272 268 269 + rate[tx] = params_rate(params); 270 + rate[!tx] = cs42xx8->rate[!tx]; 271 + 272 + ratio[tx] = rate[tx] > 0 ? cs42xx8->sysclk / rate[tx] : 0; 273 + ratio[!tx] = rate[!tx] > 0 ? cs42xx8->sysclk / rate[!tx] : 0; 274 + 275 + /* Get functional mode for tx and rx according to rate */ 276 + for (i = 0; i < 2; i++) { 277 + if (cs42xx8->slave_mode) { 278 + fm[i] = CS42XX8_FM_AUTO; 279 + } else { 280 + if (rate[i] < 50000) { 281 + fm[i] = CS42XX8_FM_SINGLE; 282 + } else if (rate[i] > 50000 && rate[i] < 100000) { 283 + fm[i] = CS42XX8_FM_DOUBLE; 284 + } else if (rate[i] > 100000 && rate[i] < 200000) { 285 + fm[i] = CS42XX8_FM_QUAD; 286 + } else { 287 + dev_err(component->dev, 288 + "unsupported sample rate\n"); 289 + return -EINVAL; 290 + } 291 + } 292 + } 293 + 273 294 for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) { 274 - if (cs42xx8_ratios[i].ratio == ratio) 295 + /* Is the ratio[tx] valid ? */ 296 + condition1 = ((fm[tx] == CS42XX8_FM_AUTO) ? 297 + (cs42xx8_ratios[i].ratio[0] == ratio[tx] || 298 + cs42xx8_ratios[i].ratio[1] == ratio[tx] || 299 + cs42xx8_ratios[i].ratio[2] == ratio[tx]) : 300 + (cs42xx8_ratios[i].ratio[fm[tx]] == ratio[tx])) && 301 + cs42xx8->sysclk >= cs42xx8_ratios[i].min_mclk && 302 + cs42xx8->sysclk <= cs42xx8_ratios[i].max_mclk; 303 + 304 + if (!ratio[tx]) 305 + condition1 = true; 306 + 307 + /* Is the ratio[!tx] valid ? */ 308 + condition2 = ((fm[!tx] == CS42XX8_FM_AUTO) ? 309 + (cs42xx8_ratios[i].ratio[0] == ratio[!tx] || 310 + cs42xx8_ratios[i].ratio[1] == ratio[!tx] || 311 + cs42xx8_ratios[i].ratio[2] == ratio[!tx]) : 312 + (cs42xx8_ratios[i].ratio[fm[!tx]] == ratio[!tx])); 313 + 314 + if (!ratio[!tx]) 315 + condition2 = true; 316 + 317 + /* 318 + * Both ratio[tx] and ratio[!tx] is valid, then we get 319 + * a proper MFreq. 320 + */ 321 + if (condition1 && condition2) 275 322 break; 276 323 } 277 324 ··· 334 273 return -EINVAL; 335 274 } 336 275 337 - mask = CS42XX8_FUNCMOD_MFREQ_MASK; 338 - val = cs42xx8_ratios[i].mclk; 276 + cs42xx8->rate[tx] = params_rate(params); 339 277 340 - fm = cs42xx8->slave_mode ? CS42XX8_FM_AUTO : cs42xx8_ratios[i].speed; 278 + mask = CS42XX8_FUNCMOD_MFREQ_MASK; 279 + val = cs42xx8_ratios[i].mfreq; 341 280 342 281 regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD, 343 282 CS42XX8_FUNCMOD_xC_FM_MASK(tx) | mask, 344 - CS42XX8_FUNCMOD_xC_FM(tx, fm) | val); 283 + CS42XX8_FUNCMOD_xC_FM(tx, fm[tx]) | val); 345 284 285 + return 0; 286 + } 287 + 288 + static int cs42xx8_hw_free(struct snd_pcm_substream *substream, 289 + struct snd_soc_dai *dai) 290 + { 291 + struct snd_soc_component *component = dai->component; 292 + struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); 293 + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 294 + 295 + /* Clear stored rate */ 296 + cs42xx8->rate[tx] = 0; 297 + 298 + regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD, 299 + CS42XX8_FUNCMOD_xC_FM_MASK(tx), 300 + CS42XX8_FUNCMOD_xC_FM(tx, CS42XX8_FM_AUTO)); 346 301 return 0; 347 302 } 348 303 ··· 379 302 .set_fmt = cs42xx8_set_dai_fmt, 380 303 .set_sysclk = cs42xx8_set_dai_sysclk, 381 304 .hw_params = cs42xx8_hw_params, 305 + .hw_free = cs42xx8_hw_free, 382 306 .digital_mute = cs42xx8_digital_mute, 383 307 }; 384 308