162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 462306a36Sopenharmony_ci// redistributing this file, you may do so under either license. 562306a36Sopenharmony_ci// 662306a36Sopenharmony_ci// Copyright(c) 2021 Intel Corporation. All rights reserved. 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci// 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <sound/sof/stream.h> 1162306a36Sopenharmony_ci#include <sound/sof/control.h> 1262306a36Sopenharmony_ci#include <trace/events/sof.h> 1362306a36Sopenharmony_ci#include "sof-priv.h" 1462306a36Sopenharmony_ci#include "sof-audio.h" 1562306a36Sopenharmony_ci#include "ipc3-priv.h" 1662306a36Sopenharmony_ci#include "ops.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_citypedef void (*ipc3_rx_callback)(struct snd_sof_dev *sdev, void *msg_buf); 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC) 2162306a36Sopenharmony_cistatic void ipc3_log_header(struct device *dev, u8 *text, u32 cmd) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci u8 *str; 2462306a36Sopenharmony_ci u8 *str2 = NULL; 2562306a36Sopenharmony_ci u32 glb; 2662306a36Sopenharmony_ci u32 type; 2762306a36Sopenharmony_ci bool is_sof_ipc_stream_position = false; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci glb = cmd & SOF_GLB_TYPE_MASK; 3062306a36Sopenharmony_ci type = cmd & SOF_CMD_TYPE_MASK; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci switch (glb) { 3362306a36Sopenharmony_ci case SOF_IPC_GLB_REPLY: 3462306a36Sopenharmony_ci str = "GLB_REPLY"; break; 3562306a36Sopenharmony_ci case SOF_IPC_GLB_COMPOUND: 3662306a36Sopenharmony_ci str = "GLB_COMPOUND"; break; 3762306a36Sopenharmony_ci case SOF_IPC_GLB_TPLG_MSG: 3862306a36Sopenharmony_ci str = "GLB_TPLG_MSG"; 3962306a36Sopenharmony_ci switch (type) { 4062306a36Sopenharmony_ci case SOF_IPC_TPLG_COMP_NEW: 4162306a36Sopenharmony_ci str2 = "COMP_NEW"; break; 4262306a36Sopenharmony_ci case SOF_IPC_TPLG_COMP_FREE: 4362306a36Sopenharmony_ci str2 = "COMP_FREE"; break; 4462306a36Sopenharmony_ci case SOF_IPC_TPLG_COMP_CONNECT: 4562306a36Sopenharmony_ci str2 = "COMP_CONNECT"; break; 4662306a36Sopenharmony_ci case SOF_IPC_TPLG_PIPE_NEW: 4762306a36Sopenharmony_ci str2 = "PIPE_NEW"; break; 4862306a36Sopenharmony_ci case SOF_IPC_TPLG_PIPE_FREE: 4962306a36Sopenharmony_ci str2 = "PIPE_FREE"; break; 5062306a36Sopenharmony_ci case SOF_IPC_TPLG_PIPE_CONNECT: 5162306a36Sopenharmony_ci str2 = "PIPE_CONNECT"; break; 5262306a36Sopenharmony_ci case SOF_IPC_TPLG_PIPE_COMPLETE: 5362306a36Sopenharmony_ci str2 = "PIPE_COMPLETE"; break; 5462306a36Sopenharmony_ci case SOF_IPC_TPLG_BUFFER_NEW: 5562306a36Sopenharmony_ci str2 = "BUFFER_NEW"; break; 5662306a36Sopenharmony_ci case SOF_IPC_TPLG_BUFFER_FREE: 5762306a36Sopenharmony_ci str2 = "BUFFER_FREE"; break; 5862306a36Sopenharmony_ci default: 5962306a36Sopenharmony_ci str2 = "unknown type"; break; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci break; 6262306a36Sopenharmony_ci case SOF_IPC_GLB_PM_MSG: 6362306a36Sopenharmony_ci str = "GLB_PM_MSG"; 6462306a36Sopenharmony_ci switch (type) { 6562306a36Sopenharmony_ci case SOF_IPC_PM_CTX_SAVE: 6662306a36Sopenharmony_ci str2 = "CTX_SAVE"; break; 6762306a36Sopenharmony_ci case SOF_IPC_PM_CTX_RESTORE: 6862306a36Sopenharmony_ci str2 = "CTX_RESTORE"; break; 6962306a36Sopenharmony_ci case SOF_IPC_PM_CTX_SIZE: 7062306a36Sopenharmony_ci str2 = "CTX_SIZE"; break; 7162306a36Sopenharmony_ci case SOF_IPC_PM_CLK_SET: 7262306a36Sopenharmony_ci str2 = "CLK_SET"; break; 7362306a36Sopenharmony_ci case SOF_IPC_PM_CLK_GET: 7462306a36Sopenharmony_ci str2 = "CLK_GET"; break; 7562306a36Sopenharmony_ci case SOF_IPC_PM_CLK_REQ: 7662306a36Sopenharmony_ci str2 = "CLK_REQ"; break; 7762306a36Sopenharmony_ci case SOF_IPC_PM_CORE_ENABLE: 7862306a36Sopenharmony_ci str2 = "CORE_ENABLE"; break; 7962306a36Sopenharmony_ci case SOF_IPC_PM_GATE: 8062306a36Sopenharmony_ci str2 = "GATE"; break; 8162306a36Sopenharmony_ci default: 8262306a36Sopenharmony_ci str2 = "unknown type"; break; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci break; 8562306a36Sopenharmony_ci case SOF_IPC_GLB_COMP_MSG: 8662306a36Sopenharmony_ci str = "GLB_COMP_MSG"; 8762306a36Sopenharmony_ci switch (type) { 8862306a36Sopenharmony_ci case SOF_IPC_COMP_SET_VALUE: 8962306a36Sopenharmony_ci str2 = "SET_VALUE"; break; 9062306a36Sopenharmony_ci case SOF_IPC_COMP_GET_VALUE: 9162306a36Sopenharmony_ci str2 = "GET_VALUE"; break; 9262306a36Sopenharmony_ci case SOF_IPC_COMP_SET_DATA: 9362306a36Sopenharmony_ci str2 = "SET_DATA"; break; 9462306a36Sopenharmony_ci case SOF_IPC_COMP_GET_DATA: 9562306a36Sopenharmony_ci str2 = "GET_DATA"; break; 9662306a36Sopenharmony_ci default: 9762306a36Sopenharmony_ci str2 = "unknown type"; break; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci break; 10062306a36Sopenharmony_ci case SOF_IPC_GLB_STREAM_MSG: 10162306a36Sopenharmony_ci str = "GLB_STREAM_MSG"; 10262306a36Sopenharmony_ci switch (type) { 10362306a36Sopenharmony_ci case SOF_IPC_STREAM_PCM_PARAMS: 10462306a36Sopenharmony_ci str2 = "PCM_PARAMS"; break; 10562306a36Sopenharmony_ci case SOF_IPC_STREAM_PCM_PARAMS_REPLY: 10662306a36Sopenharmony_ci str2 = "PCM_REPLY"; break; 10762306a36Sopenharmony_ci case SOF_IPC_STREAM_PCM_FREE: 10862306a36Sopenharmony_ci str2 = "PCM_FREE"; break; 10962306a36Sopenharmony_ci case SOF_IPC_STREAM_TRIG_START: 11062306a36Sopenharmony_ci str2 = "TRIG_START"; break; 11162306a36Sopenharmony_ci case SOF_IPC_STREAM_TRIG_STOP: 11262306a36Sopenharmony_ci str2 = "TRIG_STOP"; break; 11362306a36Sopenharmony_ci case SOF_IPC_STREAM_TRIG_PAUSE: 11462306a36Sopenharmony_ci str2 = "TRIG_PAUSE"; break; 11562306a36Sopenharmony_ci case SOF_IPC_STREAM_TRIG_RELEASE: 11662306a36Sopenharmony_ci str2 = "TRIG_RELEASE"; break; 11762306a36Sopenharmony_ci case SOF_IPC_STREAM_TRIG_DRAIN: 11862306a36Sopenharmony_ci str2 = "TRIG_DRAIN"; break; 11962306a36Sopenharmony_ci case SOF_IPC_STREAM_TRIG_XRUN: 12062306a36Sopenharmony_ci str2 = "TRIG_XRUN"; break; 12162306a36Sopenharmony_ci case SOF_IPC_STREAM_POSITION: 12262306a36Sopenharmony_ci is_sof_ipc_stream_position = true; 12362306a36Sopenharmony_ci str2 = "POSITION"; break; 12462306a36Sopenharmony_ci case SOF_IPC_STREAM_VORBIS_PARAMS: 12562306a36Sopenharmony_ci str2 = "VORBIS_PARAMS"; break; 12662306a36Sopenharmony_ci case SOF_IPC_STREAM_VORBIS_FREE: 12762306a36Sopenharmony_ci str2 = "VORBIS_FREE"; break; 12862306a36Sopenharmony_ci default: 12962306a36Sopenharmony_ci str2 = "unknown type"; break; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci break; 13262306a36Sopenharmony_ci case SOF_IPC_FW_READY: 13362306a36Sopenharmony_ci str = "FW_READY"; break; 13462306a36Sopenharmony_ci case SOF_IPC_GLB_DAI_MSG: 13562306a36Sopenharmony_ci str = "GLB_DAI_MSG"; 13662306a36Sopenharmony_ci switch (type) { 13762306a36Sopenharmony_ci case SOF_IPC_DAI_CONFIG: 13862306a36Sopenharmony_ci str2 = "CONFIG"; break; 13962306a36Sopenharmony_ci case SOF_IPC_DAI_LOOPBACK: 14062306a36Sopenharmony_ci str2 = "LOOPBACK"; break; 14162306a36Sopenharmony_ci default: 14262306a36Sopenharmony_ci str2 = "unknown type"; break; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci break; 14562306a36Sopenharmony_ci case SOF_IPC_GLB_TRACE_MSG: 14662306a36Sopenharmony_ci str = "GLB_TRACE_MSG"; 14762306a36Sopenharmony_ci switch (type) { 14862306a36Sopenharmony_ci case SOF_IPC_TRACE_DMA_PARAMS: 14962306a36Sopenharmony_ci str2 = "DMA_PARAMS"; break; 15062306a36Sopenharmony_ci case SOF_IPC_TRACE_DMA_POSITION: 15162306a36Sopenharmony_ci if (!sof_debug_check_flag(SOF_DBG_PRINT_DMA_POSITION_UPDATE_LOGS)) 15262306a36Sopenharmony_ci return; 15362306a36Sopenharmony_ci str2 = "DMA_POSITION"; break; 15462306a36Sopenharmony_ci case SOF_IPC_TRACE_DMA_PARAMS_EXT: 15562306a36Sopenharmony_ci str2 = "DMA_PARAMS_EXT"; break; 15662306a36Sopenharmony_ci case SOF_IPC_TRACE_FILTER_UPDATE: 15762306a36Sopenharmony_ci str2 = "FILTER_UPDATE"; break; 15862306a36Sopenharmony_ci case SOF_IPC_TRACE_DMA_FREE: 15962306a36Sopenharmony_ci str2 = "DMA_FREE"; break; 16062306a36Sopenharmony_ci default: 16162306a36Sopenharmony_ci str2 = "unknown type"; break; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci case SOF_IPC_GLB_TEST_MSG: 16562306a36Sopenharmony_ci str = "GLB_TEST_MSG"; 16662306a36Sopenharmony_ci switch (type) { 16762306a36Sopenharmony_ci case SOF_IPC_TEST_IPC_FLOOD: 16862306a36Sopenharmony_ci str2 = "IPC_FLOOD"; break; 16962306a36Sopenharmony_ci default: 17062306a36Sopenharmony_ci str2 = "unknown type"; break; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci case SOF_IPC_GLB_DEBUG: 17462306a36Sopenharmony_ci str = "GLB_DEBUG"; 17562306a36Sopenharmony_ci switch (type) { 17662306a36Sopenharmony_ci case SOF_IPC_DEBUG_MEM_USAGE: 17762306a36Sopenharmony_ci str2 = "MEM_USAGE"; break; 17862306a36Sopenharmony_ci default: 17962306a36Sopenharmony_ci str2 = "unknown type"; break; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci break; 18262306a36Sopenharmony_ci case SOF_IPC_GLB_PROBE: 18362306a36Sopenharmony_ci str = "GLB_PROBE"; 18462306a36Sopenharmony_ci switch (type) { 18562306a36Sopenharmony_ci case SOF_IPC_PROBE_INIT: 18662306a36Sopenharmony_ci str2 = "INIT"; break; 18762306a36Sopenharmony_ci case SOF_IPC_PROBE_DEINIT: 18862306a36Sopenharmony_ci str2 = "DEINIT"; break; 18962306a36Sopenharmony_ci case SOF_IPC_PROBE_DMA_ADD: 19062306a36Sopenharmony_ci str2 = "DMA_ADD"; break; 19162306a36Sopenharmony_ci case SOF_IPC_PROBE_DMA_INFO: 19262306a36Sopenharmony_ci str2 = "DMA_INFO"; break; 19362306a36Sopenharmony_ci case SOF_IPC_PROBE_DMA_REMOVE: 19462306a36Sopenharmony_ci str2 = "DMA_REMOVE"; break; 19562306a36Sopenharmony_ci case SOF_IPC_PROBE_POINT_ADD: 19662306a36Sopenharmony_ci str2 = "POINT_ADD"; break; 19762306a36Sopenharmony_ci case SOF_IPC_PROBE_POINT_INFO: 19862306a36Sopenharmony_ci str2 = "POINT_INFO"; break; 19962306a36Sopenharmony_ci case SOF_IPC_PROBE_POINT_REMOVE: 20062306a36Sopenharmony_ci str2 = "POINT_REMOVE"; break; 20162306a36Sopenharmony_ci default: 20262306a36Sopenharmony_ci str2 = "unknown type"; break; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci break; 20562306a36Sopenharmony_ci default: 20662306a36Sopenharmony_ci str = "unknown GLB command"; break; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (str2) { 21062306a36Sopenharmony_ci if (is_sof_ipc_stream_position) 21162306a36Sopenharmony_ci trace_sof_stream_position_ipc_rx(dev); 21262306a36Sopenharmony_ci else 21362306a36Sopenharmony_ci dev_dbg(dev, "%s: 0x%x: %s: %s\n", text, cmd, str, str2); 21462306a36Sopenharmony_ci } else { 21562306a36Sopenharmony_ci dev_dbg(dev, "%s: 0x%x: %s\n", text, cmd, str); 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci#else 21962306a36Sopenharmony_cistatic inline void ipc3_log_header(struct device *dev, u8 *text, u32 cmd) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci if ((cmd & SOF_GLB_TYPE_MASK) != SOF_IPC_GLB_TRACE_MSG) 22262306a36Sopenharmony_ci dev_dbg(dev, "%s: 0x%x\n", text, cmd); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci#endif 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic void sof_ipc3_dump_payload(struct snd_sof_dev *sdev, 22762306a36Sopenharmony_ci void *ipc_data, size_t size) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci printk(KERN_DEBUG "Size of payload following the header: %zu\n", size); 23062306a36Sopenharmony_ci print_hex_dump_debug("Message payload: ", DUMP_PREFIX_OFFSET, 23162306a36Sopenharmony_ci 16, 4, ipc_data, size, false); 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic int sof_ipc3_get_reply(struct snd_sof_dev *sdev) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct snd_sof_ipc_msg *msg = sdev->msg; 23762306a36Sopenharmony_ci struct sof_ipc_reply *reply; 23862306a36Sopenharmony_ci int ret = 0; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* get the generic reply */ 24162306a36Sopenharmony_ci reply = msg->reply_data; 24262306a36Sopenharmony_ci snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset, reply, sizeof(*reply)); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (reply->error < 0) 24562306a36Sopenharmony_ci return reply->error; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (!reply->hdr.size) { 24862306a36Sopenharmony_ci /* Reply should always be >= sizeof(struct sof_ipc_reply) */ 24962306a36Sopenharmony_ci if (msg->reply_size) 25062306a36Sopenharmony_ci dev_err(sdev->dev, 25162306a36Sopenharmony_ci "empty reply received, expected %zu bytes\n", 25262306a36Sopenharmony_ci msg->reply_size); 25362306a36Sopenharmony_ci else 25462306a36Sopenharmony_ci dev_err(sdev->dev, "empty reply received\n"); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return -EINVAL; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (msg->reply_size > 0) { 26062306a36Sopenharmony_ci if (reply->hdr.size == msg->reply_size) { 26162306a36Sopenharmony_ci ret = 0; 26262306a36Sopenharmony_ci } else if (reply->hdr.size < msg->reply_size) { 26362306a36Sopenharmony_ci dev_dbg(sdev->dev, 26462306a36Sopenharmony_ci "reply size (%u) is less than expected (%zu)\n", 26562306a36Sopenharmony_ci reply->hdr.size, msg->reply_size); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci msg->reply_size = reply->hdr.size; 26862306a36Sopenharmony_ci ret = 0; 26962306a36Sopenharmony_ci } else { 27062306a36Sopenharmony_ci dev_err(sdev->dev, 27162306a36Sopenharmony_ci "reply size (%u) exceeds the buffer size (%zu)\n", 27262306a36Sopenharmony_ci reply->hdr.size, msg->reply_size); 27362306a36Sopenharmony_ci ret = -EINVAL; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* 27762306a36Sopenharmony_ci * get the full message if reply->hdr.size <= msg->reply_size 27862306a36Sopenharmony_ci * and the reply->hdr.size > sizeof(struct sof_ipc_reply) 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ci if (!ret && msg->reply_size > sizeof(*reply)) 28162306a36Sopenharmony_ci snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset, 28262306a36Sopenharmony_ci msg->reply_data, msg->reply_size); 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci return ret; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/* wait for IPC message reply */ 28962306a36Sopenharmony_cistatic int ipc3_wait_tx_done(struct snd_sof_ipc *ipc, void *reply_data) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct snd_sof_ipc_msg *msg = &ipc->msg; 29262306a36Sopenharmony_ci struct sof_ipc_cmd_hdr *hdr = msg->msg_data; 29362306a36Sopenharmony_ci struct snd_sof_dev *sdev = ipc->sdev; 29462306a36Sopenharmony_ci int ret; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* wait for DSP IPC completion */ 29762306a36Sopenharmony_ci ret = wait_event_timeout(msg->waitq, msg->ipc_complete, 29862306a36Sopenharmony_ci msecs_to_jiffies(sdev->ipc_timeout)); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (ret == 0) { 30162306a36Sopenharmony_ci dev_err(sdev->dev, 30262306a36Sopenharmony_ci "ipc tx timed out for %#x (msg/reply size: %d/%zu)\n", 30362306a36Sopenharmony_ci hdr->cmd, hdr->size, msg->reply_size); 30462306a36Sopenharmony_ci snd_sof_handle_fw_exception(ipc->sdev, "IPC timeout"); 30562306a36Sopenharmony_ci ret = -ETIMEDOUT; 30662306a36Sopenharmony_ci } else { 30762306a36Sopenharmony_ci ret = msg->reply_error; 30862306a36Sopenharmony_ci if (ret < 0) { 30962306a36Sopenharmony_ci dev_err(sdev->dev, 31062306a36Sopenharmony_ci "ipc tx error for %#x (msg/reply size: %d/%zu): %d\n", 31162306a36Sopenharmony_ci hdr->cmd, hdr->size, msg->reply_size, ret); 31262306a36Sopenharmony_ci } else { 31362306a36Sopenharmony_ci if (sof_debug_check_flag(SOF_DBG_PRINT_IPC_SUCCESS_LOGS)) 31462306a36Sopenharmony_ci ipc3_log_header(sdev->dev, "ipc tx succeeded", hdr->cmd); 31562306a36Sopenharmony_ci if (reply_data && msg->reply_size) 31662306a36Sopenharmony_ci /* copy the data returned from DSP */ 31762306a36Sopenharmony_ci memcpy(reply_data, msg->reply_data, 31862306a36Sopenharmony_ci msg->reply_size); 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* re-enable dumps after successful IPC tx */ 32262306a36Sopenharmony_ci if (sdev->ipc_dump_printed) { 32362306a36Sopenharmony_ci sdev->dbg_dump_printed = false; 32462306a36Sopenharmony_ci sdev->ipc_dump_printed = false; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci return ret; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci/* send IPC message from host to DSP */ 33262306a36Sopenharmony_cistatic int ipc3_tx_msg_unlocked(struct snd_sof_ipc *ipc, 33362306a36Sopenharmony_ci void *msg_data, size_t msg_bytes, 33462306a36Sopenharmony_ci void *reply_data, size_t reply_bytes) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct sof_ipc_cmd_hdr *hdr = msg_data; 33762306a36Sopenharmony_ci struct snd_sof_dev *sdev = ipc->sdev; 33862306a36Sopenharmony_ci int ret; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci ipc3_log_header(sdev->dev, "ipc tx", hdr->cmd); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci ret = sof_ipc_send_msg(sdev, msg_data, msg_bytes, reply_bytes); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (ret) { 34562306a36Sopenharmony_ci dev_err_ratelimited(sdev->dev, 34662306a36Sopenharmony_ci "%s: ipc message send for %#x failed: %d\n", 34762306a36Sopenharmony_ci __func__, hdr->cmd, ret); 34862306a36Sopenharmony_ci return ret; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* now wait for completion */ 35262306a36Sopenharmony_ci return ipc3_wait_tx_done(ipc, reply_data); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic int sof_ipc3_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes, 35662306a36Sopenharmony_ci void *reply_data, size_t reply_bytes, bool no_pm) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct snd_sof_ipc *ipc = sdev->ipc; 35962306a36Sopenharmony_ci int ret; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (!msg_data || msg_bytes < sizeof(struct sof_ipc_cmd_hdr)) { 36262306a36Sopenharmony_ci dev_err_ratelimited(sdev->dev, "No IPC message to send\n"); 36362306a36Sopenharmony_ci return -EINVAL; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (!no_pm) { 36762306a36Sopenharmony_ci const struct sof_dsp_power_state target_state = { 36862306a36Sopenharmony_ci .state = SOF_DSP_PM_D0, 36962306a36Sopenharmony_ci }; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* ensure the DSP is in D0 before sending a new IPC */ 37262306a36Sopenharmony_ci ret = snd_sof_dsp_set_power_state(sdev, &target_state); 37362306a36Sopenharmony_ci if (ret < 0) { 37462306a36Sopenharmony_ci dev_err(sdev->dev, "%s: resuming DSP failed: %d\n", 37562306a36Sopenharmony_ci __func__, ret); 37662306a36Sopenharmony_ci return ret; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* Serialise IPC TX */ 38162306a36Sopenharmony_ci mutex_lock(&ipc->tx_mutex); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci ret = ipc3_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (sof_debug_check_flag(SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD)) { 38662306a36Sopenharmony_ci size_t payload_bytes, header_bytes; 38762306a36Sopenharmony_ci char *payload = NULL; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* payload is indicated by non zero msg/reply_bytes */ 39062306a36Sopenharmony_ci if (msg_bytes > sizeof(struct sof_ipc_cmd_hdr)) { 39162306a36Sopenharmony_ci payload = msg_data; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci header_bytes = sizeof(struct sof_ipc_cmd_hdr); 39462306a36Sopenharmony_ci payload_bytes = msg_bytes - header_bytes; 39562306a36Sopenharmony_ci } else if (reply_bytes > sizeof(struct sof_ipc_reply)) { 39662306a36Sopenharmony_ci payload = reply_data; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci header_bytes = sizeof(struct sof_ipc_reply); 39962306a36Sopenharmony_ci payload_bytes = reply_bytes - header_bytes; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (payload) { 40362306a36Sopenharmony_ci payload += header_bytes; 40462306a36Sopenharmony_ci sof_ipc3_dump_payload(sdev, payload, payload_bytes); 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci mutex_unlock(&ipc->tx_mutex); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci return ret; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic int sof_ipc3_set_get_data(struct snd_sof_dev *sdev, void *data, size_t data_bytes, 41462306a36Sopenharmony_ci bool set) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci size_t msg_bytes, hdr_bytes, payload_size, send_bytes; 41762306a36Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = data; 41862306a36Sopenharmony_ci struct sof_ipc_ctrl_data *cdata_chunk; 41962306a36Sopenharmony_ci struct snd_sof_ipc *ipc = sdev->ipc; 42062306a36Sopenharmony_ci size_t offset = 0; 42162306a36Sopenharmony_ci u8 *src, *dst; 42262306a36Sopenharmony_ci u32 num_msg; 42362306a36Sopenharmony_ci int ret = 0; 42462306a36Sopenharmony_ci int i; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (!cdata || data_bytes < sizeof(*cdata)) 42762306a36Sopenharmony_ci return -EINVAL; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if ((cdata->rhdr.hdr.cmd & SOF_GLB_TYPE_MASK) != SOF_IPC_GLB_COMP_MSG) { 43062306a36Sopenharmony_ci dev_err(sdev->dev, "%s: Not supported message type of %#x\n", 43162306a36Sopenharmony_ci __func__, cdata->rhdr.hdr.cmd); 43262306a36Sopenharmony_ci return -EINVAL; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* send normal size ipc in one part */ 43662306a36Sopenharmony_ci if (cdata->rhdr.hdr.size <= ipc->max_payload_size) 43762306a36Sopenharmony_ci return sof_ipc3_tx_msg(sdev, cdata, cdata->rhdr.hdr.size, 43862306a36Sopenharmony_ci cdata, cdata->rhdr.hdr.size, false); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci cdata_chunk = kzalloc(ipc->max_payload_size, GFP_KERNEL); 44162306a36Sopenharmony_ci if (!cdata_chunk) 44262306a36Sopenharmony_ci return -ENOMEM; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci switch (cdata->type) { 44562306a36Sopenharmony_ci case SOF_CTRL_TYPE_VALUE_CHAN_GET: 44662306a36Sopenharmony_ci case SOF_CTRL_TYPE_VALUE_CHAN_SET: 44762306a36Sopenharmony_ci hdr_bytes = sizeof(struct sof_ipc_ctrl_data); 44862306a36Sopenharmony_ci if (set) { 44962306a36Sopenharmony_ci src = (u8 *)cdata->chanv; 45062306a36Sopenharmony_ci dst = (u8 *)cdata_chunk->chanv; 45162306a36Sopenharmony_ci } else { 45262306a36Sopenharmony_ci src = (u8 *)cdata_chunk->chanv; 45362306a36Sopenharmony_ci dst = (u8 *)cdata->chanv; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci case SOF_CTRL_TYPE_DATA_GET: 45762306a36Sopenharmony_ci case SOF_CTRL_TYPE_DATA_SET: 45862306a36Sopenharmony_ci hdr_bytes = sizeof(struct sof_ipc_ctrl_data) + sizeof(struct sof_abi_hdr); 45962306a36Sopenharmony_ci if (set) { 46062306a36Sopenharmony_ci src = (u8 *)cdata->data->data; 46162306a36Sopenharmony_ci dst = (u8 *)cdata_chunk->data->data; 46262306a36Sopenharmony_ci } else { 46362306a36Sopenharmony_ci src = (u8 *)cdata_chunk->data->data; 46462306a36Sopenharmony_ci dst = (u8 *)cdata->data->data; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci break; 46762306a36Sopenharmony_ci default: 46862306a36Sopenharmony_ci kfree(cdata_chunk); 46962306a36Sopenharmony_ci return -EINVAL; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci msg_bytes = cdata->rhdr.hdr.size - hdr_bytes; 47362306a36Sopenharmony_ci payload_size = ipc->max_payload_size - hdr_bytes; 47462306a36Sopenharmony_ci num_msg = DIV_ROUND_UP(msg_bytes, payload_size); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* copy the header data */ 47762306a36Sopenharmony_ci memcpy(cdata_chunk, cdata, hdr_bytes); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* Serialise IPC TX */ 48062306a36Sopenharmony_ci mutex_lock(&sdev->ipc->tx_mutex); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* copy the payload data in a loop */ 48362306a36Sopenharmony_ci for (i = 0; i < num_msg; i++) { 48462306a36Sopenharmony_ci send_bytes = min(msg_bytes, payload_size); 48562306a36Sopenharmony_ci cdata_chunk->num_elems = send_bytes; 48662306a36Sopenharmony_ci cdata_chunk->rhdr.hdr.size = hdr_bytes + send_bytes; 48762306a36Sopenharmony_ci cdata_chunk->msg_index = i; 48862306a36Sopenharmony_ci msg_bytes -= send_bytes; 48962306a36Sopenharmony_ci cdata_chunk->elems_remaining = msg_bytes; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (set) 49262306a36Sopenharmony_ci memcpy(dst, src + offset, send_bytes); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci ret = ipc3_tx_msg_unlocked(sdev->ipc, 49562306a36Sopenharmony_ci cdata_chunk, cdata_chunk->rhdr.hdr.size, 49662306a36Sopenharmony_ci cdata_chunk, cdata_chunk->rhdr.hdr.size); 49762306a36Sopenharmony_ci if (ret < 0) 49862306a36Sopenharmony_ci break; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (!set) 50162306a36Sopenharmony_ci memcpy(dst + offset, src, send_bytes); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci offset += payload_size; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (sof_debug_check_flag(SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD)) { 50762306a36Sopenharmony_ci size_t header_bytes = sizeof(struct sof_ipc_reply); 50862306a36Sopenharmony_ci char *payload = (char *)cdata; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci payload += header_bytes; 51162306a36Sopenharmony_ci sof_ipc3_dump_payload(sdev, payload, data_bytes - header_bytes); 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci mutex_unlock(&sdev->ipc->tx_mutex); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci kfree(cdata_chunk); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci return ret; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ciint sof_ipc3_get_ext_windows(struct snd_sof_dev *sdev, 52262306a36Sopenharmony_ci const struct sof_ipc_ext_data_hdr *ext_hdr) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci const struct sof_ipc_window *w = 52562306a36Sopenharmony_ci container_of(ext_hdr, struct sof_ipc_window, ext_hdr); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS) 52862306a36Sopenharmony_ci return -EINVAL; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (sdev->info_window) { 53162306a36Sopenharmony_ci if (memcmp(sdev->info_window, w, ext_hdr->hdr.size)) { 53262306a36Sopenharmony_ci dev_err(sdev->dev, "mismatch between window descriptor from extended manifest and mailbox"); 53362306a36Sopenharmony_ci return -EINVAL; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci return 0; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* keep a local copy of the data */ 53962306a36Sopenharmony_ci sdev->info_window = devm_kmemdup(sdev->dev, w, ext_hdr->hdr.size, GFP_KERNEL); 54062306a36Sopenharmony_ci if (!sdev->info_window) 54162306a36Sopenharmony_ci return -ENOMEM; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci return 0; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ciint sof_ipc3_get_cc_info(struct snd_sof_dev *sdev, 54762306a36Sopenharmony_ci const struct sof_ipc_ext_data_hdr *ext_hdr) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci int ret; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci const struct sof_ipc_cc_version *cc = 55262306a36Sopenharmony_ci container_of(ext_hdr, struct sof_ipc_cc_version, ext_hdr); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (sdev->cc_version) { 55562306a36Sopenharmony_ci if (memcmp(sdev->cc_version, cc, cc->ext_hdr.hdr.size)) { 55662306a36Sopenharmony_ci dev_err(sdev->dev, 55762306a36Sopenharmony_ci "Receive diverged cc_version descriptions"); 55862306a36Sopenharmony_ci return -EINVAL; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci return 0; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci dev_dbg(sdev->dev, 56462306a36Sopenharmony_ci "Firmware info: used compiler %s %d:%d:%d%s used optimization flags %s\n", 56562306a36Sopenharmony_ci cc->name, cc->major, cc->minor, cc->micro, cc->desc, cc->optim); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* create read-only cc_version debugfs to store compiler version info */ 56862306a36Sopenharmony_ci /* use local copy of the cc_version to prevent data corruption */ 56962306a36Sopenharmony_ci if (sdev->first_boot) { 57062306a36Sopenharmony_ci sdev->cc_version = devm_kmemdup(sdev->dev, cc, cc->ext_hdr.hdr.size, GFP_KERNEL); 57162306a36Sopenharmony_ci if (!sdev->cc_version) 57262306a36Sopenharmony_ci return -ENOMEM; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci ret = snd_sof_debugfs_buf_item(sdev, sdev->cc_version, 57562306a36Sopenharmony_ci cc->ext_hdr.hdr.size, 57662306a36Sopenharmony_ci "cc_version", 0444); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci /* errors are only due to memory allocation, not debugfs */ 57962306a36Sopenharmony_ci if (ret < 0) { 58062306a36Sopenharmony_ci dev_err(sdev->dev, "snd_sof_debugfs_buf_item failed\n"); 58162306a36Sopenharmony_ci return ret; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci return 0; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci/* parse the extended FW boot data structures from FW boot message */ 58962306a36Sopenharmony_cistatic int ipc3_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci struct sof_ipc_ext_data_hdr *ext_hdr; 59262306a36Sopenharmony_ci void *ext_data; 59362306a36Sopenharmony_ci int ret = 0; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL); 59662306a36Sopenharmony_ci if (!ext_data) 59762306a36Sopenharmony_ci return -ENOMEM; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci /* get first header */ 60062306a36Sopenharmony_ci snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data, 60162306a36Sopenharmony_ci sizeof(*ext_hdr)); 60262306a36Sopenharmony_ci ext_hdr = ext_data; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) { 60562306a36Sopenharmony_ci /* read in ext structure */ 60662306a36Sopenharmony_ci snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, 60762306a36Sopenharmony_ci offset + sizeof(*ext_hdr), 60862306a36Sopenharmony_ci (void *)((u8 *)ext_data + sizeof(*ext_hdr)), 60962306a36Sopenharmony_ci ext_hdr->hdr.size - sizeof(*ext_hdr)); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n", 61262306a36Sopenharmony_ci ext_hdr->type, ext_hdr->hdr.size); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* process structure data */ 61562306a36Sopenharmony_ci switch (ext_hdr->type) { 61662306a36Sopenharmony_ci case SOF_IPC_EXT_WINDOW: 61762306a36Sopenharmony_ci ret = sof_ipc3_get_ext_windows(sdev, ext_hdr); 61862306a36Sopenharmony_ci break; 61962306a36Sopenharmony_ci case SOF_IPC_EXT_CC_INFO: 62062306a36Sopenharmony_ci ret = sof_ipc3_get_cc_info(sdev, ext_hdr); 62162306a36Sopenharmony_ci break; 62262306a36Sopenharmony_ci case SOF_IPC_EXT_UNUSED: 62362306a36Sopenharmony_ci case SOF_IPC_EXT_PROBE_INFO: 62462306a36Sopenharmony_ci case SOF_IPC_EXT_USER_ABI_INFO: 62562306a36Sopenharmony_ci /* They are supported but we don't do anything here */ 62662306a36Sopenharmony_ci break; 62762306a36Sopenharmony_ci default: 62862306a36Sopenharmony_ci dev_info(sdev->dev, "unknown ext header type %d size 0x%x\n", 62962306a36Sopenharmony_ci ext_hdr->type, ext_hdr->hdr.size); 63062306a36Sopenharmony_ci ret = 0; 63162306a36Sopenharmony_ci break; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (ret < 0) { 63562306a36Sopenharmony_ci dev_err(sdev->dev, "Failed to parse ext data type %d\n", 63662306a36Sopenharmony_ci ext_hdr->type); 63762306a36Sopenharmony_ci break; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* move to next header */ 64162306a36Sopenharmony_ci offset += ext_hdr->hdr.size; 64262306a36Sopenharmony_ci snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data, 64362306a36Sopenharmony_ci sizeof(*ext_hdr)); 64462306a36Sopenharmony_ci ext_hdr = ext_data; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci kfree(ext_data); 64862306a36Sopenharmony_ci return ret; 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistatic void ipc3_get_windows(struct snd_sof_dev *sdev) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct sof_ipc_window_elem *elem; 65462306a36Sopenharmony_ci u32 outbox_offset = 0; 65562306a36Sopenharmony_ci u32 stream_offset = 0; 65662306a36Sopenharmony_ci u32 inbox_offset = 0; 65762306a36Sopenharmony_ci u32 outbox_size = 0; 65862306a36Sopenharmony_ci u32 stream_size = 0; 65962306a36Sopenharmony_ci u32 inbox_size = 0; 66062306a36Sopenharmony_ci u32 debug_size = 0; 66162306a36Sopenharmony_ci u32 debug_offset = 0; 66262306a36Sopenharmony_ci int window_offset; 66362306a36Sopenharmony_ci int i; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (!sdev->info_window) { 66662306a36Sopenharmony_ci dev_err(sdev->dev, "%s: No window info present\n", __func__); 66762306a36Sopenharmony_ci return; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci for (i = 0; i < sdev->info_window->num_windows; i++) { 67162306a36Sopenharmony_ci elem = &sdev->info_window->window[i]; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci window_offset = snd_sof_dsp_get_window_offset(sdev, elem->id); 67462306a36Sopenharmony_ci if (window_offset < 0) { 67562306a36Sopenharmony_ci dev_warn(sdev->dev, "No offset for window %d\n", elem->id); 67662306a36Sopenharmony_ci continue; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci switch (elem->type) { 68062306a36Sopenharmony_ci case SOF_IPC_REGION_UPBOX: 68162306a36Sopenharmony_ci inbox_offset = window_offset + elem->offset; 68262306a36Sopenharmony_ci inbox_size = elem->size; 68362306a36Sopenharmony_ci snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, 68462306a36Sopenharmony_ci inbox_offset, 68562306a36Sopenharmony_ci elem->size, "inbox", 68662306a36Sopenharmony_ci SOF_DEBUGFS_ACCESS_D0_ONLY); 68762306a36Sopenharmony_ci break; 68862306a36Sopenharmony_ci case SOF_IPC_REGION_DOWNBOX: 68962306a36Sopenharmony_ci outbox_offset = window_offset + elem->offset; 69062306a36Sopenharmony_ci outbox_size = elem->size; 69162306a36Sopenharmony_ci snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, 69262306a36Sopenharmony_ci outbox_offset, 69362306a36Sopenharmony_ci elem->size, "outbox", 69462306a36Sopenharmony_ci SOF_DEBUGFS_ACCESS_D0_ONLY); 69562306a36Sopenharmony_ci break; 69662306a36Sopenharmony_ci case SOF_IPC_REGION_TRACE: 69762306a36Sopenharmony_ci snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, 69862306a36Sopenharmony_ci window_offset + elem->offset, 69962306a36Sopenharmony_ci elem->size, "etrace", 70062306a36Sopenharmony_ci SOF_DEBUGFS_ACCESS_D0_ONLY); 70162306a36Sopenharmony_ci break; 70262306a36Sopenharmony_ci case SOF_IPC_REGION_DEBUG: 70362306a36Sopenharmony_ci debug_offset = window_offset + elem->offset; 70462306a36Sopenharmony_ci debug_size = elem->size; 70562306a36Sopenharmony_ci snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, 70662306a36Sopenharmony_ci window_offset + elem->offset, 70762306a36Sopenharmony_ci elem->size, "debug", 70862306a36Sopenharmony_ci SOF_DEBUGFS_ACCESS_D0_ONLY); 70962306a36Sopenharmony_ci break; 71062306a36Sopenharmony_ci case SOF_IPC_REGION_STREAM: 71162306a36Sopenharmony_ci stream_offset = window_offset + elem->offset; 71262306a36Sopenharmony_ci stream_size = elem->size; 71362306a36Sopenharmony_ci snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, 71462306a36Sopenharmony_ci stream_offset, 71562306a36Sopenharmony_ci elem->size, "stream", 71662306a36Sopenharmony_ci SOF_DEBUGFS_ACCESS_D0_ONLY); 71762306a36Sopenharmony_ci break; 71862306a36Sopenharmony_ci case SOF_IPC_REGION_REGS: 71962306a36Sopenharmony_ci snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, 72062306a36Sopenharmony_ci window_offset + elem->offset, 72162306a36Sopenharmony_ci elem->size, "regs", 72262306a36Sopenharmony_ci SOF_DEBUGFS_ACCESS_D0_ONLY); 72362306a36Sopenharmony_ci break; 72462306a36Sopenharmony_ci case SOF_IPC_REGION_EXCEPTION: 72562306a36Sopenharmony_ci sdev->dsp_oops_offset = window_offset + elem->offset; 72662306a36Sopenharmony_ci snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, 72762306a36Sopenharmony_ci window_offset + elem->offset, 72862306a36Sopenharmony_ci elem->size, "exception", 72962306a36Sopenharmony_ci SOF_DEBUGFS_ACCESS_D0_ONLY); 73062306a36Sopenharmony_ci break; 73162306a36Sopenharmony_ci default: 73262306a36Sopenharmony_ci dev_err(sdev->dev, "%s: Illegal window info: %u\n", 73362306a36Sopenharmony_ci __func__, elem->type); 73462306a36Sopenharmony_ci return; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (outbox_size == 0 || inbox_size == 0) { 73962306a36Sopenharmony_ci dev_err(sdev->dev, "%s: Illegal mailbox window\n", __func__); 74062306a36Sopenharmony_ci return; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci sdev->dsp_box.offset = inbox_offset; 74462306a36Sopenharmony_ci sdev->dsp_box.size = inbox_size; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci sdev->host_box.offset = outbox_offset; 74762306a36Sopenharmony_ci sdev->host_box.size = outbox_size; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci sdev->stream_box.offset = stream_offset; 75062306a36Sopenharmony_ci sdev->stream_box.size = stream_size; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci sdev->debug_box.offset = debug_offset; 75362306a36Sopenharmony_ci sdev->debug_box.size = debug_size; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", 75662306a36Sopenharmony_ci inbox_offset, inbox_size); 75762306a36Sopenharmony_ci dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", 75862306a36Sopenharmony_ci outbox_offset, outbox_size); 75962306a36Sopenharmony_ci dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", 76062306a36Sopenharmony_ci stream_offset, stream_size); 76162306a36Sopenharmony_ci dev_dbg(sdev->dev, " debug region 0x%x - size 0x%x\n", 76262306a36Sopenharmony_ci debug_offset, debug_size); 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic int ipc3_init_reply_data_buffer(struct snd_sof_dev *sdev) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci struct snd_sof_ipc_msg *msg = &sdev->ipc->msg; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci msg->reply_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); 77062306a36Sopenharmony_ci if (!msg->reply_data) 77162306a36Sopenharmony_ci return -ENOMEM; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci sdev->ipc->max_payload_size = SOF_IPC_MSG_MAX_SIZE; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci return 0; 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ciint sof_ipc3_validate_fw_version(struct snd_sof_dev *sdev) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci struct sof_ipc_fw_ready *ready = &sdev->fw_ready; 78162306a36Sopenharmony_ci struct sof_ipc_fw_version *v = &ready->version; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci dev_info(sdev->dev, 78462306a36Sopenharmony_ci "Firmware info: version %d:%d:%d-%s\n", v->major, v->minor, 78562306a36Sopenharmony_ci v->micro, v->tag); 78662306a36Sopenharmony_ci dev_info(sdev->dev, 78762306a36Sopenharmony_ci "Firmware: ABI %d:%d:%d Kernel ABI %d:%d:%d\n", 78862306a36Sopenharmony_ci SOF_ABI_VERSION_MAJOR(v->abi_version), 78962306a36Sopenharmony_ci SOF_ABI_VERSION_MINOR(v->abi_version), 79062306a36Sopenharmony_ci SOF_ABI_VERSION_PATCH(v->abi_version), 79162306a36Sopenharmony_ci SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, v->abi_version)) { 79462306a36Sopenharmony_ci dev_err(sdev->dev, "incompatible FW ABI version\n"); 79562306a36Sopenharmony_ci return -EINVAL; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS) && 79962306a36Sopenharmony_ci SOF_ABI_VERSION_MINOR(v->abi_version) > SOF_ABI_MINOR) { 80062306a36Sopenharmony_ci dev_err(sdev->dev, "FW ABI is more recent than kernel\n"); 80162306a36Sopenharmony_ci return -EINVAL; 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci if (ready->flags & SOF_IPC_INFO_BUILD) { 80562306a36Sopenharmony_ci dev_info(sdev->dev, 80662306a36Sopenharmony_ci "Firmware debug build %d on %s-%s - options:\n" 80762306a36Sopenharmony_ci " GDB: %s\n" 80862306a36Sopenharmony_ci " lock debug: %s\n" 80962306a36Sopenharmony_ci " lock vdebug: %s\n", 81062306a36Sopenharmony_ci v->build, v->date, v->time, 81162306a36Sopenharmony_ci (ready->flags & SOF_IPC_INFO_GDB) ? 81262306a36Sopenharmony_ci "enabled" : "disabled", 81362306a36Sopenharmony_ci (ready->flags & SOF_IPC_INFO_LOCKS) ? 81462306a36Sopenharmony_ci "enabled" : "disabled", 81562306a36Sopenharmony_ci (ready->flags & SOF_IPC_INFO_LOCKSV) ? 81662306a36Sopenharmony_ci "enabled" : "disabled"); 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci /* copy the fw_version into debugfs at first boot */ 82062306a36Sopenharmony_ci memcpy(&sdev->fw_version, v, sizeof(*v)); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci return 0; 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic int ipc3_fw_ready(struct snd_sof_dev *sdev, u32 cmd) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; 82862306a36Sopenharmony_ci int offset; 82962306a36Sopenharmony_ci int ret; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci /* mailbox must be on 4k boundary */ 83262306a36Sopenharmony_ci offset = snd_sof_dsp_get_mailbox_offset(sdev); 83362306a36Sopenharmony_ci if (offset < 0) { 83462306a36Sopenharmony_ci dev_err(sdev->dev, "%s: no mailbox offset\n", __func__); 83562306a36Sopenharmony_ci return offset; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci dev_dbg(sdev->dev, "DSP is ready 0x%8.8x offset 0x%x\n", cmd, offset); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci /* no need to re-check version/ABI for subsequent boots */ 84162306a36Sopenharmony_ci if (!sdev->first_boot) 84262306a36Sopenharmony_ci return 0; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* 84562306a36Sopenharmony_ci * copy data from the DSP FW ready offset 84662306a36Sopenharmony_ci * Subsequent error handling is not needed for BLK_TYPE_SRAM 84762306a36Sopenharmony_ci */ 84862306a36Sopenharmony_ci ret = snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, fw_ready, 84962306a36Sopenharmony_ci sizeof(*fw_ready)); 85062306a36Sopenharmony_ci if (ret) { 85162306a36Sopenharmony_ci dev_err(sdev->dev, 85262306a36Sopenharmony_ci "Unable to read fw_ready, read from TYPE_SRAM failed\n"); 85362306a36Sopenharmony_ci return ret; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci /* make sure ABI version is compatible */ 85762306a36Sopenharmony_ci ret = sof_ipc3_validate_fw_version(sdev); 85862306a36Sopenharmony_ci if (ret < 0) 85962306a36Sopenharmony_ci return ret; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci /* now check for extended data */ 86262306a36Sopenharmony_ci ipc3_fw_parse_ext_data(sdev, offset + sizeof(struct sof_ipc_fw_ready)); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci ipc3_get_windows(sdev); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci return ipc3_init_reply_data_buffer(sdev); 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci/* IPC stream position. */ 87062306a36Sopenharmony_cistatic void ipc3_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci struct snd_soc_component *scomp = sdev->component; 87362306a36Sopenharmony_ci struct snd_sof_pcm_stream *stream; 87462306a36Sopenharmony_ci struct sof_ipc_stream_posn posn; 87562306a36Sopenharmony_ci struct snd_sof_pcm *spcm; 87662306a36Sopenharmony_ci int direction, ret; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction); 87962306a36Sopenharmony_ci if (!spcm) { 88062306a36Sopenharmony_ci dev_err(sdev->dev, "period elapsed for unknown stream, msg_id %d\n", 88162306a36Sopenharmony_ci msg_id); 88262306a36Sopenharmony_ci return; 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci stream = &spcm->stream[direction]; 88662306a36Sopenharmony_ci ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn)); 88762306a36Sopenharmony_ci if (ret < 0) { 88862306a36Sopenharmony_ci dev_warn(sdev->dev, "failed to read stream position: %d\n", ret); 88962306a36Sopenharmony_ci return; 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci trace_sof_ipc3_period_elapsed_position(sdev, &posn); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci memcpy(&stream->posn, &posn, sizeof(posn)); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci if (spcm->pcm.compress) 89762306a36Sopenharmony_ci snd_sof_compr_fragment_elapsed(stream->cstream); 89862306a36Sopenharmony_ci else if (stream->substream->runtime && 89962306a36Sopenharmony_ci !stream->substream->runtime->no_period_wakeup) 90062306a36Sopenharmony_ci /* only inform ALSA for period_wakeup mode */ 90162306a36Sopenharmony_ci snd_sof_pcm_period_elapsed(stream->substream); 90262306a36Sopenharmony_ci} 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci/* DSP notifies host of an XRUN within FW */ 90562306a36Sopenharmony_cistatic void ipc3_xrun(struct snd_sof_dev *sdev, u32 msg_id) 90662306a36Sopenharmony_ci{ 90762306a36Sopenharmony_ci struct snd_soc_component *scomp = sdev->component; 90862306a36Sopenharmony_ci struct snd_sof_pcm_stream *stream; 90962306a36Sopenharmony_ci struct sof_ipc_stream_posn posn; 91062306a36Sopenharmony_ci struct snd_sof_pcm *spcm; 91162306a36Sopenharmony_ci int direction, ret; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction); 91462306a36Sopenharmony_ci if (!spcm) { 91562306a36Sopenharmony_ci dev_err(sdev->dev, "XRUN for unknown stream, msg_id %d\n", 91662306a36Sopenharmony_ci msg_id); 91762306a36Sopenharmony_ci return; 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci stream = &spcm->stream[direction]; 92162306a36Sopenharmony_ci ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn)); 92262306a36Sopenharmony_ci if (ret < 0) { 92362306a36Sopenharmony_ci dev_warn(sdev->dev, "failed to read overrun position: %d\n", ret); 92462306a36Sopenharmony_ci return; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci dev_dbg(sdev->dev, "posn XRUN: host %llx comp %d size %d\n", 92862306a36Sopenharmony_ci posn.host_posn, posn.xrun_comp_id, posn.xrun_size); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci#if defined(CONFIG_SND_SOC_SOF_DEBUG_XRUN_STOP) 93162306a36Sopenharmony_ci /* stop PCM on XRUN - used for pipeline debug */ 93262306a36Sopenharmony_ci memcpy(&stream->posn, &posn, sizeof(posn)); 93362306a36Sopenharmony_ci snd_pcm_stop_xrun(stream->substream); 93462306a36Sopenharmony_ci#endif 93562306a36Sopenharmony_ci} 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci/* stream notifications from firmware */ 93862306a36Sopenharmony_cistatic void ipc3_stream_message(struct snd_sof_dev *sdev, void *msg_buf) 93962306a36Sopenharmony_ci{ 94062306a36Sopenharmony_ci struct sof_ipc_cmd_hdr *hdr = msg_buf; 94162306a36Sopenharmony_ci u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; 94262306a36Sopenharmony_ci u32 msg_id = SOF_IPC_MESSAGE_ID(hdr->cmd); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci switch (msg_type) { 94562306a36Sopenharmony_ci case SOF_IPC_STREAM_POSITION: 94662306a36Sopenharmony_ci ipc3_period_elapsed(sdev, msg_id); 94762306a36Sopenharmony_ci break; 94862306a36Sopenharmony_ci case SOF_IPC_STREAM_TRIG_XRUN: 94962306a36Sopenharmony_ci ipc3_xrun(sdev, msg_id); 95062306a36Sopenharmony_ci break; 95162306a36Sopenharmony_ci default: 95262306a36Sopenharmony_ci dev_err(sdev->dev, "unhandled stream message %#x\n", 95362306a36Sopenharmony_ci msg_id); 95462306a36Sopenharmony_ci break; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci} 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci/* component notifications from firmware */ 95962306a36Sopenharmony_cistatic void ipc3_comp_notification(struct snd_sof_dev *sdev, void *msg_buf) 96062306a36Sopenharmony_ci{ 96162306a36Sopenharmony_ci const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; 96262306a36Sopenharmony_ci struct sof_ipc_cmd_hdr *hdr = msg_buf; 96362306a36Sopenharmony_ci u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci switch (msg_type) { 96662306a36Sopenharmony_ci case SOF_IPC_COMP_GET_VALUE: 96762306a36Sopenharmony_ci case SOF_IPC_COMP_GET_DATA: 96862306a36Sopenharmony_ci break; 96962306a36Sopenharmony_ci default: 97062306a36Sopenharmony_ci dev_err(sdev->dev, "unhandled component message %#x\n", msg_type); 97162306a36Sopenharmony_ci return; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci if (tplg_ops->control->update) 97562306a36Sopenharmony_ci tplg_ops->control->update(sdev, msg_buf); 97662306a36Sopenharmony_ci} 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_cistatic void ipc3_trace_message(struct snd_sof_dev *sdev, void *msg_buf) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci struct sof_ipc_cmd_hdr *hdr = msg_buf; 98162306a36Sopenharmony_ci u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci switch (msg_type) { 98462306a36Sopenharmony_ci case SOF_IPC_TRACE_DMA_POSITION: 98562306a36Sopenharmony_ci ipc3_dtrace_posn_update(sdev, msg_buf); 98662306a36Sopenharmony_ci break; 98762306a36Sopenharmony_ci default: 98862306a36Sopenharmony_ci dev_err(sdev->dev, "unhandled trace message %#x\n", msg_type); 98962306a36Sopenharmony_ci break; 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci} 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_civoid sof_ipc3_do_rx_work(struct snd_sof_dev *sdev, struct sof_ipc_cmd_hdr *hdr, void *msg_buf) 99462306a36Sopenharmony_ci{ 99562306a36Sopenharmony_ci ipc3_rx_callback rx_callback = NULL; 99662306a36Sopenharmony_ci u32 cmd; 99762306a36Sopenharmony_ci int err; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci ipc3_log_header(sdev->dev, "ipc rx", hdr->cmd); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci if (hdr->size < sizeof(*hdr) || hdr->size > SOF_IPC_MSG_MAX_SIZE) { 100262306a36Sopenharmony_ci dev_err(sdev->dev, "The received message size is invalid: %u\n", 100362306a36Sopenharmony_ci hdr->size); 100462306a36Sopenharmony_ci return; 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci cmd = hdr->cmd & SOF_GLB_TYPE_MASK; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci /* check message type */ 101062306a36Sopenharmony_ci switch (cmd) { 101162306a36Sopenharmony_ci case SOF_IPC_GLB_REPLY: 101262306a36Sopenharmony_ci dev_err(sdev->dev, "ipc reply unknown\n"); 101362306a36Sopenharmony_ci break; 101462306a36Sopenharmony_ci case SOF_IPC_FW_READY: 101562306a36Sopenharmony_ci /* check for FW boot completion */ 101662306a36Sopenharmony_ci if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) { 101762306a36Sopenharmony_ci err = ipc3_fw_ready(sdev, cmd); 101862306a36Sopenharmony_ci if (err < 0) 101962306a36Sopenharmony_ci sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED); 102062306a36Sopenharmony_ci else 102162306a36Sopenharmony_ci sof_set_fw_state(sdev, SOF_FW_BOOT_READY_OK); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci /* wake up firmware loader */ 102462306a36Sopenharmony_ci wake_up(&sdev->boot_wait); 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci break; 102762306a36Sopenharmony_ci case SOF_IPC_GLB_COMPOUND: 102862306a36Sopenharmony_ci case SOF_IPC_GLB_TPLG_MSG: 102962306a36Sopenharmony_ci case SOF_IPC_GLB_PM_MSG: 103062306a36Sopenharmony_ci break; 103162306a36Sopenharmony_ci case SOF_IPC_GLB_COMP_MSG: 103262306a36Sopenharmony_ci rx_callback = ipc3_comp_notification; 103362306a36Sopenharmony_ci break; 103462306a36Sopenharmony_ci case SOF_IPC_GLB_STREAM_MSG: 103562306a36Sopenharmony_ci rx_callback = ipc3_stream_message; 103662306a36Sopenharmony_ci break; 103762306a36Sopenharmony_ci case SOF_IPC_GLB_TRACE_MSG: 103862306a36Sopenharmony_ci rx_callback = ipc3_trace_message; 103962306a36Sopenharmony_ci break; 104062306a36Sopenharmony_ci default: 104162306a36Sopenharmony_ci dev_err(sdev->dev, "%s: Unknown DSP message: 0x%x\n", __func__, cmd); 104262306a36Sopenharmony_ci break; 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci /* Call local handler for the message */ 104662306a36Sopenharmony_ci if (rx_callback) 104762306a36Sopenharmony_ci rx_callback(sdev, msg_buf); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci /* Notify registered clients */ 105062306a36Sopenharmony_ci sof_client_ipc_rx_dispatcher(sdev, msg_buf); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci ipc3_log_header(sdev->dev, "ipc rx done", hdr->cmd); 105362306a36Sopenharmony_ci} 105462306a36Sopenharmony_ciEXPORT_SYMBOL(sof_ipc3_do_rx_work); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci/* DSP firmware has sent host a message */ 105762306a36Sopenharmony_cistatic void sof_ipc3_rx_msg(struct snd_sof_dev *sdev) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci struct sof_ipc_cmd_hdr hdr; 106062306a36Sopenharmony_ci void *msg_buf; 106162306a36Sopenharmony_ci int err; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci /* read back header */ 106462306a36Sopenharmony_ci err = snd_sof_ipc_msg_data(sdev, NULL, &hdr, sizeof(hdr)); 106562306a36Sopenharmony_ci if (err < 0) { 106662306a36Sopenharmony_ci dev_warn(sdev->dev, "failed to read IPC header: %d\n", err); 106762306a36Sopenharmony_ci return; 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci if (hdr.size < sizeof(hdr) || hdr.size > SOF_IPC_MSG_MAX_SIZE) { 107162306a36Sopenharmony_ci dev_err(sdev->dev, "The received message size is invalid\n"); 107262306a36Sopenharmony_ci return; 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci /* read the full message */ 107662306a36Sopenharmony_ci msg_buf = kmalloc(hdr.size, GFP_KERNEL); 107762306a36Sopenharmony_ci if (!msg_buf) 107862306a36Sopenharmony_ci return; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci err = snd_sof_ipc_msg_data(sdev, NULL, msg_buf, hdr.size); 108162306a36Sopenharmony_ci if (err < 0) { 108262306a36Sopenharmony_ci dev_err(sdev->dev, "%s: Failed to read message: %d\n", __func__, err); 108362306a36Sopenharmony_ci kfree(msg_buf); 108462306a36Sopenharmony_ci return; 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci sof_ipc3_do_rx_work(sdev, &hdr, msg_buf); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci kfree(msg_buf); 109062306a36Sopenharmony_ci} 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_cistatic int sof_ipc3_set_core_state(struct snd_sof_dev *sdev, int core_idx, bool on) 109362306a36Sopenharmony_ci{ 109462306a36Sopenharmony_ci struct sof_ipc_pm_core_config core_cfg = { 109562306a36Sopenharmony_ci .hdr.size = sizeof(core_cfg), 109662306a36Sopenharmony_ci .hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE, 109762306a36Sopenharmony_ci }; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci if (on) 110062306a36Sopenharmony_ci core_cfg.enable_mask = sdev->enabled_cores_mask | BIT(core_idx); 110162306a36Sopenharmony_ci else 110262306a36Sopenharmony_ci core_cfg.enable_mask = sdev->enabled_cores_mask & ~BIT(core_idx); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci return sof_ipc3_tx_msg(sdev, &core_cfg, sizeof(core_cfg), NULL, 0, false); 110562306a36Sopenharmony_ci} 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_cistatic int sof_ipc3_ctx_ipc(struct snd_sof_dev *sdev, int cmd) 110862306a36Sopenharmony_ci{ 110962306a36Sopenharmony_ci struct sof_ipc_pm_ctx pm_ctx = { 111062306a36Sopenharmony_ci .hdr.size = sizeof(pm_ctx), 111162306a36Sopenharmony_ci .hdr.cmd = SOF_IPC_GLB_PM_MSG | cmd, 111262306a36Sopenharmony_ci }; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci /* send ctx save ipc to dsp */ 111562306a36Sopenharmony_ci return sof_ipc3_tx_msg(sdev, &pm_ctx, sizeof(pm_ctx), NULL, 0, false); 111662306a36Sopenharmony_ci} 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_cistatic int sof_ipc3_ctx_save(struct snd_sof_dev *sdev) 111962306a36Sopenharmony_ci{ 112062306a36Sopenharmony_ci return sof_ipc3_ctx_ipc(sdev, SOF_IPC_PM_CTX_SAVE); 112162306a36Sopenharmony_ci} 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_cistatic int sof_ipc3_ctx_restore(struct snd_sof_dev *sdev) 112462306a36Sopenharmony_ci{ 112562306a36Sopenharmony_ci return sof_ipc3_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE); 112662306a36Sopenharmony_ci} 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_cistatic int sof_ipc3_set_pm_gate(struct snd_sof_dev *sdev, u32 flags) 112962306a36Sopenharmony_ci{ 113062306a36Sopenharmony_ci struct sof_ipc_pm_gate pm_gate; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci memset(&pm_gate, 0, sizeof(pm_gate)); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci /* configure pm_gate ipc message */ 113562306a36Sopenharmony_ci pm_gate.hdr.size = sizeof(pm_gate); 113662306a36Sopenharmony_ci pm_gate.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE; 113762306a36Sopenharmony_ci pm_gate.flags = flags; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci /* send pm_gate ipc to dsp */ 114062306a36Sopenharmony_ci return sof_ipc_tx_message_no_pm_no_reply(sdev->ipc, &pm_gate, sizeof(pm_gate)); 114162306a36Sopenharmony_ci} 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_cistatic const struct sof_ipc_pm_ops ipc3_pm_ops = { 114462306a36Sopenharmony_ci .ctx_save = sof_ipc3_ctx_save, 114562306a36Sopenharmony_ci .ctx_restore = sof_ipc3_ctx_restore, 114662306a36Sopenharmony_ci .set_core_state = sof_ipc3_set_core_state, 114762306a36Sopenharmony_ci .set_pm_gate = sof_ipc3_set_pm_gate, 114862306a36Sopenharmony_ci}; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ciconst struct sof_ipc_ops ipc3_ops = { 115162306a36Sopenharmony_ci .tplg = &ipc3_tplg_ops, 115262306a36Sopenharmony_ci .pm = &ipc3_pm_ops, 115362306a36Sopenharmony_ci .pcm = &ipc3_pcm_ops, 115462306a36Sopenharmony_ci .fw_loader = &ipc3_loader_ops, 115562306a36Sopenharmony_ci .fw_tracing = &ipc3_dtrace_ops, 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci .tx_msg = sof_ipc3_tx_msg, 115862306a36Sopenharmony_ci .rx_msg = sof_ipc3_rx_msg, 115962306a36Sopenharmony_ci .set_get_data = sof_ipc3_set_get_data, 116062306a36Sopenharmony_ci .get_reply = sof_ipc3_get_reply, 116162306a36Sopenharmony_ci}; 1162