|
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"
|
21 | 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 | +} |
| 329 | + |
22 | 330 | /*
|
23 | 331 | * shim vendor-specific (vs) ops
|
24 | 332 | */
|
@@ -753,7 +1061,11 @@ const struct sdw_intel_hw_ops sdw_intel_lnl_hw_ops = {
|
753 | 1061 | .sync_check_cmdsync_unlocked = intel_check_cmdsync_unlocked,
|
754 | 1062 |
|
755 | 1063 | .program_sdi = intel_program_sdi,
|
| 1064 | + |
| 1065 | + .bpt_send_async = intel_ace2x_bpt_send_async, |
| 1066 | + .bpt_wait = intel_ace2x_bpt_wait, |
756 | 1067 | };
|
757 | 1068 | EXPORT_SYMBOL_NS(sdw_intel_lnl_hw_ops, "SOUNDWIRE_INTEL");
|
758 | 1069 |
|
759 | 1070 | MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK");
|
| 1071 | +MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_SDW_BPT"); |
0 commit comments