Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: SOF: client-probes: Add support for IPC4

Merge series from Peter Ujfalusi <peter.ujfalusi@linux.intel.com>:

The probes (the ability of probing the audio data from firmware processing
points or to receive stream of debug/trace information) is supported by IPC4
as well, but due to the differences between the two IPC version the low level
setup and information we need for probing is different.

This series will extend the existing probes support for IPC3 with IPC4 'backend'

+637 -238
+6
sound/soc/sof/Makefile
··· 27 27 snd-sof-ipc-flood-test-objs := sof-client-ipc-flood-test.o 28 28 snd-sof-ipc-msg-injector-objs := sof-client-ipc-msg-injector.o 29 29 snd-sof-probes-objs := sof-client-probes.o 30 + ifneq ($(CONFIG_SND_SOC_SOF_IPC3),) 31 + snd-sof-probes-objs += sof-client-probes-ipc3.o 32 + endif 33 + ifneq ($(CONFIG_SND_SOC_SOF_INTEL_IPC4),) 34 + snd-sof-probes-objs += sof-client-probes-ipc4.o 35 + endif 30 36 31 37 snd-sof-nocodec-objs := nocodec.o 32 38
+8
sound/soc/sof/ipc.c
··· 84 84 } 85 85 EXPORT_SYMBOL(sof_ipc_tx_message); 86 86 87 + /* IPC set or get data from host to DSP */ 88 + int sof_ipc_set_get_data(struct snd_sof_ipc *ipc, void *msg_data, 89 + size_t msg_bytes, bool set) 90 + { 91 + return ipc->ops->set_get_data(ipc->sdev, msg_data, msg_bytes, set); 92 + } 93 + EXPORT_SYMBOL(sof_ipc_set_get_data); 94 + 87 95 /* 88 96 * send IPC message from host to DSP without modifying the DSP state. 89 97 * This will be used for IPC's that can be handled by the DSP
+236
sound/soc/sof/sof-client-probes-ipc3.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + // 3 + // Copyright(c) 2019-2022 Intel Corporation. All rights reserved. 4 + // 5 + // Author: Cezary Rojewski <cezary.rojewski@intel.com> 6 + // 7 + // Code moved to this file by: 8 + // Jyri Sarha <jyri.sarha@intel.com> 9 + // 10 + 11 + #include <linux/stddef.h> 12 + #include <sound/soc.h> 13 + #include <sound/sof/header.h> 14 + #include "sof-client.h" 15 + #include "sof-client-probes.h" 16 + 17 + struct sof_probe_dma { 18 + unsigned int stream_tag; 19 + unsigned int dma_buffer_size; 20 + } __packed; 21 + 22 + struct sof_ipc_probe_dma_add_params { 23 + struct sof_ipc_cmd_hdr hdr; 24 + unsigned int num_elems; 25 + struct sof_probe_dma dma[]; 26 + } __packed; 27 + 28 + struct sof_ipc_probe_info_params { 29 + struct sof_ipc_reply rhdr; 30 + unsigned int num_elems; 31 + union { 32 + DECLARE_FLEX_ARRAY(struct sof_probe_dma, dma); 33 + DECLARE_FLEX_ARRAY(struct sof_probe_point_desc, desc); 34 + }; 35 + } __packed; 36 + 37 + struct sof_ipc_probe_point_add_params { 38 + struct sof_ipc_cmd_hdr hdr; 39 + unsigned int num_elems; 40 + struct sof_probe_point_desc desc[]; 41 + } __packed; 42 + 43 + struct sof_ipc_probe_point_remove_params { 44 + struct sof_ipc_cmd_hdr hdr; 45 + unsigned int num_elems; 46 + unsigned int buffer_id[]; 47 + } __packed; 48 + 49 + /** 50 + * ipc3_probes_init - initialize data probing 51 + * @cdev: SOF client device 52 + * @stream_tag: Extractor stream tag 53 + * @buffer_size: DMA buffer size to set for extractor 54 + * 55 + * Host chooses whether extraction is supported or not by providing 56 + * valid stream tag to DSP. Once specified, stream described by that 57 + * tag will be tied to DSP for extraction for the entire lifetime of 58 + * probe. 59 + * 60 + * Probing is initialized only once and each INIT request must be 61 + * matched by DEINIT call. 62 + */ 63 + static int ipc3_probes_init(struct sof_client_dev *cdev, u32 stream_tag, 64 + size_t buffer_size) 65 + { 66 + struct sof_ipc_probe_dma_add_params *msg; 67 + size_t size = struct_size(msg, dma, 1); 68 + struct sof_ipc_reply reply; 69 + int ret; 70 + 71 + msg = kmalloc(size, GFP_KERNEL); 72 + if (!msg) 73 + return -ENOMEM; 74 + msg->hdr.size = size; 75 + msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_INIT; 76 + msg->num_elems = 1; 77 + msg->dma[0].stream_tag = stream_tag; 78 + msg->dma[0].dma_buffer_size = buffer_size; 79 + 80 + ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply)); 81 + kfree(msg); 82 + return ret; 83 + } 84 + 85 + /** 86 + * ipc3_probes_deinit - cleanup after data probing 87 + * @cdev: SOF client device 88 + * 89 + * Host sends DEINIT request to free previously initialized probe 90 + * on DSP side once it is no longer needed. DEINIT only when there 91 + * are no probes connected and with all injectors detached. 92 + */ 93 + static int ipc3_probes_deinit(struct sof_client_dev *cdev) 94 + { 95 + struct sof_ipc_cmd_hdr msg; 96 + struct sof_ipc_reply reply; 97 + 98 + msg.size = sizeof(msg); 99 + msg.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DEINIT; 100 + 101 + return sof_client_ipc_tx_message(cdev, &msg, &reply, sizeof(reply)); 102 + } 103 + 104 + static int ipc3_probes_info(struct sof_client_dev *cdev, unsigned int cmd, 105 + void **params, size_t *num_params) 106 + { 107 + size_t max_msg_size = sof_client_get_ipc_max_payload_size(cdev); 108 + struct sof_ipc_probe_info_params msg = {{{0}}}; 109 + struct sof_ipc_probe_info_params *reply; 110 + size_t bytes; 111 + int ret; 112 + 113 + *params = NULL; 114 + *num_params = 0; 115 + 116 + reply = kzalloc(max_msg_size, GFP_KERNEL); 117 + if (!reply) 118 + return -ENOMEM; 119 + msg.rhdr.hdr.size = sizeof(msg); 120 + msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd; 121 + 122 + ret = sof_client_ipc_tx_message(cdev, &msg, reply, max_msg_size); 123 + if (ret < 0 || reply->rhdr.error < 0) 124 + goto exit; 125 + 126 + if (!reply->num_elems) 127 + goto exit; 128 + 129 + if (cmd == SOF_IPC_PROBE_DMA_INFO) 130 + bytes = sizeof(reply->dma[0]); 131 + else 132 + bytes = sizeof(reply->desc[0]); 133 + bytes *= reply->num_elems; 134 + *params = kmemdup(&reply->dma[0], bytes, GFP_KERNEL); 135 + if (!*params) { 136 + ret = -ENOMEM; 137 + goto exit; 138 + } 139 + *num_params = reply->num_elems; 140 + 141 + exit: 142 + kfree(reply); 143 + return ret; 144 + } 145 + 146 + /** 147 + * ipc3_probes_points_info - retrieve list of active probe points 148 + * @cdev: SOF client device 149 + * @desc: Returned list of active probes 150 + * @num_desc: Returned count of active probes 151 + * 152 + * Host sends PROBE_POINT_INFO request to obtain list of active probe 153 + * points, valid for disconnection when given probe is no longer 154 + * required. 155 + */ 156 + static int ipc3_probes_points_info(struct sof_client_dev *cdev, 157 + struct sof_probe_point_desc **desc, 158 + size_t *num_desc) 159 + { 160 + return ipc3_probes_info(cdev, SOF_IPC_PROBE_POINT_INFO, 161 + (void **)desc, num_desc); 162 + } 163 + 164 + /** 165 + * ipc3_probes_points_add - connect specified probes 166 + * @cdev: SOF client device 167 + * @desc: List of probe points to connect 168 + * @num_desc: Number of elements in @desc 169 + * 170 + * Dynamically connects to provided set of endpoints. Immediately 171 + * after connection is established, host must be prepared to 172 + * transfer data from or to target stream given the probing purpose. 173 + * 174 + * Each probe point should be removed using PROBE_POINT_REMOVE 175 + * request when no longer needed. 176 + */ 177 + static int ipc3_probes_points_add(struct sof_client_dev *cdev, 178 + struct sof_probe_point_desc *desc, 179 + size_t num_desc) 180 + { 181 + struct sof_ipc_probe_point_add_params *msg; 182 + size_t size = struct_size(msg, desc, num_desc); 183 + struct sof_ipc_reply reply; 184 + int ret; 185 + 186 + msg = kmalloc(size, GFP_KERNEL); 187 + if (!msg) 188 + return -ENOMEM; 189 + msg->hdr.size = size; 190 + msg->num_elems = num_desc; 191 + msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_ADD; 192 + memcpy(&msg->desc[0], desc, size - sizeof(*msg)); 193 + 194 + ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply)); 195 + kfree(msg); 196 + return ret; 197 + } 198 + 199 + /** 200 + * ipc3_probes_points_remove - disconnect specified probes 201 + * @cdev: SOF client device 202 + * @buffer_id: List of probe points to disconnect 203 + * @num_buffer_id: Number of elements in @desc 204 + * 205 + * Removes previously connected probes from list of active probe 206 + * points and frees all resources on DSP side. 207 + */ 208 + static int ipc3_probes_points_remove(struct sof_client_dev *cdev, 209 + unsigned int *buffer_id, 210 + size_t num_buffer_id) 211 + { 212 + struct sof_ipc_probe_point_remove_params *msg; 213 + size_t size = struct_size(msg, buffer_id, num_buffer_id); 214 + struct sof_ipc_reply reply; 215 + int ret; 216 + 217 + msg = kmalloc(size, GFP_KERNEL); 218 + if (!msg) 219 + return -ENOMEM; 220 + msg->hdr.size = size; 221 + msg->num_elems = num_buffer_id; 222 + msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE; 223 + memcpy(&msg->buffer_id[0], buffer_id, size - sizeof(*msg)); 224 + 225 + ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply)); 226 + kfree(msg); 227 + return ret; 228 + } 229 + 230 + const struct sof_probes_ipc_ops ipc3_probe_ops = { 231 + .init = ipc3_probes_init, 232 + .deinit = ipc3_probes_deinit, 233 + .points_info = ipc3_probes_points_info, 234 + .points_add = ipc3_probes_points_add, 235 + .points_remove = ipc3_probes_points_remove, 236 + };
+281
sound/soc/sof/sof-client-probes-ipc4.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + // 3 + // Copyright(c) 2019-2022 Intel Corporation. All rights reserved. 4 + // 5 + // Author: Jyri Sarha <jyri.sarha@intel.com> 6 + // 7 + 8 + #include <sound/soc.h> 9 + #include <sound/sof/ipc4/header.h> 10 + #include <uapi/sound/sof/header.h> 11 + #include "sof-priv.h" 12 + #include "ipc4-priv.h" 13 + #include "sof-client.h" 14 + #include "sof-client-probes.h" 15 + 16 + enum sof_ipc4_dma_type { 17 + SOF_IPC4_DMA_HDA_HOST_OUTPUT = 0, 18 + SOF_IPC4_DMA_HDA_HOST_INPUT = 1, 19 + SOF_IPC4_DMA_HDA_LINK_OUTPUT = 8, 20 + SOF_IPC4_DMA_HDA_LINK_INPUT = 9, 21 + SOF_IPC4_DMA_DMIC_LINK_INPUT = 11, 22 + SOF_IPC4_DMA_I2S_LINK_OUTPUT = 12, 23 + SOF_IPC4_DMA_I2S_LINK_INPUT = 13, 24 + }; 25 + 26 + enum sof_ipc4_probe_runtime_param { 27 + SOF_IPC4_PROBE_INJECTION_DMA = 1, 28 + SOF_IPC4_PROBE_INJECTION_DMA_DETACH, 29 + SOF_IPC4_PROBE_POINTS, 30 + SOF_IPC4_PROBE_POINTS_DISCONNECT, 31 + }; 32 + 33 + struct sof_ipc4_probe_gtw_cfg { 34 + u32 node_id; 35 + u32 dma_buffer_size; 36 + } __packed __aligned(4); 37 + 38 + #define SOF_IPC4_PROBE_NODE_ID_INDEX(x) ((x) & GENMASK(7, 0)) 39 + #define SOF_IPC4_PROBE_NODE_ID_TYPE(x) (((x) << 8) & GENMASK(12, 8)) 40 + 41 + struct sof_ipc4_probe_cfg { 42 + struct sof_ipc4_base_module_cfg base; 43 + struct sof_ipc4_probe_gtw_cfg gtw_cfg; 44 + } __packed __aligned(4); 45 + 46 + enum sof_ipc4_probe_type { 47 + SOF_IPC4_PROBE_TYPE_INPUT = 0, 48 + SOF_IPC4_PROBE_TYPE_OUTPUT, 49 + SOF_IPC4_PROBE_TYPE_INTERNAL 50 + }; 51 + 52 + struct sof_ipc4_probe_point { 53 + u32 point_id; 54 + u32 purpose; 55 + u32 stream_tag; 56 + } __packed __aligned(4); 57 + 58 + #define INVALID_PIPELINE_ID 0xFF 59 + 60 + /** 61 + * sof_ipc4_probe_get_module_info - Get IPC4 module info for probe module 62 + * @cdev: SOF client device 63 + * @return: Pointer to IPC4 probe module info 64 + * 65 + * Look up the IPC4 probe module info based on the hard coded uuid and 66 + * store the value for the future calls. 67 + */ 68 + static struct sof_man4_module *sof_ipc4_probe_get_module_info(struct sof_client_dev *cdev) 69 + { 70 + struct sof_probes_priv *priv = cdev->data; 71 + struct device *dev = &cdev->auxdev.dev; 72 + static const guid_t probe_uuid = 73 + GUID_INIT(0x7CAD0808, 0xAB10, 0xCD23, 74 + 0xEF, 0x45, 0x12, 0xAB, 0x34, 0xCD, 0x56, 0xEF); 75 + 76 + if (!priv->ipc_priv) { 77 + struct sof_ipc4_fw_module *fw_module = 78 + sof_client_ipc4_find_module(cdev, &probe_uuid); 79 + 80 + if (!fw_module) { 81 + dev_err(dev, "%s: no matching uuid found", __func__); 82 + return NULL; 83 + } 84 + 85 + priv->ipc_priv = &fw_module->man4_module_entry; 86 + } 87 + 88 + return (struct sof_man4_module *)priv->ipc_priv; 89 + } 90 + 91 + /** 92 + * ipc4_probes_init - initialize data probing 93 + * @cdev: SOF client device 94 + * @stream_tag: Extractor stream tag 95 + * @buffer_size: DMA buffer size to set for extractor 96 + * @return: 0 on success, negative error code on error 97 + * 98 + * Host chooses whether extraction is supported or not by providing 99 + * valid stream tag to DSP. Once specified, stream described by that 100 + * tag will be tied to DSP for extraction for the entire lifetime of 101 + * probe. 102 + * 103 + * Probing is initialized only once and each INIT request must be 104 + * matched by DEINIT call. 105 + */ 106 + static int ipc4_probes_init(struct sof_client_dev *cdev, u32 stream_tag, 107 + size_t buffer_size) 108 + { 109 + struct sof_man4_module *mentry = sof_ipc4_probe_get_module_info(cdev); 110 + struct sof_ipc4_msg msg; 111 + struct sof_ipc4_probe_cfg cfg; 112 + 113 + if (!mentry) 114 + return -ENODEV; 115 + 116 + memset(&cfg, '\0', sizeof(cfg)); 117 + cfg.gtw_cfg.node_id = SOF_IPC4_PROBE_NODE_ID_INDEX(stream_tag - 1) | 118 + SOF_IPC4_PROBE_NODE_ID_TYPE(SOF_IPC4_DMA_HDA_HOST_INPUT); 119 + 120 + cfg.gtw_cfg.dma_buffer_size = buffer_size; 121 + 122 + msg.primary = mentry->id; 123 + msg.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_INIT_INSTANCE); 124 + msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); 125 + msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); 126 + msg.extension = SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE(INVALID_PIPELINE_ID); 127 + msg.extension |= SOF_IPC4_MOD_EXT_CORE_ID(0); 128 + 129 + msg.data_size = sizeof(cfg); 130 + msg.data_ptr = &cfg; 131 + 132 + return sof_client_ipc_tx_message(cdev, &msg, NULL, 0); 133 + } 134 + 135 + /** 136 + * ipc4_probes_deinit - cleanup after data probing 137 + * @cdev: SOF client device 138 + * @return: 0 on success, negative error code on error 139 + * 140 + * Host sends DEINIT request to free previously initialized probe 141 + * on DSP side once it is no longer needed. DEINIT only when there 142 + * are no probes connected and with all injectors detached. 143 + */ 144 + static int ipc4_probes_deinit(struct sof_client_dev *cdev) 145 + { 146 + struct sof_man4_module *mentry = sof_ipc4_probe_get_module_info(cdev); 147 + struct sof_ipc4_msg msg; 148 + 149 + msg.primary = mentry->id; 150 + msg.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_DELETE_INSTANCE); 151 + msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); 152 + msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); 153 + msg.extension = SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE(INVALID_PIPELINE_ID); 154 + msg.extension |= SOF_IPC4_MOD_EXT_CORE_ID(0); 155 + 156 + msg.data_size = 0; 157 + msg.data_ptr = NULL; 158 + 159 + return sof_client_ipc_tx_message(cdev, &msg, NULL, 0); 160 + } 161 + 162 + /** 163 + * ipc4_probes_points_info - retrieve list of active probe points 164 + * @cdev: SOF client device 165 + * @desc: Returned list of active probes 166 + * @num_desc: Returned count of active probes 167 + * @return: 0 on success, negative error code on error 168 + * 169 + * Dummy implementation returning empty list of probes. 170 + */ 171 + static int ipc4_probes_points_info(struct sof_client_dev *cdev, 172 + struct sof_probe_point_desc **desc, 173 + size_t *num_desc) 174 + { 175 + /* TODO: Firmware side implementation needed first */ 176 + *desc = NULL; 177 + *num_desc = 0; 178 + return 0; 179 + } 180 + 181 + /** 182 + * ipc4_probes_points_add - connect specified probes 183 + * @cdev: SOF client device 184 + * @desc: List of probe points to connect 185 + * @num_desc: Number of elements in @desc 186 + * @return: 0 on success, negative error code on error 187 + * 188 + * Translates the generic probe point presentation to an IPC4 189 + * message to dynamically connect the provided set of endpoints. 190 + */ 191 + static int ipc4_probes_points_add(struct sof_client_dev *cdev, 192 + struct sof_probe_point_desc *desc, 193 + size_t num_desc) 194 + { 195 + struct sof_man4_module *mentry = sof_ipc4_probe_get_module_info(cdev); 196 + struct sof_ipc4_probe_point *points; 197 + struct sof_ipc4_msg msg; 198 + int i, ret; 199 + 200 + /* The sof_probe_point_desc and sof_ipc4_probe_point structs 201 + * are of same size and even the integers are the same in the 202 + * same order, and similar meaning, but since there is no 203 + * performance issue I wrote the conversion explicitly open for 204 + * future development. 205 + */ 206 + points = kcalloc(num_desc, sizeof(*points), GFP_KERNEL); 207 + if (!points) 208 + return -ENOMEM; 209 + 210 + for (i = 0; i < num_desc; i++) { 211 + points[i].point_id = desc[i].buffer_id; 212 + points[i].purpose = desc[i].purpose; 213 + points[i].stream_tag = desc[i].stream_tag; 214 + } 215 + 216 + msg.primary = mentry->id; 217 + msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); 218 + msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); 219 + 220 + msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_PROBE_POINTS); 221 + 222 + msg.data_size = sizeof(*points) * num_desc; 223 + msg.data_ptr = points; 224 + 225 + ret = sof_client_ipc_set_get_data(cdev, &msg, true); 226 + 227 + kfree(points); 228 + 229 + return ret; 230 + } 231 + 232 + /** 233 + * ipc4_probes_points_remove - disconnect specified probes 234 + * @cdev: SOF client device 235 + * @buffer_id: List of probe points to disconnect 236 + * @num_buffer_id: Number of elements in @desc 237 + * @return: 0 on success, negative error code on error 238 + * 239 + * Converts the generic buffer_id to IPC4 probe_point_id and remove 240 + * the probe points with an IPC4 for message. 241 + */ 242 + static int ipc4_probes_points_remove(struct sof_client_dev *cdev, 243 + unsigned int *buffer_id, size_t num_buffer_id) 244 + { 245 + struct sof_man4_module *mentry = sof_ipc4_probe_get_module_info(cdev); 246 + struct sof_ipc4_msg msg; 247 + u32 *probe_point_ids; 248 + int i, ret; 249 + 250 + probe_point_ids = kcalloc(num_buffer_id, sizeof(*probe_point_ids), 251 + GFP_KERNEL); 252 + if (!probe_point_ids) 253 + return -ENOMEM; 254 + 255 + for (i = 0; i < num_buffer_id; i++) 256 + probe_point_ids[i] = buffer_id[i]; 257 + 258 + msg.primary = mentry->id; 259 + msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); 260 + msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); 261 + 262 + msg.extension = 263 + SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_PROBE_POINTS_DISCONNECT); 264 + 265 + msg.data_size = num_buffer_id * sizeof(*probe_point_ids); 266 + msg.data_ptr = probe_point_ids; 267 + 268 + ret = sof_client_ipc_set_get_data(cdev, &msg, true); 269 + 270 + kfree(probe_point_ids); 271 + 272 + return ret; 273 + } 274 + 275 + const struct sof_probes_ipc_ops ipc4_probe_ops = { 276 + .init = ipc4_probes_init, 277 + .deinit = ipc4_probes_deinit, 278 + .points_info = ipc4_probes_points_info, 279 + .points_add = ipc4_probes_points_add, 280 + .points_remove = ipc4_probes_points_remove, 281 + };
+30 -238
sound/soc/sof/sof-client-probes.c
··· 13 13 #include <linux/module.h> 14 14 #include <linux/pm_runtime.h> 15 15 #include <linux/string_helpers.h> 16 + #include <linux/stddef.h> 16 17 17 18 #include <sound/soc.h> 18 19 #include <sound/sof/header.h> ··· 29 28 static bool __read_mostly sof_probes_enabled; 30 29 module_param_named(enable, sof_probes_enabled, bool, 0444); 31 30 MODULE_PARM_DESC(enable, "Enable SOF probes support"); 32 - 33 - struct sof_probes_priv { 34 - struct dentry *dfs_points; 35 - struct dentry *dfs_points_remove; 36 - u32 extractor_stream_tag; 37 - struct snd_soc_card card; 38 - 39 - const struct sof_probes_host_ops *host_ops; 40 - }; 41 - 42 - struct sof_probe_point_desc { 43 - unsigned int buffer_id; 44 - unsigned int purpose; 45 - unsigned int stream_tag; 46 - } __packed; 47 - 48 - struct sof_probe_dma { 49 - unsigned int stream_tag; 50 - unsigned int dma_buffer_size; 51 - } __packed; 52 - 53 - struct sof_ipc_probe_dma_add_params { 54 - struct sof_ipc_cmd_hdr hdr; 55 - unsigned int num_elems; 56 - struct sof_probe_dma dma[]; 57 - } __packed; 58 - 59 - struct sof_ipc_probe_info_params { 60 - struct sof_ipc_reply rhdr; 61 - unsigned int num_elems; 62 - union { 63 - struct sof_probe_dma dma[0]; 64 - struct sof_probe_point_desc desc[0]; 65 - }; 66 - } __packed; 67 - 68 - struct sof_ipc_probe_point_add_params { 69 - struct sof_ipc_cmd_hdr hdr; 70 - unsigned int num_elems; 71 - struct sof_probe_point_desc desc[]; 72 - } __packed; 73 - 74 - struct sof_ipc_probe_point_remove_params { 75 - struct sof_ipc_cmd_hdr hdr; 76 - unsigned int num_elems; 77 - unsigned int buffer_id[]; 78 - } __packed; 79 - 80 - /** 81 - * sof_probes_init - initialize data probing 82 - * @cdev: SOF client device 83 - * @stream_tag: Extractor stream tag 84 - * @buffer_size: DMA buffer size to set for extractor 85 - * 86 - * Host chooses whether extraction is supported or not by providing 87 - * valid stream tag to DSP. Once specified, stream described by that 88 - * tag will be tied to DSP for extraction for the entire lifetime of 89 - * probe. 90 - * 91 - * Probing is initialized only once and each INIT request must be 92 - * matched by DEINIT call. 93 - */ 94 - static int sof_probes_init(struct sof_client_dev *cdev, u32 stream_tag, 95 - size_t buffer_size) 96 - { 97 - struct sof_ipc_probe_dma_add_params *msg; 98 - size_t size = struct_size(msg, dma, 1); 99 - struct sof_ipc_reply reply; 100 - int ret; 101 - 102 - msg = kmalloc(size, GFP_KERNEL); 103 - if (!msg) 104 - return -ENOMEM; 105 - msg->hdr.size = size; 106 - msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_INIT; 107 - msg->num_elems = 1; 108 - msg->dma[0].stream_tag = stream_tag; 109 - msg->dma[0].dma_buffer_size = buffer_size; 110 - 111 - ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply)); 112 - kfree(msg); 113 - return ret; 114 - } 115 - 116 - /** 117 - * sof_probes_deinit - cleanup after data probing 118 - * @cdev: SOF client device 119 - * 120 - * Host sends DEINIT request to free previously initialized probe 121 - * on DSP side once it is no longer needed. DEINIT only when there 122 - * are no probes connected and with all injectors detached. 123 - */ 124 - static int sof_probes_deinit(struct sof_client_dev *cdev) 125 - { 126 - struct sof_ipc_cmd_hdr msg; 127 - struct sof_ipc_reply reply; 128 - 129 - msg.size = sizeof(msg); 130 - msg.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DEINIT; 131 - 132 - return sof_client_ipc_tx_message(cdev, &msg, &reply, sizeof(reply)); 133 - } 134 - 135 - static int sof_probes_info(struct sof_client_dev *cdev, unsigned int cmd, 136 - void **params, size_t *num_params) 137 - { 138 - size_t max_msg_size = sof_client_get_ipc_max_payload_size(cdev); 139 - struct sof_ipc_probe_info_params msg = {{{0}}}; 140 - struct sof_ipc_probe_info_params *reply; 141 - size_t bytes; 142 - int ret; 143 - 144 - *params = NULL; 145 - *num_params = 0; 146 - 147 - reply = kzalloc(max_msg_size, GFP_KERNEL); 148 - if (!reply) 149 - return -ENOMEM; 150 - msg.rhdr.hdr.size = sizeof(msg); 151 - msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd; 152 - 153 - ret = sof_client_ipc_tx_message(cdev, &msg, reply, max_msg_size); 154 - if (ret < 0 || reply->rhdr.error < 0) 155 - goto exit; 156 - 157 - if (!reply->num_elems) 158 - goto exit; 159 - 160 - if (cmd == SOF_IPC_PROBE_DMA_INFO) 161 - bytes = sizeof(reply->dma[0]); 162 - else 163 - bytes = sizeof(reply->desc[0]); 164 - bytes *= reply->num_elems; 165 - *params = kmemdup(&reply->dma[0], bytes, GFP_KERNEL); 166 - if (!*params) { 167 - ret = -ENOMEM; 168 - goto exit; 169 - } 170 - *num_params = reply->num_elems; 171 - 172 - exit: 173 - kfree(reply); 174 - return ret; 175 - } 176 - 177 - /** 178 - * sof_probes_points_info - retrieve list of active probe points 179 - * @cdev: SOF client device 180 - * @desc: Returned list of active probes 181 - * @num_desc: Returned count of active probes 182 - * 183 - * Host sends PROBE_POINT_INFO request to obtain list of active probe 184 - * points, valid for disconnection when given probe is no longer 185 - * required. 186 - */ 187 - static int sof_probes_points_info(struct sof_client_dev *cdev, 188 - struct sof_probe_point_desc **desc, 189 - size_t *num_desc) 190 - { 191 - return sof_probes_info(cdev, SOF_IPC_PROBE_POINT_INFO, 192 - (void **)desc, num_desc); 193 - } 194 - 195 - /** 196 - * sof_probes_points_add - connect specified probes 197 - * @cdev: SOF client device 198 - * @desc: List of probe points to connect 199 - * @num_desc: Number of elements in @desc 200 - * 201 - * Dynamically connects to provided set of endpoints. Immediately 202 - * after connection is established, host must be prepared to 203 - * transfer data from or to target stream given the probing purpose. 204 - * 205 - * Each probe point should be removed using PROBE_POINT_REMOVE 206 - * request when no longer needed. 207 - */ 208 - static int sof_probes_points_add(struct sof_client_dev *cdev, 209 - struct sof_probe_point_desc *desc, 210 - size_t num_desc) 211 - { 212 - struct sof_ipc_probe_point_add_params *msg; 213 - size_t size = struct_size(msg, desc, num_desc); 214 - struct sof_ipc_reply reply; 215 - int ret; 216 - 217 - msg = kmalloc(size, GFP_KERNEL); 218 - if (!msg) 219 - return -ENOMEM; 220 - msg->hdr.size = size; 221 - msg->num_elems = num_desc; 222 - msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_ADD; 223 - memcpy(&msg->desc[0], desc, size - sizeof(*msg)); 224 - 225 - ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply)); 226 - kfree(msg); 227 - return ret; 228 - } 229 - 230 - /** 231 - * sof_probes_points_remove - disconnect specified probes 232 - * @cdev: SOF client device 233 - * @buffer_id: List of probe points to disconnect 234 - * @num_buffer_id: Number of elements in @desc 235 - * 236 - * Removes previously connected probes from list of active probe 237 - * points and frees all resources on DSP side. 238 - */ 239 - static int sof_probes_points_remove(struct sof_client_dev *cdev, 240 - unsigned int *buffer_id, size_t num_buffer_id) 241 - { 242 - struct sof_ipc_probe_point_remove_params *msg; 243 - size_t size = struct_size(msg, buffer_id, num_buffer_id); 244 - struct sof_ipc_reply reply; 245 - int ret; 246 - 247 - msg = kmalloc(size, GFP_KERNEL); 248 - if (!msg) 249 - return -ENOMEM; 250 - msg->hdr.size = size; 251 - msg->num_elems = num_buffer_id; 252 - msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE; 253 - memcpy(&msg->buffer_id[0], buffer_id, size - sizeof(*msg)); 254 - 255 - ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply)); 256 - kfree(msg); 257 - return ret; 258 - } 259 31 260 32 static int sof_probes_compr_startup(struct snd_compr_stream *cstream, 261 33 struct snd_soc_dai *dai) ··· 63 289 struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card); 64 290 struct sof_probes_priv *priv = cdev->data; 65 291 const struct sof_probes_host_ops *ops = priv->host_ops; 292 + const struct sof_probes_ipc_ops *ipc = priv->ipc_ops; 66 293 struct sof_probe_point_desc *desc; 67 294 size_t num_desc; 68 295 int i, ret; 69 296 70 297 /* disconnect all probe points */ 71 - ret = sof_probes_points_info(cdev, &desc, &num_desc); 298 + ret = ipc->points_info(cdev, &desc, &num_desc); 72 299 if (ret < 0) { 73 300 dev_err(dai->dev, "Failed to get probe points: %d\n", ret); 74 301 goto exit; 75 302 } 76 303 77 304 for (i = 0; i < num_desc; i++) 78 - sof_probes_points_remove(cdev, &desc[i].buffer_id, 1); 305 + ipc->points_remove(cdev, &desc[i].buffer_id, 1); 79 306 kfree(desc); 80 307 81 308 exit: 82 - ret = sof_probes_deinit(cdev); 309 + ret = ipc->deinit(cdev); 83 310 if (ret < 0) 84 311 dev_err(dai->dev, "Failed to deinit probe: %d\n", ret); 85 312 ··· 103 328 struct snd_compr_runtime *rtd = cstream->runtime; 104 329 struct sof_probes_priv *priv = cdev->data; 105 330 const struct sof_probes_host_ops *ops = priv->host_ops; 331 + const struct sof_probes_ipc_ops *ipc = priv->ipc_ops; 106 332 int ret; 107 333 108 334 cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG; ··· 116 340 if (ret) 117 341 return ret; 118 342 119 - ret = sof_probes_init(cdev, priv->extractor_stream_tag, rtd->dma_bytes); 343 + ret = ipc->init(cdev, priv->extractor_stream_tag, rtd->dma_bytes); 120 344 if (ret < 0) { 121 345 dev_err(dai->dev, "Failed to init probe: %d\n", ret); 122 346 return ret; ··· 195 419 struct sof_probes_priv *priv = cdev->data; 196 420 struct device *dev = &cdev->auxdev.dev; 197 421 struct sof_probe_point_desc *desc; 422 + const struct sof_probes_ipc_ops *ipc = priv->ipc_ops; 198 423 int remaining, offset; 199 424 size_t num_desc; 200 425 char *buf; ··· 216 439 goto exit; 217 440 } 218 441 219 - ret = sof_probes_points_info(cdev, &desc, &num_desc); 442 + ret = ipc->points_info(cdev, &desc, &num_desc); 220 443 if (ret < 0) 221 444 goto exit; 222 445 ··· 252 475 { 253 476 struct sof_client_dev *cdev = file->private_data; 254 477 struct sof_probes_priv *priv = cdev->data; 478 + const struct sof_probes_ipc_ops *ipc = priv->ipc_ops; 255 479 struct device *dev = &cdev->auxdev.dev; 256 480 struct sof_probe_point_desc *desc; 257 481 u32 num_elems, *array; ··· 283 505 goto exit; 284 506 } 285 507 286 - ret = sof_probes_points_add(cdev, desc, bytes / sizeof(*desc)); 508 + ret = ipc->points_add(cdev, desc, bytes / sizeof(*desc)); 287 509 if (!ret) 288 510 ret = count; 289 511 ··· 311 533 { 312 534 struct sof_client_dev *cdev = file->private_data; 313 535 struct sof_probes_priv *priv = cdev->data; 536 + const struct sof_probes_ipc_ops *ipc = priv->ipc_ops; 314 537 struct device *dev = &cdev->auxdev.dev; 315 538 int ret, err; 316 539 u32 *array; ··· 331 552 goto exit; 332 553 } 333 554 334 - ret = sof_probes_points_remove(cdev, &array[1], array[0]); 555 + ret = ipc->points_remove(cdev, &array[1], array[0]); 335 556 if (!ret) 336 557 ret = count; 337 558 ··· 399 620 if (!sof_probes_enabled) 400 621 return -ENXIO; 401 622 402 - /* only ipc3 is supported */ 403 - if (sof_client_get_ipc_type(cdev) != SOF_IPC) 404 - return -ENXIO; 405 - 406 623 if (!dev->platform_data) { 407 624 dev_err(dev, "missing platform data\n"); 408 625 return -ENODEV; ··· 417 642 } 418 643 419 644 priv->host_ops = ops; 645 + 646 + switch (sof_client_get_ipc_type(cdev)) { 647 + #ifdef CONFIG_SND_SOC_SOF_INTEL_IPC4 648 + case SOF_INTEL_IPC4: 649 + priv->ipc_ops = &ipc4_probe_ops; 650 + break; 651 + #endif 652 + #ifdef CONFIG_SND_SOC_SOF_IPC3 653 + case SOF_IPC: 654 + priv->ipc_ops = &ipc3_probe_ops; 655 + break; 656 + #endif 657 + default: 658 + dev_err(dev, "Matching IPC ops not found."); 659 + return -ENODEV; 660 + } 661 + 420 662 cdev->data = priv; 421 663 422 664 /* register probes component driver and dai */
+34
sound/soc/sof/sof-client-probes.h
··· 28 28 struct snd_soc_dai *dai); 29 29 }; 30 30 31 + struct sof_probe_point_desc { 32 + unsigned int buffer_id; 33 + unsigned int purpose; 34 + unsigned int stream_tag; 35 + } __packed; 36 + 37 + struct sof_probes_ipc_ops { 38 + int (*init)(struct sof_client_dev *cdev, u32 stream_tag, 39 + size_t buffer_size); 40 + int (*deinit)(struct sof_client_dev *cdev); 41 + int (*points_info)(struct sof_client_dev *cdev, 42 + struct sof_probe_point_desc **desc, 43 + size_t *num_desc); 44 + int (*points_add)(struct sof_client_dev *cdev, 45 + struct sof_probe_point_desc *desc, 46 + size_t num_desc); 47 + int (*points_remove)(struct sof_client_dev *cdev, 48 + unsigned int *buffer_id, size_t num_buffer_id); 49 + }; 50 + 51 + extern const struct sof_probes_ipc_ops ipc3_probe_ops; 52 + extern const struct sof_probes_ipc_ops ipc4_probe_ops; 53 + 54 + struct sof_probes_priv { 55 + struct dentry *dfs_points; 56 + struct dentry *dfs_points_remove; 57 + u32 extractor_stream_tag; 58 + struct snd_soc_card card; 59 + void *ipc_priv; 60 + 61 + const struct sof_probes_host_ops *host_ops; 62 + const struct sof_probes_ipc_ops *ipc_ops; 63 + }; 64 + 31 65 #endif
+34
sound/soc/sof/sof-client.c
··· 16 16 #include "ops.h" 17 17 #include "sof-client.h" 18 18 #include "sof-priv.h" 19 + #include "ipc4-priv.h" 19 20 20 21 /** 21 22 * struct sof_ipc_event_entry - IPC client event description ··· 265 264 return -EINVAL; 266 265 } 267 266 EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, SND_SOC_SOF_CLIENT); 267 + 268 + int sof_client_ipc_set_get_data(struct sof_client_dev *cdev, void *ipc_msg, 269 + bool set) 270 + { 271 + if (cdev->sdev->pdata->ipc_type == SOF_IPC) { 272 + struct sof_ipc_cmd_hdr *hdr = ipc_msg; 273 + 274 + return sof_ipc_set_get_data(cdev->sdev->ipc, ipc_msg, hdr->size, 275 + set); 276 + } else if (cdev->sdev->pdata->ipc_type == SOF_INTEL_IPC4) { 277 + struct sof_ipc4_msg *msg = ipc_msg; 278 + 279 + return sof_ipc_set_get_data(cdev->sdev->ipc, ipc_msg, 280 + msg->data_size, set); 281 + } 282 + 283 + return -EINVAL; 284 + } 285 + EXPORT_SYMBOL_NS_GPL(sof_client_ipc_set_get_data, SND_SOC_SOF_CLIENT); 286 + 287 + #ifdef CONFIG_SND_SOC_SOF_INTEL_IPC4 288 + struct sof_ipc4_fw_module *sof_client_ipc4_find_module(struct sof_client_dev *c, const guid_t *uuid) 289 + { 290 + struct snd_sof_dev *sdev = c->sdev; 291 + 292 + if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) 293 + return sof_ipc4_find_module_by_uuid(sdev, uuid); 294 + dev_err(sdev->dev, "Only supported with IPC4\n"); 295 + 296 + return NULL; 297 + } 298 + EXPORT_SYMBOL_NS_GPL(sof_client_ipc4_find_module, SND_SOC_SOF_CLIENT); 299 + #endif 268 300 269 301 int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state) 270 302 {
+6
sound/soc/sof/sof-client.h
··· 13 13 struct snd_sof_dev; 14 14 struct dentry; 15 15 16 + struct sof_ipc4_fw_module; 17 + 16 18 /** 17 19 * struct sof_client_dev - SOF client device 18 20 * @auxdev: auxiliary device ··· 39 37 40 38 int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg, 41 39 void *reply_data, size_t reply_bytes); 40 + int sof_client_ipc_set_get_data(struct sof_client_dev *cdev, void *ipc_msg, 41 + bool set); 42 + 43 + struct sof_ipc4_fw_module *sof_client_ipc4_find_module(struct sof_client_dev *c, const guid_t *u); 42 44 43 45 struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev); 44 46 struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev);
+2
sound/soc/sof/sof-priv.h
··· 680 680 } 681 681 int sof_ipc_tx_message(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes, 682 682 void *reply_data, size_t reply_bytes); 683 + int sof_ipc_set_get_data(struct snd_sof_ipc *ipc, void *msg_data, 684 + size_t msg_bytes, bool set); 683 685 int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes, 684 686 void *reply_data, size_t reply_bytes); 685 687 int sof_ipc_send_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,