mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-03 23:58:05 +00:00
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>
This commit is contained in:
parent
49783c6f4a
commit
c8a4556d98
2 changed files with 83 additions and 0 deletions
|
@ -93,8 +93,30 @@ static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream,
|
||||||
struct snd_soc_dai *dai)
|
struct snd_soc_dai *dai)
|
||||||
{
|
{
|
||||||
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(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]);
|
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]);
|
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_START:
|
||||||
case SNDRV_PCM_TRIGGER_RESUME:
|
case SNDRV_PCM_TRIGGER_RESUME:
|
||||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
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) {
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||||
ret = regmap_fields_write(i2sctl->spken, id,
|
ret = regmap_fields_write(i2sctl->spken, id,
|
||||||
LPAIF_I2SCTL_SPKEN_ENABLE);
|
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_STOP:
|
||||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||||
|
/*
|
||||||
|
* To ensure lpass BCLK/LRCLK is disabled during
|
||||||
|
* device suspend.
|
||||||
|
*/
|
||||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||||
ret = regmap_fields_write(i2sctl->spken, id,
|
ret = regmap_fields_write(i2sctl->spken, id,
|
||||||
LPAIF_I2SCTL_SPKEN_DISABLE);
|
LPAIF_I2SCTL_SPKEN_DISABLE);
|
||||||
|
@ -315,12 +353,53 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
|
||||||
return ret;
|
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 = {
|
const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = {
|
||||||
.set_sysclk = lpass_cpu_daiops_set_sysclk,
|
.set_sysclk = lpass_cpu_daiops_set_sysclk,
|
||||||
.startup = lpass_cpu_daiops_startup,
|
.startup = lpass_cpu_daiops_startup,
|
||||||
.shutdown = lpass_cpu_daiops_shutdown,
|
.shutdown = lpass_cpu_daiops_shutdown,
|
||||||
.hw_params = lpass_cpu_daiops_hw_params,
|
.hw_params = lpass_cpu_daiops_hw_params,
|
||||||
.trigger = lpass_cpu_daiops_trigger,
|
.trigger = lpass_cpu_daiops_trigger,
|
||||||
|
.prepare = lpass_cpu_daiops_prepare,
|
||||||
};
|
};
|
||||||
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops);
|
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops);
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,10 @@ struct lpass_data {
|
||||||
/* MI2S SD lines to use for playback/capture */
|
/* MI2S SD lines to use for playback/capture */
|
||||||
unsigned int mi2s_playback_sd_mode[LPASS_MAX_MI2S_PORTS];
|
unsigned int mi2s_playback_sd_mode[LPASS_MAX_MI2S_PORTS];
|
||||||
unsigned int mi2s_capture_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;
|
int hdmi_port_enable;
|
||||||
|
|
||||||
/* low-power audio interface (LPAIF) registers */
|
/* low-power audio interface (LPAIF) registers */
|
||||||
|
|
Loading…
Reference in a new issue