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
15 struct sof_probe_dma {
16 unsigned int stream_tag;
17 unsigned int dma_buffer_size;
18 } __packed;
19
20 struct 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
26 struct 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
35 struct 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
41 struct 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 */
sof_ipc_probe_init(struct snd_sof_dev * sdev,u32 stream_tag,size_t buffer_size)61 static 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 */
sof_ipc_probe_deinit(struct snd_sof_dev * sdev)92 static 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
sof_ipc_probe_info(struct snd_sof_dev * sdev,unsigned int cmd,void ** params,size_t * num_params)104 static 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
141 exit:
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 */
sof_ipc_probe_points_info(struct snd_sof_dev * sdev,struct sof_probe_point_desc ** desc,size_t * num_desc)156 int 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 }
163 EXPORT_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 */
sof_ipc_probe_points_add(struct snd_sof_dev * sdev,struct sof_probe_point_desc * desc,size_t num_desc)178 int 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 }
199 EXPORT_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 */
sof_ipc_probe_points_remove(struct snd_sof_dev * sdev,unsigned int * buffer_id,size_t num_buffer_id)210 int 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 }
231 EXPORT_SYMBOL(sof_ipc_probe_points_remove);
232
sof_probe_compr_startup(struct snd_compr_stream * cstream,struct snd_soc_dai * dai)233 static 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
sof_probe_compr_shutdown(struct snd_compr_stream * cstream,struct snd_soc_dai * dai)249 static 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
268 exit:
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
sof_probe_compr_set_params(struct snd_compr_stream * cstream,struct snd_compr_params * params,struct snd_soc_dai * dai)279 static 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
sof_probe_compr_trigger(struct snd_compr_stream * cstream,int cmd,struct snd_soc_dai * dai)307 static 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
sof_probe_compr_pointer(struct snd_compr_stream * cstream,struct snd_compr_tstamp * tstamp,struct snd_soc_dai * dai)315 static 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
324 struct 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 };
331 EXPORT_SYMBOL(sof_probe_compr_ops);
332
sof_probe_compr_copy(struct snd_soc_component * component,struct snd_compr_stream * cstream,char __user * buf,size_t count)333 static 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
361 const struct snd_compress_ops sof_probe_compressed_ops = {
362 .copy = sof_probe_compr_copy,
363 };
364 EXPORT_SYMBOL(sof_probe_compressed_ops);
365