Unverified Commit c8a4556d authored by Srinivasa Rao Mandadapu's avatar Srinivasa Rao Mandadapu Committed by Mark Brown
Browse files

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: default avatarJudy Hsiao <judyhsiao@chromium.org>
Signed-off-by: default avatarJudy Hsiao <judyhsiao@chromium.org>
Signed-off-by: default avatarSrinivasa Rao Mandadapu <srivasam@codeaurora.org>
Reviewed-by: default avatarSrinivas Kandagatla <srinivas.kandagatla@linaro.org>
Link: https://lore.kernel.org/r/20210604154545.1198337-1-judyhsiao@chromium.org


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 49783c6f
Loading
Loading
Loading
Loading
+79 −0
Original line number Diff line number Diff line
@@ -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);

+4 −0
Original line number Diff line number Diff line
@@ -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 */