@@ -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;
};
/**