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