From e9b8b2a3a3e3d62c082caaa975312257ca492131 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 23 Oct 2025 13:25:40 +0800 Subject: [PATCH 1/3] ASoC: SOF: Intel: add hda_dsp_stream_pair_get/put helpers Currently, hda_dsp_stream_get/put are used to get/put the host dma. However, we may want to use a hda stream that both host and link dma are available. Add helper to find the hda stream and reserve/release it. Signed-off-by: Bard Liao --- sound/soc/sof/intel/hda-stream.c | 41 +++++++++++++++++++++++++++++--- sound/soc/sof/intel/hda.h | 3 +++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 9c3b3a9aaf83c9..85da1eb8f742e1 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -210,8 +210,8 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, } /* get next unused stream */ -struct hdac_ext_stream * -hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags) +static struct hdac_ext_stream * +_hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags, bool pair) { const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata); struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; @@ -233,7 +233,14 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags) if (hda_stream->host_reserved) continue; + if (pair && hext_stream->link_locked) + continue; + s->opened = true; + + if (pair) + hext_stream->link_locked = true; + break; } } @@ -264,14 +271,27 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags) return hext_stream; } +struct hdac_ext_stream * +hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags) +{ + return _hda_dsp_stream_get(sdev, direction, flags, false); +} + +struct hdac_ext_stream * +hda_dsp_stream_pair_get(struct snd_sof_dev *sdev, int direction, u32 flags) +{ + return _hda_dsp_stream_get(sdev, direction, flags, true); +} + /* free a stream */ -int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) +static int _hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag, bool pair) { const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata); struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct hdac_bus *bus = sof_to_bus(sdev); struct sof_intel_hda_stream *hda_stream; struct hdac_ext_stream *hext_stream; + struct hdac_ext_stream *link_stream; struct hdac_stream *s; bool dmi_l1_enable = true; bool found = false; @@ -292,6 +312,8 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) if (s->direction == direction && s->stream_tag == stream_tag) { s->opened = false; found = true; + if (pair) + link_stream = hext_stream; } else if (!(hda_stream->flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE)) { dmi_l1_enable = false; } @@ -312,9 +334,22 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) return -ENODEV; } + if (pair) + snd_hdac_ext_stream_release(link_stream, HDAC_EXT_STREAM_TYPE_LINK); + return 0; } +int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) +{ + return _hda_dsp_stream_put(sdev, direction, stream_tag, false); +} + +int hda_dsp_stream_pair_put(struct snd_sof_dev *sdev, int direction, int stream_tag) +{ + return _hda_dsp_stream_put(sdev, direction, stream_tag, true); +} + static int hda_dsp_stream_reset(struct snd_sof_dev *sdev, struct hdac_stream *hstream) { int sd_offset = SOF_STREAM_SD_OFFSET(hstream); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 562fe8be79c1e3..0b6340bba14603 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -694,7 +694,10 @@ u64 hda_dsp_get_stream_ldp(struct snd_sof_dev *sdev, struct hdac_ext_stream * hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags); +struct hdac_ext_stream * + hda_dsp_stream_pair_get(struct snd_sof_dev *sdev, int direction, u32 flags); int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag); +int hda_dsp_stream_pair_put(struct snd_sof_dev *sdev, int direction, int stream_tag); int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream, int enable, u32 size); From 26638be5e830d239e3f51da19b0139bc22c93bd5 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 4 Nov 2025 10:50:35 +0800 Subject: [PATCH 2/3] ASoC: SOF: Intel: add hda_dma_prepare/cleanup helpers SoundWire BPT stream needs to use link and host DMAs. Thus we need helpers to prepare and cleanup the link and host DMAs. Currently the SoundWire BPT stream uses hda_cl_prepare/cleanup helpers. It works fine because we assume the SwoundWire BPT will not run with audio streams simultaneously. The new helpers are copied from hda_cl_prepare/cleanup and add a flag to reserve the paired host and link DMAs. The new helpers will be used by both code loader and SoundWire BPT. Signed-off-by: Bard Liao --- sound/soc/sof/intel/hda-loader.c | 94 +------------------------ sound/soc/sof/intel/hda-stream.c | 116 +++++++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 8 +++ 3 files changed, 127 insertions(+), 91 deletions(-) diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 2cc11d8b0f7080..2b3abcf75d558b 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -53,65 +53,8 @@ hda_cl_prepare(struct device *dev, unsigned int format, unsigned int size, struct snd_dma_buffer *dmab, bool persistent_buffer, int direction, bool is_iccmax) { - struct snd_sof_dev *sdev = dev_get_drvdata(dev); - struct hdac_ext_stream *hext_stream; - struct hdac_stream *hstream; - int ret; - - hext_stream = hda_dsp_stream_get(sdev, direction, 0); - - if (!hext_stream) { - dev_err(sdev->dev, "error: no stream available\n"); - return ERR_PTR(-ENODEV); - } - hstream = &hext_stream->hstream; - hstream->substream = NULL; - - /* - * Allocate DMA buffer if it is temporary or if the buffer is intended - * to be persistent but not yet allocated. - * We cannot rely solely on !dmab->area as caller might use a struct on - * stack (when it is temporary) without clearing it to 0. - */ - if (!persistent_buffer || !dmab->area) { - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, dev, size, dmab); - if (ret < 0) { - dev_err(sdev->dev, "%s: memory alloc failed: %d\n", - __func__, ret); - goto out_put; - } - } - - hstream->period_bytes = 0;/* initialize period_bytes */ - hstream->format_val = format; - hstream->bufsize = size; - - if (is_iccmax) { - ret = hda_dsp_iccmax_stream_hw_params(sdev, hext_stream, dmab, NULL); - if (ret < 0) { - dev_err(sdev->dev, "error: iccmax stream prepare failed: %d\n", ret); - goto out_free; - } - } else { - ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL); - if (ret < 0) { - dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret); - goto out_free; - } - hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, size); - } - - return hext_stream; - -out_free: - snd_dma_free_pages(dmab); - dmab->area = NULL; - dmab->bytes = 0; - hstream->bufsize = 0; - hstream->format_val = 0; -out_put: - hda_dsp_stream_put(sdev, direction, hstream->stream_tag); - return ERR_PTR(ret); + return hda_data_stream_prepare(dev, format, size, dmab, persistent_buffer, + direction, is_iccmax, false); } EXPORT_SYMBOL_NS(hda_cl_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON"); @@ -275,38 +218,7 @@ EXPORT_SYMBOL_NS(hda_cl_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON"); int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab, bool persistent_buffer, struct hdac_ext_stream *hext_stream) { - struct snd_sof_dev *sdev = dev_get_drvdata(dev); - struct hdac_stream *hstream = &hext_stream->hstream; - int sd_offset = SOF_STREAM_SD_OFFSET(hstream); - int ret = 0; - - if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) - ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0); - else - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, - SOF_HDA_SD_CTL_DMA_START, 0); - - hda_dsp_stream_put(sdev, hstream->direction, hstream->stream_tag); - hstream->running = 0; - hstream->substream = NULL; - - /* reset BDL address */ - snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, - sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, 0); - snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, - sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 0); - - snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset, 0); - - if (!persistent_buffer) { - snd_dma_free_pages(dmab); - dmab->area = NULL; - dmab->bytes = 0; - hstream->bufsize = 0; - hstream->format_val = 0; - } - - return ret; + return hda_data_stream_cleanup(dev, dmab, persistent_buffer, hext_stream, false); } EXPORT_SYMBOL_NS(hda_cl_cleanup, "SND_SOC_SOF_INTEL_HDA_COMMON"); diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 85da1eb8f742e1..0915f01ebecf9b 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -1246,3 +1246,119 @@ u64 hda_dsp_get_stream_ldp(struct snd_sof_dev *sdev, return ((u64)ldp_u << 32) | ldp_l; } EXPORT_SYMBOL_NS(hda_dsp_get_stream_ldp, "SND_SOC_SOF_INTEL_HDA_COMMON"); + +struct hdac_ext_stream * +hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int size, + struct snd_dma_buffer *dmab, bool persistent_buffer, int direction, + bool is_iccmax, bool pair) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + struct hdac_ext_stream *hext_stream; + struct hdac_stream *hstream; + int ret; + + if (pair) + hext_stream = hda_dsp_stream_pair_get(sdev, direction, 0); + else + hext_stream = hda_dsp_stream_get(sdev, direction, 0); + + if (!hext_stream) { + dev_err(sdev->dev, "%s: no stream available\n", __func__); + return ERR_PTR(-ENODEV); + } + hstream = &hext_stream->hstream; + hstream->substream = NULL; + + /* + * Allocate DMA buffer if it is temporary or if the buffer is intended + * to be persistent but not yet allocated. + * We cannot rely solely on !dmab->area as caller might use a struct on + * stack (when it is temporary) without clearing it to 0. + */ + if (!persistent_buffer || !dmab->area) { + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, dev, size, dmab); + if (ret < 0) { + dev_err(sdev->dev, "%s: memory alloc failed: %d\n", + __func__, ret); + goto out_put; + } + } + + hstream->period_bytes = 0; /* initialize period_bytes */ + hstream->format_val = format; + hstream->bufsize = size; + + if (is_iccmax) { + ret = hda_dsp_iccmax_stream_hw_params(sdev, hext_stream, dmab, NULL); + if (ret < 0) { + dev_err(sdev->dev, "%s: iccmax stream prepare failed: %d\n", + __func__, ret); + goto out_free; + } + } else { + ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL); + if (ret < 0) { + dev_err(sdev->dev, "%s: hdac prepare failed: %d\n", __func__, ret); + goto out_free; + } + hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, size); + } + + return hext_stream; + +out_free: + snd_dma_free_pages(dmab); + dmab->area = NULL; + dmab->bytes = 0; + hstream->bufsize = 0; + hstream->format_val = 0; +out_put: + if (pair) + hda_dsp_stream_pair_put(sdev, direction, hstream->stream_tag); + else + hda_dsp_stream_put(sdev, direction, hstream->stream_tag); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_NS(hda_data_stream_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON"); + +int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab, + bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + struct hdac_stream *hstream = hdac_stream(hext_stream); + int sd_offset = SOF_STREAM_SD_OFFSET(hstream); + int ret = 0; + + if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) + ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0); + else + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, + SOF_HDA_SD_CTL_DMA_START, 0); + + if (pair) + hda_dsp_stream_pair_put(sdev, hstream->direction, hstream->stream_tag); + else + hda_dsp_stream_put(sdev, hstream->direction, hstream->stream_tag); + + hstream->running = 0; + hstream->substream = NULL; + + /* reset BDL address */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL, 0); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 0); + + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset, 0); + + if (!persistent_buffer) { + snd_dma_free_pages(dmab); + dmab->area = NULL; + dmab->bytes = 0; + hstream->bufsize = 0; + hstream->format_val = 0; + } + + return ret; +} +EXPORT_SYMBOL_NS(hda_data_stream_cleanup, "SND_SOC_SOF_INTEL_HDA_COMMON"); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 0b6340bba14603..8008c0b65ca741 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -905,6 +905,14 @@ int sdw_hda_dai_hw_free(struct snd_pcm_substream *substream, int sdw_hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai); +struct hdac_ext_stream * +hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int size, + struct snd_dma_buffer *dmab, bool persistent_buffer, int direction, + bool is_iccmax, bool pair); + +int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab, + bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair); + /* common dai driver */ extern struct snd_soc_dai_driver skl_dai[]; int hda_dsp_dais_suspend(struct snd_sof_dev *sdev); From 5558ee37dbfbf9dd461dbbfce95f5bdeaca66c12 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 27 Oct 2025 16:59:08 +0800 Subject: [PATCH 3/3] ASoC: SOF: Intel: hda-sdw-bpt: support simultaneous audio and BPT streams Currently the SoundWire BPT stream uses the paired link DMA but not reserve it. It works without any issue because we assume the SoundWire BPT will not run with audio streams simultaneously. To support simultaneous audio and BPT streams, we need to use the hda_dma_prepare/cleanup helpers to reserve the pair link host DMA. Signed-off-by: Bard Liao --- sound/soc/sof/intel/hda-sdw-bpt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda-sdw-bpt.c b/sound/soc/sof/intel/hda-sdw-bpt.c index ae2f8d55dbd087..728ffe7ae54d85 100644 --- a/sound/soc/sof/intel/hda-sdw-bpt.c +++ b/sound/soc/sof/intel/hda-sdw-bpt.c @@ -118,7 +118,8 @@ static int hda_sdw_bpt_dma_prepare(struct device *dev, struct hdac_ext_stream ** dev_dbg(dev, "direction %d format_val %#x\n", direction, format); - bpt_stream = hda_cl_prepare(dev, format, bpt_num_bytes, dmab_bdl, false, direction, false); + bpt_stream = hda_data_stream_prepare(dev, format, bpt_num_bytes, dmab_bdl, + false, direction, false, true); if (IS_ERR(bpt_stream)) { dev_err(sdev->dev, "%s: SDW BPT DMA prepare failed: dir %d\n", __func__, direction); @@ -162,7 +163,7 @@ static int hda_sdw_bpt_dma_deprepare(struct device *dev, struct hdac_ext_stream u32 mask; int ret; - ret = hda_cl_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream); + ret = hda_data_stream_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream, true); if (ret < 0) { dev_err(sdev->dev, "%s: SDW BPT DMA cleanup failed\n", __func__);