Commit 389b01ab authored by Mark Brown's avatar Mark Brown

ASoC: SOF: pcm/Intel: Pause-resume improvements for

Merge series from Peter Ujfalusi <peter.ujfalusi@linux.intel.com>:

Last minute patch for correct the pasue/resume operation with IPC4.  The
issues are hardto reproduce and needs extended stress testing to be hit,
in which case the audio breaks due to DMA errors.
parents efd090c0 6d0a21dd
......@@ -337,7 +337,13 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
/* cmd must be for audio stream */
switch (cmd) {
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (!sdev->dspless_mode_selected)
break;
fallthrough;
case SNDRV_PCM_TRIGGER_START:
if (hstream->running)
break;
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
1 << hstream->index,
1 << hstream->index);
......@@ -360,8 +366,11 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
hstream->running = true;
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (!sdev->dspless_mode_selected)
break;
fallthrough;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
sd_offset,
......
......@@ -836,5 +836,6 @@ const struct sof_ipc_pcm_ops ipc4_pcm_ops = {
.pcm_setup = sof_ipc4_pcm_setup,
.pcm_free = sof_ipc4_pcm_free,
.delay = sof_ipc4_pcm_delay,
.ipc_first_on_start = true
.ipc_first_on_start = true,
.platform_stop_during_hw_free = true,
};
......@@ -211,16 +211,22 @@ static int sof_pcm_hw_free(struct snd_soc_component *component,
dev_dbg(component->dev, "pcm: free stream %d dir %d\n",
spcm->pcm.pcm_id, substream->stream);
/* free PCM in the DSP */
if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) {
ret = pcm_ops->hw_free(component, substream);
if (ret < 0)
err = ret;
if (spcm->prepared[substream->stream]) {
/* stop DMA first if needed */
if (pcm_ops && pcm_ops->platform_stop_during_hw_free)
snd_sof_pcm_platform_trigger(sdev, substream, SNDRV_PCM_TRIGGER_STOP);
/* free PCM in the DSP */
if (pcm_ops && pcm_ops->hw_free) {
ret = pcm_ops->hw_free(component, substream);
if (ret < 0)
err = ret;
}
spcm->prepared[substream->stream] = false;
}
/* stop DMA */
/* reset DMA */
ret = snd_sof_pcm_platform_hw_free(sdev, substream);
if (ret < 0) {
dev_err(component->dev, "error: platform hw free failed\n");
......@@ -362,8 +368,9 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_STOP:
/* invoke platform trigger to stop DMA even if pcm_ops failed */
snd_sof_pcm_platform_trigger(sdev, substream, cmd);
/* invoke platform trigger to stop DMA even if pcm_ops isn't set or if it failed */
if (!pcm_ops || (pcm_ops && !pcm_ops->platform_stop_during_hw_free))
snd_sof_pcm_platform_trigger(sdev, substream, cmd);
break;
default:
break;
......
......@@ -805,16 +805,22 @@ int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *subs
const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
int ret;
/* Send PCM_FREE IPC to reset pipeline */
if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) {
ret = pcm_ops->hw_free(sdev->component, substream);
if (ret < 0)
return ret;
}
if (spcm->prepared[substream->stream]) {
/* stop DMA first if needed */
if (pcm_ops && pcm_ops->platform_stop_during_hw_free)
snd_sof_pcm_platform_trigger(sdev, substream, SNDRV_PCM_TRIGGER_STOP);
/* Send PCM_FREE IPC to reset pipeline */
if (pcm_ops && pcm_ops->hw_free) {
ret = pcm_ops->hw_free(sdev->component, substream);
if (ret < 0)
return ret;
}
spcm->prepared[substream->stream] = false;
spcm->prepared[substream->stream] = false;
}
/* stop the DMA */
/* reset the DMA */
ret = snd_sof_pcm_platform_hw_free(sdev, substream);
if (ret < 0)
return ret;
......
......@@ -108,6 +108,11 @@ struct snd_sof_dai_config_data {
* STOP pcm trigger
* @ipc_first_on_start: Send IPC before invoking platform trigger during
* START/PAUSE_RELEASE triggers
* @platform_stop_during_hw_free: Invoke the platform trigger during hw_free. This is needed for
* IPC4 where a pipeline is only paused during stop/pause/suspend
* triggers. The FW keeps the host DMA running in this case and
* therefore the host must do the same and should stop the DMA during
* hw_free.
*/
struct sof_ipc_pcm_ops {
int (*hw_params)(struct snd_soc_component *component, struct snd_pcm_substream *substream,
......@@ -123,6 +128,7 @@ struct sof_ipc_pcm_ops {
struct snd_pcm_substream *substream);
bool reset_hw_params_during_stop;
bool ipc_first_on_start;
bool platform_stop_during_hw_free;
};
/**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment