Unverified Commit 3849c4d6 authored by Mark Brown's avatar Mark Brown
Browse files

ASoC: SOF: ipc4/Intel: Fix delay reporting

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

The current version of delay reporting code can report incorrect
values when paired with a firmware which enables this feature.

Unfortunately there are several smaller issues that needed to be addressed
to correct the behavior:

Wrong information was used for the host side of counter
For MTL/LNL used incorrect (in a sense that it was verified only on MTL)
link side counter function.
The link side counter needs compensation logic if pause/resume is used.
The offset values were not refreshed from firmware.
Finally, not strictly connected, but the ALSA buffer size needs to be
constrained to avoid constant xrun from media players (like mpv)

The series applies cleanly for 6.9 and 6.8.y stable, but older stable
would need manual backport, but it is questionable if it is needed as
MTL/LNL is missing features.
parents 56ebbd19 1abc2642
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -56,6 +56,9 @@ struct hdac_ext_stream {
	u32 pphcldpl;
	u32 pphcldpu;

	u32 pplcllpl;
	u32 pplcllpu;

	bool decoupled:1;
	bool link_locked:1;
	bool link_prepared;
+3 −0
Original line number Diff line number Diff line
@@ -57,6 +57,9 @@ struct snd_sof_dsp_ops sof_hda_common_ops = {
	.pcm_pointer	= hda_dsp_pcm_pointer,
	.pcm_ack	= hda_dsp_pcm_ack,

	.get_dai_frame_counter = hda_dsp_get_stream_llp,
	.get_host_byte_counter = hda_dsp_get_stream_ldp,

	/* firmware loading */
	.load_firmware = snd_sof_load_firmware_raw,

+11 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@

#include <sound/pcm_params.h>
#include <sound/hdaudio_ext.h>
#include <sound/hda_register.h>
#include <sound/hda-mlink.h>
#include <sound/sof/ipc4/header.h>
#include <uapi/sound/sof/header.h>
@@ -362,6 +363,16 @@ static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
		snd_hdac_ext_stream_clear(hext_stream);

		/*
		 * Save the LLP registers in case the stream is
		 * restarting due PAUSE_RELEASE, or START without a pcm
		 * close/open since in this case the LLP register is not reset
		 * to 0 and the delay calculation will return with invalid
		 * results.
		 */
		hext_stream->pplcllpl = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPL);
		hext_stream->pplcllpu = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPU);
		break;
	default:
		dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
+29 −0
Original line number Diff line number Diff line
@@ -259,8 +259,37 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev,
		snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_FORMAT,
					     SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32);

	/*
	 * The dsp_max_burst_size_in_ms is the length of the maximum burst size
	 * of the host DMA in the ALSA buffer.
	 *
	 * On playback start the DMA will transfer dsp_max_burst_size_in_ms
	 * amount of data in one initial burst to fill up the host DMA buffer.
	 * Consequent DMA burst sizes are shorter and their length can vary.
	 * To make sure that userspace allocate large enough ALSA buffer we need
	 * to place a constraint on the buffer time.
	 *
	 * On capture the DMA will transfer 1ms chunks.
	 *
	 * Exact dsp_max_burst_size_in_ms constraint is racy, so set the
	 * constraint to a minimum of 2x dsp_max_burst_size_in_ms.
	 */
	if (spcm->stream[direction].dsp_max_burst_size_in_ms)
		snd_pcm_hw_constraint_minmax(substream->runtime,
			SNDRV_PCM_HW_PARAM_BUFFER_TIME,
			spcm->stream[direction].dsp_max_burst_size_in_ms * USEC_PER_MSEC * 2,
			UINT_MAX);

	/* binding pcm substream to hda stream */
	substream->runtime->private_data = &dsp_stream->hstream;

	/*
	 * Reset the llp cache values (they are used for LLP compensation in
	 * case the counter is not reset)
	 */
	dsp_stream->pplcllpl = 0;
	dsp_stream->pplcllpu = 0;

	return 0;
}

+70 −0
Original line number Diff line number Diff line
@@ -1063,3 +1063,73 @@ snd_pcm_uframes_t hda_dsp_stream_get_position(struct hdac_stream *hstream,

	return pos;
}

#define merge_u64(u32_u, u32_l) (((u64)(u32_u) << 32) | (u32_l))

/**
 * hda_dsp_get_stream_llp - Retrieve the LLP (Linear Link Position) of the stream
 * @sdev: SOF device
 * @component: ASoC component
 * @substream: PCM substream
 *
 * Returns the raw Linear Link Position value
 */
u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev,
			   struct snd_soc_component *component,
			   struct snd_pcm_substream *substream)
{
	struct hdac_stream *hstream = substream->runtime->private_data;
	struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
	u32 llp_l, llp_u;

	/*
	 * The pplc_addr have been calculated during probe in
	 * hda_dsp_stream_init():
	 * pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
	 *	       SOF_HDA_PPLC_BASE +
	 *	       SOF_HDA_PPLC_MULTI * total_stream +
	 *	       SOF_HDA_PPLC_INTERVAL * stream_index
	 *
	 * Use this pre-calculated address to avoid repeated re-calculation.
	 */
	llp_l = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPL);
	llp_u = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPU);

	/* Compensate the LLP counter with the saved offset */
	if (hext_stream->pplcllpl || hext_stream->pplcllpu)
		return merge_u64(llp_u, llp_l) -
		       merge_u64(hext_stream->pplcllpu, hext_stream->pplcllpl);

	return merge_u64(llp_u, llp_l);
}

/**
 * hda_dsp_get_stream_ldp - Retrieve the LDP (Linear DMA Position) of the stream
 * @sdev: SOF device
 * @component: ASoC component
 * @substream: PCM substream
 *
 * Returns the raw Linear Link Position value
 */
u64 hda_dsp_get_stream_ldp(struct snd_sof_dev *sdev,
			   struct snd_soc_component *component,
			   struct snd_pcm_substream *substream)
{
	struct hdac_stream *hstream = substream->runtime->private_data;
	struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
	u32 ldp_l, ldp_u;

	/*
	 * The pphc_addr have been calculated during probe in
	 * hda_dsp_stream_init():
	 * pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
	 *	       SOF_HDA_PPHC_BASE +
	 *	       SOF_HDA_PPHC_INTERVAL * stream_index
	 *
	 * Use this pre-calculated address to avoid repeated re-calculation.
	 */
	ldp_l = readl(hext_stream->pphc_addr + AZX_REG_PPHCLDPL);
	ldp_u = readl(hext_stream->pphc_addr + AZX_REG_PPHCLDPU);

	return ((u64)ldp_u << 32) | ldp_l;
}
Loading