root/sound/soc/sof/intel/hda-ipc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. hda_dsp_ipc_host_done
  2. hda_dsp_ipc_dsp_done
  3. hda_dsp_ipc_send_msg
  4. hda_dsp_ipc_get_reply
  5. hda_dsp_ipc_is_sof
  6. hda_dsp_ipc_irq_thread
  7. hda_dsp_ipc_irq_handler
  8. hda_dsp_ipc_get_mailbox_offset
  9. hda_dsp_ipc_get_window_offset
  10. hda_ipc_msg_data
  11. hda_ipc_pcm_params

   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 int hda_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev)
 270 {
 271         return HDA_DSP_MBOX_UPLINK_OFFSET;
 272 }
 273 
 274 int hda_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id)
 275 {
 276         return SRAM_WINDOW_OFFSET(id);
 277 }
 278 
 279 void hda_ipc_msg_data(struct snd_sof_dev *sdev,
 280                       struct snd_pcm_substream *substream,
 281                       void *p, size_t sz)
 282 {
 283         if (!substream || !sdev->stream_box.size) {
 284                 sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
 285         } else {
 286                 struct hdac_stream *hstream = substream->runtime->private_data;
 287                 struct sof_intel_hda_stream *hda_stream;
 288 
 289                 hda_stream = container_of(hstream,
 290                                           struct sof_intel_hda_stream,
 291                                           hda_stream.hstream);
 292 
 293                 /* The stream might already be closed */
 294                 if (hstream)
 295                         sof_mailbox_read(sdev, hda_stream->stream.posn_offset,
 296                                          p, sz);
 297         }
 298 }
 299 
 300 int hda_ipc_pcm_params(struct snd_sof_dev *sdev,
 301                        struct snd_pcm_substream *substream,
 302                        const struct sof_ipc_pcm_params_reply *reply)
 303 {
 304         struct hdac_stream *hstream = substream->runtime->private_data;
 305         struct sof_intel_hda_stream *hda_stream;
 306         /* validate offset */
 307         size_t posn_offset = reply->posn_offset;
 308 
 309         hda_stream = container_of(hstream, struct sof_intel_hda_stream,
 310                                   hda_stream.hstream);
 311 
 312         /* check for unaligned offset or overflow */
 313         if (posn_offset > sdev->stream_box.size ||
 314             posn_offset % sizeof(struct sof_ipc_stream_posn) != 0)
 315                 return -EINVAL;
 316 
 317         hda_stream->stream.posn_offset = sdev->stream_box.offset + posn_offset;
 318 
 319         dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu",
 320                 substream->stream, hda_stream->stream.posn_offset);
 321 
 322         return 0;
 323 }

/* [<][>][^][v][top][bottom][index][help] */