1 // SPDX-License-Identifier: (GPL-2.0 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) 2018 Intel Corporation. All rights reserved. 7 // 8 // Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com> 9 // Ranjani Sridharan <ranjani.sridharan@linux.intel.com> 10 // Rander Wang <rander.wang@intel.com> 11 // Keyon Jie <yang.jie@linux.intel.com> 12 // 13 14 /* 15 * Hardware interface for generic Intel audio DSP HDA IP 16 */ 17 18 #include "../ops.h" 19 #include "hda.h" 20 21 static void hda_dsp_ipc_host_done(struct snd_sof_dev *sdev) 22 { 23 /* 24 * tell DSP cmd is done - clear busy 25 * interrupt and send reply msg to dsp 26 */ 27 snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, 28 HDA_DSP_REG_HIPCT, 29 HDA_DSP_REG_HIPCT_BUSY, 30 HDA_DSP_REG_HIPCT_BUSY); 31 32 /* unmask BUSY interrupt */ 33 snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, 34 HDA_DSP_REG_HIPCCTL, 35 HDA_DSP_REG_HIPCCTL_BUSY, 36 HDA_DSP_REG_HIPCCTL_BUSY); 37 } 38 39 static void hda_dsp_ipc_dsp_done(struct snd_sof_dev *sdev) 40 { 41 /* 42 * set DONE bit - tell DSP we have received the reply msg 43 * from DSP, and processed it, don't send more reply to host 44 */ 45 snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, 46 HDA_DSP_REG_HIPCIE, 47 HDA_DSP_REG_HIPCIE_DONE, 48 HDA_DSP_REG_HIPCIE_DONE); 49 50 /* unmask Done interrupt */ 51 snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, 52 HDA_DSP_REG_HIPCCTL, 53 HDA_DSP_REG_HIPCCTL_DONE, 54 HDA_DSP_REG_HIPCCTL_DONE); 55 } 56 57 int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) 58 { 59 /* send IPC message to DSP */ 60 sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, 61 msg->msg_size); 62 snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI, 63 HDA_DSP_REG_HIPCI_BUSY); 64 65 return 0; 66 } 67 68 void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev) 69 { 70 struct snd_sof_ipc_msg *msg = sdev->msg; 71 struct sof_ipc_reply reply; 72 struct sof_ipc_cmd_hdr *hdr; 73 int ret = 0; 74 75 /* 76 * Sometimes, there is unexpected reply ipc arriving. The reply 77 * ipc belongs to none of the ipcs sent from driver. 78 * In this case, the driver must ignore the ipc. 79 */ 80 if (!msg) { 81 dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n"); 82 return; 83 } 84 85 hdr = msg->msg_data; 86 if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE)) { 87 /* 88 * memory windows are powered off before sending IPC reply, 89 * so we can't read the mailbox for CTX_SAVE reply. 90 */ 91 reply.error = 0; 92 reply.hdr.cmd = SOF_IPC_GLB_REPLY; 93 reply.hdr.size = sizeof(reply); 94 memcpy(msg->reply_data, &reply, sizeof(reply)); 95 goto out; 96 } 97 98 /* get IPC reply from DSP in the mailbox */ 99 sof_mailbox_read(sdev, sdev->host_box.offset, &reply, 100 sizeof(reply)); 101 102 if (reply.error < 0) { 103 memcpy(msg->reply_data, &reply, sizeof(reply)); 104 ret = reply.error; 105 } else { 106 /* reply correct size ? */ 107 if (reply.hdr.size != msg->reply_size) { 108 dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", 109 msg->reply_size, reply.hdr.size); 110 ret = -EINVAL; 111 } 112 113 /* read the message */ 114 if (msg->reply_size > 0) 115 sof_mailbox_read(sdev, sdev->host_box.offset, 116 msg->reply_data, msg->reply_size); 117 } 118 119 out: 120 msg->reply_error = ret; 121 122 } 123 124 static bool hda_dsp_ipc_is_sof(uint32_t msg) 125 { 126 return (msg & (HDA_DSP_IPC_PURGE_FW | 0xf << 9)) != msg || 127 (msg & HDA_DSP_IPC_PURGE_FW) != HDA_DSP_IPC_PURGE_FW; 128 } 129 130 /* IPC handler thread */ 131 irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) 132 { 133 struct snd_sof_dev *sdev = context; 134 u32 hipci; 135 u32 hipcie; 136 u32 hipct; 137 u32 hipcte; 138 u32 msg; 139 u32 msg_ext; 140 bool ipc_irq = false; 141 142 /* read IPC status */ 143 hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, 144 HDA_DSP_REG_HIPCIE); 145 hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT); 146 hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI); 147 hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCTE); 148 149 /* is this a reply message from the DSP */ 150 if (hipcie & HDA_DSP_REG_HIPCIE_DONE) { 151 msg = hipci & HDA_DSP_REG_HIPCI_MSG_MASK; 152 msg_ext = hipcie & HDA_DSP_REG_HIPCIE_MSG_MASK; 153 154 dev_vdbg(sdev->dev, 155 "ipc: firmware response, msg:0x%x, msg_ext:0x%x\n", 156 msg, msg_ext); 157 158 /* mask Done interrupt */ 159 snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, 160 HDA_DSP_REG_HIPCCTL, 161 HDA_DSP_REG_HIPCCTL_DONE, 0); 162 163 /* 164 * Make sure the interrupt thread cannot be preempted between 165 * waking up the sender and re-enabling the interrupt. Also 166 * protect against a theoretical race with sof_ipc_tx_message(): 167 * if the DSP is fast enough to receive an IPC message, reply to 168 * it, and the host interrupt processing calls this function on 169 * a different core from the one, where the sending is taking 170 * place, the message might not yet be marked as expecting a 171 * reply. 172 */ 173 spin_lock_irq(&sdev->ipc_lock); 174 175 /* handle immediate reply from DSP core - ignore ROM messages */ 176 if (hda_dsp_ipc_is_sof(msg)) { 177 hda_dsp_ipc_get_reply(sdev); 178 snd_sof_ipc_reply(sdev, msg); 179 } 180 181 /* wake up sleeper if we are loading code */ 182 if (sdev->code_loading) { 183 sdev->code_loading = 0; 184 wake_up(&sdev->waitq); 185 } 186 187 /* set the done bit */ 188 hda_dsp_ipc_dsp_done(sdev); 189 190 spin_unlock_irq(&sdev->ipc_lock); 191 192 ipc_irq = true; 193 } 194 195 /* is this a new message from DSP */ 196 if (hipct & HDA_DSP_REG_HIPCT_BUSY) { 197 msg = hipct & HDA_DSP_REG_HIPCT_MSG_MASK; 198 msg_ext = hipcte & HDA_DSP_REG_HIPCTE_MSG_MASK; 199 200 dev_vdbg(sdev->dev, 201 "ipc: firmware initiated, msg:0x%x, msg_ext:0x%x\n", 202 msg, msg_ext); 203 204 /* mask BUSY interrupt */ 205 snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, 206 HDA_DSP_REG_HIPCCTL, 207 HDA_DSP_REG_HIPCCTL_BUSY, 0); 208 209 /* handle messages from DSP */ 210 if ((hipct & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { 211 /* this is a PANIC message !! */ 212 snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext)); 213 } else { 214 /* normal message - process normally */ 215 snd_sof_ipc_msgs_rx(sdev); 216 } 217 218 hda_dsp_ipc_host_done(sdev); 219 220 ipc_irq = true; 221 } 222 223 if (!ipc_irq) { 224 /* 225 * This interrupt is not shared so no need to return IRQ_NONE. 226 */ 227 dev_dbg_ratelimited(sdev->dev, 228 "nothing to do in IPC IRQ thread\n"); 229 } 230 231 /* re-enable IPC interrupt */ 232 snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, 233 HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); 234 235 return IRQ_HANDLED; 236 } 237 238 /* is this IRQ for ADSP ? - we only care about IPC here */ 239 irqreturn_t hda_dsp_ipc_irq_handler(int irq, void *context) 240 { 241 struct snd_sof_dev *sdev = context; 242 int ret = IRQ_NONE; 243 u32 irq_status; 244 245 spin_lock(&sdev->hw_lock); 246 247 /* store status */ 248 irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS); 249 dev_vdbg(sdev->dev, "irq handler: irq_status:0x%x\n", irq_status); 250 251 /* invalid message ? */ 252 if (irq_status == 0xffffffff) 253 goto out; 254 255 /* IPC message ? */ 256 if (irq_status & HDA_DSP_ADSPIS_IPC) { 257 /* disable IPC interrupt */ 258 snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, 259 HDA_DSP_REG_ADSPIC, 260 HDA_DSP_ADSPIC_IPC, 0); 261 ret = IRQ_WAKE_THREAD; 262 } 263 264 out: 265 spin_unlock(&sdev->hw_lock); 266 return ret; 267 } 268 269 /* IPC Firmware ready */ 270 271 static void ipc_get_windows(struct snd_sof_dev *sdev) 272 { 273 struct sof_ipc_window_elem *elem; 274 u32 outbox_offset = 0; 275 u32 stream_offset = 0; 276 u32 inbox_offset = 0; 277 u32 outbox_size = 0; 278 u32 stream_size = 0; 279 u32 inbox_size = 0; 280 int i; 281 282 if (!sdev->info_window) { 283 dev_err(sdev->dev, "error: have no window info\n"); 284 return; 285 } 286 287 for (i = 0; i < sdev->info_window->num_windows; i++) { 288 elem = &sdev->info_window->window[i]; 289 290 switch (elem->type) { 291 case SOF_IPC_REGION_UPBOX: 292 inbox_offset = 293 elem->offset + SRAM_WINDOW_OFFSET(elem->id); 294 inbox_size = elem->size; 295 snd_sof_debugfs_io_item(sdev, 296 sdev->bar[HDA_DSP_BAR] + 297 inbox_offset, 298 elem->size, "inbox", 299 SOF_DEBUGFS_ACCESS_D0_ONLY); 300 break; 301 case SOF_IPC_REGION_DOWNBOX: 302 outbox_offset = 303 elem->offset + SRAM_WINDOW_OFFSET(elem->id); 304 outbox_size = elem->size; 305 snd_sof_debugfs_io_item(sdev, 306 sdev->bar[HDA_DSP_BAR] + 307 outbox_offset, 308 elem->size, "outbox", 309 SOF_DEBUGFS_ACCESS_D0_ONLY); 310 break; 311 case SOF_IPC_REGION_TRACE: 312 snd_sof_debugfs_io_item(sdev, 313 sdev->bar[HDA_DSP_BAR] + 314 elem->offset + 315 SRAM_WINDOW_OFFSET 316 (elem->id), 317 elem->size, "etrace", 318 SOF_DEBUGFS_ACCESS_D0_ONLY); 319 break; 320 case SOF_IPC_REGION_DEBUG: 321 snd_sof_debugfs_io_item(sdev, 322 sdev->bar[HDA_DSP_BAR] + 323 elem->offset + 324 SRAM_WINDOW_OFFSET 325 (elem->id), 326 elem->size, "debug", 327 SOF_DEBUGFS_ACCESS_D0_ONLY); 328 break; 329 case SOF_IPC_REGION_STREAM: 330 stream_offset = 331 elem->offset + SRAM_WINDOW_OFFSET(elem->id); 332 stream_size = elem->size; 333 snd_sof_debugfs_io_item(sdev, 334 sdev->bar[HDA_DSP_BAR] + 335 elem->offset + 336 SRAM_WINDOW_OFFSET 337 (elem->id), 338 elem->size, "stream", 339 SOF_DEBUGFS_ACCESS_D0_ONLY); 340 break; 341 case SOF_IPC_REGION_REGS: 342 snd_sof_debugfs_io_item(sdev, 343 sdev->bar[HDA_DSP_BAR] + 344 elem->offset + 345 SRAM_WINDOW_OFFSET 346 (elem->id), 347 elem->size, "regs", 348 SOF_DEBUGFS_ACCESS_D0_ONLY); 349 break; 350 case SOF_IPC_REGION_EXCEPTION: 351 sdev->dsp_oops_offset = elem->offset + 352 SRAM_WINDOW_OFFSET(elem->id); 353 snd_sof_debugfs_io_item(sdev, 354 sdev->bar[HDA_DSP_BAR] + 355 elem->offset + 356 SRAM_WINDOW_OFFSET 357 (elem->id), 358 elem->size, "exception", 359 SOF_DEBUGFS_ACCESS_D0_ONLY); 360 break; 361 default: 362 dev_err(sdev->dev, "error: get illegal window info\n"); 363 return; 364 } 365 } 366 367 if (outbox_size == 0 || inbox_size == 0) { 368 dev_err(sdev->dev, "error: get illegal mailbox window\n"); 369 return; 370 } 371 372 snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size, 373 outbox_offset, outbox_size); 374 sdev->stream_box.offset = stream_offset; 375 sdev->stream_box.size = stream_size; 376 377 dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", 378 inbox_offset, inbox_size); 379 dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", 380 outbox_offset, outbox_size); 381 dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", 382 stream_offset, stream_size); 383 } 384 385 /* check for ABI compatibility and create memory windows on first boot */ 386 int hda_dsp_ipc_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) 387 { 388 struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; 389 u32 offset; 390 int ret; 391 392 /* mailbox must be on 4k boundary */ 393 offset = HDA_DSP_MBOX_UPLINK_OFFSET; 394 395 dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset 0x%x\n", 396 msg_id, offset); 397 398 /* no need to re-check version/ABI for subsequent boots */ 399 if (!sdev->first_boot) 400 return 0; 401 402 /* copy data from the DSP FW ready offset */ 403 sof_block_read(sdev, sdev->mmio_bar, offset, fw_ready, 404 sizeof(*fw_ready)); 405 406 /* make sure ABI version is compatible */ 407 ret = snd_sof_ipc_valid(sdev); 408 if (ret < 0) 409 return ret; 410 411 /* now check for extended data */ 412 snd_sof_fw_parse_ext_data(sdev, sdev->mmio_bar, 413 HDA_DSP_MBOX_UPLINK_OFFSET + 414 sizeof(struct sof_ipc_fw_ready)); 415 416 ipc_get_windows(sdev); 417 418 return 0; 419 } 420 421 void hda_ipc_msg_data(struct snd_sof_dev *sdev, 422 struct snd_pcm_substream *substream, 423 void *p, size_t sz) 424 { 425 if (!substream || !sdev->stream_box.size) { 426 sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz); 427 } else { 428 struct hdac_stream *hstream = substream->runtime->private_data; 429 struct sof_intel_hda_stream *hda_stream; 430 431 hda_stream = container_of(hstream, 432 struct sof_intel_hda_stream, 433 hda_stream.hstream); 434 435 /* The stream might already be closed */ 436 if (hstream) 437 sof_mailbox_read(sdev, hda_stream->stream.posn_offset, 438 p, sz); 439 } 440 } 441 442 int hda_ipc_pcm_params(struct snd_sof_dev *sdev, 443 struct snd_pcm_substream *substream, 444 const struct sof_ipc_pcm_params_reply *reply) 445 { 446 struct hdac_stream *hstream = substream->runtime->private_data; 447 struct sof_intel_hda_stream *hda_stream; 448 /* validate offset */ 449 size_t posn_offset = reply->posn_offset; 450 451 hda_stream = container_of(hstream, struct sof_intel_hda_stream, 452 hda_stream.hstream); 453 454 /* check for unaligned offset or overflow */ 455 if (posn_offset > sdev->stream_box.size || 456 posn_offset % sizeof(struct sof_ipc_stream_posn) != 0) 457 return -EINVAL; 458 459 hda_stream->stream.posn_offset = sdev->stream_box.offset + posn_offset; 460 461 dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu", 462 substream->stream, hda_stream->stream.posn_offset); 463 464 return 0; 465 } 466