soundwire: intel_ace2x: add BPT send_async/wait callbacks

Add support for BTP API using Cadence and hda-sdw-bpt helpers.

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>
Tested-by: shumingf@realtek.com
Link: https://lore.kernel.org/r/20250227140615.8147-14-yung-chuan.liao@linux.intel.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by Pierre-Louis Bossart and committed by Vinod Koul 4c1ce9f3 5cdc2376

+312
+312
drivers/soundwire/intel_ace2x.c
··· 13 13 #include <linux/soundwire/sdw_intel.h> 14 14 #include <sound/hdaudio.h> 15 15 #include <sound/hda-mlink.h> 16 + #include <sound/hda-sdw-bpt.h> 16 17 #include <sound/hda_register.h> 17 18 #include <sound/pcm_params.h> 18 19 #include "cadence_master.h" 19 20 #include "bus.h" 20 21 #include "intel.h" 22 + 23 + static int sdw_slave_bpt_stream_add(struct sdw_slave *slave, struct sdw_stream_runtime *stream) 24 + { 25 + struct sdw_stream_config sconfig = {0}; 26 + struct sdw_port_config pconfig = {0}; 27 + int ret; 28 + 29 + /* arbitrary configuration */ 30 + sconfig.frame_rate = 16000; 31 + sconfig.ch_count = 1; 32 + sconfig.bps = 32; /* this is required for BPT/BRA */ 33 + sconfig.direction = SDW_DATA_DIR_RX; 34 + sconfig.type = SDW_STREAM_BPT; 35 + 36 + pconfig.num = 0; 37 + pconfig.ch_mask = BIT(0); 38 + 39 + ret = sdw_stream_add_slave(slave, &sconfig, &pconfig, 1, stream); 40 + if (ret) 41 + dev_err(&slave->dev, "%s: failed: %d\n", __func__, ret); 42 + 43 + return ret; 44 + } 45 + 46 + static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave *slave, 47 + struct sdw_bpt_msg *msg) 48 + { 49 + struct sdw_cdns *cdns = &sdw->cdns; 50 + struct sdw_bus *bus = &cdns->bus; 51 + struct sdw_master_prop *prop = &bus->prop; 52 + struct sdw_stream_runtime *stream; 53 + struct sdw_stream_config sconfig; 54 + struct sdw_port_config *pconfig; 55 + unsigned int pdi0_buffer_size; 56 + unsigned int tx_dma_bandwidth; 57 + unsigned int pdi1_buffer_size; 58 + unsigned int rx_dma_bandwidth; 59 + unsigned int data_per_frame; 60 + unsigned int tx_total_bytes; 61 + struct sdw_cdns_pdi *pdi0; 62 + struct sdw_cdns_pdi *pdi1; 63 + unsigned int num_frames; 64 + int command; 65 + int ret1; 66 + int ret; 67 + int dir; 68 + int i; 69 + 70 + stream = sdw_alloc_stream("BPT", SDW_STREAM_BPT); 71 + if (!stream) 72 + return -ENOMEM; 73 + 74 + cdns->bus.bpt_stream = stream; 75 + 76 + ret = sdw_slave_bpt_stream_add(slave, stream); 77 + if (ret < 0) 78 + goto release_stream; 79 + 80 + /* handle PDI0 first */ 81 + dir = SDW_DATA_DIR_TX; 82 + 83 + pdi0 = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, 1, dir, 0); 84 + if (!pdi0) { 85 + dev_err(cdns->dev, "%s: sdw_cdns_alloc_pdi0 failed\n", __func__); 86 + ret = -EINVAL; 87 + goto remove_slave; 88 + } 89 + 90 + sdw_cdns_config_stream(cdns, 1, dir, pdi0); 91 + 92 + /* handle PDI1 */ 93 + dir = SDW_DATA_DIR_RX; 94 + 95 + pdi1 = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, 1, dir, 1); 96 + if (!pdi1) { 97 + dev_err(cdns->dev, "%s: sdw_cdns_alloc_pdi1 failed\n", __func__); 98 + ret = -EINVAL; 99 + goto remove_slave; 100 + } 101 + 102 + sdw_cdns_config_stream(cdns, 1, dir, pdi1); 103 + 104 + /* 105 + * the port config direction, number of channels and frame 106 + * rate is totally arbitrary 107 + */ 108 + sconfig.direction = dir; 109 + sconfig.ch_count = 1; 110 + sconfig.frame_rate = 16000; 111 + sconfig.type = SDW_STREAM_BPT; 112 + sconfig.bps = 32; /* this is required for BPT/BRA */ 113 + 114 + /* Port configuration */ 115 + pconfig = kcalloc(2, sizeof(*pconfig), GFP_KERNEL); 116 + if (!pconfig) { 117 + ret = -ENOMEM; 118 + goto remove_slave; 119 + } 120 + 121 + for (i = 0; i < 2 /* num_pdi */; i++) { 122 + pconfig[i].num = i; 123 + pconfig[i].ch_mask = 1; 124 + } 125 + 126 + ret = sdw_stream_add_master(&cdns->bus, &sconfig, pconfig, 2, stream); 127 + kfree(pconfig); 128 + 129 + if (ret < 0) { 130 + dev_err(cdns->dev, "add master to stream failed:%d\n", ret); 131 + goto remove_slave; 132 + } 133 + 134 + ret = sdw_prepare_stream(cdns->bus.bpt_stream); 135 + if (ret < 0) 136 + goto remove_master; 137 + 138 + command = (msg->flags & SDW_MSG_FLAG_WRITE) ? 0 : 1; 139 + 140 + ret = sdw_cdns_bpt_find_buffer_sizes(command, cdns->bus.params.row, cdns->bus.params.col, 141 + msg->len, SDW_BPT_MSG_MAX_BYTES, &data_per_frame, 142 + &pdi0_buffer_size, &pdi1_buffer_size, &num_frames); 143 + if (ret < 0) 144 + goto deprepare_stream; 145 + 146 + sdw->bpt_ctx.pdi0_buffer_size = pdi0_buffer_size; 147 + sdw->bpt_ctx.pdi1_buffer_size = pdi1_buffer_size; 148 + sdw->bpt_ctx.num_frames = num_frames; 149 + sdw->bpt_ctx.data_per_frame = data_per_frame; 150 + tx_dma_bandwidth = div_u64((u64)pdi0_buffer_size * 8 * (u64)prop->default_frame_rate, 151 + num_frames); 152 + rx_dma_bandwidth = div_u64((u64)pdi1_buffer_size * 8 * (u64)prop->default_frame_rate, 153 + num_frames); 154 + 155 + dev_dbg(cdns->dev, "Message len %d transferred in %d frames (%d per frame)\n", 156 + msg->len, num_frames, data_per_frame); 157 + dev_dbg(cdns->dev, "sizes pdi0 %d pdi1 %d tx_bandwidth %d rx_bandwidth %d\n", 158 + pdi0_buffer_size, pdi1_buffer_size, tx_dma_bandwidth, rx_dma_bandwidth); 159 + 160 + ret = hda_sdw_bpt_open(cdns->dev->parent, /* PCI device */ 161 + sdw->instance, &sdw->bpt_ctx.bpt_tx_stream, 162 + &sdw->bpt_ctx.dmab_tx_bdl, pdi0_buffer_size, tx_dma_bandwidth, 163 + &sdw->bpt_ctx.bpt_rx_stream, &sdw->bpt_ctx.dmab_rx_bdl, 164 + pdi1_buffer_size, rx_dma_bandwidth); 165 + if (ret < 0) { 166 + dev_err(cdns->dev, "%s: hda_sdw_bpt_open failed %d\n", __func__, ret); 167 + goto deprepare_stream; 168 + } 169 + 170 + if (!command) { 171 + ret = sdw_cdns_prepare_write_dma_buffer(msg->dev_num, msg->addr, msg->buf, 172 + msg->len, data_per_frame, 173 + sdw->bpt_ctx.dmab_tx_bdl.area, 174 + pdi0_buffer_size, &tx_total_bytes); 175 + } else { 176 + ret = sdw_cdns_prepare_read_dma_buffer(msg->dev_num, msg->addr, msg->len, 177 + data_per_frame, 178 + sdw->bpt_ctx.dmab_tx_bdl.area, 179 + pdi0_buffer_size, &tx_total_bytes); 180 + } 181 + 182 + if (!ret) 183 + return 0; 184 + 185 + dev_err(cdns->dev, "%s: sdw_prepare_%s_dma_buffer failed %d\n", 186 + __func__, command ? "read" : "write", ret); 187 + 188 + ret1 = hda_sdw_bpt_close(cdns->dev->parent, /* PCI device */ 189 + sdw->bpt_ctx.bpt_tx_stream, &sdw->bpt_ctx.dmab_tx_bdl, 190 + sdw->bpt_ctx.bpt_rx_stream, &sdw->bpt_ctx.dmab_rx_bdl); 191 + if (ret1 < 0) 192 + dev_err(cdns->dev, "%s: hda_sdw_bpt_close failed: ret %d\n", 193 + __func__, ret1); 194 + 195 + deprepare_stream: 196 + sdw_deprepare_stream(cdns->bus.bpt_stream); 197 + 198 + remove_master: 199 + ret1 = sdw_stream_remove_master(&cdns->bus, cdns->bus.bpt_stream); 200 + if (ret1 < 0) 201 + dev_err(cdns->dev, "%s: remove master failed: %d\n", 202 + __func__, ret1); 203 + 204 + remove_slave: 205 + ret1 = sdw_stream_remove_slave(slave, cdns->bus.bpt_stream); 206 + if (ret1 < 0) 207 + dev_err(cdns->dev, "%s: remove slave failed: %d\n", 208 + __func__, ret1); 209 + 210 + release_stream: 211 + sdw_release_stream(cdns->bus.bpt_stream); 212 + cdns->bus.bpt_stream = NULL; 213 + 214 + return ret; 215 + } 216 + 217 + static void intel_ace2x_bpt_close_stream(struct sdw_intel *sdw, struct sdw_slave *slave, 218 + struct sdw_bpt_msg *msg) 219 + { 220 + struct sdw_cdns *cdns = &sdw->cdns; 221 + int ret; 222 + 223 + ret = hda_sdw_bpt_close(cdns->dev->parent /* PCI device */, sdw->bpt_ctx.bpt_tx_stream, 224 + &sdw->bpt_ctx.dmab_tx_bdl, sdw->bpt_ctx.bpt_rx_stream, 225 + &sdw->bpt_ctx.dmab_rx_bdl); 226 + if (ret < 0) 227 + dev_err(cdns->dev, "%s: hda_sdw_bpt_close failed: ret %d\n", 228 + __func__, ret); 229 + 230 + ret = sdw_deprepare_stream(cdns->bus.bpt_stream); 231 + if (ret < 0) 232 + dev_err(cdns->dev, "%s: sdw_deprepare_stream failed: ret %d\n", 233 + __func__, ret); 234 + 235 + ret = sdw_stream_remove_master(&cdns->bus, cdns->bus.bpt_stream); 236 + if (ret < 0) 237 + dev_err(cdns->dev, "%s: remove master failed: %d\n", 238 + __func__, ret); 239 + 240 + ret = sdw_stream_remove_slave(slave, cdns->bus.bpt_stream); 241 + if (ret < 0) 242 + dev_err(cdns->dev, "%s: remove slave failed: %d\n", 243 + __func__, ret); 244 + 245 + cdns->bus.bpt_stream = NULL; 246 + } 247 + 248 + #define INTEL_BPT_MSG_BYTE_ALIGNMENT 32 249 + 250 + static int intel_ace2x_bpt_send_async(struct sdw_intel *sdw, struct sdw_slave *slave, 251 + struct sdw_bpt_msg *msg) 252 + { 253 + struct sdw_cdns *cdns = &sdw->cdns; 254 + int ret; 255 + 256 + if (msg->len % INTEL_BPT_MSG_BYTE_ALIGNMENT) { 257 + dev_err(cdns->dev, "BPT message length %d is not a multiple of %d bytes\n", 258 + msg->len, INTEL_BPT_MSG_BYTE_ALIGNMENT); 259 + return -EINVAL; 260 + } 261 + 262 + dev_dbg(cdns->dev, "BPT Transfer start\n"); 263 + 264 + ret = intel_ace2x_bpt_open_stream(sdw, slave, msg); 265 + if (ret < 0) 266 + return ret; 267 + 268 + ret = hda_sdw_bpt_send_async(cdns->dev->parent, /* PCI device */ 269 + sdw->bpt_ctx.bpt_tx_stream, sdw->bpt_ctx.bpt_rx_stream); 270 + if (ret < 0) { 271 + dev_err(cdns->dev, "%s: hda_sdw_bpt_send_async failed: %d\n", 272 + __func__, ret); 273 + 274 + intel_ace2x_bpt_close_stream(sdw, slave, msg); 275 + 276 + return ret; 277 + } 278 + 279 + ret = sdw_enable_stream(cdns->bus.bpt_stream); 280 + if (ret < 0) { 281 + dev_err(cdns->dev, "%s: sdw_stream_enable failed: %d\n", 282 + __func__, ret); 283 + intel_ace2x_bpt_close_stream(sdw, slave, msg); 284 + } 285 + 286 + return ret; 287 + } 288 + 289 + static int intel_ace2x_bpt_wait(struct sdw_intel *sdw, struct sdw_slave *slave, 290 + struct sdw_bpt_msg *msg) 291 + { 292 + struct sdw_cdns *cdns = &sdw->cdns; 293 + int ret; 294 + 295 + dev_dbg(cdns->dev, "BPT Transfer wait\n"); 296 + 297 + ret = hda_sdw_bpt_wait(cdns->dev->parent, /* PCI device */ 298 + sdw->bpt_ctx.bpt_tx_stream, sdw->bpt_ctx.bpt_rx_stream); 299 + if (ret < 0) 300 + dev_err(cdns->dev, "%s: hda_sdw_bpt_wait failed: %d\n", __func__, ret); 301 + 302 + ret = sdw_disable_stream(cdns->bus.bpt_stream); 303 + if (ret < 0) { 304 + dev_err(cdns->dev, "%s: sdw_stream_enable failed: %d\n", 305 + __func__, ret); 306 + goto err; 307 + } 308 + 309 + if (msg->flags & SDW_MSG_FLAG_WRITE) { 310 + ret = sdw_cdns_check_write_response(cdns->dev, sdw->bpt_ctx.dmab_rx_bdl.area, 311 + sdw->bpt_ctx.pdi1_buffer_size, 312 + sdw->bpt_ctx.num_frames); 313 + if (ret < 0) 314 + dev_err(cdns->dev, "%s: BPT Write failed %d\n", __func__, ret); 315 + } else { 316 + ret = sdw_cdns_check_read_response(cdns->dev, sdw->bpt_ctx.dmab_rx_bdl.area, 317 + sdw->bpt_ctx.pdi1_buffer_size, 318 + msg->buf, msg->len, sdw->bpt_ctx.num_frames, 319 + sdw->bpt_ctx.data_per_frame); 320 + if (ret < 0) 321 + dev_err(cdns->dev, "%s: BPT Read failed %d\n", __func__, ret); 322 + } 323 + 324 + err: 325 + intel_ace2x_bpt_close_stream(sdw, slave, msg); 326 + 327 + return ret; 328 + } 21 329 22 330 /* 23 331 * shim vendor-specific (vs) ops ··· 1061 753 .sync_check_cmdsync_unlocked = intel_check_cmdsync_unlocked, 1062 754 1063 755 .program_sdi = intel_program_sdi, 756 + 757 + .bpt_send_async = intel_ace2x_bpt_send_async, 758 + .bpt_wait = intel_ace2x_bpt_wait, 1064 759 }; 1065 760 EXPORT_SYMBOL_NS(sdw_intel_lnl_hw_ops, "SOUNDWIRE_INTEL"); 1066 761 1067 762 MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK"); 763 + MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_SDW_BPT");