summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2022-05-05 16:08:39 +0100
committerMark Brown <broonie@kernel.org>2022-05-05 16:08:39 +0100
commitcae640c5ff94134ddb928122365a84c2e0c478d5 (patch)
tree28e9e4c5a2c69002481c9e82119291d49804a2d9
parente14bd35ef44606c7d55ccb3660cededd421a14a1 (diff)
parentceb89acc4dc8f071f63f8d64442c7a5d768e4c9d (diff)
downloadlinux-cae640c5ff94134ddb928122365a84c2e0c478d5.tar.bz2
ASoC: SOF: IPC4: Introduce message handling functionality
Merge series from Peter Ujfalusi <peter.ujfalusi@linux.intel.com>: The series adds the basic IPC4 message handling code, implementing the ipc callbacks. Due to the difference between IPC3 and IPC4 messaging we need to introduce new message container for IPC4, but the SOF internal callbacks and structures can be kept as they were and leaving it for the IPC specific code to handle the differences. The series provides the foundation for both lowe level (sound/soc/sof/intel) and high level IPC4 implementation (topologies, firmware loading, control handling, etc).
-rw-r--r--include/sound/sof/ipc4/header.h460
-rw-r--r--sound/soc/sof/Makefile3
-rw-r--r--sound/soc/sof/ipc4.c604
-rw-r--r--sound/soc/sof/sof-priv.h4
4 files changed, 1070 insertions, 1 deletions
diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h
new file mode 100644
index 000000000000..b8b8e5b5e3e1
--- /dev/null
+++ b/include/sound/sof/ipc4/header.h
@@ -0,0 +1,460 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2022 Intel Corporation. All rights reserved.
+ */
+
+#ifndef __INCLUDE_SOUND_SOF_IPC4_HEADER_H__
+#define __INCLUDE_SOUND_SOF_IPC4_HEADER_H__
+
+#include <linux/types.h>
+#include <uapi/sound/sof/abi.h>
+
+/* maximum message size for mailbox Tx/Rx */
+#define SOF_IPC4_MSG_MAX_SIZE 4096
+
+/** \addtogroup sof_uapi uAPI
+ * SOF uAPI specification.
+ * @{
+ */
+
+/**
+ * struct sof_ipc4_msg - Placeholder of an IPC4 message
+ * @header_u64: IPC4 header as single u64 number
+ * @primary: Primary, mandatory part of the header
+ * @extension: Extended part of the header, if not used it should be
+ * set to 0
+ * @data_size: Size of data in bytes pointed by @data_ptr
+ * @data_ptr: Pointer to the optional payload of a message
+ */
+struct sof_ipc4_msg {
+ union {
+ u64 header_u64;
+ struct {
+ u32 primary;
+ u32 extension;
+ };
+ };
+
+ size_t data_size;
+ void *data_ptr;
+};
+
+/**
+ * struct sof_ipc4_tuple - Generic type/ID and parameter tuple
+ * @type: type/ID
+ * @size: size of the @value array in bytes
+ * @value: value for the given type
+ */
+struct sof_ipc4_tuple {
+ uint32_t type;
+ uint32_t size;
+ uint32_t value[];
+} __packed;
+
+/*
+ * IPC4 messages have two 32 bit identifier made up as follows :-
+ *
+ * header - msg type, msg id, msg direction ...
+ * extension - extra params such as msg data size in mailbox
+ *
+ * These are sent at the start of the IPC message in the mailbox. Messages
+ * should not be sent in the doorbell (special exceptions for firmware).
+ */
+
+/*
+ * IPC4 primary header bit allocation for messages
+ * bit 0-23: message type specific
+ * bit 24-28: type: enum sof_ipc4_global_msg if target is SOF_IPC4_FW_GEN_MSG
+ * enum sof_ipc4_module_type if target is SOF_IPC4_MODULE_MSG
+ * bit 29: response - sof_ipc4_msg_dir
+ * bit 30: target - enum sof_ipc4_msg_target
+ * bit 31: reserved, unused
+ */
+
+/* Value of target field - must fit into 1 bit */
+enum sof_ipc4_msg_target {
+ /* Global FW message */
+ SOF_IPC4_FW_GEN_MSG,
+
+ /* Module message */
+ SOF_IPC4_MODULE_MSG
+};
+
+/* Value of type field - must fit into 5 bits */
+enum sof_ipc4_global_msg {
+ SOF_IPC4_GLB_BOOT_CONFIG,
+ SOF_IPC4_GLB_ROM_CONTROL,
+ SOF_IPC4_GLB_IPCGATEWAY_CMD,
+
+ /* 3 .. 12: RESERVED - do not use */
+
+ SOF_IPC4_GLB_PERF_MEASUREMENTS_CMD = 13,
+ SOF_IPC4_GLB_CHAIN_DMA,
+
+ SOF_IPC4_GLB_LOAD_MULTIPLE_MODULES,
+ SOF_IPC4_GLB_UNLOAD_MULTIPLE_MODULES,
+
+ /* pipeline settings */
+ SOF_IPC4_GLB_CREATE_PIPELINE,
+ SOF_IPC4_GLB_DELETE_PIPELINE,
+ SOF_IPC4_GLB_SET_PIPELINE_STATE,
+ SOF_IPC4_GLB_GET_PIPELINE_STATE,
+ SOF_IPC4_GLB_GET_PIPELINE_CONTEXT_SIZE,
+ SOF_IPC4_GLB_SAVE_PIPELINE,
+ SOF_IPC4_GLB_RESTORE_PIPELINE,
+
+ /* Loads library (using Code Load or HD/A Host Output DMA) */
+ SOF_IPC4_GLB_LOAD_LIBRARY,
+
+ /* 25: RESERVED - do not use */
+
+ SOF_IPC4_GLB_INTERNAL_MESSAGE = 26,
+
+ /* Notification (FW to SW driver) */
+ SOF_IPC4_GLB_NOTIFICATION,
+
+ /* 28 .. 31: RESERVED - do not use */
+
+ SOF_IPC4_GLB_TYPE_LAST,
+};
+
+/* Value of response field - must fit into 1 bit */
+enum sof_ipc4_msg_dir {
+ SOF_IPC4_MSG_REQUEST,
+ SOF_IPC4_MSG_REPLY,
+};
+
+enum sof_ipc4_pipeline_state {
+ SOF_IPC4_PIPE_INVALID_STATE,
+ SOF_IPC4_PIPE_UNINITIALIZED,
+ SOF_IPC4_PIPE_RESET,
+ SOF_IPC4_PIPE_PAUSED,
+ SOF_IPC4_PIPE_RUNNING,
+ SOF_IPC4_PIPE_EOS
+};
+
+/* Generic message fields (bit 24-30) */
+
+/* encoded to header's msg_tgt field */
+#define SOF_IPC4_MSG_TARGET_SHIFT 30
+#define SOF_IPC4_MSG_TARGET_MASK BIT(30)
+#define SOF_IPC4_MSG_TARGET(x) ((x) << SOF_IPC4_MSG_TARGET_SHIFT)
+#define SOF_IPC4_MSG_IS_MODULE_MSG(x) ((x) & SOF_IPC4_MSG_TARGET_MASK ? 1 : 0)
+
+/* encoded to header's rsp field */
+#define SOF_IPC4_MSG_DIR_SHIFT 29
+#define SOF_IPC4_MSG_DIR_MASK BIT(29)
+#define SOF_IPC4_MSG_DIR(x) ((x) << SOF_IPC4_MSG_DIR_SHIFT)
+
+/* encoded to header's type field */
+#define SOF_IPC4_MSG_TYPE_SHIFT 24
+#define SOF_IPC4_MSG_TYPE_MASK GENMASK(28, 24)
+#define SOF_IPC4_MSG_TYPE_SET(x) (((x) << SOF_IPC4_MSG_TYPE_SHIFT) & \
+ SOF_IPC4_MSG_TYPE_MASK)
+#define SOF_IPC4_MSG_TYPE_GET(x) (((x) & SOF_IPC4_MSG_TYPE_MASK) >> \
+ SOF_IPC4_MSG_TYPE_SHIFT)
+
+/* Global message type specific field definitions */
+
+/* pipeline creation ipc msg */
+#define SOF_IPC4_GLB_PIPE_INSTANCE_SHIFT 16
+#define SOF_IPC4_GLB_PIPE_INSTANCE_MASK GENMASK(23, 16)
+#define SOF_IPC4_GLB_PIPE_INSTANCE_ID(x) ((x) << SOF_IPC4_GLB_PIPE_INSTANCE_SHIFT)
+
+#define SOF_IPC4_GLB_PIPE_PRIORITY_SHIFT 11
+#define SOF_IPC4_GLB_PIPE_PRIORITY_MASK GENMASK(15, 11)
+#define SOF_IPC4_GLB_PIPE_PRIORITY(x) ((x) << SOF_IPC4_GLB_PIPE_PRIORITY_SHIFT)
+
+#define SOF_IPC4_GLB_PIPE_MEM_SIZE_SHIFT 0
+#define SOF_IPC4_GLB_PIPE_MEM_SIZE_MASK GENMASK(10, 0)
+#define SOF_IPC4_GLB_PIPE_MEM_SIZE(x) ((x) << SOF_IPC4_GLB_PIPE_MEM_SIZE_SHIFT)
+
+#define SOF_IPC4_GLB_PIPE_EXT_LP_SHIFT 0
+#define SOF_IPC4_GLB_PIPE_EXT_LP_MASK BIT(0)
+#define SOF_IPC4_GLB_PIPE_EXT_LP(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_LP_SHIFT)
+
+/* pipeline set state ipc msg */
+#define SOF_IPC4_GLB_PIPE_STATE_ID_SHIFT 16
+#define SOF_IPC4_GLB_PIPE_STATE_ID_MASK GENMASK(23, 16)
+#define SOF_IPC4_GLB_PIPE_STATE_ID(x) ((x) << SOF_IPC4_GLB_PIPE_STATE_ID_SHIFT)
+
+#define SOF_IPC4_GLB_PIPE_STATE_SHIFT 0
+#define SOF_IPC4_GLB_PIPE_STATE_MASK GENMASK(15, 0)
+#define SOF_IPC4_GLB_PIPE_STATE(x) ((x) << SOF_IPC4_GLB_PIPE_STATE_SHIFT)
+
+enum sof_ipc4_channel_config {
+ /* one channel only. */
+ SOF_IPC4_CHANNEL_CONFIG_MONO,
+ /* L & R. */
+ SOF_IPC4_CHANNEL_CONFIG_STEREO,
+ /* L, R & LFE; PCM only. */
+ SOF_IPC4_CHANNEL_CONFIG_2_POINT_1,
+ /* L, C & R; MP3 & AAC only. */
+ SOF_IPC4_CHANNEL_CONFIG_3_POINT_0,
+ /* L, C, R & LFE; PCM only. */
+ SOF_IPC4_CHANNEL_CONFIG_3_POINT_1,
+ /* L, R, Ls & Rs; PCM only. */
+ SOF_IPC4_CHANNEL_CONFIG_QUATRO,
+ /* L, C, R & Cs; MP3 & AAC only. */
+ SOF_IPC4_CHANNEL_CONFIG_4_POINT_0,
+ /* L, C, R, Ls & Rs. */
+ SOF_IPC4_CHANNEL_CONFIG_5_POINT_0,
+ /* L, C, R, Ls, Rs & LFE. */
+ SOF_IPC4_CHANNEL_CONFIG_5_POINT_1,
+ /* one channel replicated in two. */
+ SOF_IPC4_CHANNEL_CONFIG_DUAL_MONO,
+ /* Stereo (L,R) in 4 slots, 1st stream: [ L, R, -, - ] */
+ SOF_IPC4_CHANNEL_CONFIG_I2S_DUAL_STEREO_0,
+ /* Stereo (L,R) in 4 slots, 2nd stream: [ -, -, L, R ] */
+ SOF_IPC4_CHANNEL_CONFIG_I2S_DUAL_STEREO_1,
+ /* L, C, R, Ls, Rs & LFE., LS, RS */
+ SOF_IPC4_CHANNEL_CONFIG_7_POINT_1,
+};
+
+enum sof_ipc4_interleaved_style {
+ SOF_IPC4_CHANNELS_INTERLEAVED,
+ SOF_IPC4_CHANNELS_NONINTERLEAVED,
+};
+
+enum sof_ipc4_sample_type {
+ SOF_IPC4_MSB_INTEGER, /* integer with Most Significant Byte first */
+ SOF_IPC4_LSB_INTEGER, /* integer with Least Significant Byte first */
+};
+
+struct sof_ipc4_audio_format {
+ uint32_t sampling_frequency;
+ uint32_t bit_depth;
+ uint32_t ch_map;
+ uint32_t ch_cfg; /* sof_ipc4_channel_config */
+ uint32_t interleaving_style;
+ uint32_t fmt_cfg; /* channels_count valid_bit_depth s_type */
+} __packed __aligned(4);
+
+#define SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT_SHIFT 0
+#define SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT_MASK GENMASK(7, 0)
+#define SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(x) \
+ ((x) & SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT_MASK)
+#define SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH_SHIFT 8
+#define SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH_MASK GENMASK(15, 8)
+#define SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(x) \
+ (((x) & SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH_MASK) >> \
+ SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH_SHIFT)
+#define SOF_IPC4_AUDIO_FORMAT_CFG_SAMPLE_TYPE_SHIFT 16
+#define SOF_IPC4_AUDIO_FORMAT_CFG_SAMPLE_TYPE_MASK GENMASK(23, 16)
+#define SOF_IPC4_AUDIO_FORMAT_CFG_SAMPLE_TYPE(x) \
+ (((x) & SOF_IPC4_AUDIO_FORMAT_CFG_SAMPLE_TYPE_MASK) >> \
+ SOF_IPC4_AUDIO_FORMAT_CFG_SAMPLE_TYPE_SHIFT)
+
+/* Module message type specific field definitions */
+
+enum sof_ipc4_module_type {
+ SOF_IPC4_MOD_INIT_INSTANCE,
+ SOF_IPC4_MOD_CONFIG_GET,
+ SOF_IPC4_MOD_CONFIG_SET,
+ SOF_IPC4_MOD_LARGE_CONFIG_GET,
+ SOF_IPC4_MOD_LARGE_CONFIG_SET,
+ SOF_IPC4_MOD_BIND,
+ SOF_IPC4_MOD_UNBIND,
+ SOF_IPC4_MOD_SET_DX,
+ SOF_IPC4_MOD_SET_D0IX,
+ SOF_IPC4_MOD_ENTER_MODULE_RESTORE,
+ SOF_IPC4_MOD_EXIT_MODULE_RESTORE,
+ SOF_IPC4_MOD_DELETE_INSTANCE,
+
+ SOF_IPC4_MOD_TYPE_LAST,
+};
+
+struct sof_ipc4_base_module_cfg {
+ uint32_t cpc; /* the max count of Cycles Per Chunk processing */
+ uint32_t ibs; /* input Buffer Size (in bytes) */
+ uint32_t obs; /* output Buffer Size (in bytes) */
+ uint32_t is_pages; /* number of physical pages used */
+ struct sof_ipc4_audio_format audio_fmt;
+} __packed __aligned(4);
+
+/* common module ipc msg */
+#define SOF_IPC4_MOD_INSTANCE_SHIFT 16
+#define SOF_IPC4_MOD_INSTANCE_MASK GENMASK(23, 16)
+#define SOF_IPC4_MOD_INSTANCE(x) ((x) << SOF_IPC4_MOD_INSTANCE_SHIFT)
+
+#define SOF_IPC4_MOD_ID_SHIFT 0
+#define SOF_IPC4_MOD_ID_MASK GENMASK(15, 0)
+#define SOF_IPC4_MOD_ID(x) ((x) << SOF_IPC4_MOD_ID_SHIFT)
+
+/* init module ipc msg */
+#define SOF_IPC4_MOD_EXT_PARAM_SIZE_SHIFT 0
+#define SOF_IPC4_MOD_EXT_PARAM_SIZE_MASK GENMASK(15, 0)
+#define SOF_IPC4_MOD_EXT_PARAM_SIZE(x) ((x) << SOF_IPC4_MOD_EXT_PARAM_SIZE_SHIFT)
+
+#define SOF_IPC4_MOD_EXT_PPL_ID_SHIFT 16
+#define SOF_IPC4_MOD_EXT_PPL_ID_MASK GENMASK(23, 16)
+#define SOF_IPC4_MOD_EXT_PPL_ID(x) ((x) << SOF_IPC4_MOD_EXT_PPL_ID_SHIFT)
+
+#define SOF_IPC4_MOD_EXT_CORE_ID_SHIFT 24
+#define SOF_IPC4_MOD_EXT_CORE_ID_MASK GENMASK(27, 24)
+#define SOF_IPC4_MOD_EXT_CORE_ID(x) ((x) << SOF_IPC4_MOD_EXT_CORE_ID_SHIFT)
+
+#define SOF_IPC4_MOD_EXT_DOMAIN_SHIFT 28
+#define SOF_IPC4_MOD_EXT_DOMAIN_MASK BIT(28)
+#define SOF_IPC4_MOD_EXT_DOMAIN(x) ((x) << SOF_IPC4_MOD_EXT_DOMAIN_SHIFT)
+
+/* bind/unbind module ipc msg */
+#define SOF_IPC4_MOD_EXT_DST_MOD_ID_SHIFT 0
+#define SOF_IPC4_MOD_EXT_DST_MOD_ID_MASK GENMASK(15, 0)
+#define SOF_IPC4_MOD_EXT_DST_MOD_ID(x) ((x) << SOF_IPC4_MOD_EXT_DST_MOD_ID_SHIFT)
+
+#define SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE_SHIFT 16
+#define SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE_MASK GENMASK(23, 16)
+#define SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE(x) ((x) << SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE_SHIFT)
+
+#define SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID_SHIFT 24
+#define SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID_MASK GENMASK(26, 24)
+#define SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID(x) ((x) << SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID_SHIFT)
+
+#define SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID_SHIFT 27
+#define SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID_MASK GENMASK(29, 27)
+#define SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID(x) ((x) << SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID_SHIFT)
+
+#define MOD_ENABLE_LOG 6
+#define MOD_SYSTEM_TIME 20
+
+/* set module large config */
+#define SOF_IPC4_MOD_EXT_MSG_SIZE_SHIFT 0
+#define SOF_IPC4_MOD_EXT_MSG_SIZE_MASK GENMASK(19, 0)
+#define SOF_IPC4_MOD_EXT_MSG_SIZE(x) ((x) << SOF_IPC4_MOD_EXT_MSG_SIZE_SHIFT)
+
+#define SOF_IPC4_MOD_EXT_MSG_PARAM_ID_SHIFT 20
+#define SOF_IPC4_MOD_EXT_MSG_PARAM_ID_MASK GENMASK(27, 20)
+#define SOF_IPC4_MOD_EXT_MSG_PARAM_ID(x) ((x) << SOF_IPC4_MOD_EXT_MSG_PARAM_ID_SHIFT)
+
+#define SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK_SHIFT 28
+#define SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK_MASK BIT(28)
+#define SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK(x) ((x) << SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK_SHIFT)
+
+#define SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK_SHIFT 29
+#define SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK_MASK BIT(29)
+#define SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(x) ((x) << SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK_SHIFT)
+
+/* Init instance messagees */
+#define SOF_IPC4_MOD_INIT_BASEFW_MOD_ID 0
+#define SOF_IPC4_MOD_INIT_BASEFW_INSTANCE_ID 0
+
+enum sof_ipc4_base_fw_params {
+ SOF_IPC4_FW_PARAM_ENABLE_LOGS = 6,
+ SOF_IPC4_FW_PARAM_FW_CONFIG,
+ SOF_IPC4_FW_PARAM_HW_CONFIG_GET,
+ SOF_IPC4_FW_PARAM_MODULES_INFO_GET,
+ SOF_IPC4_FW_PARAM_LIBRARIES_INFO_GET = 16,
+ SOF_IPC4_FW_PARAM_SYSTEM_TIME = 20,
+};
+
+enum sof_ipc4_fw_config_params {
+ SOF_IPC4_FW_CFG_FW_VERSION,
+ SOF_IPC4_FW_CFG_MEMORY_RECLAIMED,
+ SOF_IPC4_FW_CFG_SLOW_CLOCK_FREQ_HZ,
+ SOF_IPC4_FW_CFG_FAST_CLOCK_FREQ_HZ,
+ SOF_IPC4_FW_CFG_DMA_BUFFER_CONFIG,
+ SOF_IPC4_FW_CFG_ALH_SUPPORT_LEVEL,
+ SOF_IPC4_FW_CFG_DL_MAILBOX_BYTES,
+ SOF_IPC4_FW_CFG_UL_MAILBOX_BYTES,
+ SOF_IPC4_FW_CFG_TRACE_LOG_BYTES,
+ SOF_IPC4_FW_CFG_MAX_PPL_COUNT,
+ SOF_IPC4_FW_CFG_MAX_ASTATE_COUNT,
+ SOF_IPC4_FW_CFG_MAX_MODULE_PIN_COUNT,
+ SOF_IPC4_FW_CFG_MODULES_COUNT,
+ SOF_IPC4_FW_CFG_MAX_MOD_INST_COUNT,
+ SOF_IPC4_FW_CFG_MAX_LL_TASKS_PER_PRI_COUNT,
+ SOF_IPC4_FW_CFG_LL_PRI_COUNT,
+ SOF_IPC4_FW_CFG_MAX_DP_TASKS_COUNT,
+ SOF_IPC4_FW_CFG_MAX_LIBS_COUNT,
+ SOF_IPC4_FW_CFG_SCHEDULER_CONFIG,
+ SOF_IPC4_FW_CFG_XTAL_FREQ_HZ,
+ SOF_IPC4_FW_CFG_CLOCKS_CONFIG,
+ SOF_IPC4_FW_CFG_RESERVED,
+ SOF_IPC4_FW_CFG_POWER_GATING_POLICY,
+ SOF_IPC4_FW_CFG_ASSERT_MODE,
+};
+
+struct sof_ipc4_fw_version {
+ uint16_t major;
+ uint16_t minor;
+ uint16_t hotfix;
+ uint16_t build;
+} __packed;
+
+/* Reply messages */
+
+/*
+ * IPC4 primary header bit allocation for replies
+ * bit 0-23: status
+ * bit 24-28: type: enum sof_ipc4_global_msg if target is SOF_IPC4_FW_GEN_MSG
+ * enum sof_ipc4_module_type if target is SOF_IPC4_MODULE_MSG
+ * bit 29: response - sof_ipc4_msg_dir
+ * bit 30: target - enum sof_ipc4_msg_target
+ * bit 31: reserved, unused
+ */
+
+#define SOF_IPC4_REPLY_STATUS GENMASK(23, 0)
+
+/* Notification messages */
+
+/*
+ * IPC4 primary header bit allocation for notifications
+ * bit 0-15: notification type specific
+ * bit 16-23: enum sof_ipc4_notification_type
+ * bit 24-28: SOF_IPC4_GLB_NOTIFICATION
+ * bit 29: response - sof_ipc4_msg_dir
+ * bit 30: target - enum sof_ipc4_msg_target
+ * bit 31: reserved, unused
+ */
+
+#define SOF_IPC4_MSG_IS_NOTIFICATION(x) (SOF_IPC4_MSG_TYPE_GET(x) == \
+ SOF_IPC4_GLB_NOTIFICATION)
+
+#define SOF_IPC4_NOTIFICATION_TYPE_SHIFT 16
+#define SOF_IPC4_NOTIFICATION_TYPE_MASK GENMASK(23, 16)
+#define SOF_IPC4_NOTIFICATION_TYPE_GET(x) (((x) & SOF_IPC4_NOTIFICATION_TYPE_MASK) >> \
+ SOF_IPC4_NOTIFICATION_TYPE_SHIFT)
+
+/* Value of notification type field - must fit into 8 bits */
+enum sof_ipc4_notification_type {
+ /* Phrase detected (notification from WoV module) */
+ SOF_IPC4_NOTIFY_PHRASE_DETECTED = 4,
+ /* Event from a resource (pipeline or module instance) */
+ SOF_IPC4_NOTIFY_RESOURCE_EVENT,
+ /* Debug log buffer status changed */
+ SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS,
+ /* Timestamp captured at the link */
+ SOF_IPC4_NOTIFY_TIMESTAMP_CAPTURED,
+ /* FW complete initialization */
+ SOF_IPC4_NOTIFY_FW_READY,
+ /* Audio classifier result (ACA) */
+ SOF_IPC4_NOTIFY_FW_AUD_CLASS_RESULT,
+ /* Exception caught by DSP FW */
+ SOF_IPC4_NOTIFY_EXCEPTION_CAUGHT,
+ /* 11 is skipped by the existing cavs firmware */
+ /* Custom module notification */
+ SOF_IPC4_NOTIFY_MODULE_NOTIFICATION = 12,
+ /* 13 is reserved - do not use */
+ /* Probe notify data available */
+ SOF_IPC4_NOTIFY_PROBE_DATA_AVAILABLE = 14,
+ /* AM module notifications */
+ SOF_IPC4_NOTIFY_ASYNC_MSG_SRVC_MESSAGE,
+
+ SOF_IPC4_NOTIFY_TYPE_LAST,
+};
+
+struct sof_ipc4_notify_resource_data {
+ uint32_t resource_type;
+ uint32_t resource_id;
+ uint32_t event_type;
+ uint32_t reserved;
+ uint32_t data[6];
+} __packed __aligned(4);
+
+/** @}*/
+
+#endif
diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile
index e7dc47b01437..482a4c85f4c8 100644
--- a/sound/soc/sof/Makefile
+++ b/sound/soc/sof/Makefile
@@ -2,7 +2,8 @@
snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\
control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o\
- ipc3-topology.o ipc3-control.o ipc3.o ipc3-pcm.o ipc3-loader.o
+ ipc3-topology.o ipc3-control.o ipc3.o ipc3-pcm.o ipc3-loader.o\
+ ipc4.o
ifneq ($(CONFIG_SND_SOC_SOF_CLIENT),)
snd-sof-objs += sof-client.o
endif
diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c
new file mode 100644
index 000000000000..b561312a3e0f
--- /dev/null
+++ b/sound/soc/sof/ipc4.c
@@ -0,0 +1,604 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+//
+// Authors: Rander Wang <rander.wang@linux.intel.com>
+// Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
+//
+#include <sound/sof/header.h>
+#include <sound/sof/ipc4/header.h>
+#include "sof-priv.h"
+#include "sof-audio.h"
+#include "ops.h"
+
+#ifdef DEBUG_VERBOSE
+#define sof_ipc4_dump_payload(sdev, ipc_data, size) \
+ print_hex_dump_debug("Message payload: ", \
+ DUMP_PREFIX_OFFSET, \
+ 16, 4, ipc_data, size, false)
+#else
+#define sof_ipc4_dump_payload(sdev, ipc_data, size) do { } while (0)
+#endif
+
+static const struct sof_ipc4_fw_status {
+ int status;
+ char *msg;
+} ipc4_status[] = {
+ {0, "The operation was successful"},
+ {1, "Invalid parameter specified"},
+ {2, "Unknown message type specified"},
+ {3, "Not enough space in the IPC reply buffer to complete the request"},
+ {4, "The system or resource is busy"},
+ {5, "Replaced ADSP IPC PENDING (unused)"},
+ {6, "Unknown error while processing the request"},
+ {7, "Unsupported operation requested"},
+ {8, "Reserved (ADSP_STAGE_UNINITIALIZED removed)"},
+ {9, "Specified resource not found"},
+ {10, "A resource's ID requested to be created is already assigned"},
+ {11, "Reserved (ADSP_IPC_OUT_OF_MIPS removed)"},
+ {12, "Required resource is in invalid state"},
+ {13, "Requested power transition failed to complete"},
+ {14, "Manifest of the library being loaded is invalid"},
+ {15, "Requested service or data is unavailable on the target platform"},
+ {42, "Library target address is out of storage memory range"},
+ {43, "Reserved"},
+ {44, "Image verification by CSE failed"},
+ {100, "General module management error"},
+ {101, "Module loading failed"},
+ {102, "Integrity check of the loaded module content failed"},
+ {103, "Attempt to unload code of the module in use"},
+ {104, "Other failure of module instance initialization request"},
+ {105, "Reserved (ADSP_IPC_OUT_OF_MIPS removed)"},
+ {106, "Reserved (ADSP_IPC_CONFIG_GET_ERROR removed)"},
+ {107, "Reserved (ADSP_IPC_CONFIG_SET_ERROR removed)"},
+ {108, "Reserved (ADSP_IPC_LARGE_CONFIG_GET_ERROR removed)"},
+ {109, "Reserved (ADSP_IPC_LARGE_CONFIG_SET_ERROR removed)"},
+ {110, "Invalid (out of range) module ID provided"},
+ {111, "Invalid module instance ID provided"},
+ {112, "Invalid queue (pin) ID provided"},
+ {113, "Invalid destination queue (pin) ID provided"},
+ {114, "Reserved (ADSP_IPC_BIND_UNBIND_DST_SINK_UNSUPPORTED removed)"},
+ {115, "Reserved (ADSP_IPC_UNLOAD_INST_EXISTS removed)"},
+ {116, "Invalid target code ID provided"},
+ {117, "Injection DMA buffer is too small for probing the input pin"},
+ {118, "Extraction DMA buffer is too small for probing the output pin"},
+ {120, "Invalid ID of configuration item provided in TLV list"},
+ {121, "Invalid length of configuration item provided in TLV list"},
+ {122, "Invalid structure of configuration item provided"},
+ {140, "Initialization of DMA Gateway failed"},
+ {141, "Invalid ID of gateway provided"},
+ {142, "Setting state of DMA Gateway failed"},
+ {143, "DMA_CONTROL message targeting gateway not allocated yet"},
+ {150, "Attempt to configure SCLK while I2S port is running"},
+ {151, "Attempt to configure MCLK while I2S port is running"},
+ {152, "Attempt to stop SCLK that is not running"},
+ {153, "Attempt to stop MCLK that is not running"},
+ {160, "Reserved (ADSP_IPC_PIPELINE_NOT_INITIALIZED removed)"},
+ {161, "Reserved (ADSP_IPC_PIPELINE_NOT_EXIST removed)"},
+ {162, "Reserved (ADSP_IPC_PIPELINE_SAVE_FAILED removed)"},
+ {163, "Reserved (ADSP_IPC_PIPELINE_RESTORE_FAILED removed)"},
+ {165, "Reserved (ADSP_IPC_PIPELINE_ALREADY_EXISTS removed)"},
+};
+
+static int sof_ipc4_check_reply_status(struct snd_sof_dev *sdev, u32 status)
+{
+ int i, ret;
+
+ status &= SOF_IPC4_REPLY_STATUS;
+
+ if (!status)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(ipc4_status); i++) {
+ if (ipc4_status[i].status == status) {
+ dev_err(sdev->dev, "FW reported error: %u - %s\n",
+ status, ipc4_status[i].msg);
+ goto to_errno;
+ }
+ }
+
+ if (i == ARRAY_SIZE(ipc4_status))
+ dev_err(sdev->dev, "FW reported error: %u - Unknown\n", status);
+
+to_errno:
+ switch (status) {
+ case 8:
+ case 11:
+ case 105 ... 109:
+ case 114 ... 115:
+ case 160 ... 163:
+ case 165:
+ ret = -ENOENT;
+ break;
+ case 4:
+ case 150:
+ case 151:
+ ret = -EBUSY;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC)
+#define DBG_IPC4_MSG_TYPE_ENTRY(type) [SOF_IPC4_##type] = #type
+static const char * const ipc4_dbg_mod_msg_type[] = {
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_INIT_INSTANCE),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_CONFIG_GET),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_CONFIG_SET),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_LARGE_CONFIG_GET),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_LARGE_CONFIG_SET),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_BIND),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_UNBIND),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_SET_DX),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_SET_D0IX),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_ENTER_MODULE_RESTORE),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_EXIT_MODULE_RESTORE),
+ DBG_IPC4_MSG_TYPE_ENTRY(MOD_DELETE_INSTANCE),
+};
+
+static const char * const ipc4_dbg_glb_msg_type[] = {
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_BOOT_CONFIG),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_ROM_CONTROL),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_IPCGATEWAY_CMD),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_PERF_MEASUREMENTS_CMD),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_CHAIN_DMA),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_LOAD_MULTIPLE_MODULES),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_UNLOAD_MULTIPLE_MODULES),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_CREATE_PIPELINE),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_DELETE_PIPELINE),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_SET_PIPELINE_STATE),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_GET_PIPELINE_STATE),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_GET_PIPELINE_CONTEXT_SIZE),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_SAVE_PIPELINE),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_RESTORE_PIPELINE),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_LOAD_LIBRARY),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_INTERNAL_MESSAGE),
+ DBG_IPC4_MSG_TYPE_ENTRY(GLB_NOTIFICATION),
+};
+
+#define DBG_IPC4_NOTIFICATION_TYPE_ENTRY(type) [SOF_IPC4_NOTIFY_##type] = #type
+static const char * const ipc4_dbg_notification_type[] = {
+ DBG_IPC4_NOTIFICATION_TYPE_ENTRY(PHRASE_DETECTED),
+ DBG_IPC4_NOTIFICATION_TYPE_ENTRY(RESOURCE_EVENT),
+ DBG_IPC4_NOTIFICATION_TYPE_ENTRY(LOG_BUFFER_STATUS),
+ DBG_IPC4_NOTIFICATION_TYPE_ENTRY(TIMESTAMP_CAPTURED),
+ DBG_IPC4_NOTIFICATION_TYPE_ENTRY(FW_READY),
+ DBG_IPC4_NOTIFICATION_TYPE_ENTRY(FW_AUD_CLASS_RESULT),
+ DBG_IPC4_NOTIFICATION_TYPE_ENTRY(EXCEPTION_CAUGHT),
+ DBG_IPC4_NOTIFICATION_TYPE_ENTRY(MODULE_NOTIFICATION),
+ DBG_IPC4_NOTIFICATION_TYPE_ENTRY(PROBE_DATA_AVAILABLE),
+ DBG_IPC4_NOTIFICATION_TYPE_ENTRY(ASYNC_MSG_SRVC_MESSAGE),
+};
+
+static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_msg *msg,
+ bool data_size_valid)
+{
+ u32 val, type;
+ const u8 *str2 = NULL;
+ const u8 *str = NULL;
+
+ val = msg->primary & SOF_IPC4_MSG_TARGET_MASK;
+ type = SOF_IPC4_MSG_TYPE_GET(msg->primary);
+
+ if (val == SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG)) {
+ /* Module message */
+ if (type < SOF_IPC4_MOD_TYPE_LAST)
+ str = ipc4_dbg_mod_msg_type[type];
+ if (!str)
+ str = "Unknown Module message type";
+ } else {
+ /* Global FW message */
+ if (type < SOF_IPC4_GLB_TYPE_LAST)
+ str = ipc4_dbg_glb_msg_type[type];
+ if (!str)
+ str = "Unknown Global message type";
+
+ if (type == SOF_IPC4_GLB_NOTIFICATION) {
+ /* Notification message */
+ u32 notif = SOF_IPC4_NOTIFICATION_TYPE_GET(msg->primary);
+
+ if (notif < SOF_IPC4_NOTIFY_TYPE_LAST)
+ str2 = ipc4_dbg_notification_type[notif];
+ if (!str2)
+ str2 = "Unknown Global notification";
+ }
+ }
+
+ if (str2) {
+ if (data_size_valid && msg->data_size)
+ dev_dbg(dev, "%s: %#x|%#x: %s|%s [data size: %zu]\n",
+ text, msg->primary, msg->extension, str, str2,
+ msg->data_size);
+ else
+ dev_dbg(dev, "%s: %#x|%#x: %s|%s\n", text, msg->primary,
+ msg->extension, str, str2);
+ } else {
+ if (data_size_valid && msg->data_size)
+ dev_dbg(dev, "%s: %#x|%#x: %s [data size: %zu]\n",
+ text, msg->primary, msg->extension, str,
+ msg->data_size);
+ else
+ dev_dbg(dev, "%s: %#x|%#x: %s\n", text, msg->primary,
+ msg->extension, str);
+ }
+}
+#else /* CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC */
+static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_msg *msg,
+ bool data_size_valid)
+{
+ if (data_size_valid && msg->data_size)
+ dev_dbg(dev, "%s: %#x|%#x [data size: %zu]\n", text,
+ msg->primary, msg->extension, msg->data_size);
+ else
+ dev_dbg(dev, "%s: %#x|%#x\n", text, msg->primary, msg->extension);
+}
+#endif
+
+static int sof_ipc4_get_reply(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_ipc_msg *msg = sdev->msg;
+ struct sof_ipc4_msg *ipc4_reply;
+ int ret;
+
+ /* get the generic reply */
+ ipc4_reply = msg->reply_data;
+
+ sof_ipc4_log_header(sdev->dev, "ipc tx reply", ipc4_reply, false);
+
+ ret = sof_ipc4_check_reply_status(sdev, ipc4_reply->primary);
+ if (ret)
+ return ret;
+
+ /* No other information is expected for non large config get replies */
+ if (!msg->reply_size || !SOF_IPC4_MSG_IS_MODULE_MSG(ipc4_reply->primary) ||
+ (SOF_IPC4_MSG_TYPE_GET(ipc4_reply->primary) != SOF_IPC4_MOD_LARGE_CONFIG_GET))
+ return 0;
+
+ /* Read the requested payload */
+ snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, ipc4_reply->data_ptr,
+ msg->reply_size);
+
+ return 0;
+}
+
+/* wait for IPC message reply */
+static int ipc4_wait_tx_done(struct snd_sof_ipc *ipc, void *reply_data)
+{
+ struct snd_sof_ipc_msg *msg = &ipc->msg;
+ struct sof_ipc4_msg *ipc4_msg = msg->msg_data;
+ struct snd_sof_dev *sdev = ipc->sdev;
+ int ret;
+
+ /* wait for DSP IPC completion */
+ ret = wait_event_timeout(msg->waitq, msg->ipc_complete,
+ msecs_to_jiffies(sdev->ipc_timeout));
+ if (ret == 0) {
+ dev_err(sdev->dev, "ipc timed out for %#x|%#x\n",
+ ipc4_msg->primary, ipc4_msg->extension);
+ return -ETIMEDOUT;
+ }
+
+ if (msg->reply_error) {
+ dev_err(sdev->dev, "ipc error for msg %#x|%#x\n",
+ ipc4_msg->primary, ipc4_msg->extension);
+ ret = msg->reply_error;
+ } else {
+ if (reply_data) {
+ struct sof_ipc4_msg *ipc4_reply = msg->reply_data;
+ struct sof_ipc4_msg *ipc4_reply_data = reply_data;
+
+ /* Copy the header */
+ ipc4_reply_data->header_u64 = ipc4_reply->header_u64;
+ if (msg->reply_size && ipc4_reply_data->data_ptr) {
+ /* copy the payload returned from DSP */
+ memcpy(ipc4_reply_data->data_ptr, ipc4_reply->data_ptr,
+ msg->reply_size);
+ ipc4_reply_data->data_size = msg->reply_size;
+ }
+ }
+
+ ret = 0;
+ sof_ipc4_log_header(sdev->dev, "ipc tx done ", ipc4_msg, true);
+ }
+
+ /* re-enable dumps after successful IPC tx */
+ if (sdev->ipc_dump_printed) {
+ sdev->dbg_dump_printed = false;
+ sdev->ipc_dump_printed = false;
+ }
+
+ return ret;
+}
+
+static int ipc4_tx_msg_unlocked(struct snd_sof_ipc *ipc,
+ void *msg_data, size_t msg_bytes,
+ void *reply_data, size_t reply_bytes)
+{
+ struct sof_ipc4_msg *ipc4_msg = msg_data;
+ struct snd_sof_dev *sdev = ipc->sdev;
+ int ret;
+
+ if (msg_bytes > ipc->max_payload_size || reply_bytes > ipc->max_payload_size)
+ return -EINVAL;
+
+ ret = sof_ipc_send_msg(sdev, msg_data, msg_bytes, reply_bytes);
+ if (ret) {
+ dev_err_ratelimited(sdev->dev,
+ "%s: ipc message send for %#x|%#x failed: %d\n",
+ __func__, ipc4_msg->primary, ipc4_msg->extension, ret);
+ return ret;
+ }
+
+ sof_ipc4_log_header(sdev->dev, "ipc tx ", msg_data, true);
+
+ /* now wait for completion */
+ return ipc4_wait_tx_done(ipc, reply_data);
+}
+
+static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,
+ void *reply_data, size_t reply_bytes, bool no_pm)
+{
+ struct snd_sof_ipc *ipc = sdev->ipc;
+#ifdef DEBUG_VERBOSE
+ struct sof_ipc4_msg *msg = NULL;
+#endif
+ int ret;
+
+ if (!msg_data)
+ return -EINVAL;
+
+ /* Serialise IPC TX */
+ mutex_lock(&ipc->tx_mutex);
+
+ ret = ipc4_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes);
+
+ mutex_unlock(&ipc->tx_mutex);
+
+#ifdef DEBUG_VERBOSE
+ /* payload is indicated by non zero msg/reply_bytes */
+ if (msg_bytes)
+ msg = msg_data;
+ else if (reply_bytes)
+ msg = reply_data;
+
+ if (msg)
+ sof_ipc4_dump_payload(sdev, msg->data_ptr, msg->data_size);
+#endif
+
+ return ret;
+}
+
+static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data,
+ size_t payload_bytes, bool set)
+{
+ size_t payload_limit = sdev->ipc->max_payload_size;
+ struct sof_ipc4_msg *ipc4_msg = data;
+ struct sof_ipc4_msg tx = {{ 0 }};
+ struct sof_ipc4_msg rx = {{ 0 }};
+ size_t remaining = payload_bytes;
+ size_t offset = 0;
+ size_t chunk_size;
+ int ret;
+
+ if (!data)
+ return -EINVAL;
+
+ if ((ipc4_msg->primary & SOF_IPC4_MSG_TARGET_MASK) !=
+ SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG))
+ return -EINVAL;
+
+ ipc4_msg->primary &= ~SOF_IPC4_MSG_TYPE_MASK;
+ tx.primary = ipc4_msg->primary;
+ tx.extension = ipc4_msg->extension;
+
+ if (set)
+ tx.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET);
+ else
+ tx.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_GET);
+
+ tx.extension &= ~SOF_IPC4_MOD_EXT_MSG_SIZE_MASK;
+ tx.extension |= SOF_IPC4_MOD_EXT_MSG_SIZE(payload_bytes);
+
+ tx.extension |= SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(1);
+
+ /* Serialise IPC TX */
+ mutex_lock(&sdev->ipc->tx_mutex);
+
+ do {
+ size_t tx_size, rx_size;
+
+ if (remaining > payload_limit) {
+ chunk_size = payload_limit;
+ } else {
+ chunk_size = remaining;
+ if (set)
+ tx.extension |= SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK(1);
+ }
+
+ if (offset) {
+ tx.extension &= ~SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK_MASK;
+ tx.extension &= ~SOF_IPC4_MOD_EXT_MSG_SIZE_MASK;
+ tx.extension |= SOF_IPC4_MOD_EXT_MSG_SIZE(offset);
+ }
+
+ if (set) {
+ tx.data_size = chunk_size;
+ tx.data_ptr = ipc4_msg->data_ptr + offset;
+
+ tx_size = chunk_size;
+ rx_size = 0;
+ } else {
+ rx.primary = 0;
+ rx.extension = 0;
+ rx.data_size = chunk_size;
+ rx.data_ptr = ipc4_msg->data_ptr + offset;
+
+ tx_size = 0;
+ rx_size = chunk_size;
+ }
+
+ /* Send the message for the current chunk */
+ ret = ipc4_tx_msg_unlocked(sdev->ipc, &tx, tx_size, &rx, rx_size);
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "%s: large config %s failed at offset %zu: %d\n",
+ __func__, set ? "set" : "get", offset, ret);
+ goto out;
+ }
+
+ if (!set && rx.extension & SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK_MASK) {
+ /* Verify the firmware reported total payload size */
+ rx_size = rx.extension & SOF_IPC4_MOD_EXT_MSG_SIZE_MASK;
+
+ if (rx_size > payload_bytes) {
+ dev_err(sdev->dev,
+ "%s: Receive buffer (%zu) is too small for %zu\n",
+ __func__, payload_bytes, rx_size);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (rx_size < chunk_size) {
+ chunk_size = rx_size;
+ remaining = rx_size;
+ } else if (rx_size < payload_bytes) {
+ remaining = rx_size;
+ }
+ }
+
+ offset += chunk_size;
+ remaining -= chunk_size;
+ } while (remaining);
+
+ /* Adjust the received data size if needed */
+ if (!set && payload_bytes != offset)
+ ipc4_msg->data_size = offset;
+
+ sof_ipc4_dump_payload(sdev, ipc4_msg->data_ptr, ipc4_msg->data_size);
+
+out:
+ mutex_unlock(&sdev->ipc->tx_mutex);
+
+ return ret;
+}
+
+static int sof_ipc4_init_msg_memory(struct snd_sof_dev *sdev)
+{
+ struct sof_ipc4_msg *ipc4_msg;
+ struct snd_sof_ipc_msg *msg = &sdev->ipc->msg;
+
+ /* TODO: get max_payload_size from firmware */
+ sdev->ipc->max_payload_size = SOF_IPC4_MSG_MAX_SIZE;
+
+ /* Allocate memory for the ipc4 container and the maximum payload */
+ msg->reply_data = devm_kzalloc(sdev->dev, sdev->ipc->max_payload_size +
+ sizeof(struct sof_ipc4_msg), GFP_KERNEL);
+ if (!msg->reply_data)
+ return -ENOMEM;
+
+ ipc4_msg = msg->reply_data;
+ ipc4_msg->data_ptr = msg->reply_data + sizeof(struct sof_ipc4_msg);
+
+ return 0;
+}
+
+static int ipc4_fw_ready(struct snd_sof_dev *sdev, struct sof_ipc4_msg *ipc4_msg)
+{
+ int inbox_offset, inbox_size, outbox_offset, outbox_size;
+
+ /* no need to re-check version/ABI for subsequent boots */
+ if (!sdev->first_boot)
+ return 0;
+
+ /* Set up the windows for IPC communication */
+ inbox_offset = snd_sof_dsp_get_mailbox_offset(sdev);
+ if (inbox_offset < 0) {
+ dev_err(sdev->dev, "%s: No mailbox offset\n", __func__);
+ return inbox_offset;
+ }
+ inbox_size = SOF_IPC4_MSG_MAX_SIZE;
+ outbox_offset = snd_sof_dsp_get_window_offset(sdev, 1);
+ outbox_size = SOF_IPC4_MSG_MAX_SIZE;
+
+ sdev->dsp_box.offset = inbox_offset;
+ sdev->dsp_box.size = inbox_size;
+ sdev->host_box.offset = outbox_offset;
+ sdev->host_box.size = outbox_size;
+
+ dev_dbg(sdev->dev, "mailbox upstream 0x%x - size 0x%x\n",
+ inbox_offset, inbox_size);
+ dev_dbg(sdev->dev, "mailbox downstream 0x%x - size 0x%x\n",
+ outbox_offset, outbox_size);
+
+ return sof_ipc4_init_msg_memory(sdev);
+}
+
+static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev)
+{
+ struct sof_ipc4_msg *ipc4_msg = sdev->ipc->msg.rx_data;
+ size_t data_size = 0;
+ int err;
+
+ if (!ipc4_msg || !SOF_IPC4_MSG_IS_NOTIFICATION(ipc4_msg->primary))
+ return;
+
+ ipc4_msg->data_ptr = NULL;
+ ipc4_msg->data_size = 0;
+
+ sof_ipc4_log_header(sdev->dev, "ipc rx ", ipc4_msg, false);
+
+ switch (SOF_IPC4_NOTIFICATION_TYPE_GET(ipc4_msg->primary)) {
+ case SOF_IPC4_NOTIFY_FW_READY:
+ /* check for FW boot completion */
+ if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) {
+ err = ipc4_fw_ready(sdev, ipc4_msg);
+ if (err < 0)
+ sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED);
+ else
+ sof_set_fw_state(sdev, SOF_FW_BOOT_READY_OK);
+
+ /* wake up firmware loader */
+ wake_up(&sdev->boot_wait);
+ }
+
+ break;
+ case SOF_IPC4_NOTIFY_RESOURCE_EVENT:
+ data_size = sizeof(struct sof_ipc4_notify_resource_data);
+ break;
+ default:
+ dev_dbg(sdev->dev, "%s: Unhandled DSP message: %#x|%#x\n", __func__,
+ ipc4_msg->primary, ipc4_msg->extension);
+ break;
+ }
+
+ if (data_size) {
+ ipc4_msg->data_ptr = kmalloc(data_size, GFP_KERNEL);
+ if (!ipc4_msg->data_ptr)
+ return;
+
+ ipc4_msg->data_size = data_size;
+ snd_sof_ipc_msg_data(sdev, NULL, ipc4_msg->data_ptr, ipc4_msg->data_size);
+ }
+
+ sof_ipc4_log_header(sdev->dev, "ipc rx done ", ipc4_msg, true);
+
+ if (data_size) {
+ kfree(ipc4_msg->data_ptr);
+ ipc4_msg->data_ptr = NULL;
+ ipc4_msg->data_size = 0;
+ }
+}
+
+const struct sof_ipc_ops ipc4_ops = {
+ .tx_msg = sof_ipc4_tx_msg,
+ .rx_msg = sof_ipc4_rx_msg,
+ .set_get_data = sof_ipc4_set_get_data,
+ .get_reply = sof_ipc4_get_reply,
+};
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index 4801849cb2ab..8ea196305e4b 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -350,6 +350,9 @@ struct snd_sof_ipc_msg {
size_t reply_size;
int reply_error;
+ /* notification, firmware initiated messages */
+ void *rx_data;
+
wait_queue_head_t waitq;
bool ipc_complete;
};
@@ -765,5 +768,6 @@ static inline int sof_resume_clients(struct snd_sof_dev *sdev)
/* Main ops for IPC implementations */
extern const struct sof_ipc_ops ipc3_ops;
+extern const struct sof_ipc_ops ipc4_ops;
#endif