On 04/06/2021 16:45, Judy Hsiao wrote:
From: Srinivasa Rao Mandadapu srivasam@codeaurora.org
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.
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 (am from https://patchwork.kernel.org/patch/12276369/) (also found at https://lore.kernel.org/r/20210524142114.18676-1-srivasam@codeaurora.org)
Reviewed-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org
Changes Since V6: -- Removed clk_disable OSR clock in lpass_cpu_daiops_prepare error case as failure of prepare will result in calling shutdown which should take care of this. Changes Since V5: -- Fixed nit. -- Updated `mi2s_was_prepared[dai->driver->id] = true;` after checking for errors. Changes Since V4: -- Replaced the __clk_is_enabled(BCLK) check by the self maintained. mi2s_was_prepared bool state. -- Removed unrelated changes. -- Refined comments. Changes Since V3: -- Checked BCLK is off before enabling it in lpass_cpu_daiops_prepare as lpass_cpu_daiops_prepare can be called multiple times -- Checked BCLK is on before disabling it in lpass_cpu_daiops_shutdown to fix the WARN. It is because BCLK may not be enabled if lpass_cpu_daiops_prepare is not called before lpass_cpu_daiops_shutdown -- Added more comments Changes Since V2: -- Updated comments as per linux style -- Removed unrelated changes Changes Since V1: -- Enabled BCLK and LRCLK in dai ops prepare API instead of startup API -- Added comments
sound/soc/qcom/lpass-cpu.c | 79 ++++++++++++++++++++++++++++++++++++++ sound/soc/qcom/lpass.h | 4 ++ 2 files changed, 83 insertions(+)
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index af8cb64924a0..647423a6cb57 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -93,8 +93,30 @@ static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
struct lpaif_i2sctl *i2sctl = drvdata->i2sctl;
unsigned int id = dai->driver->id;
clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]);
/*
* Ensure LRCLK is disabled even in device node validation.
* Will not impact if disabled in lpass_cpu_daiops_trigger()
* suspend.
*/
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
regmap_fields_write(i2sctl->spken, id, LPAIF_I2SCTL_SPKEN_DISABLE);
else
regmap_fields_write(i2sctl->micen, id, LPAIF_I2SCTL_MICEN_DISABLE);
/*
* BCLK may not be enabled if lpass_cpu_daiops_prepare is called before
* lpass_cpu_daiops_shutdown. It's paired with the clk_enable in
* lpass_cpu_daiops_prepare.
*/
if (drvdata->mi2s_was_prepared[dai->driver->id]) {
drvdata->mi2s_was_prepared[dai->driver->id] = false;
clk_disable(drvdata->mi2s_bit_clk[dai->driver->id]);
}
clk_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]); }
@@ -275,6 +297,18 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
/*
* Ensure lpass BCLK/LRCLK is enabled during
* device resume as lpass_cpu_daiops_prepare() is not called
* after the device resumes. We don't check mi2s_was_prepared before
* enable/disable BCLK in trigger events because:
* 1. These trigger events are paired, so the BCLK
* enable_count is balanced.
* 2. the BCLK can be shared (ex: headset and headset mic),
* we need to increase the enable_count so that we don't
* turn off the shared BCLK while other devices are using
* it.
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ret = regmap_fields_write(i2sctl->spken, id, LPAIF_I2SCTL_SPKEN_ENABLE);*/
@@ -296,6 +330,10 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
/*
* To ensure lpass BCLK/LRCLK is disabled during
* device suspend.
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ret = regmap_fields_write(i2sctl->spken, id, LPAIF_I2SCTL_SPKEN_DISABLE);*/
@@ -315,12 +353,53 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, return ret; }
+static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
+{
- struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
- struct lpaif_i2sctl *i2sctl = drvdata->i2sctl;
- unsigned int id = dai->driver->id;
- int ret;
- /*
* Ensure lpass BCLK/LRCLK is enabled bit before playback/capture
* data flow starts. This allows other codec to have some delay before
* the data flow.
* (ex: to drop start up pop noise before capture starts).
*/
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
ret = regmap_fields_write(i2sctl->spken, id, LPAIF_I2SCTL_SPKEN_ENABLE);
- else
ret = regmap_fields_write(i2sctl->micen, id, LPAIF_I2SCTL_MICEN_ENABLE);
- if (ret) {
dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret);
return ret;
- }
- /*
* Check mi2s_was_prepared before enabling BCLK as lpass_cpu_daiops_prepare can
* be called multiple times. It's paired with the clk_disable in
* lpass_cpu_daiops_shutdown.
*/
- if (!drvdata->mi2s_was_prepared[dai->driver->id]) {
ret = clk_enable(drvdata->mi2s_bit_clk[id]);
if (ret) {
dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret);
return ret;
}
drvdata->mi2s_was_prepared[dai->driver->id] = true;
- }
- return 0;
+}
- const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = { .set_sysclk = lpass_cpu_daiops_set_sysclk, .startup = lpass_cpu_daiops_startup, .shutdown = lpass_cpu_daiops_shutdown, .hw_params = lpass_cpu_daiops_hw_params, .trigger = lpass_cpu_daiops_trigger,
- .prepare = lpass_cpu_daiops_prepare, }; EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops);
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index 83b2e08ade06..7f72214404ba 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -67,6 +67,10 @@ struct lpass_data { /* MI2S SD lines to use for playback/capture */ unsigned int mi2s_playback_sd_mode[LPASS_MAX_MI2S_PORTS]; unsigned int mi2s_capture_sd_mode[LPASS_MAX_MI2S_PORTS];
/* The state of MI2S prepare dai_ops was called */
bool mi2s_was_prepared[LPASS_MAX_MI2S_PORTS];
int hdmi_port_enable;
/* low-power audio interface (LPAIF) registers */