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
This commit is contained in:
Mark Brown 2024-10-18 21:47:03 +01:00
commit d641c164f8
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
4 changed files with 62 additions and 32 deletions

View File

@ -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;
}
hw_params = &rtd->dpcm[substream->stream].hw_params;
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;
dai_runtime->suspended = false;
/*
@ -415,15 +412,11 @@ 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,
dai_runtime->pdi->intel_alh_id);
}
return ret;
/* Inform DSP about PDI stream number */
return intel_params_stream(sdw, substream, dai, hw_params, sdw->instance,
dai_runtime->pdi->intel_alh_id);
}
static int

View File

@ -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:

View File

@ -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;

View File

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