Unverified Commit 04c80277 authored by Kai Vehmanen's avatar Kai Vehmanen Committed by Mark Brown
Browse files

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: default avatarKai Vehmanen <kai.vehmanen@linux.intel.com>
Signed-off-by: default avatarPierre-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: default avatarMark Brown <broonie@kernel.org>
parent f1b1b9b1
Loading
Loading
Loading
Loading
+15 −12
Original line number Diff line number Diff line
@@ -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)
+1 −1
Original line number Diff line number Diff line
@@ -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;
		}
	}

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