linux/sound/soc/sof/intel/hda-ipc.c
<<
>>
Prefs
   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
  21static 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
  39static 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
  57int 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
  68void 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
 119out:
 120        msg->reply_error = ret;
 121
 122}
 123
 124static 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 */
 131irqreturn_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 */
 239irqreturn_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
 264out:
 265        spin_unlock(&sdev->hw_lock);
 266        return ret;
 267}
 268
 269/* IPC Firmware ready */
 270
 271static 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 */
 386int 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
 421void 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
 442int 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