linux/sound/soc/sof/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// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
   9//
  10// Generic IPC layer that can work over MMIO and SPI/I2C. PHY layer provided
  11// by platform driver code.
  12//
  13
  14#include <linux/mutex.h>
  15#include <linux/types.h>
  16
  17#include "sof-priv.h"
  18#include "ops.h"
  19
  20/*
  21 * IPC message default size and timeout (ms).
  22 * TODO: allow platforms to set size and timeout.
  23 */
  24#define IPC_TIMEOUT_MS          300
  25
  26static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id);
  27static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd);
  28
  29/*
  30 * IPC message Tx/Rx message handling.
  31 */
  32
  33/* SOF generic IPC data */
  34struct snd_sof_ipc {
  35        struct snd_sof_dev *sdev;
  36
  37        /* protects messages and the disable flag */
  38        struct mutex tx_mutex;
  39        /* disables further sending of ipc's */
  40        bool disable_ipc_tx;
  41
  42        struct snd_sof_ipc_msg msg;
  43};
  44
  45struct sof_ipc_ctrl_data_params {
  46        size_t msg_bytes;
  47        size_t hdr_bytes;
  48        size_t pl_size;
  49        size_t elems;
  50        u32 num_msg;
  51        u8 *src;
  52        u8 *dst;
  53};
  54
  55#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC)
  56static void ipc_log_header(struct device *dev, u8 *text, u32 cmd)
  57{
  58        u8 *str;
  59        u8 *str2 = NULL;
  60        u32 glb;
  61        u32 type;
  62
  63        glb = cmd & SOF_GLB_TYPE_MASK;
  64        type = cmd & SOF_CMD_TYPE_MASK;
  65
  66        switch (glb) {
  67        case SOF_IPC_GLB_REPLY:
  68                str = "GLB_REPLY"; break;
  69        case SOF_IPC_GLB_COMPOUND:
  70                str = "GLB_COMPOUND"; break;
  71        case SOF_IPC_GLB_TPLG_MSG:
  72                str = "GLB_TPLG_MSG";
  73                switch (type) {
  74                case SOF_IPC_TPLG_COMP_NEW:
  75                        str2 = "COMP_NEW"; break;
  76                case SOF_IPC_TPLG_COMP_FREE:
  77                        str2 = "COMP_FREE"; break;
  78                case SOF_IPC_TPLG_COMP_CONNECT:
  79                        str2 = "COMP_CONNECT"; break;
  80                case SOF_IPC_TPLG_PIPE_NEW:
  81                        str2 = "PIPE_NEW"; break;
  82                case SOF_IPC_TPLG_PIPE_FREE:
  83                        str2 = "PIPE_FREE"; break;
  84                case SOF_IPC_TPLG_PIPE_CONNECT:
  85                        str2 = "PIPE_CONNECT"; break;
  86                case SOF_IPC_TPLG_PIPE_COMPLETE:
  87                        str2 = "PIPE_COMPLETE"; break;
  88                case SOF_IPC_TPLG_BUFFER_NEW:
  89                        str2 = "BUFFER_NEW"; break;
  90                case SOF_IPC_TPLG_BUFFER_FREE:
  91                        str2 = "BUFFER_FREE"; break;
  92                default:
  93                        str2 = "unknown type"; break;
  94                }
  95                break;
  96        case SOF_IPC_GLB_PM_MSG:
  97                str = "GLB_PM_MSG";
  98                switch (type) {
  99                case SOF_IPC_PM_CTX_SAVE:
 100                        str2 = "CTX_SAVE"; break;
 101                case SOF_IPC_PM_CTX_RESTORE:
 102                        str2 = "CTX_RESTORE"; break;
 103                case SOF_IPC_PM_CTX_SIZE:
 104                        str2 = "CTX_SIZE"; break;
 105                case SOF_IPC_PM_CLK_SET:
 106                        str2 = "CLK_SET"; break;
 107                case SOF_IPC_PM_CLK_GET:
 108                        str2 = "CLK_GET"; break;
 109                case SOF_IPC_PM_CLK_REQ:
 110                        str2 = "CLK_REQ"; break;
 111                case SOF_IPC_PM_CORE_ENABLE:
 112                        str2 = "CORE_ENABLE"; break;
 113                default:
 114                        str2 = "unknown type"; break;
 115                }
 116                break;
 117        case SOF_IPC_GLB_COMP_MSG:
 118                str = "GLB_COMP_MSG";
 119                switch (type) {
 120                case SOF_IPC_COMP_SET_VALUE:
 121                        str2 = "SET_VALUE"; break;
 122                case SOF_IPC_COMP_GET_VALUE:
 123                        str2 = "GET_VALUE"; break;
 124                case SOF_IPC_COMP_SET_DATA:
 125                        str2 = "SET_DATA"; break;
 126                case SOF_IPC_COMP_GET_DATA:
 127                        str2 = "GET_DATA"; break;
 128                default:
 129                        str2 = "unknown type"; break;
 130                }
 131                break;
 132        case SOF_IPC_GLB_STREAM_MSG:
 133                str = "GLB_STREAM_MSG";
 134                switch (type) {
 135                case SOF_IPC_STREAM_PCM_PARAMS:
 136                        str2 = "PCM_PARAMS"; break;
 137                case SOF_IPC_STREAM_PCM_PARAMS_REPLY:
 138                        str2 = "PCM_REPLY"; break;
 139                case SOF_IPC_STREAM_PCM_FREE:
 140                        str2 = "PCM_FREE"; break;
 141                case SOF_IPC_STREAM_TRIG_START:
 142                        str2 = "TRIG_START"; break;
 143                case SOF_IPC_STREAM_TRIG_STOP:
 144                        str2 = "TRIG_STOP"; break;
 145                case SOF_IPC_STREAM_TRIG_PAUSE:
 146                        str2 = "TRIG_PAUSE"; break;
 147                case SOF_IPC_STREAM_TRIG_RELEASE:
 148                        str2 = "TRIG_RELEASE"; break;
 149                case SOF_IPC_STREAM_TRIG_DRAIN:
 150                        str2 = "TRIG_DRAIN"; break;
 151                case SOF_IPC_STREAM_TRIG_XRUN:
 152                        str2 = "TRIG_XRUN"; break;
 153                case SOF_IPC_STREAM_POSITION:
 154                        str2 = "POSITION"; break;
 155                case SOF_IPC_STREAM_VORBIS_PARAMS:
 156                        str2 = "VORBIS_PARAMS"; break;
 157                case SOF_IPC_STREAM_VORBIS_FREE:
 158                        str2 = "VORBIS_FREE"; break;
 159                default:
 160                        str2 = "unknown type"; break;
 161                }
 162                break;
 163        case SOF_IPC_FW_READY:
 164                str = "FW_READY"; break;
 165        case SOF_IPC_GLB_DAI_MSG:
 166                str = "GLB_DAI_MSG";
 167                switch (type) {
 168                case SOF_IPC_DAI_CONFIG:
 169                        str2 = "CONFIG"; break;
 170                case SOF_IPC_DAI_LOOPBACK:
 171                        str2 = "LOOPBACK"; break;
 172                default:
 173                        str2 = "unknown type"; break;
 174                }
 175                break;
 176        case SOF_IPC_GLB_TRACE_MSG:
 177                str = "GLB_TRACE_MSG"; break;
 178        case SOF_IPC_GLB_TEST_MSG:
 179                str = "GLB_TEST_MSG";
 180                switch (type) {
 181                case SOF_IPC_TEST_IPC_FLOOD:
 182                        str2 = "IPC_FLOOD"; break;
 183                default:
 184                        str2 = "unknown type"; break;
 185                }
 186                break;
 187        default:
 188                str = "unknown GLB command"; break;
 189        }
 190
 191        if (str2)
 192                dev_dbg(dev, "%s: 0x%x: %s: %s\n", text, cmd, str, str2);
 193        else
 194                dev_dbg(dev, "%s: 0x%x: %s\n", text, cmd, str);
 195}
 196#else
 197static inline void ipc_log_header(struct device *dev, u8 *text, u32 cmd)
 198{
 199        if ((cmd & SOF_GLB_TYPE_MASK) != SOF_IPC_GLB_TRACE_MSG)
 200                dev_dbg(dev, "%s: 0x%x\n", text, cmd);
 201}
 202#endif
 203
 204/* wait for IPC message reply */
 205static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg,
 206                        void *reply_data)
 207{
 208        struct snd_sof_dev *sdev = ipc->sdev;
 209        struct sof_ipc_cmd_hdr *hdr = msg->msg_data;
 210        int ret;
 211
 212        /* wait for DSP IPC completion */
 213        ret = wait_event_timeout(msg->waitq, msg->ipc_complete,
 214                                 msecs_to_jiffies(IPC_TIMEOUT_MS));
 215
 216        if (ret == 0) {
 217                dev_err(sdev->dev, "error: ipc timed out for 0x%x size %d\n",
 218                        hdr->cmd, hdr->size);
 219                snd_sof_dsp_dbg_dump(ipc->sdev, SOF_DBG_REGS | SOF_DBG_MBOX);
 220                snd_sof_ipc_dump(ipc->sdev);
 221                snd_sof_trace_notify_for_error(ipc->sdev);
 222                ret = -ETIMEDOUT;
 223        } else {
 224                /* copy the data returned from DSP */
 225                ret = msg->reply_error;
 226                if (msg->reply_size)
 227                        memcpy(reply_data, msg->reply_data, msg->reply_size);
 228                if (ret < 0)
 229                        dev_err(sdev->dev, "error: ipc error for 0x%x size %zu\n",
 230                                hdr->cmd, msg->reply_size);
 231                else
 232                        ipc_log_header(sdev->dev, "ipc tx succeeded", hdr->cmd);
 233        }
 234
 235        return ret;
 236}
 237
 238/* send IPC message from host to DSP */
 239static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header,
 240                                       void *msg_data, size_t msg_bytes,
 241                                       void *reply_data, size_t reply_bytes)
 242{
 243        struct snd_sof_dev *sdev = ipc->sdev;
 244        struct snd_sof_ipc_msg *msg;
 245        int ret;
 246
 247        if (ipc->disable_ipc_tx)
 248                return -ENODEV;
 249
 250        /*
 251         * The spin-lock is also still needed to protect message objects against
 252         * other atomic contexts.
 253         */
 254        spin_lock_irq(&sdev->ipc_lock);
 255
 256        /* initialise the message */
 257        msg = &ipc->msg;
 258
 259        msg->header = header;
 260        msg->msg_size = msg_bytes;
 261        msg->reply_size = reply_bytes;
 262        msg->reply_error = 0;
 263
 264        /* attach any data */
 265        if (msg_bytes)
 266                memcpy(msg->msg_data, msg_data, msg_bytes);
 267
 268        sdev->msg = msg;
 269
 270        ret = snd_sof_dsp_send_msg(sdev, msg);
 271        /* Next reply that we receive will be related to this message */
 272        if (!ret)
 273                msg->ipc_complete = false;
 274
 275        spin_unlock_irq(&sdev->ipc_lock);
 276
 277        if (ret < 0) {
 278                /* So far IPC TX never fails, consider making the above void */
 279                dev_err_ratelimited(sdev->dev,
 280                                    "error: ipc tx failed with error %d\n",
 281                                    ret);
 282                return ret;
 283        }
 284
 285        ipc_log_header(sdev->dev, "ipc tx", msg->header);
 286
 287        /* now wait for completion */
 288        if (!ret)
 289                ret = tx_wait_done(ipc, msg, reply_data);
 290
 291        return ret;
 292}
 293
 294/* send IPC message from host to DSP */
 295int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header,
 296                       void *msg_data, size_t msg_bytes, void *reply_data,
 297                       size_t reply_bytes)
 298{
 299        int ret;
 300
 301        if (msg_bytes > SOF_IPC_MSG_MAX_SIZE ||
 302            reply_bytes > SOF_IPC_MSG_MAX_SIZE)
 303                return -ENOBUFS;
 304
 305        /* Serialise IPC TX */
 306        mutex_lock(&ipc->tx_mutex);
 307
 308        ret = sof_ipc_tx_message_unlocked(ipc, header, msg_data, msg_bytes,
 309                                          reply_data, reply_bytes);
 310
 311        mutex_unlock(&ipc->tx_mutex);
 312
 313        return ret;
 314}
 315EXPORT_SYMBOL(sof_ipc_tx_message);
 316
 317/* handle reply message from DSP */
 318int snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id)
 319{
 320        struct snd_sof_ipc_msg *msg = &sdev->ipc->msg;
 321
 322        if (msg->ipc_complete) {
 323                dev_err(sdev->dev, "error: no reply expected, received 0x%x",
 324                        msg_id);
 325                return -EINVAL;
 326        }
 327
 328        /* wake up and return the error if we have waiters on this message ? */
 329        msg->ipc_complete = true;
 330        wake_up(&msg->waitq);
 331
 332        return 0;
 333}
 334EXPORT_SYMBOL(snd_sof_ipc_reply);
 335
 336/* DSP firmware has sent host a message  */
 337void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
 338{
 339        struct sof_ipc_cmd_hdr hdr;
 340        u32 cmd, type;
 341        int err = 0;
 342
 343        /* read back header */
 344        snd_sof_ipc_msg_data(sdev, NULL, &hdr, sizeof(hdr));
 345        ipc_log_header(sdev->dev, "ipc rx", hdr.cmd);
 346
 347        cmd = hdr.cmd & SOF_GLB_TYPE_MASK;
 348        type = hdr.cmd & SOF_CMD_TYPE_MASK;
 349
 350        /* check message type */
 351        switch (cmd) {
 352        case SOF_IPC_GLB_REPLY:
 353                dev_err(sdev->dev, "error: ipc reply unknown\n");
 354                break;
 355        case SOF_IPC_FW_READY:
 356                /* check for FW boot completion */
 357                if (!sdev->boot_complete) {
 358                        err = sof_ops(sdev)->fw_ready(sdev, cmd);
 359                        if (err < 0) {
 360                                /*
 361                                 * this indicates a mismatch in ABI
 362                                 * between the driver and fw
 363                                 */
 364                                dev_err(sdev->dev, "error: ABI mismatch %d\n",
 365                                        err);
 366                        } else {
 367                                /* firmware boot completed OK */
 368                                sdev->boot_complete = true;
 369                        }
 370
 371                        /* wake up firmware loader */
 372                        wake_up(&sdev->boot_wait);
 373                }
 374                break;
 375        case SOF_IPC_GLB_COMPOUND:
 376        case SOF_IPC_GLB_TPLG_MSG:
 377        case SOF_IPC_GLB_PM_MSG:
 378        case SOF_IPC_GLB_COMP_MSG:
 379                break;
 380        case SOF_IPC_GLB_STREAM_MSG:
 381                /* need to pass msg id into the function */
 382                ipc_stream_message(sdev, hdr.cmd);
 383                break;
 384        case SOF_IPC_GLB_TRACE_MSG:
 385                ipc_trace_message(sdev, type);
 386                break;
 387        default:
 388                dev_err(sdev->dev, "error: unknown DSP message 0x%x\n", cmd);
 389                break;
 390        }
 391
 392        ipc_log_header(sdev->dev, "ipc rx done", hdr.cmd);
 393}
 394EXPORT_SYMBOL(snd_sof_ipc_msgs_rx);
 395
 396/*
 397 * IPC trace mechanism.
 398 */
 399
 400static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id)
 401{
 402        struct sof_ipc_dma_trace_posn posn;
 403
 404        switch (msg_id) {
 405        case SOF_IPC_TRACE_DMA_POSITION:
 406                /* read back full message */
 407                snd_sof_ipc_msg_data(sdev, NULL, &posn, sizeof(posn));
 408                snd_sof_trace_update_pos(sdev, &posn);
 409                break;
 410        default:
 411                dev_err(sdev->dev, "error: unhandled trace message %x\n",
 412                        msg_id);
 413                break;
 414        }
 415}
 416
 417/*
 418 * IPC stream position.
 419 */
 420
 421static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id)
 422{
 423        struct snd_sof_pcm_stream *stream;
 424        struct sof_ipc_stream_posn posn;
 425        struct snd_sof_pcm *spcm;
 426        int direction;
 427
 428        spcm = snd_sof_find_spcm_comp(sdev, msg_id, &direction);
 429        if (!spcm) {
 430                dev_err(sdev->dev,
 431                        "error: period elapsed for unknown stream, msg_id %d\n",
 432                        msg_id);
 433                return;
 434        }
 435
 436        stream = &spcm->stream[direction];
 437        snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
 438
 439        dev_dbg(sdev->dev, "posn : host 0x%llx dai 0x%llx wall 0x%llx\n",
 440                posn.host_posn, posn.dai_posn, posn.wallclock);
 441
 442        memcpy(&stream->posn, &posn, sizeof(posn));
 443
 444        /* only inform ALSA for period_wakeup mode */
 445        if (!stream->substream->runtime->no_period_wakeup)
 446                snd_sof_pcm_period_elapsed(stream->substream);
 447}
 448
 449/* DSP notifies host of an XRUN within FW */
 450static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id)
 451{
 452        struct snd_sof_pcm_stream *stream;
 453        struct sof_ipc_stream_posn posn;
 454        struct snd_sof_pcm *spcm;
 455        int direction;
 456
 457        spcm = snd_sof_find_spcm_comp(sdev, msg_id, &direction);
 458        if (!spcm) {
 459                dev_err(sdev->dev, "error: XRUN for unknown stream, msg_id %d\n",
 460                        msg_id);
 461                return;
 462        }
 463
 464        stream = &spcm->stream[direction];
 465        snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
 466
 467        dev_dbg(sdev->dev,  "posn XRUN: host %llx comp %d size %d\n",
 468                posn.host_posn, posn.xrun_comp_id, posn.xrun_size);
 469
 470#if defined(CONFIG_SND_SOC_SOF_DEBUG_XRUN_STOP)
 471        /* stop PCM on XRUN - used for pipeline debug */
 472        memcpy(&stream->posn, &posn, sizeof(posn));
 473        snd_pcm_stop_xrun(stream->substream);
 474#endif
 475}
 476
 477/* stream notifications from DSP FW */
 478static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd)
 479{
 480        /* get msg cmd type and msd id */
 481        u32 msg_type = msg_cmd & SOF_CMD_TYPE_MASK;
 482        u32 msg_id = SOF_IPC_MESSAGE_ID(msg_cmd);
 483
 484        switch (msg_type) {
 485        case SOF_IPC_STREAM_POSITION:
 486                ipc_period_elapsed(sdev, msg_id);
 487                break;
 488        case SOF_IPC_STREAM_TRIG_XRUN:
 489                ipc_xrun(sdev, msg_id);
 490                break;
 491        default:
 492                dev_err(sdev->dev, "error: unhandled stream message %x\n",
 493                        msg_id);
 494                break;
 495        }
 496}
 497
 498/* get stream position IPC - use faster MMIO method if available on platform */
 499int snd_sof_ipc_stream_posn(struct snd_sof_dev *sdev,
 500                            struct snd_sof_pcm *spcm, int direction,
 501                            struct sof_ipc_stream_posn *posn)
 502{
 503        struct sof_ipc_stream stream;
 504        int err;
 505
 506        /* read position via slower IPC */
 507        stream.hdr.size = sizeof(stream);
 508        stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_POSITION;
 509        stream.comp_id = spcm->stream[direction].comp_id;
 510
 511        /* send IPC to the DSP */
 512        err = sof_ipc_tx_message(sdev->ipc,
 513                                 stream.hdr.cmd, &stream, sizeof(stream), &posn,
 514                                 sizeof(*posn));
 515        if (err < 0) {
 516                dev_err(sdev->dev, "error: failed to get stream %d position\n",
 517                        stream.comp_id);
 518                return err;
 519        }
 520
 521        return 0;
 522}
 523EXPORT_SYMBOL(snd_sof_ipc_stream_posn);
 524
 525static int sof_get_ctrl_copy_params(enum sof_ipc_ctrl_type ctrl_type,
 526                                    struct sof_ipc_ctrl_data *src,
 527                                    struct sof_ipc_ctrl_data *dst,
 528                                    struct sof_ipc_ctrl_data_params *sparams)
 529{
 530        switch (ctrl_type) {
 531        case SOF_CTRL_TYPE_VALUE_CHAN_GET:
 532        case SOF_CTRL_TYPE_VALUE_CHAN_SET:
 533                sparams->src = (u8 *)src->chanv;
 534                sparams->dst = (u8 *)dst->chanv;
 535                break;
 536        case SOF_CTRL_TYPE_VALUE_COMP_GET:
 537        case SOF_CTRL_TYPE_VALUE_COMP_SET:
 538                sparams->src = (u8 *)src->compv;
 539                sparams->dst = (u8 *)dst->compv;
 540                break;
 541        case SOF_CTRL_TYPE_DATA_GET:
 542        case SOF_CTRL_TYPE_DATA_SET:
 543                sparams->src = (u8 *)src->data->data;
 544                sparams->dst = (u8 *)dst->data->data;
 545                break;
 546        default:
 547                return -EINVAL;
 548        }
 549
 550        /* calculate payload size and number of messages */
 551        sparams->pl_size = SOF_IPC_MSG_MAX_SIZE - sparams->hdr_bytes;
 552        sparams->num_msg = DIV_ROUND_UP(sparams->msg_bytes, sparams->pl_size);
 553
 554        return 0;
 555}
 556
 557static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev,
 558                                       struct sof_ipc_ctrl_data *cdata,
 559                                       struct sof_ipc_ctrl_data_params *sparams,
 560                                       bool send)
 561{
 562        struct sof_ipc_ctrl_data *partdata;
 563        size_t send_bytes;
 564        size_t offset = 0;
 565        size_t msg_bytes;
 566        size_t pl_size;
 567        int err;
 568        int i;
 569
 570        /* allocate max ipc size because we have at least one */
 571        partdata = kzalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
 572        if (!partdata)
 573                return -ENOMEM;
 574
 575        if (send)
 576                err = sof_get_ctrl_copy_params(cdata->type, cdata, partdata,
 577                                               sparams);
 578        else
 579                err = sof_get_ctrl_copy_params(cdata->type, partdata, cdata,
 580                                               sparams);
 581        if (err < 0)
 582                return err;
 583
 584        msg_bytes = sparams->msg_bytes;
 585        pl_size = sparams->pl_size;
 586
 587        /* copy the header data */
 588        memcpy(partdata, cdata, sparams->hdr_bytes);
 589
 590        /* Serialise IPC TX */
 591        mutex_lock(&sdev->ipc->tx_mutex);
 592
 593        /* copy the payload data in a loop */
 594        for (i = 0; i < sparams->num_msg; i++) {
 595                send_bytes = min(msg_bytes, pl_size);
 596                partdata->num_elems = send_bytes;
 597                partdata->rhdr.hdr.size = sparams->hdr_bytes + send_bytes;
 598                partdata->msg_index = i;
 599                msg_bytes -= send_bytes;
 600                partdata->elems_remaining = msg_bytes;
 601
 602                if (send)
 603                        memcpy(sparams->dst, sparams->src + offset, send_bytes);
 604
 605                err = sof_ipc_tx_message_unlocked(sdev->ipc,
 606                                                  partdata->rhdr.hdr.cmd,
 607                                                  partdata,
 608                                                  partdata->rhdr.hdr.size,
 609                                                  partdata,
 610                                                  partdata->rhdr.hdr.size);
 611                if (err < 0)
 612                        break;
 613
 614                if (!send)
 615                        memcpy(sparams->dst + offset, sparams->src, send_bytes);
 616
 617                offset += pl_size;
 618        }
 619
 620        mutex_unlock(&sdev->ipc->tx_mutex);
 621
 622        kfree(partdata);
 623        return err;
 624}
 625
 626/*
 627 * IPC get()/set() for kcontrols.
 628 */
 629int snd_sof_ipc_set_get_comp_data(struct snd_sof_ipc *ipc,
 630                                  struct snd_sof_control *scontrol,
 631                                  u32 ipc_cmd,
 632                                  enum sof_ipc_ctrl_type ctrl_type,
 633                                  enum sof_ipc_ctrl_cmd ctrl_cmd,
 634                                  bool send)
 635{
 636        struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
 637        struct snd_sof_dev *sdev = ipc->sdev;
 638        struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
 639        struct sof_ipc_fw_version *v = &ready->version;
 640        struct sof_ipc_ctrl_data_params sparams;
 641        size_t send_bytes;
 642        int err;
 643
 644        /* read or write firmware volume */
 645        if (scontrol->readback_offset != 0) {
 646                /* write/read value header via mmaped region */
 647                send_bytes = sizeof(struct sof_ipc_ctrl_value_chan) *
 648                cdata->num_elems;
 649                if (send)
 650                        snd_sof_dsp_block_write(sdev, sdev->mmio_bar,
 651                                                scontrol->readback_offset,
 652                                                cdata->chanv, send_bytes);
 653
 654                else
 655                        snd_sof_dsp_block_read(sdev, sdev->mmio_bar,
 656                                               scontrol->readback_offset,
 657                                               cdata->chanv, send_bytes);
 658                return 0;
 659        }
 660
 661        cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd;
 662        cdata->cmd = ctrl_cmd;
 663        cdata->type = ctrl_type;
 664        cdata->comp_id = scontrol->comp_id;
 665        cdata->msg_index = 0;
 666
 667        /* calculate header and data size */
 668        switch (cdata->type) {
 669        case SOF_CTRL_TYPE_VALUE_CHAN_GET:
 670        case SOF_CTRL_TYPE_VALUE_CHAN_SET:
 671                sparams.msg_bytes = scontrol->num_channels *
 672                        sizeof(struct sof_ipc_ctrl_value_chan);
 673                sparams.hdr_bytes = sizeof(struct sof_ipc_ctrl_data);
 674                sparams.elems = scontrol->num_channels;
 675                break;
 676        case SOF_CTRL_TYPE_VALUE_COMP_GET:
 677        case SOF_CTRL_TYPE_VALUE_COMP_SET:
 678                sparams.msg_bytes = scontrol->num_channels *
 679                        sizeof(struct sof_ipc_ctrl_value_comp);
 680                sparams.hdr_bytes = sizeof(struct sof_ipc_ctrl_data);
 681                sparams.elems = scontrol->num_channels;
 682                break;
 683        case SOF_CTRL_TYPE_DATA_GET:
 684        case SOF_CTRL_TYPE_DATA_SET:
 685                sparams.msg_bytes = cdata->data->size;
 686                sparams.hdr_bytes = sizeof(struct sof_ipc_ctrl_data) +
 687                        sizeof(struct sof_abi_hdr);
 688                sparams.elems = cdata->data->size;
 689                break;
 690        default:
 691                return -EINVAL;
 692        }
 693
 694        cdata->rhdr.hdr.size = sparams.msg_bytes + sparams.hdr_bytes;
 695        cdata->num_elems = sparams.elems;
 696        cdata->elems_remaining = 0;
 697
 698        /* send normal size ipc in one part */
 699        if (cdata->rhdr.hdr.size <= SOF_IPC_MSG_MAX_SIZE) {
 700                err = sof_ipc_tx_message(sdev->ipc, cdata->rhdr.hdr.cmd, cdata,
 701                                         cdata->rhdr.hdr.size, cdata,
 702                                         cdata->rhdr.hdr.size);
 703
 704                if (err < 0)
 705                        dev_err(sdev->dev, "error: set/get ctrl ipc comp %d\n",
 706                                cdata->comp_id);
 707
 708                return err;
 709        }
 710
 711        /* data is bigger than max ipc size, chop into smaller pieces */
 712        dev_dbg(sdev->dev, "large ipc size %u, control size %u\n",
 713                cdata->rhdr.hdr.size, scontrol->size);
 714
 715        /* large messages is only supported from ABI 3.3.0 onwards */
 716        if (v->abi_version < SOF_ABI_VER(3, 3, 0)) {
 717                dev_err(sdev->dev, "error: incompatible FW ABI version\n");
 718                return -EINVAL;
 719        }
 720
 721        err = sof_set_get_large_ctrl_data(sdev, cdata, &sparams, send);
 722
 723        if (err < 0)
 724                dev_err(sdev->dev, "error: set/get large ctrl ipc comp %d\n",
 725                        cdata->comp_id);
 726
 727        return err;
 728}
 729EXPORT_SYMBOL(snd_sof_ipc_set_get_comp_data);
 730
 731/*
 732 * IPC layer enumeration.
 733 */
 734
 735int snd_sof_dsp_mailbox_init(struct snd_sof_dev *sdev, u32 dspbox,
 736                             size_t dspbox_size, u32 hostbox,
 737                             size_t hostbox_size)
 738{
 739        sdev->dsp_box.offset = dspbox;
 740        sdev->dsp_box.size = dspbox_size;
 741        sdev->host_box.offset = hostbox;
 742        sdev->host_box.size = hostbox_size;
 743        return 0;
 744}
 745EXPORT_SYMBOL(snd_sof_dsp_mailbox_init);
 746
 747int snd_sof_ipc_valid(struct snd_sof_dev *sdev)
 748{
 749        struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
 750        struct sof_ipc_fw_version *v = &ready->version;
 751
 752        dev_info(sdev->dev,
 753                 "Firmware info: version %d:%d:%d-%s\n",  v->major, v->minor,
 754                 v->micro, v->tag);
 755        dev_info(sdev->dev,
 756                 "Firmware: ABI %d:%d:%d Kernel ABI %d:%d:%d\n",
 757                 SOF_ABI_VERSION_MAJOR(v->abi_version),
 758                 SOF_ABI_VERSION_MINOR(v->abi_version),
 759                 SOF_ABI_VERSION_PATCH(v->abi_version),
 760                 SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH);
 761
 762        if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, v->abi_version)) {
 763                dev_err(sdev->dev, "error: incompatible FW ABI version\n");
 764                return -EINVAL;
 765        }
 766
 767        if (v->abi_version > SOF_ABI_VERSION) {
 768                if (!IS_ENABLED(CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS)) {
 769                        dev_warn(sdev->dev, "warn: FW ABI is more recent than kernel\n");
 770                } else {
 771                        dev_err(sdev->dev, "error: FW ABI is more recent than kernel\n");
 772                        return -EINVAL;
 773                }
 774        }
 775
 776        if (ready->flags & SOF_IPC_INFO_BUILD) {
 777                dev_info(sdev->dev,
 778                         "Firmware debug build %d on %s-%s - options:\n"
 779                         " GDB: %s\n"
 780                         " lock debug: %s\n"
 781                         " lock vdebug: %s\n",
 782                         v->build, v->date, v->time,
 783                         (ready->flags & SOF_IPC_INFO_GDB) ?
 784                                "enabled" : "disabled",
 785                         (ready->flags & SOF_IPC_INFO_LOCKS) ?
 786                                "enabled" : "disabled",
 787                         (ready->flags & SOF_IPC_INFO_LOCKSV) ?
 788                                "enabled" : "disabled");
 789        }
 790
 791        /* copy the fw_version into debugfs at first boot */
 792        memcpy(&sdev->fw_version, v, sizeof(*v));
 793
 794        return 0;
 795}
 796EXPORT_SYMBOL(snd_sof_ipc_valid);
 797
 798struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev)
 799{
 800        struct snd_sof_ipc *ipc;
 801        struct snd_sof_ipc_msg *msg;
 802
 803        /* check if mandatory ops required for ipc are defined */
 804        if (!sof_ops(sdev)->fw_ready) {
 805                dev_err(sdev->dev, "error: ipc mandatory ops not defined\n");
 806                return NULL;
 807        }
 808
 809        ipc = devm_kzalloc(sdev->dev, sizeof(*ipc), GFP_KERNEL);
 810        if (!ipc)
 811                return NULL;
 812
 813        mutex_init(&ipc->tx_mutex);
 814        ipc->sdev = sdev;
 815        msg = &ipc->msg;
 816
 817        /* indicate that we aren't sending a message ATM */
 818        msg->ipc_complete = true;
 819
 820        /* pre-allocate message data */
 821        msg->msg_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE,
 822                                     GFP_KERNEL);
 823        if (!msg->msg_data)
 824                return NULL;
 825
 826        msg->reply_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE,
 827                                       GFP_KERNEL);
 828        if (!msg->reply_data)
 829                return NULL;
 830
 831        init_waitqueue_head(&msg->waitq);
 832
 833        return ipc;
 834}
 835EXPORT_SYMBOL(snd_sof_ipc_init);
 836
 837void snd_sof_ipc_free(struct snd_sof_dev *sdev)
 838{
 839        struct snd_sof_ipc *ipc = sdev->ipc;
 840
 841        /* disable sending of ipc's */
 842        mutex_lock(&ipc->tx_mutex);
 843        ipc->disable_ipc_tx = true;
 844        mutex_unlock(&ipc->tx_mutex);
 845}
 846EXPORT_SYMBOL(snd_sof_ipc_free);
 847