hda-ipc.c (10233B)
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) 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 <sound/sof/ipc4/header.h> 19#include "../ops.h" 20#include "hda.h" 21 22static void hda_dsp_ipc_host_done(struct snd_sof_dev *sdev) 23{ 24 /* 25 * tell DSP cmd is done - clear busy 26 * interrupt and send reply msg to dsp 27 */ 28 snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, 29 HDA_DSP_REG_HIPCT, 30 HDA_DSP_REG_HIPCT_BUSY, 31 HDA_DSP_REG_HIPCT_BUSY); 32 33 /* unmask BUSY interrupt */ 34 snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, 35 HDA_DSP_REG_HIPCCTL, 36 HDA_DSP_REG_HIPCCTL_BUSY, 37 HDA_DSP_REG_HIPCCTL_BUSY); 38} 39 40static void hda_dsp_ipc_dsp_done(struct snd_sof_dev *sdev) 41{ 42 /* 43 * set DONE bit - tell DSP we have received the reply msg 44 * from DSP, and processed it, don't send more reply to host 45 */ 46 snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, 47 HDA_DSP_REG_HIPCIE, 48 HDA_DSP_REG_HIPCIE_DONE, 49 HDA_DSP_REG_HIPCIE_DONE); 50 51 /* unmask Done interrupt */ 52 snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, 53 HDA_DSP_REG_HIPCCTL, 54 HDA_DSP_REG_HIPCCTL_DONE, 55 HDA_DSP_REG_HIPCCTL_DONE); 56} 57 58int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) 59{ 60 /* send IPC message to DSP */ 61 sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, 62 msg->msg_size); 63 snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI, 64 HDA_DSP_REG_HIPCI_BUSY); 65 66 return 0; 67} 68 69int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) 70{ 71 struct sof_ipc4_msg *msg_data = msg->msg_data; 72 73 /* send the message via mailbox */ 74 if (msg_data->data_size) 75 sof_mailbox_write(sdev, sdev->host_box.offset, msg_data->data_ptr, 76 msg_data->data_size); 77 78 snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE, msg_data->extension); 79 snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI, 80 msg_data->primary | HDA_DSP_REG_HIPCI_BUSY); 81 82 return 0; 83} 84 85void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev) 86{ 87 struct snd_sof_ipc_msg *msg = sdev->msg; 88 struct sof_ipc_reply reply; 89 struct sof_ipc_cmd_hdr *hdr; 90 91 /* 92 * Sometimes, there is unexpected reply ipc arriving. The reply 93 * ipc belongs to none of the ipcs sent from driver. 94 * In this case, the driver must ignore the ipc. 95 */ 96 if (!msg) { 97 dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n"); 98 return; 99 } 100 101 hdr = msg->msg_data; 102 if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE) || 103 hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) { 104 /* 105 * memory windows are powered off before sending IPC reply, 106 * so we can't read the mailbox for CTX_SAVE and PM_GATE 107 * replies. 108 */ 109 reply.error = 0; 110 reply.hdr.cmd = SOF_IPC_GLB_REPLY; 111 reply.hdr.size = sizeof(reply); 112 memcpy(msg->reply_data, &reply, sizeof(reply)); 113 114 msg->reply_error = 0; 115 } else { 116 snd_sof_ipc_get_reply(sdev); 117 } 118} 119 120irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context) 121{ 122 struct sof_ipc4_msg notification_data = {{ 0 }}; 123 struct snd_sof_dev *sdev = context; 124 bool ipc_irq = false; 125 u32 hipcie, hipct; 126 127 hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE); 128 if (hipcie & HDA_DSP_REG_HIPCIE_DONE) { 129 /* DSP received the message */ 130 snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL, 131 HDA_DSP_REG_HIPCCTL_DONE, 0); 132 hda_dsp_ipc_dsp_done(sdev); 133 134 ipc_irq = true; 135 } 136 137 hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT); 138 if (hipct & HDA_DSP_REG_HIPCT_BUSY) { 139 /* Message from DSP (reply or notification) */ 140 u32 hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, 141 HDA_DSP_REG_HIPCTE); 142 u32 primary = hipct & HDA_DSP_REG_HIPCT_MSG_MASK; 143 u32 extension = hipcte & HDA_DSP_REG_HIPCTE_MSG_MASK; 144 145 /* mask BUSY interrupt */ 146 snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL, 147 HDA_DSP_REG_HIPCCTL_BUSY, 0); 148 149 if (primary & SOF_IPC4_MSG_DIR_MASK) { 150 /* Reply received */ 151 struct sof_ipc4_msg *data = sdev->ipc->msg.reply_data; 152 153 data->primary = primary; 154 data->extension = extension; 155 156 spin_lock_irq(&sdev->ipc_lock); 157 158 snd_sof_ipc_get_reply(sdev); 159 snd_sof_ipc_reply(sdev, data->primary); 160 161 spin_unlock_irq(&sdev->ipc_lock); 162 } else { 163 /* Notification received */ 164 165 notification_data.primary = primary; 166 notification_data.extension = extension; 167 sdev->ipc->msg.rx_data = ¬ification_data; 168 snd_sof_ipc_msgs_rx(sdev); 169 sdev->ipc->msg.rx_data = NULL; 170 } 171 172 /* Let DSP know that we have finished processing the message */ 173 hda_dsp_ipc_host_done(sdev); 174 175 ipc_irq = true; 176 } 177 178 if (!ipc_irq) 179 /* This interrupt is not shared so no need to return IRQ_NONE. */ 180 dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n"); 181 182 return IRQ_HANDLED; 183} 184 185/* IPC handler thread */ 186irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) 187{ 188 struct snd_sof_dev *sdev = context; 189 u32 hipci; 190 u32 hipcie; 191 u32 hipct; 192 u32 hipcte; 193 u32 msg; 194 u32 msg_ext; 195 bool ipc_irq = false; 196 197 /* read IPC status */ 198 hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, 199 HDA_DSP_REG_HIPCIE); 200 hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT); 201 hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI); 202 hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCTE); 203 204 /* is this a reply message from the DSP */ 205 if (hipcie & HDA_DSP_REG_HIPCIE_DONE) { 206 msg = hipci & HDA_DSP_REG_HIPCI_MSG_MASK; 207 msg_ext = hipcie & HDA_DSP_REG_HIPCIE_MSG_MASK; 208 209 dev_vdbg(sdev->dev, 210 "ipc: firmware response, msg:0x%x, msg_ext:0x%x\n", 211 msg, msg_ext); 212 213 /* mask Done interrupt */ 214 snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, 215 HDA_DSP_REG_HIPCCTL, 216 HDA_DSP_REG_HIPCCTL_DONE, 0); 217 218 /* 219 * Make sure the interrupt thread cannot be preempted between 220 * waking up the sender and re-enabling the interrupt. Also 221 * protect against a theoretical race with sof_ipc_tx_message(): 222 * if the DSP is fast enough to receive an IPC message, reply to 223 * it, and the host interrupt processing calls this function on 224 * a different core from the one, where the sending is taking 225 * place, the message might not yet be marked as expecting a 226 * reply. 227 */ 228 spin_lock_irq(&sdev->ipc_lock); 229 230 /* handle immediate reply from DSP core */ 231 hda_dsp_ipc_get_reply(sdev); 232 snd_sof_ipc_reply(sdev, msg); 233 234 /* set the done bit */ 235 hda_dsp_ipc_dsp_done(sdev); 236 237 spin_unlock_irq(&sdev->ipc_lock); 238 239 ipc_irq = true; 240 } 241 242 /* is this a new message from DSP */ 243 if (hipct & HDA_DSP_REG_HIPCT_BUSY) { 244 msg = hipct & HDA_DSP_REG_HIPCT_MSG_MASK; 245 msg_ext = hipcte & HDA_DSP_REG_HIPCTE_MSG_MASK; 246 247 dev_vdbg(sdev->dev, 248 "ipc: firmware initiated, msg:0x%x, msg_ext:0x%x\n", 249 msg, msg_ext); 250 251 /* mask BUSY interrupt */ 252 snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, 253 HDA_DSP_REG_HIPCCTL, 254 HDA_DSP_REG_HIPCCTL_BUSY, 0); 255 256 /* handle messages from DSP */ 257 if ((hipct & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { 258 struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 259 bool non_recoverable = true; 260 261 /* 262 * This is a PANIC message! 263 * 264 * If it is arriving during firmware boot and it is not 265 * the last boot attempt then change the non_recoverable 266 * to false as the DSP might be able to boot in the next 267 * iteration(s) 268 */ 269 if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS && 270 hda->boot_iteration < HDA_FW_BOOT_ATTEMPTS) 271 non_recoverable = false; 272 273 snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext), 274 non_recoverable); 275 } else { 276 /* normal message - process normally */ 277 snd_sof_ipc_msgs_rx(sdev); 278 } 279 280 hda_dsp_ipc_host_done(sdev); 281 282 ipc_irq = true; 283 } 284 285 if (!ipc_irq) { 286 /* 287 * This interrupt is not shared so no need to return IRQ_NONE. 288 */ 289 dev_dbg_ratelimited(sdev->dev, 290 "nothing to do in IPC IRQ thread\n"); 291 } 292 293 return IRQ_HANDLED; 294} 295 296/* Check if an IPC IRQ occurred */ 297bool hda_dsp_check_ipc_irq(struct snd_sof_dev *sdev) 298{ 299 bool ret = false; 300 u32 irq_status; 301 302 /* store status */ 303 irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS); 304 dev_vdbg(sdev->dev, "irq handler: irq_status:0x%x\n", irq_status); 305 306 /* invalid message ? */ 307 if (irq_status == 0xffffffff) 308 goto out; 309 310 /* IPC message ? */ 311 if (irq_status & HDA_DSP_ADSPIS_IPC) 312 ret = true; 313 314out: 315 return ret; 316} 317 318int hda_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev) 319{ 320 return HDA_DSP_MBOX_UPLINK_OFFSET; 321} 322 323int hda_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id) 324{ 325 return SRAM_WINDOW_OFFSET(id); 326} 327 328int hda_ipc_msg_data(struct snd_sof_dev *sdev, 329 struct snd_pcm_substream *substream, 330 void *p, size_t sz) 331{ 332 if (!substream || !sdev->stream_box.size) { 333 sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz); 334 } else { 335 struct hdac_stream *hstream = substream->runtime->private_data; 336 struct sof_intel_hda_stream *hda_stream; 337 338 hda_stream = container_of(hstream, 339 struct sof_intel_hda_stream, 340 hext_stream.hstream); 341 342 /* The stream might already be closed */ 343 if (!hstream) 344 return -ESTRPIPE; 345 346 sof_mailbox_read(sdev, hda_stream->sof_intel_stream.posn_offset, p, sz); 347 } 348 349 return 0; 350} 351 352int hda_set_stream_data_offset(struct snd_sof_dev *sdev, 353 struct snd_pcm_substream *substream, 354 size_t posn_offset) 355{ 356 struct hdac_stream *hstream = substream->runtime->private_data; 357 struct sof_intel_hda_stream *hda_stream; 358 359 hda_stream = container_of(hstream, struct sof_intel_hda_stream, 360 hext_stream.hstream); 361 362 /* check for unaligned offset or overflow */ 363 if (posn_offset > sdev->stream_box.size || 364 posn_offset % sizeof(struct sof_ipc_stream_posn) != 0) 365 return -EINVAL; 366 367 hda_stream->sof_intel_stream.posn_offset = sdev->stream_box.offset + posn_offset; 368 369 dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu", 370 substream->stream, hda_stream->sof_intel_stream.posn_offset); 371 372 return 0; 373}