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 hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) { 88 /* 89 * memory windows are powered off before sending IPC reply, 90 * so we can't read the mailbox for CTX_SAVE and PM_GATE 91 * replies. 92 */ 93 reply.error = 0; 94 reply.hdr.cmd = SOF_IPC_GLB_REPLY; 95 reply.hdr.size = sizeof(reply); 96 memcpy(msg->reply_data, &reply, sizeof(reply)); 97 goto out; 98 } 99 100 /* get IPC reply from DSP in the mailbox */ 101 sof_mailbox_read(sdev, sdev->host_box.offset, &reply, 102 sizeof(reply)); 103 104 if (reply.error < 0) { 105 memcpy(msg->reply_data, &reply, sizeof(reply)); 106 ret = reply.error; 107 } else { 108 /* reply correct size ? */ 109 if (reply.hdr.size != msg->reply_size) { 110 dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", 111 msg->reply_size, reply.hdr.size); 112 ret = -EINVAL; 113 } 114 115 /* read the message */ 116 if (msg->reply_size > 0) 117 sof_mailbox_read(sdev, sdev->host_box.offset, 118 msg->reply_data, msg->reply_size); 119 } 120 121 out: 122 msg->reply_error = ret; 123 124 } 125 126 static bool hda_dsp_ipc_is_sof(uint32_t msg) 127 { 128 return (msg & (HDA_DSP_IPC_PURGE_FW | 0xf << 9)) != msg || 129 (msg & HDA_DSP_IPC_PURGE_FW) != HDA_DSP_IPC_PURGE_FW; 130 } 131 132 /* IPC handler thread */ 133 irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) 134 { 135 struct snd_sof_dev *sdev = context; 136 u32 hipci; 137 u32 hipcie; 138 u32 hipct; 139 u32 hipcte; 140 u32 msg; 141 u32 msg_ext; 142 bool ipc_irq = false; 143 144 /* read IPC status */ 145 hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, 146 HDA_DSP_REG_HIPCIE); 147 hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT); 148 hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI); 149 hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCTE); 150 151 /* is this a reply message from the DSP */ 152 if (hipcie & HDA_DSP_REG_HIPCIE_DONE) { 153 msg = hipci & HDA_DSP_REG_HIPCI_MSG_MASK; 154 msg_ext = hipcie & HDA_DSP_REG_HIPCIE_MSG_MASK; 155 156 dev_vdbg(sdev->dev, 157 "ipc: firmware response, msg:0x%x, msg_ext:0x%x\n", 158 msg, msg_ext); 159 160 /* mask Done interrupt */ 161 snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, 162 HDA_DSP_REG_HIPCCTL, 163 HDA_DSP_REG_HIPCCTL_DONE, 0); 164 165 /* 166 * Make sure the interrupt thread cannot be preempted between 167 * waking up the sender and re-enabling the interrupt. Also 168 * protect against a theoretical race with sof_ipc_tx_message(): 169 * if the DSP is fast enough to receive an IPC message, reply to 170 * it, and the host interrupt processing calls this function on 171 * a different core from the one, where the sending is taking 172 * place, the message might not yet be marked as expecting a 173 * reply. 174 */ 175 spin_lock_irq(&sdev->ipc_lock); 176 177 /* handle immediate reply from DSP core - ignore ROM messages */ 178 if (hda_dsp_ipc_is_sof(msg)) { 179 hda_dsp_ipc_get_reply(sdev); 180 snd_sof_ipc_reply(sdev, msg); 181 } 182 183 /* wake up sleeper if we are loading code */ 184 if (sdev->code_loading) { 185 sdev->code_loading = 0; 186 wake_up(&sdev->waitq); 187 } 188 189 /* set the done bit */ 190 hda_dsp_ipc_dsp_done(sdev); 191 192 spin_unlock_irq(&sdev->ipc_lock); 193 194 ipc_irq = true; 195 } 196 197 /* is this a new message from DSP */ 198 if (hipct & HDA_DSP_REG_HIPCT_BUSY) { 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 ipc_irq = true; 223 } 224 225 if (!ipc_irq) { 226 /* 227 * This interrupt is not shared so no need to return IRQ_NONE. 228 */ 229 dev_dbg_ratelimited(sdev->dev, 230 "nothing to do in IPC IRQ thread\n"); 231 } 232 233 /* re-enable IPC interrupt */ 234 snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, 235 HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); 236 237 return IRQ_HANDLED; 238 } 239 240 /* is this IRQ for ADSP ? - we only care about IPC here */ 241 irqreturn_t hda_dsp_ipc_irq_handler(int irq, void *context) 242 { 243 struct snd_sof_dev *sdev = context; 244 int ret = IRQ_NONE; 245 u32 irq_status; 246 247 spin_lock(&sdev->hw_lock); 248 249 /* store status */ 250 irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS); 251 dev_vdbg(sdev->dev, "irq handler: irq_status:0x%x\n", irq_status); 252 253 /* invalid message ? */ 254 if (irq_status == 0xffffffff) 255 goto out; 256 257 /* IPC message ? */ 258 if (irq_status & HDA_DSP_ADSPIS_IPC) { 259 /* disable IPC interrupt */ 260 snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, 261 HDA_DSP_REG_ADSPIC, 262 HDA_DSP_ADSPIC_IPC, 0); 263 ret = IRQ_WAKE_THREAD; 264 } 265 266 out: 267 spin_unlock(&sdev->hw_lock); 268 return ret; 269 } 270 271 int hda_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev) 272 { 273 return HDA_DSP_MBOX_UPLINK_OFFSET; 274 } 275 276 int hda_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id) 277 { 278 return SRAM_WINDOW_OFFSET(id); 279 } 280 281 void hda_ipc_msg_data(struct snd_sof_dev *sdev, 282 struct snd_pcm_substream *substream, 283 void *p, size_t sz) 284 { 285 if (!substream || !sdev->stream_box.size) { 286 sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz); 287 } else { 288 struct hdac_stream *hstream = substream->runtime->private_data; 289 struct sof_intel_hda_stream *hda_stream; 290 291 hda_stream = container_of(hstream, 292 struct sof_intel_hda_stream, 293 hda_stream.hstream); 294 295 /* The stream might already be closed */ 296 if (hstream) 297 sof_mailbox_read(sdev, hda_stream->stream.posn_offset, 298 p, sz); 299 } 300 } 301 302 int hda_ipc_pcm_params(struct snd_sof_dev *sdev, 303 struct snd_pcm_substream *substream, 304 const struct sof_ipc_pcm_params_reply *reply) 305 { 306 struct hdac_stream *hstream = substream->runtime->private_data; 307 struct sof_intel_hda_stream *hda_stream; 308 /* validate offset */ 309 size_t posn_offset = reply->posn_offset; 310 311 hda_stream = container_of(hstream, struct sof_intel_hda_stream, 312 hda_stream.hstream); 313 314 /* check for unaligned offset or overflow */ 315 if (posn_offset > sdev->stream_box.size || 316 posn_offset % sizeof(struct sof_ipc_stream_posn) != 0) 317 return -EINVAL; 318 319 hda_stream->stream.posn_offset = sdev->stream_box.offset + posn_offset; 320 321 dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu", 322 substream->stream, hda_stream->stream.posn_offset); 323 324 return 0; 325 } 326