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