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

ASoC: qcom: lpass-cpu: Fix pop noise during audio capture begin

This patch fixes PoP noise of around 15ms observed during audio
capture begin.
Enables BCLK and LRCLK in snd_soc_dai_ops prepare call for
introducing some delay before capture start.

(am from https://patchwork.kernel.org/patch/12276369/)
(also found at https://lore.kernel.org/r/20210524142114.18676-1-srivasam@codeaurora.org)

Co-developed-by: Judy Hsiao <judyhsiao@chromium.org>
Signed-off-by: Judy Hsiao <judyhsiao@chromium.org>
Signed-off-by: Srinivasa Rao Mandadapu <srivasam@codeaurora.org>
Reviewed-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20210604154545.1198337-1-judyhsiao@chromium.org
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Srinivasa Rao Mandadapu and committed by
Mark Brown
c8a4556d 49783c6f

+83
+79
sound/soc/qcom/lpass-cpu.c
··· 93 93 struct snd_soc_dai *dai) 94 94 { 95 95 struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 96 + struct lpaif_i2sctl *i2sctl = drvdata->i2sctl; 97 + unsigned int id = dai->driver->id; 96 98 97 99 clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]); 100 + /* 101 + * Ensure LRCLK is disabled even in device node validation. 102 + * Will not impact if disabled in lpass_cpu_daiops_trigger() 103 + * suspend. 104 + */ 105 + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 106 + regmap_fields_write(i2sctl->spken, id, LPAIF_I2SCTL_SPKEN_DISABLE); 107 + else 108 + regmap_fields_write(i2sctl->micen, id, LPAIF_I2SCTL_MICEN_DISABLE); 109 + 110 + /* 111 + * BCLK may not be enabled if lpass_cpu_daiops_prepare is called before 112 + * lpass_cpu_daiops_shutdown. It's paired with the clk_enable in 113 + * lpass_cpu_daiops_prepare. 114 + */ 115 + if (drvdata->mi2s_was_prepared[dai->driver->id]) { 116 + drvdata->mi2s_was_prepared[dai->driver->id] = false; 117 + clk_disable(drvdata->mi2s_bit_clk[dai->driver->id]); 118 + } 119 + 98 120 clk_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]); 99 121 } 100 122 ··· 297 275 case SNDRV_PCM_TRIGGER_START: 298 276 case SNDRV_PCM_TRIGGER_RESUME: 299 277 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 278 + /* 279 + * Ensure lpass BCLK/LRCLK is enabled during 280 + * device resume as lpass_cpu_daiops_prepare() is not called 281 + * after the device resumes. We don't check mi2s_was_prepared before 282 + * enable/disable BCLK in trigger events because: 283 + * 1. These trigger events are paired, so the BCLK 284 + * enable_count is balanced. 285 + * 2. the BCLK can be shared (ex: headset and headset mic), 286 + * we need to increase the enable_count so that we don't 287 + * turn off the shared BCLK while other devices are using 288 + * it. 289 + */ 300 290 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 301 291 ret = regmap_fields_write(i2sctl->spken, id, 302 292 LPAIF_I2SCTL_SPKEN_ENABLE); ··· 330 296 case SNDRV_PCM_TRIGGER_STOP: 331 297 case SNDRV_PCM_TRIGGER_SUSPEND: 332 298 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 299 + /* 300 + * To ensure lpass BCLK/LRCLK is disabled during 301 + * device suspend. 302 + */ 333 303 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 334 304 ret = regmap_fields_write(i2sctl->spken, id, 335 305 LPAIF_I2SCTL_SPKEN_DISABLE); ··· 353 315 return ret; 354 316 } 355 317 318 + static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream, 319 + struct snd_soc_dai *dai) 320 + { 321 + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 322 + struct lpaif_i2sctl *i2sctl = drvdata->i2sctl; 323 + unsigned int id = dai->driver->id; 324 + int ret; 325 + 326 + /* 327 + * Ensure lpass BCLK/LRCLK is enabled bit before playback/capture 328 + * data flow starts. This allows other codec to have some delay before 329 + * the data flow. 330 + * (ex: to drop start up pop noise before capture starts). 331 + */ 332 + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 333 + ret = regmap_fields_write(i2sctl->spken, id, LPAIF_I2SCTL_SPKEN_ENABLE); 334 + else 335 + ret = regmap_fields_write(i2sctl->micen, id, LPAIF_I2SCTL_MICEN_ENABLE); 336 + 337 + if (ret) { 338 + dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret); 339 + return ret; 340 + } 341 + 342 + /* 343 + * Check mi2s_was_prepared before enabling BCLK as lpass_cpu_daiops_prepare can 344 + * be called multiple times. It's paired with the clk_disable in 345 + * lpass_cpu_daiops_shutdown. 346 + */ 347 + if (!drvdata->mi2s_was_prepared[dai->driver->id]) { 348 + ret = clk_enable(drvdata->mi2s_bit_clk[id]); 349 + if (ret) { 350 + dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret); 351 + return ret; 352 + } 353 + drvdata->mi2s_was_prepared[dai->driver->id] = true; 354 + } 355 + return 0; 356 + } 357 + 356 358 const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = { 357 359 .set_sysclk = lpass_cpu_daiops_set_sysclk, 358 360 .startup = lpass_cpu_daiops_startup, 359 361 .shutdown = lpass_cpu_daiops_shutdown, 360 362 .hw_params = lpass_cpu_daiops_hw_params, 361 363 .trigger = lpass_cpu_daiops_trigger, 364 + .prepare = lpass_cpu_daiops_prepare, 362 365 }; 363 366 EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops); 364 367
+4
sound/soc/qcom/lpass.h
··· 67 67 /* MI2S SD lines to use for playback/capture */ 68 68 unsigned int mi2s_playback_sd_mode[LPASS_MAX_MI2S_PORTS]; 69 69 unsigned int mi2s_capture_sd_mode[LPASS_MAX_MI2S_PORTS]; 70 + 71 + /* The state of MI2S prepare dai_ops was called */ 72 + bool mi2s_was_prepared[LPASS_MAX_MI2S_PORTS]; 73 + 70 74 int hdmi_port_enable; 71 75 72 76 /* low-power audio interface (LPAIF) registers */