* [PATCH 0/4] ASoC: SOF: Intel: Handle ACE2+ link DMA allocation restrictions
@ 2026-07-30 12:51 Peter Ujfalusi
2026-07-30 12:51 ` [PATCH 1/4] ASoC: SOF: Intel: hda: Fold mlink enumeration into hda_dsp_ctrl_init_chip() Peter Ujfalusi
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Peter Ujfalusi @ 2026-07-30 12:51 UTC (permalink / raw)
To: lgirdwood, broonie
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
liam.r.girdwood
Hi,
We have seen cases when the delay reporting unexpectedly behaves incorrectly,
counters are not counting in hardware registers under seemingly random
conditions.
It turned out that there are few cases that the driver must handle in order
to make sure that LLP, PPLC counters are working correctly:
- non-alt links must not be reset during probe
- Concurrent (cross-direction) hazard: when SoundWire shares a physical
link DMA stream index with HDaudio, iDisp or UAOL across the two
directions, the LLP and timestamp values for the affected stream are
wrong. SSP and DMIC are not affected because every DMA request from
those links carries one sample block.
- Sequential (playback only) hazard: once a HDaudio or iDisp link has
used a playback stream index, that index cannot drive any non
HDA/iDisp link in the same direction until the next controller
reset (CRST#).
For users the impact was not visible as the link counter issue only affected
the delay reporting which already have defensive path to filter out
incorrect delays and the DSP caused delay for normal PCMs are negligible to
cause A/V sync issues for example.
Regards,
Peter
--
Peter Ujfalusi (4):
ASoC: SOF: Intel: hda: Fold mlink enumeration into
hda_dsp_ctrl_init_chip()
ASoC: SOF: Intel: hda: Keep non-alt mlinks powered at probe on ACE2+
ASoC: SOF: Intel: hda: Remove unused hda_bus_ml_put_all()
ASoC: SOF: Intel: hda: Avoid ACE2+ link DMA stream allocation hazards
include/sound/hda-mlink.h | 23 +++++++++-
sound/soc/sof/intel/hda-ctrl.c | 19 ++++++++
sound/soc/sof/intel/hda-dai-ops.c | 76 ++++++++++++++++++++++++++++---
sound/soc/sof/intel/hda-dai.c | 2 +-
sound/soc/sof/intel/hda-mlink.c | 35 ++++++++------
sound/soc/sof/intel/hda.c | 4 --
sound/soc/sof/intel/hda.h | 26 ++++++++++-
7 files changed, 158 insertions(+), 27 deletions(-)
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/4] ASoC: SOF: Intel: hda: Fold mlink enumeration into hda_dsp_ctrl_init_chip()
2026-07-30 12:51 [PATCH 0/4] ASoC: SOF: Intel: Handle ACE2+ link DMA allocation restrictions Peter Ujfalusi
@ 2026-07-30 12:51 ` Peter Ujfalusi
2026-07-30 12:51 ` [PATCH 2/4] ASoC: SOF: Intel: hda: Keep non-alt mlinks powered at probe on ACE2+ Peter Ujfalusi
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Peter Ujfalusi @ 2026-07-30 12:51 UTC (permalink / raw)
To: lgirdwood, broonie
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
liam.r.girdwood
Move the hda_bus_ml_init() call from hda_init_caps() into
hda_dsp_ctrl_init_chip(), right after the HDA controller reset has
been de-asserted and unsolicited responses have been accepted.
hda_dsp_ctrl_init_chip() already calls hda_bus_ml_reset_losidv() at
the end of its sequence to clear the stream-to-link mapping. On first
boot this call was a no-op because the multi-link list had not yet
been populated: hda_bus_ml_init() only runs later in hda_init_caps().
Enumerating the links inside init_chip() makes the LOSIDV reset
effective on first boot as well, without adding a second reset call
from the probe path.
hda_bus_ml_init() now returns early when the hlink_list is already
populated, so the subsequent invocations from the D3 resume path
(hda_resume() -> hda_dsp_ctrl_init_chip(false)) are no-ops.
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
sound/soc/sof/intel/hda-ctrl.c | 8 ++++++++
sound/soc/sof/intel/hda-mlink.c | 4 ++++
sound/soc/sof/intel/hda.c | 2 --
3 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c
index 8332d4bda558..a9ead78d3fcb 100644
--- a/sound/soc/sof/intel/hda-ctrl.c
+++ b/sound/soc/sof/intel/hda-ctrl.c
@@ -223,6 +223,14 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec)
/* Accept unsolicited responses */
snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
+ /* Perform a one-time enumeration of the Multi-Link capability */
+ ret = hda_bus_ml_init(bus);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: failed to enumerate multi-links\n",
+ __func__);
+ goto err;
+ }
+
if (detect_codec)
hda_codec_detect_mask(sdev);
diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c
index 92314e3b568a..ca8551befdb5 100644
--- a/sound/soc/sof/intel/hda-mlink.c
+++ b/sound/soc/sof/intel/hda-mlink.c
@@ -432,6 +432,10 @@ int hda_bus_ml_init(struct hdac_bus *bus)
if (!bus->mlcap)
return 0;
+ /* Enumeration is a one time operation, skip if already done */
+ if (!list_empty(&bus->hlink_list))
+ return 0;
+
link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count);
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index dc85903b8d46..26db0e46b827 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -625,8 +625,6 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
return ret;
}
- hda_bus_ml_init(bus);
-
/* Skip SoundWire if it is not supported */
if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
goto skip_soundwire;
--
2.55.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/4] ASoC: SOF: Intel: hda: Keep non-alt mlinks powered at probe on ACE2+
2026-07-30 12:51 [PATCH 0/4] ASoC: SOF: Intel: Handle ACE2+ link DMA allocation restrictions Peter Ujfalusi
2026-07-30 12:51 ` [PATCH 1/4] ASoC: SOF: Intel: hda: Fold mlink enumeration into hda_dsp_ctrl_init_chip() Peter Ujfalusi
@ 2026-07-30 12:51 ` Peter Ujfalusi
2026-07-30 12:51 ` [PATCH 3/4] ASoC: SOF: Intel: hda: Remove unused hda_bus_ml_put_all() Peter Ujfalusi
2026-07-30 12:51 ` [PATCH 4/4] ASoC: SOF: Intel: hda: Avoid ACE2+ link DMA stream allocation hazards Peter Ujfalusi
3 siblings, 0 replies; 5+ messages in thread
From: Peter Ujfalusi @ 2026-07-30 12:51 UTC (permalink / raw)
To: lgirdwood, broonie
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
liam.r.girdwood
Drop the hda_bus_ml_put_all() call at the end of hda_init_caps().
On multi-link (mlink) capable platforms the non-alternate links
(HDaudio and iDisp) are powered on by hardware when CRST# is
de-asserted (LCTL.SPA = 1) and their ref_count is pre-charged to 1 in
hda_ml_alloc_h2link() to match this state. The put_all call
immediately dropped that reference and toggled LCTL.SPA back to 0,
relying on the first stream open to power the link up again.
On ACE2+ platforms this redundant SPA 1->0->1 toggle at probe leaves
the Processing Pipe Capability (PPLC) Linear Link Position counters in
a state where they do not advance on the first stream after boot. The
counters only start working after the first full runtime suspend/resume
cycle, which includes a CRST# assert/deassert that fully resets the PPC
AON block.
Keep the non-alt links powered from CRST# de-assert through first use.
System suspend still powers them down via hda_bus_ml_suspend(), and
resume relies on CRST# de-assert to bring them back up, so no other
path is affected.
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
sound/soc/sof/intel/hda.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index 26db0e46b827..77c7d2bc6fa2 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -668,8 +668,6 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
if (!HDA_IDISP_CODEC(bus->codec_mask))
hda_codec_i915_display_power(sdev, false);
- hda_bus_ml_put_all(bus);
-
return 0;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 3/4] ASoC: SOF: Intel: hda: Remove unused hda_bus_ml_put_all()
2026-07-30 12:51 [PATCH 0/4] ASoC: SOF: Intel: Handle ACE2+ link DMA allocation restrictions Peter Ujfalusi
2026-07-30 12:51 ` [PATCH 1/4] ASoC: SOF: Intel: hda: Fold mlink enumeration into hda_dsp_ctrl_init_chip() Peter Ujfalusi
2026-07-30 12:51 ` [PATCH 2/4] ASoC: SOF: Intel: hda: Keep non-alt mlinks powered at probe on ACE2+ Peter Ujfalusi
@ 2026-07-30 12:51 ` Peter Ujfalusi
2026-07-30 12:51 ` [PATCH 4/4] ASoC: SOF: Intel: hda: Avoid ACE2+ link DMA stream allocation hazards Peter Ujfalusi
3 siblings, 0 replies; 5+ messages in thread
From: Peter Ujfalusi @ 2026-07-30 12:51 UTC (permalink / raw)
To: lgirdwood, broonie
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
liam.r.girdwood
The helper became unused after probe no longer drops all non-alt
links, so remove the dead API and implementation.
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
include/sound/hda-mlink.h | 2 --
sound/soc/sof/intel/hda-mlink.c | 13 -------------
2 files changed, 15 deletions(-)
diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h
index fed69998c93f..d9789b048c61 100644
--- a/include/sound/hda-mlink.h
+++ b/include/sound/hda-mlink.h
@@ -49,7 +49,6 @@ int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num);
int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y,
int channel_mask, int stream_id, int dir);
-void hda_bus_ml_put_all(struct hdac_bus *bus);
void hda_bus_ml_reset_losidv(struct hdac_bus *bus);
int hda_bus_ml_resume(struct hdac_bus *bus);
int hda_bus_ml_suspend(struct hdac_bus *bus);
@@ -169,7 +168,6 @@ hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y,
return 0;
}
-static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { }
static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { }
static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; }
static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; }
diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c
index ca8551befdb5..e0107cbd06e6 100644
--- a/sound/soc/sof/intel/hda-mlink.c
+++ b/sound/soc/sof/intel/hda-mlink.c
@@ -884,19 +884,6 @@ int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y,
return 0;
} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, "SND_SOC_SOF_HDA_MLINK");
-void hda_bus_ml_put_all(struct hdac_bus *bus)
-{
- struct hdac_ext_link *hlink;
-
- list_for_each_entry(hlink, &bus->hlink_list, list) {
- struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
-
- if (!h2link->alt)
- snd_hdac_ext_bus_link_put(bus, hlink);
- }
-}
-EXPORT_SYMBOL_NS(hda_bus_ml_put_all, "SND_SOC_SOF_HDA_MLINK");
-
void hda_bus_ml_reset_losidv(struct hdac_bus *bus)
{
struct hdac_ext_link *hlink;
--
2.55.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 4/4] ASoC: SOF: Intel: hda: Avoid ACE2+ link DMA stream allocation hazards
2026-07-30 12:51 [PATCH 0/4] ASoC: SOF: Intel: Handle ACE2+ link DMA allocation restrictions Peter Ujfalusi
` (2 preceding siblings ...)
2026-07-30 12:51 ` [PATCH 3/4] ASoC: SOF: Intel: hda: Remove unused hda_bus_ml_put_all() Peter Ujfalusi
@ 2026-07-30 12:51 ` Peter Ujfalusi
3 siblings, 0 replies; 5+ messages in thread
From: Peter Ujfalusi @ 2026-07-30 12:51 UTC (permalink / raw)
To: lgirdwood, broonie
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
liam.r.girdwood
On ACE2+ platforms the link DMA stream allocator must avoid two
hardware errata in mlink-capable systems:
- Concurrent (cross-direction) hazard: when SoundWire shares a physical
link DMA stream index with HDaudio, iDisp or UAOL across the two
directions, the LLP and timestamp values for the affected stream are
wrong. SSP and DMIC are not affected because every DMA request from
those links carries one sample block.
- Sequential (playback only) hazard: once a HDaudio or iDisp link has
used a playback stream index, that index cannot drive any non
HDA/iDisp link in the same direction until the next controller
reset (CRST#).
Track the active link type per direction in two masks (one for SoundWire,
one for HDA/iDisp/UAOL) and the persistent set of playback stream
indices touched by HDA/iDisp in a third mask. The link DMA allocator
skips streams that would violate either rule. Streams are released from
the active masks when the stream is released; all masks are cleared in
hda_dsp_ctrl_init_chip() because the CRST# performed there clears the
hardware state as well.
A new helper hda_bus_ml_link_get_type() returns the link type from the
existing extended link descriptor so the SOF allocator can tell
SoundWire, HDA/iDisp and UAOL apart without duplicating the parsing.
The implementation is generic. On platforms older than ACE2 every link
is reported as HDA, only the sequential mask is ever set and it has no
effect because no other link types are present, so behavior is
unchanged.
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
include/sound/hda-mlink.h | 21 +++++++++
sound/soc/sof/intel/hda-ctrl.c | 11 +++++
sound/soc/sof/intel/hda-dai-ops.c | 76 ++++++++++++++++++++++++++++---
sound/soc/sof/intel/hda-dai.c | 2 +-
sound/soc/sof/intel/hda-mlink.c | 18 ++++++++
sound/soc/sof/intel/hda.h | 26 ++++++++++-
6 files changed, 146 insertions(+), 8 deletions(-)
diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h
index d9789b048c61..ba35f03576b9 100644
--- a/include/sound/hda-mlink.h
+++ b/include/sound/hda-mlink.h
@@ -9,6 +9,22 @@
struct hdac_bus;
struct hdac_ext_link;
+/**
+ * enum hda_bus_ml_link_type - mlink link type, used by SOF link DMA
+ * allocator constraints (see struct sof_intel_hda_dev).
+ *
+ * @HDA_BUS_ML_LINK_HDA: non-alt link, i.e. HDA codec or iDisp
+ * @HDA_BUS_ML_LINK_SDW: alt link, SoundWire
+ * @HDA_BUS_ML_LINK_UAOL: alt link, USB Audio Offload
+ * @HDA_BUS_ML_LINK_OTHER: alt link, SSP or DMIC
+ */
+enum hda_bus_ml_link_type {
+ HDA_BUS_ML_LINK_HDA,
+ HDA_BUS_ML_LINK_SDW,
+ HDA_BUS_ML_LINK_UAOL,
+ HDA_BUS_ML_LINK_OTHER,
+};
+
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK)
int hda_bus_ml_init(struct hdac_bus *bus);
@@ -53,6 +69,8 @@ void hda_bus_ml_reset_losidv(struct hdac_bus *bus);
int hda_bus_ml_resume(struct hdac_bus *bus);
int hda_bus_ml_suspend(struct hdac_bus *bus);
+enum hda_bus_ml_link_type hda_bus_ml_link_get_type(struct hdac_ext_link *hlink);
+
struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus);
struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus);
struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus);
@@ -172,6 +190,9 @@ static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { }
static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; }
static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; }
+static inline enum hda_bus_ml_link_type
+hda_bus_ml_link_get_type(struct hdac_ext_link *hlink) { return HDA_BUS_ML_LINK_HDA; }
+
static inline struct hdac_ext_link *
hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) { return NULL; }
diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c
index a9ead78d3fcb..aeb34310eebd 100644
--- a/sound/soc/sof/intel/hda-ctrl.c
+++ b/sound/soc/sof/intel/hda-ctrl.c
@@ -186,6 +186,7 @@ EXPORT_SYMBOL_NS(hda_dsp_ctrl_clock_power_gating, "SND_SOC_SOF_INTEL_HDA_COMMON"
int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec)
{
struct hdac_bus *bus = sof_to_bus(sdev);
+ struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus);
struct hdac_stream *stream;
int sd_offset, ret = 0;
u32 gctl;
@@ -193,6 +194,16 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec)
if (bus->chip_init)
return 0;
+ /*
+ * The controller reset clears the ACE2+ link DMA stream allocation
+ * constraints; reset the masks to reflect this.
+ */
+ memset(sof_hda->link_dma_active_sdw_mask, 0,
+ sizeof(sof_hda->link_dma_active_sdw_mask));
+ memset(sof_hda->link_dma_active_multi_mask, 0,
+ sizeof(sof_hda->link_dma_active_multi_mask));
+ sof_hda->link_dma_out_hda_used_mask = 0;
+
hda_codec_set_codec_wakeup(sdev, true);
hda_dsp_ctrl_misc_clock_gating(sdev, false);
diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c
index b2c559559962..f0be42048db3 100644
--- a/sound/soc/sof/intel/hda-dai-ops.c
+++ b/sound/soc/sof/intel/hda-dai-ops.c
@@ -20,7 +20,7 @@
/* These ops are only applicable for the HDA DAI's in their current form */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
/*
- * This function checks if the host dma channel corresponding
+ * This function checks if the host DMA stream corresponding
* to the link DMA stream_tag argument is assigned to one
* of the FEs connected to the BE DAI.
*/
@@ -42,23 +42,53 @@ static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
}
static struct hdac_ext_stream *
-hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream)
+hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream,
+ enum hda_bus_ml_link_type link_type)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus);
struct sof_intel_hda_stream *hda_stream;
const struct sof_intel_dsp_desc *chip;
struct snd_sof_dev *sdev;
struct hdac_ext_stream *res = NULL;
struct hdac_stream *hstream = NULL;
-
int stream_dir = substream->stream;
+ bool is_multi = link_type == HDA_BUS_ML_LINK_HDA || link_type == HDA_BUS_ML_LINK_UAOL;
+ bool is_play = stream_dir == SNDRV_PCM_STREAM_PLAYBACK;
+ bool is_sdw = link_type == HDA_BUS_ML_LINK_SDW;
+ bool is_hda = link_type == HDA_BUS_ML_LINK_HDA;
+ u32 concur_block_mask = 0;
+ u32 seq_block_mask = 0;
+ unsigned int stream_idx;
if (!bus->ppcap) {
dev_err(bus->dev, "stream type not supported\n");
return NULL;
}
+ /*
+ * On ACE2+ the link DMA stream allocator must avoid two HW errata,
+ * see the comment on struct sof_intel_hda_dev.
+ *
+ * - Concurrent cross-direction: SoundWire conflicts with HDA, iDisp
+ * and UAOL on the same physical stream index; SSP and DMIC are safe.
+ * - Sequential playback: a stream index previously used by an HDA/iDisp
+ * link cannot drive any non-HDA/iDisp link in the same direction
+ * until the next controller reset.
+ *
+ * The masks are protected by bus->reg_lock; sample them inside the
+ * lock together with the stream walk to keep the decision atomic
+ * with concurrent allocations and releases.
+ */
guard(spinlock_irq)(&bus->reg_lock);
+
+ if (is_sdw)
+ concur_block_mask = sof_hda->link_dma_active_multi_mask[!stream_dir];
+ else if (is_multi)
+ concur_block_mask = sof_hda->link_dma_active_sdw_mask[!stream_dir];
+ if (is_play && !is_hda)
+ seq_block_mask = sof_hda->link_dma_out_hda_used_mask;
+
list_for_each_entry(hstream, &bus->stream_list, list) {
struct hdac_ext_stream *hext_stream =
stream_to_hdac_ext_stream(hstream);
@@ -69,6 +99,12 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream
sdev = hda_stream->sdev;
chip = get_chip_info(sdev->pdata);
+ stream_idx = hstream->stream_tag - 1;
+
+ /* skip streams blocked by the ACE2+ allocator constraints */
+ if ((concur_block_mask | seq_block_mask) & BIT(stream_idx))
+ continue;
+
/* check if link is available */
if (!hext_stream->link_locked) {
/*
@@ -95,7 +131,7 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream
/*
* This must be a hostless stream.
- * So reserve the host DMA channel.
+ * So reserve the host DMA stream.
*/
hda_stream->host_reserved = 1;
break;
@@ -109,6 +145,16 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream
res->link_locked = 1;
res->link_substream = substream;
+
+ stream_idx = res->hstream.stream_tag - 1;
+ if (is_sdw)
+ sof_hda->link_dma_active_sdw_mask[stream_dir] |= BIT(stream_idx);
+ else if (is_multi)
+ sof_hda->link_dma_active_multi_mask[stream_dir] |= BIT(stream_idx);
+
+ /* persistent OUT HDA/iDisp shadow, cleared only on CRST# */
+ if (is_hda && is_play)
+ sof_hda->link_dma_out_hda_used_mask |= BIT(stream_idx);
}
return res;
@@ -143,11 +189,13 @@ static struct hdac_ext_stream *hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev
static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev,
struct snd_soc_dai *cpu_dai,
- struct snd_pcm_substream *substream)
+ struct snd_pcm_substream *substream,
+ struct hdac_ext_link *hlink)
{
struct hdac_ext_stream *hext_stream;
+ enum hda_bus_ml_link_type link_type = hda_bus_ml_link_get_type(hlink);
- hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream);
+ hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream, link_type);
if (!hext_stream)
return NULL;
@@ -160,6 +208,22 @@ static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai
struct snd_pcm_substream *substream)
{
struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream);
+ struct sof_intel_hda_dev *sof_hda = sdev->pdata->hw_pdata;
+ struct hdac_bus *bus = sof_to_bus(sdev);
+ int dir = substream->stream;
+ unsigned int stream_idx = hext_stream->hstream.stream_tag - 1;
+
+ /*
+ * Drop the stream index from the per-direction active concurrency masks.
+ * The two masks are mutually exclusive for a given stream/direction
+ * (and a stream of the SSP/DMIC kind appears in neither), so a blind
+ * clear of both is safe and lets us avoid having to remember the
+ * link type at allocation time.
+ */
+ scoped_guard(spinlock_irq, &bus->reg_lock) {
+ sof_hda->link_dma_active_sdw_mask[dir] &= ~BIT(stream_idx);
+ sof_hda->link_dma_active_multi_mask[dir] &= ~BIT(stream_idx);
+ }
snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index 15faedeec16d..bb44d4f8a4da 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -188,7 +188,7 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
if (!hext_stream) {
if (ops->assign_hext_stream)
- hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream);
+ hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream, hlink);
}
if (!hext_stream)
diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c
index e0107cbd06e6..6f02fb5b70ce 100644
--- a/sound/soc/sof/intel/hda-mlink.c
+++ b/sound/soc/sof/intel/hda-mlink.c
@@ -894,6 +894,24 @@ void hda_bus_ml_reset_losidv(struct hdac_bus *bus)
}
EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, "SND_SOC_SOF_HDA_MLINK");
+enum hda_bus_ml_link_type hda_bus_ml_link_get_type(struct hdac_ext_link *hlink)
+{
+ struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
+
+ if (!h2link->alt)
+ return HDA_BUS_ML_LINK_HDA;
+
+ switch (h2link->elid) {
+ case AZX_REG_ML_LEPTR_ID_SDW:
+ return HDA_BUS_ML_LINK_SDW;
+ case AZX_REG_ML_LEPTR_ID_INTEL_UAOL:
+ return HDA_BUS_ML_LINK_UAOL;
+ default:
+ return HDA_BUS_ML_LINK_OTHER;
+ }
+}
+EXPORT_SYMBOL_NS(hda_bus_ml_link_get_type, "SND_SOC_SOF_HDA_MLINK");
+
int hda_bus_ml_resume(struct hdac_bus *bus)
{
struct hdac_ext_link *hlink;
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index 799e49539b4a..1609589929a1 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -523,6 +523,29 @@ struct sof_intel_hda_dev {
/* the maximum number of streams (playback + capture) supported */
u32 stream_max;
+ /*
+ * ACE2+ link DMA stream allocation constraints (stream index =
+ * stream_tag - 1, shared between input and output directions). All
+ * masks are cleared by hda_dsp_ctrl_init_chip() on controller reset
+ * (CRST#).
+ *
+ * - Concurrent (cross-direction) constraint: a SoundWire stream and
+ * a HDA/iDisp/UAOL stream cannot share a physical stream index
+ * across directions, the resulting LLP/timestamp values are wrong.
+ * link_dma_active_sdw_mask and link_dma_active_multi_mask
+ * (indexed by SNDRV_PCM_STREAM_*) track currently allocated
+ * streams per direction in each of the conflicting groups; SSP
+ * and DMIC do not participate. Bits are cleared on stream release.
+ *
+ * - Sequential (playback only) constraint: once a HDA/iDisp link
+ * has used a playback stream index, that index cannot drive a
+ * non-HDA/iDisp link in the same direction until the next CRST#.
+ * link_dma_out_hda_used_mask records this.
+ */
+ u32 link_dma_active_sdw_mask[SNDRV_PCM_STREAM_LAST + 1];
+ u32 link_dma_active_multi_mask[SNDRV_PCM_STREAM_LAST + 1];
+ u32 link_dma_out_hda_used_mask;
+
/* PM related */
bool l1_disabled;/* is DMI link L1 disabled? */
@@ -1031,7 +1054,8 @@ struct hda_dai_widget_dma_ops {
struct snd_pcm_substream *substream);
struct hdac_ext_stream *(*assign_hext_stream)(struct snd_sof_dev *sdev,
struct snd_soc_dai *cpu_dai,
- struct snd_pcm_substream *substream);
+ struct snd_pcm_substream *substream,
+ struct hdac_ext_link *hlink);
void (*release_hext_stream)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
struct snd_pcm_substream *substream);
void (*setup_hext_stream)(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,
--
2.55.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-07-30 12:51 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-30 12:51 [PATCH 0/4] ASoC: SOF: Intel: Handle ACE2+ link DMA allocation restrictions Peter Ujfalusi
2026-07-30 12:51 ` [PATCH 1/4] ASoC: SOF: Intel: hda: Fold mlink enumeration into hda_dsp_ctrl_init_chip() Peter Ujfalusi
2026-07-30 12:51 ` [PATCH 2/4] ASoC: SOF: Intel: hda: Keep non-alt mlinks powered at probe on ACE2+ Peter Ujfalusi
2026-07-30 12:51 ` [PATCH 3/4] ASoC: SOF: Intel: hda: Remove unused hda_bus_ml_put_all() Peter Ujfalusi
2026-07-30 12:51 ` [PATCH 4/4] ASoC: SOF: Intel: hda: Avoid ACE2+ link DMA stream allocation hazards Peter Ujfalusi
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.