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 u32 cmd = msg->header; 60 61 /* send IPC message to DSP */ 62 sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, 63 msg->msg_size); 64 snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI, 65 cmd | HDA_DSP_REG_HIPCI_BUSY); 66 67 return 0; 68 } 69 70 void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev) 71 { 72 struct snd_sof_ipc_msg *msg = sdev->msg; 73 struct sof_ipc_reply reply; 74 struct sof_ipc_cmd_hdr *hdr; 75 unsigned long flags; 76 int ret = 0; 77 78 /* 79 * Sometimes, there is unexpected reply ipc arriving. The reply 80 * ipc belongs to none of the ipcs sent from driver. 81 * In this case, the driver must ignore the ipc. 82 */ 83 if (!msg) { 84 dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n"); 85 return; 86 } 87 spin_lock_irqsave(&sdev->ipc_lock, flags); 88 89 hdr = msg->msg_data; 90 if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE)) { 91 /* 92 * memory windows are powered off before sending IPC reply, 93 * so we can't read the mailbox for CTX_SAVE reply. 94 */ 95 reply.error = 0; 96 reply.hdr.cmd = SOF_IPC_GLB_REPLY; 97 reply.hdr.size = sizeof(reply); 98 memcpy(msg->reply_data, &reply, sizeof(reply)); 99 goto out; 100 } 101 102 /* get IPC reply from DSP in the mailbox */ 103 sof_mailbox_read(sdev, sdev->host_box.offset, &reply, 104 sizeof(reply)); 105 106 if (reply.error < 0) { 107 memcpy(msg->reply_data, &reply, sizeof(reply)); 108 ret = reply.error; 109 } else { 110 /* reply correct size ? */ 111 if (reply.hdr.size != msg->reply_size) { 112 dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", 113 msg->reply_size, reply.hdr.size); 114 ret = -EINVAL; 115 } 116 117 /* read the message */ 118 if (msg->reply_size > 0) 119 sof_mailbox_read(sdev, sdev->host_box.offset, 120 msg->reply_data, msg->reply_size); 121 } 122 123 out: 124 msg->reply_error = ret; 125 126 spin_unlock_irqrestore(&sdev->ipc_lock, flags); 127 } 128 129 static bool hda_dsp_ipc_is_sof(uint32_t msg) 130 { 131 return (msg & (HDA_DSP_IPC_PURGE_FW | 0xf << 9)) != msg || 132 (msg & HDA_DSP_IPC_PURGE_FW) != HDA_DSP_IPC_PURGE_FW; 133 } 134 135 /* IPC handler thread */ 136 irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) 137 { 138 struct snd_sof_dev *sdev = context; 139 irqreturn_t ret = IRQ_NONE; 140 u32 hipci; 141 u32 hipcie; 142 u32 hipct; 143 u32 hipcte; 144 u32 hipcctl; 145 u32 msg; 146 u32 msg_ext; 147 148 /* read IPC status */ 149 hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, 150 HDA_DSP_REG_HIPCIE); 151 hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT); 152 hipcctl = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL); 153 154 /* reenable IPC interrupt */ 155 snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, 156 HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); 157 158 /* is this a reply message from the DSP */ 159 if (hipcie & HDA_DSP_REG_HIPCIE_DONE && 160 hipcctl & HDA_DSP_REG_HIPCCTL_DONE) { 161 hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, 162 HDA_DSP_REG_HIPCI); 163 msg = hipci & HDA_DSP_REG_HIPCI_MSG_MASK; 164 msg_ext = hipcie & HDA_DSP_REG_HIPCIE_MSG_MASK; 165 166 dev_vdbg(sdev->dev, 167 "ipc: firmware response, msg:0x%x, msg_ext:0x%x\n", 168 msg, msg_ext); 169 170 /* mask Done interrupt */ 171 snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, 172 HDA_DSP_REG_HIPCCTL, 173 HDA_DSP_REG_HIPCCTL_DONE, 0); 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 ret = IRQ_HANDLED; 191 } 192 193 /* is this a new message from DSP */ 194 if (hipct & HDA_DSP_REG_HIPCT_BUSY && 195 hipcctl & HDA_DSP_REG_HIPCCTL_BUSY) { 196 197 hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, 198 HDA_DSP_REG_HIPCTE); 199 msg = hipct & HDA_DSP_REG_HIPCT_MSG_MASK; 200 msg_ext = hipcte & HDA_DSP_REG_HIPCTE_MSG_MASK; 201 202 dev_vdbg(sdev->dev, 203 "ipc: firmware initiated, msg:0x%x, msg_ext:0x%x\n", 204 msg, msg_ext); 205 206 /* mask BUSY interrupt */ 207 snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, 208 HDA_DSP_REG_HIPCCTL, 209 HDA_DSP_REG_HIPCCTL_BUSY, 0); 210 211 /* handle messages from DSP */ 212 if ((hipct & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { 213 /* this is a PANIC message !! */ 214 snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext)); 215 } else { 216 /* normal message - process normally */ 217 snd_sof_ipc_msgs_rx(sdev); 218 } 219 220 hda_dsp_ipc_host_done(sdev); 221 222 ret = IRQ_HANDLED; 223 } 224 225 return ret; 226 } 227 228 /* is this IRQ for ADSP ? - we only care about IPC here */ 229 irqreturn_t hda_dsp_ipc_irq_handler(int irq, void *context) 230 { 231 struct snd_sof_dev *sdev = context; 232 int ret = IRQ_NONE; 233 u32 irq_status; 234 235 spin_lock(&sdev->hw_lock); 236 237 /* store status */ 238 irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS); 239 dev_vdbg(sdev->dev, "irq handler: irq_status:0x%x\n", irq_status); 240 241 /* invalid message ? */ 242 if (irq_status == 0xffffffff) 243 goto out; 244 245 /* IPC message ? */ 246 if (irq_status & HDA_DSP_ADSPIS_IPC) { 247 /* disable IPC interrupt */ 248 snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, 249 HDA_DSP_REG_ADSPIC, 250 HDA_DSP_ADSPIC_IPC, 0); 251 ret = IRQ_WAKE_THREAD; 252 } 253 254 out: 255 spin_unlock(&sdev->hw_lock); 256 return ret; 257 } 258 259 /* IPC Firmware ready */ 260 261 static void ipc_get_windows(struct snd_sof_dev *sdev) 262 { 263 struct sof_ipc_window_elem *elem; 264 u32 outbox_offset = 0; 265 u32 stream_offset = 0; 266 u32 inbox_offset = 0; 267 u32 outbox_size = 0; 268 u32 stream_size = 0; 269 u32 inbox_size = 0; 270 int i; 271 272 if (!sdev->info_window) { 273 dev_err(sdev->dev, "error: have no window info\n"); 274 return; 275 } 276 277 for (i = 0; i < sdev->info_window->num_windows; i++) { 278 elem = &sdev->info_window->window[i]; 279 280 switch (elem->type) { 281 case SOF_IPC_REGION_UPBOX: 282 inbox_offset = 283 elem->offset + SRAM_WINDOW_OFFSET(elem->id); 284 inbox_size = elem->size; 285 snd_sof_debugfs_io_item(sdev, 286 sdev->bar[HDA_DSP_BAR] + 287 inbox_offset, 288 elem->size, "inbox", 289 SOF_DEBUGFS_ACCESS_D0_ONLY); 290 break; 291 case SOF_IPC_REGION_DOWNBOX: 292 outbox_offset = 293 elem->offset + SRAM_WINDOW_OFFSET(elem->id); 294 outbox_size = elem->size; 295 snd_sof_debugfs_io_item(sdev, 296 sdev->bar[HDA_DSP_BAR] + 297 outbox_offset, 298 elem->size, "outbox", 299 SOF_DEBUGFS_ACCESS_D0_ONLY); 300 break; 301 case SOF_IPC_REGION_TRACE: 302 snd_sof_debugfs_io_item(sdev, 303 sdev->bar[HDA_DSP_BAR] + 304 elem->offset + 305 SRAM_WINDOW_OFFSET 306 (elem->id), 307 elem->size, "etrace", 308 SOF_DEBUGFS_ACCESS_D0_ONLY); 309 break; 310 case SOF_IPC_REGION_DEBUG: 311 snd_sof_debugfs_io_item(sdev, 312 sdev->bar[HDA_DSP_BAR] + 313 elem->offset + 314 SRAM_WINDOW_OFFSET 315 (elem->id), 316 elem->size, "debug", 317 SOF_DEBUGFS_ACCESS_D0_ONLY); 318 break; 319 case SOF_IPC_REGION_STREAM: 320 stream_offset = 321 elem->offset + SRAM_WINDOW_OFFSET(elem->id); 322 stream_size = elem->size; 323 snd_sof_debugfs_io_item(sdev, 324 sdev->bar[HDA_DSP_BAR] + 325 elem->offset + 326 SRAM_WINDOW_OFFSET 327 (elem->id), 328 elem->size, "stream", 329 SOF_DEBUGFS_ACCESS_D0_ONLY); 330 break; 331 case SOF_IPC_REGION_REGS: 332 snd_sof_debugfs_io_item(sdev, 333 sdev->bar[HDA_DSP_BAR] + 334 elem->offset + 335 SRAM_WINDOW_OFFSET 336 (elem->id), 337 elem->size, "regs", 338 SOF_DEBUGFS_ACCESS_D0_ONLY); 339 break; 340 case SOF_IPC_REGION_EXCEPTION: 341 sdev->dsp_oops_offset = elem->offset + 342 SRAM_WINDOW_OFFSET(elem->id); 343 snd_sof_debugfs_io_item(sdev, 344 sdev->bar[HDA_DSP_BAR] + 345 elem->offset + 346 SRAM_WINDOW_OFFSET 347 (elem->id), 348 elem->size, "exception", 349 SOF_DEBUGFS_ACCESS_D0_ONLY); 350 break; 351 default: 352 dev_err(sdev->dev, "error: get illegal window info\n"); 353 return; 354 } 355 } 356 357 if (outbox_size == 0 || inbox_size == 0) { 358 dev_err(sdev->dev, "error: get illegal mailbox window\n"); 359 return; 360 } 361 362 snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size, 363 outbox_offset, outbox_size); 364 sdev->stream_box.offset = stream_offset; 365 sdev->stream_box.size = stream_size; 366 367 dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", 368 inbox_offset, inbox_size); 369 dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", 370 outbox_offset, outbox_size); 371 dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", 372 stream_offset, stream_size); 373 } 374 375 /* check for ABI compatibility and create memory windows on first boot */ 376 int hda_dsp_ipc_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) 377 { 378 struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; 379 u32 offset; 380 int ret; 381 382 /* mailbox must be on 4k boundary */ 383 offset = HDA_DSP_MBOX_UPLINK_OFFSET; 384 385 dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset 0x%x\n", 386 msg_id, offset); 387 388 /* no need to re-check version/ABI for subsequent boots */ 389 if (!sdev->first_boot) 390 return 0; 391 392 /* copy data from the DSP FW ready offset */ 393 sof_block_read(sdev, sdev->mmio_bar, offset, fw_ready, 394 sizeof(*fw_ready)); 395 396 /* make sure ABI version is compatible */ 397 ret = snd_sof_ipc_valid(sdev); 398 if (ret < 0) 399 return ret; 400 401 /* now check for extended data */ 402 snd_sof_fw_parse_ext_data(sdev, sdev->mmio_bar, 403 HDA_DSP_MBOX_UPLINK_OFFSET + 404 sizeof(struct sof_ipc_fw_ready)); 405 406 ipc_get_windows(sdev); 407 408 return 0; 409 } 410 411 void hda_ipc_msg_data(struct snd_sof_dev *sdev, 412 struct snd_pcm_substream *substream, 413 void *p, size_t sz) 414 { 415 if (!substream || !sdev->stream_box.size) { 416 sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz); 417 } else { 418 struct hdac_stream *hstream = substream->runtime->private_data; 419 struct sof_intel_hda_stream *hda_stream; 420 421 hda_stream = container_of(hstream, 422 struct sof_intel_hda_stream, 423 hda_stream.hstream); 424 425 /* The stream might already be closed */ 426 if (hstream) 427 sof_mailbox_read(sdev, hda_stream->stream.posn_offset, 428 p, sz); 429 } 430 } 431 432 int hda_ipc_pcm_params(struct snd_sof_dev *sdev, 433 struct snd_pcm_substream *substream, 434 const struct sof_ipc_pcm_params_reply *reply) 435 { 436 struct hdac_stream *hstream = substream->runtime->private_data; 437 struct sof_intel_hda_stream *hda_stream; 438 /* validate offset */ 439 size_t posn_offset = reply->posn_offset; 440 441 hda_stream = container_of(hstream, struct sof_intel_hda_stream, 442 hda_stream.hstream); 443 444 /* check for unaligned offset or overflow */ 445 if (posn_offset > sdev->stream_box.size || 446 posn_offset % sizeof(struct sof_ipc_stream_posn) != 0) 447 return -EINVAL; 448 449 hda_stream->stream.posn_offset = sdev->stream_box.offset + posn_offset; 450 451 dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu", 452 substream->stream, hda_stream->stream.posn_offset); 453 454 return 0; 455 } 456