summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKai Vehmanen <kai.vehmanen@linux.intel.com>2019-07-22 09:13:43 -0500
committerMark Brown <broonie@kernel.org>2019-07-23 12:18:18 +0100
commit04c8027764bc82a325d3abc6f39a6a4642a937cb (patch)
treebfa0eeaeebd596479a786025fad3e098ff42182f
parentf1b1b9b136827915624136624ff54aba5890a15b (diff)
downloadlinux-stable-04c8027764bc82a325d3abc6f39a6a4642a937cb.tar.gz
linux-stable-04c8027764bc82a325d3abc6f39a6a4642a937cb.tar.bz2
linux-stable-04c8027764bc82a325d3abc6f39a6a4642a937cb.zip
ASoC: SOF: reset DMA state in prepare
When application goes through SUSPEND/STOP->PREPARE->START cycle, we should always reprogram the SOF device to start DMA from a known state so that hw_ptr/appl_ptrs remain valid. This is expected by ALSA core as it resets the buffer state as part of prepare (see snd_pcm_do_prepare()). Fix the issue by forcing reconfiguration of the FW with STREAM_PCM_PARAMS in prepare(). Use combined logic to handle prepare and the existing flow to reprogram hw-params after system suspend. Without the fix, first call to pcm pointer() will return an invalid hw_ptr and application may immediately observe XRUN status, unless "start_threshold" SW parameter is set to maximum value by the application. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com> Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Link: https://lore.kernel.org/r/20190722141402.7194-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--sound/soc/sof/pcm.c27
-rw-r--r--sound/soc/sof/pm.c2
-rw-r--r--sound/soc/sof/sof-priv.h2
3 files changed, 17 insertions, 14 deletions
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index 334e9d59b1ba..3b8955e755b2 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -208,12 +208,11 @@ static int sof_pcm_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
+ spcm->prepared[substream->stream] = true;
+
/* save pcm hw_params */
memcpy(&spcm->params[substream->stream], params, sizeof(*params));
- /* clear hw_params_upon_resume flag */
- spcm->hw_params_upon_resume[substream->stream] = 0;
-
return ret;
}
@@ -236,6 +235,9 @@ static int sof_pcm_hw_free(struct snd_pcm_substream *substream)
if (!spcm)
return -EINVAL;
+ if (!spcm->prepared[substream->stream])
+ return 0;
+
dev_dbg(sdev->dev, "pcm: free stream %d dir %d\n", spcm->pcm.pcm_id,
substream->stream);
@@ -258,6 +260,8 @@ static int sof_pcm_hw_free(struct snd_pcm_substream *substream)
if (ret < 0)
dev_err(sdev->dev, "error: platform hw free failed\n");
+ spcm->prepared[substream->stream] = false;
+
return ret;
}
@@ -278,11 +282,7 @@ static int sof_pcm_prepare(struct snd_pcm_substream *substream)
if (!spcm)
return -EINVAL;
- /*
- * check if hw_params needs to be set-up again.
- * This is only needed when resuming from system sleep.
- */
- if (!spcm->hw_params_upon_resume[substream->stream])
+ if (spcm->prepared[substream->stream])
return 0;
dev_dbg(sdev->dev, "pcm: prepare stream %d dir %d\n", spcm->pcm.pcm_id,
@@ -311,6 +311,7 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
struct snd_sof_pcm *spcm;
struct sof_ipc_stream stream;
struct sof_ipc_reply reply;
+ bool reset_hw_params = false;
int ret;
/* nothing to do for BE */
@@ -351,6 +352,7 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP;
+ reset_hw_params = true;
break;
default:
dev_err(sdev->dev, "error: unhandled trigger cmd %d\n", cmd);
@@ -363,17 +365,17 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream,
sizeof(stream), &reply, sizeof(reply));
- if (ret < 0 || cmd != SNDRV_PCM_TRIGGER_SUSPEND)
+ if (ret < 0 || !reset_hw_params)
return ret;
/*
- * The hw_free op is usually called when the pcm stream is closed.
- * Since the stream is not closed during suspend, the DSP needs to be
- * notified explicitly to free pcm to prevent errors upon resume.
+ * In case of stream is stopped, DSP must be reprogrammed upon
+ * restart, so free PCM here.
*/
stream.hdr.size = sizeof(stream);
stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE;
stream.comp_id = spcm->stream[substream->stream].comp_id;
+ spcm->prepared[substream->stream] = false;
/* send IPC to the DSP */
return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream,
@@ -481,6 +483,7 @@ static int sof_pcm_open(struct snd_pcm_substream *substream)
spcm->stream[substream->stream].posn.host_posn = 0;
spcm->stream[substream->stream].posn.dai_posn = 0;
spcm->stream[substream->stream].substream = substream;
+ spcm->prepared[substream->stream] = false;
ret = snd_sof_pcm_platform_open(sdev, substream);
if (ret < 0)
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index 278abfd10490..48c6d78d72e2 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -233,7 +233,7 @@ static int sof_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
state = substream->runtime->status->state;
if (state == SNDRV_PCM_STATE_SUSPENDED)
- spcm->hw_params_upon_resume[dir] = 1;
+ spcm->prepared[dir] = false;
}
}
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index b8c0b2a22684..fa5cb7d2a660 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -297,7 +297,7 @@ struct snd_sof_pcm {
struct snd_sof_pcm_stream stream[2];
struct list_head list; /* list in sdev pcm list */
struct snd_pcm_hw_params params[2];
- int hw_params_upon_resume[2]; /* set up hw_params upon resume */
+ bool prepared[2]; /* PCM_PARAMS set successfully */
};
/* ALSA SOF Kcontrol device */