Unverified Commit d641c164 authored by Mark Brown's avatar Mark Brown
Browse files

ASoC/SoundWire: clean up link DMA during stop for IPC4

Merge series from Bard Liao <yung-chuan.liao@linux.intel.com>:

Clean up the link DMA for playback during stop for IPC4 is required to
reset the DMA read/write pointers when the stream is prepared and
restarted after a call to snd_pcm_drain()/snd_pcm_drop().

The change is mainly on ASoC. We may go via ASoC tree with Vinod's
Acked-by tag

Ranjani Sridharan (4):
  ASoC: SOF: ipc4-topology: Do not set ALH node_id for aggregated DAIs
  ASoC: SOF: Intel: hda: Handle prepare without close for non-HDA DAI's
  soundwire: intel_ace2x: Send PDI stream number during prepare
  ASoC: SOF: Intel: hda: Always clean up link DMA during stop

 drivers/soundwire/intel_ace2x.c   | 19 +++++-----------
 sound/soc/sof/intel/hda-dai-ops.c | 23 +++++++++----------
 sound/soc/sof/intel/hda-dai.c     | 37 +++++++++++++++++++++++++++----
 sound/soc/sof/ipc4-topology.c     | 15 +++++++++++--
 4 files changed, 62 insertions(+), 32 deletions(-)

--
2.43.0
parents da95e891 ab559379
Loading
Loading
Loading
Loading
+6 −13
Original line number Diff line number Diff line
@@ -376,11 +376,12 @@ static int intel_hw_params(struct snd_pcm_substream *substream,
static int intel_prepare(struct snd_pcm_substream *substream,
			 struct snd_soc_dai *dai)
{
	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
	struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
	struct sdw_intel *sdw = cdns_to_intel(cdns);
	struct sdw_cdns_dai_runtime *dai_runtime;
	struct snd_pcm_hw_params *hw_params;
	int ch, dir;
	int ret = 0;

	dai_runtime = cdns->dai_runtime_array[dai->id];
	if (!dai_runtime) {
@@ -389,12 +390,8 @@ static int intel_prepare(struct snd_pcm_substream *substream,
		return -EIO;
	}

	if (dai_runtime->suspended) {
		struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
		struct snd_pcm_hw_params *hw_params;

	hw_params = &rtd->dpcm[substream->stream].hw_params;

	if (dai_runtime->suspended) {
		dai_runtime->suspended = false;

		/*
@@ -415,17 +412,13 @@ static int intel_prepare(struct snd_pcm_substream *substream,
		/* the SHIM will be configured in the callback functions */

		sdw_cdns_config_stream(cdns, ch, dir, dai_runtime->pdi);
	}

	/* Inform DSP about PDI stream number */
		ret = intel_params_stream(sdw, substream, dai,
					  hw_params,
					  sdw->instance,
	return intel_params_stream(sdw, substream, dai, hw_params, sdw->instance,
				   dai_runtime->pdi->intel_alh_id);
}

	return ret;
}

static int
intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
+10 −13
Original line number Diff line number Diff line
@@ -346,20 +346,21 @@ static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		snd_hdac_ext_stream_start(hext_stream);
		break;
	case SNDRV_PCM_TRIGGER_SUSPEND:
	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.
		 * Save the LLP registers since in case of PAUSE the LLP
		 * register are not reset to 0, the delay calculation will use
		 * the saved offsets for compensating the delay calculation.
		 */
		hext_stream->pplcllpl = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPL);
		hext_stream->pplcllpu = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPU);
		snd_hdac_ext_stream_clear(hext_stream);
		break;
	case SNDRV_PCM_TRIGGER_SUSPEND:
	case SNDRV_PCM_TRIGGER_STOP:
		hext_stream->pplcllpl = 0;
		hext_stream->pplcllpu = 0;
		snd_hdac_ext_stream_clear(hext_stream);
		break;
	default:
		dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
@@ -512,7 +513,6 @@ static const struct hda_dai_widget_dma_ops sdw_ipc4_chain_dma_ops = {
static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
				 struct snd_pcm_substream *substream, int cmd)
{
	struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream);
	struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);

	switch (cmd) {
@@ -527,9 +527,6 @@ static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c
		if (ret < 0)
			return ret;

		if (cmd == SNDRV_PCM_TRIGGER_STOP)
			return hda_link_dma_cleanup(substream, hext_stream, cpu_dai);

		break;
	}
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+33 −4
Original line number Diff line number Diff line
@@ -302,6 +302,7 @@ static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, i
	}

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_SUSPEND:
		ret = hda_link_dma_cleanup(substream, hext_stream, dai);
		if (ret < 0) {
@@ -370,6 +371,13 @@ static int non_hda_dai_hw_params_data(struct snd_pcm_substream *substream,
		return -EINVAL;
	}

	sdev = widget_to_sdev(w);
	hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);

	/* nothing more to do if the link is already prepared */
	if (hext_stream && hext_stream->link_prepared)
		return 0;

	/* use HDaudio stream handling */
	ret = hda_dai_hw_params_data(substream, params, cpu_dai, data, flags);
	if (ret < 0) {
@@ -377,7 +385,6 @@ static int non_hda_dai_hw_params_data(struct snd_pcm_substream *substream,
		return ret;
	}

	sdev = widget_to_sdev(w);
	if (sdev->dspless_mode_selected)
		return 0;

@@ -482,6 +489,31 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
	int ret;
	int i;

	ops = hda_dai_get_ops(substream, cpu_dai);
	if (!ops) {
		dev_err(cpu_dai->dev, "DAI widget ops not set\n");
		return -EINVAL;
	}

	sdev = widget_to_sdev(w);
	hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);

	/* nothing more to do if the link is already prepared */
	if (hext_stream && hext_stream->link_prepared)
		return 0;

	/*
	 * reset the PCMSyCM registers to handle a prepare callback when the PCM is restarted
	 * due to xruns or after a call to snd_pcm_drain/drop()
	 */
	ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id,
					     0, 0, substream->stream);
	if (ret < 0) {
		dev_err(cpu_dai->dev, "%s:  hdac_bus_eml_sdw_map_stream_ch failed %d\n",
			__func__, ret);
		return ret;
	}

	data.dai_index = (link_id << 8) | cpu_dai->id;
	data.dai_node_id = intel_alh_id;
	ret = non_hda_dai_hw_params_data(substream, params, cpu_dai, &data, flags);
@@ -490,10 +522,7 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
		return ret;
	}

	ops = hda_dai_get_ops(substream, cpu_dai);
	sdev = widget_to_sdev(w);
	hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);

	if (!hext_stream)
		return -ENODEV;

+13 −2
Original line number Diff line number Diff line
@@ -3129,9 +3129,20 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
		 * group_id during copier's ipc_prepare op.
		 */
		if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) {
			struct sof_ipc4_alh_configuration_blob *blob;

			blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;
			ipc4_copier->dai_index = data->dai_node_id;

			/*
			 * no need to set the node_id for aggregated DAI's. These will be assigned
			 * a group_id during widget ipc_prepare
			 */
			if (blob->alh_cfg.device_count == 1) {
				copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
			copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(data->dai_node_id);
				copier_data->gtw_cfg.node_id |=
					SOF_IPC4_NODE_INDEX(data->dai_node_id);
			}
		}

		break;