ASoC: SOF: Intel: hda-sdw-bpt: add helpers for SoundWire BPT DMA

Add SoundWire BPT DMA helpers as a separate module to avoid circular
dependencies.

For now this assumes no link DMA, only coupled mode.

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>
Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Acked-by: Mark Brown <broonie@kernel.org>
Tested-by: shumingf@realtek.com
Link: https://lore.kernel.org/r/20250227140615.8147-12-yung-chuan.liao@linux.intel.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by Pierre-Louis Bossart and committed by Vinod Koul 5d5cb86f 7f17a73a

+399
+69
include/sound/hda-sdw-bpt.h
··· 1 + /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ 2 + /* 3 + * This file is provided under a dual BSD/GPLv2 license. When using or 4 + * redistributing this file, you may do so under either license. 5 + * 6 + * Copyright(c) 2025 Intel Corporation. 7 + */ 8 + 9 + #ifndef __HDA_SDW_BPT_H 10 + #define __HDA_SDW_BPT_H 11 + 12 + #include <linux/device.h> 13 + 14 + struct hdac_ext_stream; 15 + struct snd_dma_buffer; 16 + 17 + #if IS_ENABLED(CONFIG_SND_SOF_SOF_HDA_SDW_BPT) 18 + int hda_sdw_bpt_open(struct device *dev, int link_id, struct hdac_ext_stream **bpt_tx_stream, 19 + struct snd_dma_buffer *dmab_tx_bdl, u32 bpt_tx_num_bytes, 20 + u32 tx_dma_bandwidth, struct hdac_ext_stream **bpt_rx_stream, 21 + struct snd_dma_buffer *dmab_rx_bdl, u32 bpt_rx_num_bytes, 22 + u32 rx_dma_bandwidth); 23 + 24 + int hda_sdw_bpt_send_async(struct device *dev, struct hdac_ext_stream *bpt_tx_stream, 25 + struct hdac_ext_stream *bpt_rx_stream); 26 + 27 + int hda_sdw_bpt_wait(struct device *dev, struct hdac_ext_stream *bpt_tx_stream, 28 + struct hdac_ext_stream *bpt_rx_stream); 29 + 30 + int hda_sdw_bpt_close(struct device *dev, struct hdac_ext_stream *bpt_tx_stream, 31 + struct snd_dma_buffer *dmab_tx_bdl, struct hdac_ext_stream *bpt_rx_stream, 32 + struct snd_dma_buffer *dmab_rx_bdl); 33 + #else 34 + static inline int hda_sdw_bpt_open(struct device *dev, int link_id, 35 + struct hdac_ext_stream **bpt_tx_stream, 36 + struct snd_dma_buffer *dmab_tx_bdl, u32 bpt_tx_num_bytes, 37 + u32 tx_dma_bandwidth, struct hdac_ext_stream **bpt_rx_stream, 38 + struct snd_dma_buffer *dmab_rx_bdl, u32 bpt_rx_num_bytes, 39 + u32 rx_dma_bandwidth) 40 + { 41 + WARN_ONCE(1, "SoundWire BPT is disabled"); 42 + return -EOPNOTSUPP; 43 + } 44 + 45 + static inline int hda_sdw_bpt_send_async(struct device *dev, struct hdac_ext_stream *bpt_tx_stream, 46 + struct hdac_ext_stream *bpt_rx_stream) 47 + { 48 + WARN_ONCE(1, "SoundWire BPT is disabled"); 49 + return -EOPNOTSUPP; 50 + } 51 + 52 + static inline int hda_sdw_bpt_wait(struct device *dev, struct hdac_ext_stream *bpt_tx_stream, 53 + struct hdac_ext_stream *bpt_rx_stream) 54 + { 55 + WARN_ONCE(1, "SoundWire BPT is disabled"); 56 + return -EOPNOTSUPP; 57 + } 58 + 59 + static inline int hda_sdw_bpt_close(struct device *dev, struct hdac_ext_stream *bpt_tx_stream, 60 + struct snd_dma_buffer *dmab_tx_bdl, 61 + struct hdac_ext_stream *bpt_rx_stream, 62 + struct snd_dma_buffer *dmab_rx_bdl) 63 + { 64 + WARN_ONCE(1, "SoundWire BPT is disabled"); 65 + return -EOPNOTSUPP; 66 + } 67 + #endif 68 + 69 + #endif /* __HDA_SDW_BPT_H */
+7
sound/soc/sof/intel/Kconfig
··· 268 268 tristate 269 269 select SND_SOC_SOF_HDA_GENERIC 270 270 select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE 271 + select SND_SOF_SOF_HDA_SDW_BPT if SND_SOC_SOF_INTEL_SOUNDWIRE 271 272 select SND_SOC_SOF_IPC4 272 273 select SND_SOC_SOF_INTEL_MTL 273 274 ··· 342 341 If unsure select "N". 343 342 344 343 endif ## SND_SOC_SOF_HDA_GENERIC 344 + 345 + config SND_SOF_SOF_HDA_SDW_BPT 346 + tristate 347 + help 348 + This option is not user-selectable but automagically handled by 349 + 'select' statements at a higher level. 345 350 346 351 config SND_SOC_SOF_HDA_LINK_BASELINE 347 352 tristate
+4
sound/soc/sof/intel/Makefile
··· 12 12 13 13 snd-sof-intel-hda-mlink-y := hda-mlink.o 14 14 15 + snd-sof-intel-hda-sdw-bpt-objs := hda-sdw-bpt.o 16 + 15 17 snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-probes.o 16 18 17 19 snd-sof-intel-hda-y := hda-codec.o ··· 27 25 obj-$(CONFIG_SND_SOC_SOF_HDA_GENERIC) += snd-sof-intel-hda-generic.o 28 26 obj-$(CONFIG_SND_SOC_SOF_HDA_MLINK) += snd-sof-intel-hda-mlink.o 29 27 obj-$(CONFIG_SND_SOC_SOF_HDA) += snd-sof-intel-hda.o 28 + 29 + obj-$(CONFIG_SND_SOF_SOF_HDA_SDW_BPT) += snd-sof-intel-hda-sdw-bpt.o 30 30 31 31 snd-sof-pci-intel-tng-y := pci-tng.o 32 32 snd-sof-pci-intel-skl-y := pci-skl.o skl.o hda-loader-skl.o
+319
sound/soc/sof/intel/hda-sdw-bpt.c
··· 1 + // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 2 + // 3 + // This file is provided under a dual BSD/GPLv2 license. When using or 4 + // redistributing this file, you may do so under either license. 5 + // 6 + // Copyright(c) 2025 Intel Corporation. 7 + // 8 + 9 + /* 10 + * Hardware interface for SoundWire BPT support with HDA DMA 11 + */ 12 + 13 + #include <sound/hdaudio_ext.h> 14 + #include <sound/hda-mlink.h> 15 + #include <sound/hda-sdw-bpt.h> 16 + #include <sound/sof.h> 17 + #include "../ops.h" 18 + #include "../sof-priv.h" 19 + #include "hda.h" 20 + 21 + #define BPT_FREQUENCY 192000 /* The max rate defined in rate_bits[] hdac_device.c */ 22 + #define BPT_MULTIPLIER ((BPT_FREQUENCY / 48000) - 1) 23 + 24 + static int hda_sdw_bpt_dma_prepare(struct device *dev, struct hdac_ext_stream **sdw_bpt_stream, 25 + struct snd_dma_buffer *dmab_bdl, u32 bpt_num_bytes, 26 + unsigned int num_channels, int direction) 27 + { 28 + struct snd_sof_dev *sdev = dev_get_drvdata(dev); 29 + struct hdac_ext_stream *bpt_stream; 30 + unsigned int format = HDA_CL_STREAM_FORMAT; 31 + 32 + /* 33 + * the baseline format needs to be adjusted to 34 + * bandwidth requirements 35 + */ 36 + format |= (num_channels - 1); 37 + format |= BPT_MULTIPLIER << AC_FMT_MULT_SHIFT; 38 + 39 + dev_dbg(dev, "direction %d format_val %#x\n", direction, format); 40 + 41 + bpt_stream = hda_cl_prepare(dev, format, bpt_num_bytes, dmab_bdl, false, direction, false); 42 + if (IS_ERR(bpt_stream)) { 43 + dev_err(sdev->dev, "%s: SDW BPT DMA prepare failed: dir %d\n", 44 + __func__, direction); 45 + return PTR_ERR(bpt_stream); 46 + } 47 + *sdw_bpt_stream = bpt_stream; 48 + 49 + if (hdac_stream(bpt_stream)->direction == SNDRV_PCM_STREAM_PLAYBACK) { 50 + struct hdac_bus *bus = sof_to_bus(sdev); 51 + struct hdac_ext_link *hlink; 52 + int stream_tag; 53 + 54 + stream_tag = hdac_stream(bpt_stream)->stream_tag; 55 + hlink = hdac_bus_eml_sdw_get_hlink(bus); 56 + 57 + snd_hdac_ext_bus_link_set_stream_id(hlink, stream_tag); 58 + } 59 + return 0; 60 + } 61 + 62 + static int hda_sdw_bpt_dma_deprepare(struct device *dev, struct hdac_ext_stream *sdw_bpt_stream, 63 + struct snd_dma_buffer *dmab_bdl) 64 + { 65 + struct snd_sof_dev *sdev = dev_get_drvdata(dev); 66 + int ret; 67 + 68 + ret = hda_cl_cleanup(sdev->dev, dmab_bdl, true, sdw_bpt_stream); 69 + if (ret < 0) { 70 + dev_err(sdev->dev, "%s: SDW BPT DMA cleanup failed\n", 71 + __func__); 72 + return ret; 73 + } 74 + 75 + if (hdac_stream(sdw_bpt_stream)->direction == SNDRV_PCM_STREAM_PLAYBACK) { 76 + struct hdac_bus *bus = sof_to_bus(sdev); 77 + struct hdac_ext_link *hlink; 78 + int stream_tag; 79 + 80 + stream_tag = hdac_stream(sdw_bpt_stream)->stream_tag; 81 + hlink = hdac_bus_eml_sdw_get_hlink(bus); 82 + 83 + snd_hdac_ext_bus_link_clear_stream_id(hlink, stream_tag); 84 + } 85 + 86 + return 0; 87 + } 88 + 89 + static int hda_sdw_bpt_dma_enable(struct device *dev, struct hdac_ext_stream *sdw_bpt_stream) 90 + { 91 + struct snd_sof_dev *sdev = dev_get_drvdata(dev); 92 + int ret; 93 + 94 + ret = hda_cl_trigger(sdev->dev, sdw_bpt_stream, SNDRV_PCM_TRIGGER_START); 95 + if (ret < 0) 96 + dev_err(sdev->dev, "%s: SDW BPT DMA trigger start failed\n", __func__); 97 + 98 + return ret; 99 + } 100 + 101 + static int hda_sdw_bpt_dma_disable(struct device *dev, struct hdac_ext_stream *sdw_bpt_stream) 102 + { 103 + struct snd_sof_dev *sdev = dev_get_drvdata(dev); 104 + int ret; 105 + 106 + ret = hda_cl_trigger(sdev->dev, sdw_bpt_stream, SNDRV_PCM_TRIGGER_STOP); 107 + if (ret < 0) 108 + dev_err(sdev->dev, "%s: SDW BPT DMA trigger stop failed\n", __func__); 109 + 110 + return ret; 111 + } 112 + 113 + int hda_sdw_bpt_open(struct device *dev, int link_id, struct hdac_ext_stream **bpt_tx_stream, 114 + struct snd_dma_buffer *dmab_tx_bdl, u32 bpt_tx_num_bytes, 115 + u32 tx_dma_bandwidth, struct hdac_ext_stream **bpt_rx_stream, 116 + struct snd_dma_buffer *dmab_rx_bdl, u32 bpt_rx_num_bytes, 117 + u32 rx_dma_bandwidth) 118 + { 119 + struct snd_sof_dev *sdev = dev_get_drvdata(dev); 120 + unsigned int num_channels_tx; 121 + unsigned int num_channels_rx; 122 + int ret1; 123 + int ret; 124 + 125 + num_channels_tx = DIV_ROUND_UP(tx_dma_bandwidth, BPT_FREQUENCY * 32); 126 + 127 + ret = hda_sdw_bpt_dma_prepare(dev, bpt_tx_stream, dmab_tx_bdl, bpt_tx_num_bytes, 128 + num_channels_tx, SNDRV_PCM_STREAM_PLAYBACK); 129 + if (ret < 0) { 130 + dev_err(dev, "%s: hda_sdw_bpt_dma_prepare failed for TX: %d\n", 131 + __func__, ret); 132 + return ret; 133 + } 134 + 135 + num_channels_rx = DIV_ROUND_UP(rx_dma_bandwidth, BPT_FREQUENCY * 32); 136 + 137 + ret = hda_sdw_bpt_dma_prepare(dev, bpt_rx_stream, dmab_rx_bdl, bpt_rx_num_bytes, 138 + num_channels_rx, SNDRV_PCM_STREAM_CAPTURE); 139 + if (ret < 0) { 140 + dev_err(dev, "%s: hda_sdw_bpt_dma_prepare failed for RX: %d\n", 141 + __func__, ret); 142 + 143 + ret1 = hda_sdw_bpt_dma_deprepare(dev, *bpt_tx_stream, dmab_tx_bdl); 144 + if (ret1 < 0) 145 + dev_err(dev, "%s: hda_sdw_bpt_dma_deprepare failed for TX: %d\n", 146 + __func__, ret1); 147 + return ret; 148 + } 149 + 150 + /* we need to map the channels in PCMSyCM registers */ 151 + ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, 152 + 0, /* cpu_dai->id -> PDI0 */ 153 + GENMASK(num_channels_tx - 1, 0), 154 + hdac_stream(*bpt_tx_stream)->stream_tag, 155 + SNDRV_PCM_STREAM_PLAYBACK); 156 + if (ret < 0) { 157 + dev_err(dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed for TX: %d\n", 158 + __func__, ret); 159 + goto close; 160 + } 161 + 162 + ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, 163 + 1, /* cpu_dai->id -> PDI1 */ 164 + GENMASK(num_channels_rx - 1, 0), 165 + hdac_stream(*bpt_rx_stream)->stream_tag, 166 + SNDRV_PCM_STREAM_CAPTURE); 167 + if (!ret) 168 + return 0; 169 + 170 + dev_err(dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed for RX: %d\n", 171 + __func__, ret); 172 + 173 + close: 174 + ret1 = hda_sdw_bpt_close(dev, *bpt_tx_stream, dmab_tx_bdl, *bpt_rx_stream, dmab_rx_bdl); 175 + if (ret1 < 0) 176 + dev_err(dev, "%s: hda_sdw_bpt_close failed: %d\n", 177 + __func__, ret1); 178 + 179 + return ret; 180 + } 181 + EXPORT_SYMBOL_NS(hda_sdw_bpt_open, "SND_SOC_SOF_INTEL_HDA_SDW_BPT"); 182 + 183 + int hda_sdw_bpt_send_async(struct device *dev, struct hdac_ext_stream *bpt_tx_stream, 184 + struct hdac_ext_stream *bpt_rx_stream) 185 + { 186 + int ret1; 187 + int ret; 188 + 189 + ret = hda_sdw_bpt_dma_enable(dev, bpt_tx_stream); 190 + if (ret < 0) { 191 + dev_err(dev, "%s: hda_sdw_bpt_dma_enable failed for TX: %d\n", 192 + __func__, ret); 193 + return ret; 194 + } 195 + 196 + ret = hda_sdw_bpt_dma_enable(dev, bpt_rx_stream); 197 + if (ret < 0) { 198 + dev_err(dev, "%s: hda_sdw_bpt_dma_enable failed for RX: %d\n", 199 + __func__, ret); 200 + 201 + ret1 = hda_sdw_bpt_dma_disable(dev, bpt_tx_stream); 202 + if (ret1 < 0) 203 + dev_err(dev, "%s: hda_sdw_bpt_dma_disable failed for TX: %d\n", 204 + __func__, ret1); 205 + } 206 + 207 + return ret; 208 + } 209 + EXPORT_SYMBOL_NS(hda_sdw_bpt_send_async, "SND_SOC_SOF_INTEL_HDA_SDW_BPT"); 210 + 211 + /* 212 + * 3s is several orders of magnitude larger than what is needed for a 213 + * typical firmware download. 214 + */ 215 + #define HDA_BPT_IOC_TIMEOUT_MS 3000 216 + 217 + int hda_sdw_bpt_wait(struct device *dev, struct hdac_ext_stream *bpt_tx_stream, 218 + struct hdac_ext_stream *bpt_rx_stream) 219 + { 220 + struct sof_intel_hda_stream *hda_tx_stream; 221 + struct sof_intel_hda_stream *hda_rx_stream; 222 + snd_pcm_uframes_t tx_position; 223 + snd_pcm_uframes_t rx_position; 224 + unsigned long time_tx_left; 225 + unsigned long time_rx_left; 226 + int ret = 0; 227 + int ret1; 228 + int i; 229 + 230 + hda_tx_stream = container_of(bpt_tx_stream, struct sof_intel_hda_stream, hext_stream); 231 + hda_rx_stream = container_of(bpt_rx_stream, struct sof_intel_hda_stream, hext_stream); 232 + 233 + time_tx_left = wait_for_completion_timeout(&hda_tx_stream->ioc, 234 + msecs_to_jiffies(HDA_BPT_IOC_TIMEOUT_MS)); 235 + if (!time_tx_left) { 236 + tx_position = hda_dsp_stream_get_position(hdac_stream(bpt_tx_stream), 237 + SNDRV_PCM_STREAM_PLAYBACK, false); 238 + dev_err(dev, "%s: SDW BPT TX DMA did not complete: %ld\n", 239 + __func__, tx_position); 240 + ret = -ETIMEDOUT; 241 + goto dma_disable; 242 + } 243 + 244 + /* Make sure the DMA is flushed */ 245 + i = 0; 246 + do { 247 + tx_position = hda_dsp_stream_get_position(hdac_stream(bpt_tx_stream), 248 + SNDRV_PCM_STREAM_PLAYBACK, false); 249 + usleep_range(1000, 1010); 250 + i++; 251 + } while (tx_position && i < HDA_BPT_IOC_TIMEOUT_MS); 252 + if (tx_position) { 253 + dev_err(dev, "%s: SDW BPT TX DMA position %ld was not cleared\n", 254 + __func__, tx_position); 255 + ret = -ETIMEDOUT; 256 + goto dma_disable; 257 + } 258 + 259 + /* the wait should be minimal here */ 260 + time_rx_left = wait_for_completion_timeout(&hda_rx_stream->ioc, 261 + msecs_to_jiffies(HDA_BPT_IOC_TIMEOUT_MS)); 262 + if (!time_rx_left) { 263 + rx_position = hda_dsp_stream_get_position(hdac_stream(bpt_rx_stream), 264 + SNDRV_PCM_STREAM_CAPTURE, false); 265 + dev_err(dev, "%s: SDW BPT RX DMA did not complete: %ld\n", 266 + __func__, rx_position); 267 + ret = -ETIMEDOUT; 268 + goto dma_disable; 269 + } 270 + 271 + /* Make sure the DMA is flushed */ 272 + i = 0; 273 + do { 274 + rx_position = hda_dsp_stream_get_position(hdac_stream(bpt_rx_stream), 275 + SNDRV_PCM_STREAM_CAPTURE, false); 276 + usleep_range(1000, 1010); 277 + i++; 278 + } while (rx_position && i < HDA_BPT_IOC_TIMEOUT_MS); 279 + if (rx_position) { 280 + dev_err(dev, "%s: SDW BPT RX DMA position %ld was not cleared\n", 281 + __func__, rx_position); 282 + ret = -ETIMEDOUT; 283 + goto dma_disable; 284 + } 285 + 286 + dma_disable: 287 + ret1 = hda_sdw_bpt_dma_disable(dev, bpt_rx_stream); 288 + if (!ret) 289 + ret = ret1; 290 + 291 + ret1 = hda_sdw_bpt_dma_disable(dev, bpt_tx_stream); 292 + if (!ret) 293 + ret = ret1; 294 + 295 + return ret; 296 + } 297 + EXPORT_SYMBOL_NS(hda_sdw_bpt_wait, "SND_SOC_SOF_INTEL_HDA_SDW_BPT"); 298 + 299 + int hda_sdw_bpt_close(struct device *dev, struct hdac_ext_stream *bpt_tx_stream, 300 + struct snd_dma_buffer *dmab_tx_bdl, struct hdac_ext_stream *bpt_rx_stream, 301 + struct snd_dma_buffer *dmab_rx_bdl) 302 + { 303 + int ret; 304 + int ret1; 305 + 306 + ret = hda_sdw_bpt_dma_deprepare(dev, bpt_rx_stream, dmab_rx_bdl); 307 + 308 + ret1 = hda_sdw_bpt_dma_deprepare(dev, bpt_tx_stream, dmab_tx_bdl); 309 + if (!ret) 310 + ret = ret1; 311 + 312 + return ret; 313 + } 314 + EXPORT_SYMBOL_NS(hda_sdw_bpt_close, "SND_SOC_SOF_INTEL_HDA_SDW_BPT"); 315 + 316 + MODULE_LICENSE("Dual BSD/GPL"); 317 + MODULE_DESCRIPTION("SOF helpers for HDaudio SoundWire BPT"); 318 + MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_COMMON"); 319 + MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK");