1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 2// 3// This file is provided under a dual BSD/GPLv2 license. When using or 4// redistributing this file, you may do so under either license. 5// 6// Copyright(c) 2019-2020 Intel Corporation. All rights reserved. 7// 8// Author: Cezary Rojewski <cezary.rojewski@intel.com> 9// 10 11#include "sof-priv.h" 12#include "probe.h" 13 14/** 15 * sof_ipc_probe_init - initialize data probing 16 * @sdev: SOF sound device 17 * @stream_tag: Extractor stream tag 18 * @buffer_size: DMA buffer size to set for extractor 19 * 20 * Host chooses whether extraction is supported or not by providing 21 * valid stream tag to DSP. Once specified, stream described by that 22 * tag will be tied to DSP for extraction for the entire lifetime of 23 * probe. 24 * 25 * Probing is initialized only once and each INIT request must be 26 * matched by DEINIT call. 27 */ 28int sof_ipc_probe_init(struct snd_sof_dev *sdev, 29 u32 stream_tag, size_t buffer_size) 30{ 31 struct sof_ipc_probe_dma_add_params *msg; 32 struct sof_ipc_reply reply; 33 size_t size = struct_size(msg, dma, 1); 34 int ret; 35 36 msg = kmalloc(size, GFP_KERNEL); 37 if (!msg) 38 return -ENOMEM; 39 msg->hdr.size = size; 40 msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_INIT; 41 msg->num_elems = 1; 42 msg->dma[0].stream_tag = stream_tag; 43 msg->dma[0].dma_buffer_size = buffer_size; 44 45 ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, 46 &reply, sizeof(reply)); 47 kfree(msg); 48 return ret; 49} 50EXPORT_SYMBOL(sof_ipc_probe_init); 51 52/** 53 * sof_ipc_probe_deinit - cleanup after data probing 54 * @sdev: SOF sound device 55 * 56 * Host sends DEINIT request to free previously initialized probe 57 * on DSP side once it is no longer needed. DEINIT only when there 58 * are no probes connected and with all injectors detached. 59 */ 60int sof_ipc_probe_deinit(struct snd_sof_dev *sdev) 61{ 62 struct sof_ipc_cmd_hdr msg; 63 struct sof_ipc_reply reply; 64 65 msg.size = sizeof(msg); 66 msg.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DEINIT; 67 68 return sof_ipc_tx_message(sdev->ipc, msg.cmd, &msg, msg.size, 69 &reply, sizeof(reply)); 70} 71EXPORT_SYMBOL(sof_ipc_probe_deinit); 72 73static int sof_ipc_probe_info(struct snd_sof_dev *sdev, unsigned int cmd, 74 void **params, size_t *num_params) 75{ 76 struct sof_ipc_probe_info_params msg = {{{0}}}; 77 struct sof_ipc_probe_info_params *reply; 78 size_t bytes; 79 int ret; 80 81 *params = NULL; 82 *num_params = 0; 83 84 reply = kzalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); 85 if (!reply) 86 return -ENOMEM; 87 msg.rhdr.hdr.size = sizeof(msg); 88 msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd; 89 90 ret = sof_ipc_tx_message(sdev->ipc, msg.rhdr.hdr.cmd, &msg, 91 msg.rhdr.hdr.size, reply, SOF_IPC_MSG_MAX_SIZE); 92 if (ret < 0 || reply->rhdr.error < 0) 93 goto exit; 94 95 if (!reply->num_elems) 96 goto exit; 97 98 if (cmd == SOF_IPC_PROBE_DMA_INFO) 99 bytes = sizeof(reply->dma[0]); 100 else 101 bytes = sizeof(reply->desc[0]); 102 bytes *= reply->num_elems; 103 *params = kmemdup(&reply->dma[0], bytes, GFP_KERNEL); 104 if (!*params) { 105 ret = -ENOMEM; 106 goto exit; 107 } 108 *num_params = reply->num_elems; 109 110exit: 111 kfree(reply); 112 return ret; 113} 114 115/** 116 * sof_ipc_probe_dma_info - retrieve list of active injection dmas 117 * @sdev: SOF sound device 118 * @dma: Returned list of active dmas 119 * @num_dma: Returned count of active dmas 120 * 121 * Host sends DMA_INFO request to obtain list of injection dmas it 122 * can use to transfer data over with. 123 * 124 * Note that list contains only injection dmas as there is only one 125 * extractor (dma) and it is always assigned on probing init. 126 * DSP knows exactly where data from extraction probes is going to, 127 * which is not the case for injection where multiple streams 128 * could be engaged. 129 */ 130int sof_ipc_probe_dma_info(struct snd_sof_dev *sdev, 131 struct sof_probe_dma **dma, size_t *num_dma) 132{ 133 return sof_ipc_probe_info(sdev, SOF_IPC_PROBE_DMA_INFO, 134 (void **)dma, num_dma); 135} 136EXPORT_SYMBOL(sof_ipc_probe_dma_info); 137 138/** 139 * sof_ipc_probe_dma_add - attach to specified dmas 140 * @sdev: SOF sound device 141 * @dma: List of streams (dmas) to attach to 142 * @num_dma: Number of elements in @dma 143 * 144 * Contrary to extraction, injection streams are never assigned 145 * on init. Before attempting any data injection, host is responsible 146 * for specifying streams which will be later used to transfer data 147 * to connected probe points. 148 */ 149int sof_ipc_probe_dma_add(struct snd_sof_dev *sdev, 150 struct sof_probe_dma *dma, size_t num_dma) 151{ 152 struct sof_ipc_probe_dma_add_params *msg; 153 struct sof_ipc_reply reply; 154 size_t size = struct_size(msg, dma, num_dma); 155 int ret; 156 157 msg = kmalloc(size, GFP_KERNEL); 158 if (!msg) 159 return -ENOMEM; 160 msg->hdr.size = size; 161 msg->num_elems = num_dma; 162 msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DMA_ADD; 163 memcpy(&msg->dma[0], dma, size - sizeof(*msg)); 164 165 ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, 166 &reply, sizeof(reply)); 167 kfree(msg); 168 return ret; 169} 170EXPORT_SYMBOL(sof_ipc_probe_dma_add); 171 172/** 173 * sof_ipc_probe_dma_remove - detach from specified dmas 174 * @sdev: SOF sound device 175 * @stream_tag: List of stream tags to detach from 176 * @num_stream_tag: Number of elements in @stream_tag 177 * 178 * Host sends DMA_REMOVE request to free previously attached stream 179 * from being occupied for injection. Each detach operation should 180 * match equivalent DMA_ADD. Detach only when all probes tied to 181 * given stream have been disconnected. 182 */ 183int sof_ipc_probe_dma_remove(struct snd_sof_dev *sdev, 184 unsigned int *stream_tag, size_t num_stream_tag) 185{ 186 struct sof_ipc_probe_dma_remove_params *msg; 187 struct sof_ipc_reply reply; 188 size_t size = struct_size(msg, stream_tag, num_stream_tag); 189 int ret; 190 191 msg = kmalloc(size, GFP_KERNEL); 192 if (!msg) 193 return -ENOMEM; 194 msg->hdr.size = size; 195 msg->num_elems = num_stream_tag; 196 msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DMA_REMOVE; 197 memcpy(&msg->stream_tag[0], stream_tag, size - sizeof(*msg)); 198 199 ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, 200 &reply, sizeof(reply)); 201 kfree(msg); 202 return ret; 203} 204EXPORT_SYMBOL(sof_ipc_probe_dma_remove); 205 206/** 207 * sof_ipc_probe_points_info - retrieve list of active probe points 208 * @sdev: SOF sound device 209 * @desc: Returned list of active probes 210 * @num_desc: Returned count of active probes 211 * 212 * Host sends PROBE_POINT_INFO request to obtain list of active probe 213 * points, valid for disconnection when given probe is no longer 214 * required. 215 */ 216int sof_ipc_probe_points_info(struct snd_sof_dev *sdev, 217 struct sof_probe_point_desc **desc, size_t *num_desc) 218{ 219 return sof_ipc_probe_info(sdev, SOF_IPC_PROBE_POINT_INFO, 220 (void **)desc, num_desc); 221} 222EXPORT_SYMBOL(sof_ipc_probe_points_info); 223 224/** 225 * sof_ipc_probe_points_add - connect specified probes 226 * @sdev: SOF sound device 227 * @desc: List of probe points to connect 228 * @num_desc: Number of elements in @desc 229 * 230 * Dynamically connects to provided set of endpoints. Immediately 231 * after connection is established, host must be prepared to 232 * transfer data from or to target stream given the probing purpose. 233 * 234 * Each probe point should be removed using PROBE_POINT_REMOVE 235 * request when no longer needed. 236 */ 237int sof_ipc_probe_points_add(struct snd_sof_dev *sdev, 238 struct sof_probe_point_desc *desc, size_t num_desc) 239{ 240 struct sof_ipc_probe_point_add_params *msg; 241 struct sof_ipc_reply reply; 242 size_t size = struct_size(msg, desc, num_desc); 243 int ret; 244 245 msg = kmalloc(size, GFP_KERNEL); 246 if (!msg) 247 return -ENOMEM; 248 msg->hdr.size = size; 249 msg->num_elems = num_desc; 250 msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_ADD; 251 memcpy(&msg->desc[0], desc, size - sizeof(*msg)); 252 253 ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, 254 &reply, sizeof(reply)); 255 kfree(msg); 256 return ret; 257} 258EXPORT_SYMBOL(sof_ipc_probe_points_add); 259 260/** 261 * sof_ipc_probe_points_remove - disconnect specified probes 262 * @sdev: SOF sound device 263 * @buffer_id: List of probe points to disconnect 264 * @num_buffer_id: Number of elements in @desc 265 * 266 * Removes previously connected probes from list of active probe 267 * points and frees all resources on DSP side. 268 */ 269int sof_ipc_probe_points_remove(struct snd_sof_dev *sdev, 270 unsigned int *buffer_id, size_t num_buffer_id) 271{ 272 struct sof_ipc_probe_point_remove_params *msg; 273 struct sof_ipc_reply reply; 274 size_t size = struct_size(msg, buffer_id, num_buffer_id); 275 int ret; 276 277 msg = kmalloc(size, GFP_KERNEL); 278 if (!msg) 279 return -ENOMEM; 280 msg->hdr.size = size; 281 msg->num_elems = num_buffer_id; 282 msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE; 283 memcpy(&msg->buffer_id[0], buffer_id, size - sizeof(*msg)); 284 285 ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, 286 &reply, sizeof(reply)); 287 kfree(msg); 288 return ret; 289} 290EXPORT_SYMBOL(sof_ipc_probe_points_remove); 291