Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v5.16 364 lines 10 kB view raw
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-2021 Intel Corporation. All rights reserved. 7// Author: Cezary Rojewski <cezary.rojewski@intel.com> 8// 9 10#include <sound/soc.h> 11#include "ops.h" 12#include "sof-priv.h" 13#include "sof-probes.h" 14 15struct sof_probe_dma { 16 unsigned int stream_tag; 17 unsigned int dma_buffer_size; 18} __packed; 19 20struct sof_ipc_probe_dma_add_params { 21 struct sof_ipc_cmd_hdr hdr; 22 unsigned int num_elems; 23 struct sof_probe_dma dma[]; 24} __packed; 25 26struct sof_ipc_probe_info_params { 27 struct sof_ipc_reply rhdr; 28 unsigned int num_elems; 29 union { 30 struct sof_probe_dma dma[0]; 31 struct sof_probe_point_desc desc[0]; 32 }; 33} __packed; 34 35struct sof_ipc_probe_point_add_params { 36 struct sof_ipc_cmd_hdr hdr; 37 unsigned int num_elems; 38 struct sof_probe_point_desc desc[]; 39} __packed; 40 41struct sof_ipc_probe_point_remove_params { 42 struct sof_ipc_cmd_hdr hdr; 43 unsigned int num_elems; 44 unsigned int buffer_id[]; 45} __packed; 46 47/** 48 * sof_ipc_probe_init - initialize data probing 49 * @sdev: SOF sound device 50 * @stream_tag: Extractor stream tag 51 * @buffer_size: DMA buffer size to set for extractor 52 * 53 * Host chooses whether extraction is supported or not by providing 54 * valid stream tag to DSP. Once specified, stream described by that 55 * tag will be tied to DSP for extraction for the entire lifetime of 56 * probe. 57 * 58 * Probing is initialized only once and each INIT request must be 59 * matched by DEINIT call. 60 */ 61static int sof_ipc_probe_init(struct snd_sof_dev *sdev, u32 stream_tag, 62 size_t buffer_size) 63{ 64 struct sof_ipc_probe_dma_add_params *msg; 65 struct sof_ipc_reply reply; 66 size_t size = struct_size(msg, dma, 1); 67 int ret; 68 69 msg = kmalloc(size, GFP_KERNEL); 70 if (!msg) 71 return -ENOMEM; 72 msg->hdr.size = size; 73 msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_INIT; 74 msg->num_elems = 1; 75 msg->dma[0].stream_tag = stream_tag; 76 msg->dma[0].dma_buffer_size = buffer_size; 77 78 ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, 79 &reply, sizeof(reply)); 80 kfree(msg); 81 return ret; 82} 83 84/** 85 * sof_ipc_probe_deinit - cleanup after data probing 86 * @sdev: SOF sound device 87 * 88 * Host sends DEINIT request to free previously initialized probe 89 * on DSP side once it is no longer needed. DEINIT only when there 90 * are no probes connected and with all injectors detached. 91 */ 92static int sof_ipc_probe_deinit(struct snd_sof_dev *sdev) 93{ 94 struct sof_ipc_cmd_hdr msg; 95 struct sof_ipc_reply reply; 96 97 msg.size = sizeof(msg); 98 msg.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DEINIT; 99 100 return sof_ipc_tx_message(sdev->ipc, msg.cmd, &msg, msg.size, 101 &reply, sizeof(reply)); 102} 103 104static int sof_ipc_probe_info(struct snd_sof_dev *sdev, unsigned int cmd, 105 void **params, size_t *num_params) 106{ 107 struct sof_ipc_probe_info_params msg = {{{0}}}; 108 struct sof_ipc_probe_info_params *reply; 109 size_t bytes; 110 int ret; 111 112 *params = NULL; 113 *num_params = 0; 114 115 reply = kzalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); 116 if (!reply) 117 return -ENOMEM; 118 msg.rhdr.hdr.size = sizeof(msg); 119 msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd; 120 121 ret = sof_ipc_tx_message(sdev->ipc, msg.rhdr.hdr.cmd, &msg, 122 msg.rhdr.hdr.size, reply, SOF_IPC_MSG_MAX_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 141exit: 142 kfree(reply); 143 return ret; 144} 145 146/** 147 * sof_ipc_probe_points_info - retrieve list of active probe points 148 * @sdev: SOF sound 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 */ 156int sof_ipc_probe_points_info(struct snd_sof_dev *sdev, 157 struct sof_probe_point_desc **desc, 158 size_t *num_desc) 159{ 160 return sof_ipc_probe_info(sdev, SOF_IPC_PROBE_POINT_INFO, 161 (void **)desc, num_desc); 162} 163EXPORT_SYMBOL(sof_ipc_probe_points_info); 164 165/** 166 * sof_ipc_probe_points_add - connect specified probes 167 * @sdev: SOF sound device 168 * @desc: List of probe points to connect 169 * @num_desc: Number of elements in @desc 170 * 171 * Dynamically connects to provided set of endpoints. Immediately 172 * after connection is established, host must be prepared to 173 * transfer data from or to target stream given the probing purpose. 174 * 175 * Each probe point should be removed using PROBE_POINT_REMOVE 176 * request when no longer needed. 177 */ 178int sof_ipc_probe_points_add(struct snd_sof_dev *sdev, 179 struct sof_probe_point_desc *desc, size_t num_desc) 180{ 181 struct sof_ipc_probe_point_add_params *msg; 182 struct sof_ipc_reply reply; 183 size_t size = struct_size(msg, desc, num_desc); 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_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, 195 &reply, sizeof(reply)); 196 kfree(msg); 197 return ret; 198} 199EXPORT_SYMBOL(sof_ipc_probe_points_add); 200 201/** 202 * sof_ipc_probe_points_remove - disconnect specified probes 203 * @sdev: SOF sound device 204 * @buffer_id: List of probe points to disconnect 205 * @num_buffer_id: Number of elements in @desc 206 * 207 * Removes previously connected probes from list of active probe 208 * points and frees all resources on DSP side. 209 */ 210int sof_ipc_probe_points_remove(struct snd_sof_dev *sdev, 211 unsigned int *buffer_id, size_t num_buffer_id) 212{ 213 struct sof_ipc_probe_point_remove_params *msg; 214 struct sof_ipc_reply reply; 215 size_t size = struct_size(msg, buffer_id, num_buffer_id); 216 int ret; 217 218 msg = kmalloc(size, GFP_KERNEL); 219 if (!msg) 220 return -ENOMEM; 221 msg->hdr.size = size; 222 msg->num_elems = num_buffer_id; 223 msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE; 224 memcpy(&msg->buffer_id[0], buffer_id, size - sizeof(*msg)); 225 226 ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, 227 &reply, sizeof(reply)); 228 kfree(msg); 229 return ret; 230} 231EXPORT_SYMBOL(sof_ipc_probe_points_remove); 232 233static int sof_probe_compr_startup(struct snd_compr_stream *cstream, 234 struct snd_soc_dai *dai) 235{ 236 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); 237 int ret; 238 239 ret = snd_sof_probe_compr_assign(sdev, cstream, dai); 240 if (ret < 0) { 241 dev_err(dai->dev, "Failed to assign probe stream: %d\n", ret); 242 return ret; 243 } 244 245 sdev->extractor_stream_tag = ret; 246 return 0; 247} 248 249static int sof_probe_compr_shutdown(struct snd_compr_stream *cstream, 250 struct snd_soc_dai *dai) 251{ 252 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); 253 struct sof_probe_point_desc *desc; 254 size_t num_desc; 255 int i, ret; 256 257 /* disconnect all probe points */ 258 ret = sof_ipc_probe_points_info(sdev, &desc, &num_desc); 259 if (ret < 0) { 260 dev_err(dai->dev, "Failed to get probe points: %d\n", ret); 261 goto exit; 262 } 263 264 for (i = 0; i < num_desc; i++) 265 sof_ipc_probe_points_remove(sdev, &desc[i].buffer_id, 1); 266 kfree(desc); 267 268exit: 269 ret = sof_ipc_probe_deinit(sdev); 270 if (ret < 0) 271 dev_err(dai->dev, "Failed to deinit probe: %d\n", ret); 272 273 sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID; 274 snd_compr_free_pages(cstream); 275 276 return snd_sof_probe_compr_free(sdev, cstream, dai); 277} 278 279static int sof_probe_compr_set_params(struct snd_compr_stream *cstream, 280 struct snd_compr_params *params, 281 struct snd_soc_dai *dai) 282{ 283 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); 284 struct snd_compr_runtime *rtd = cstream->runtime; 285 int ret; 286 287 cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG; 288 cstream->dma_buffer.dev.dev = sdev->dev; 289 ret = snd_compr_malloc_pages(cstream, rtd->buffer_size); 290 if (ret < 0) 291 return ret; 292 293 ret = snd_sof_probe_compr_set_params(sdev, cstream, params, dai); 294 if (ret < 0) 295 return ret; 296 297 ret = sof_ipc_probe_init(sdev, sdev->extractor_stream_tag, 298 rtd->dma_bytes); 299 if (ret < 0) { 300 dev_err(dai->dev, "Failed to init probe: %d\n", ret); 301 return ret; 302 } 303 304 return 0; 305} 306 307static int sof_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd, 308 struct snd_soc_dai *dai) 309{ 310 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); 311 312 return snd_sof_probe_compr_trigger(sdev, cstream, cmd, dai); 313} 314 315static int sof_probe_compr_pointer(struct snd_compr_stream *cstream, 316 struct snd_compr_tstamp *tstamp, 317 struct snd_soc_dai *dai) 318{ 319 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); 320 321 return snd_sof_probe_compr_pointer(sdev, cstream, tstamp, dai); 322} 323 324struct snd_soc_cdai_ops sof_probe_compr_ops = { 325 .startup = sof_probe_compr_startup, 326 .shutdown = sof_probe_compr_shutdown, 327 .set_params = sof_probe_compr_set_params, 328 .trigger = sof_probe_compr_trigger, 329 .pointer = sof_probe_compr_pointer, 330}; 331EXPORT_SYMBOL(sof_probe_compr_ops); 332 333static int sof_probe_compr_copy(struct snd_soc_component *component, 334 struct snd_compr_stream *cstream, 335 char __user *buf, size_t count) 336{ 337 struct snd_compr_runtime *rtd = cstream->runtime; 338 unsigned int offset, n; 339 void *ptr; 340 int ret; 341 342 if (count > rtd->buffer_size) 343 count = rtd->buffer_size; 344 345 div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset); 346 ptr = rtd->dma_area + offset; 347 n = rtd->buffer_size - offset; 348 349 if (count < n) { 350 ret = copy_to_user(buf, ptr, count); 351 } else { 352 ret = copy_to_user(buf, ptr, n); 353 ret += copy_to_user(buf + n, rtd->dma_area, count - n); 354 } 355 356 if (ret) 357 return count - ret; 358 return count; 359} 360 361const struct snd_compress_ops sof_probe_compressed_ops = { 362 .copy = sof_probe_compr_copy, 363}; 364EXPORT_SYMBOL(sof_probe_compressed_ops);