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 // SOF client support:
8 //  Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
9 //  Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
10 //
11 
12 #include <linux/debugfs.h>
13 #include <linux/module.h>
14 #include <linux/pm_runtime.h>
15 #include <sound/soc.h>
16 #include <sound/sof/header.h>
17 #include "sof-client.h"
18 #include "sof-client-probes.h"
19 
20 #define SOF_PROBES_SUSPEND_DELAY_MS 3000
21 /* only extraction supported for now */
22 #define SOF_PROBES_NUM_DAI_LINKS 1
23 
24 #define SOF_PROBES_INVALID_NODE_ID UINT_MAX
25 
26 static bool __read_mostly sof_probes_enabled;
27 module_param_named(enable, sof_probes_enabled, bool, 0444);
28 MODULE_PARM_DESC(enable, "Enable SOF probes support");
29 
30 struct sof_probes_priv {
31 	struct dentry *dfs_points;
32 	struct dentry *dfs_points_remove;
33 	u32 extractor_stream_tag;
34 	struct snd_soc_card card;
35 
36 	const struct sof_probes_host_ops *host_ops;
37 };
38 
39 struct sof_probe_point_desc {
40 	unsigned int buffer_id;
41 	unsigned int purpose;
42 	unsigned int stream_tag;
43 } __packed;
44 
45 struct sof_probe_dma {
46 	unsigned int stream_tag;
47 	unsigned int dma_buffer_size;
48 } __packed;
49 
50 struct sof_ipc_probe_dma_add_params {
51 	struct sof_ipc_cmd_hdr hdr;
52 	unsigned int num_elems;
53 	struct sof_probe_dma dma[];
54 } __packed;
55 
56 struct sof_ipc_probe_info_params {
57 	struct sof_ipc_reply rhdr;
58 	unsigned int num_elems;
59 	union {
60 		struct sof_probe_dma dma[0];
61 		struct sof_probe_point_desc desc[0];
62 	};
63 } __packed;
64 
65 struct sof_ipc_probe_point_add_params {
66 	struct sof_ipc_cmd_hdr hdr;
67 	unsigned int num_elems;
68 	struct sof_probe_point_desc desc[];
69 } __packed;
70 
71 struct sof_ipc_probe_point_remove_params {
72 	struct sof_ipc_cmd_hdr hdr;
73 	unsigned int num_elems;
74 	unsigned int buffer_id[];
75 } __packed;
76 
77 /**
78  * sof_probes_init - initialize data probing
79  * @cdev:		SOF client device
80  * @stream_tag:		Extractor stream tag
81  * @buffer_size:	DMA buffer size to set for extractor
82  *
83  * Host chooses whether extraction is supported or not by providing
84  * valid stream tag to DSP. Once specified, stream described by that
85  * tag will be tied to DSP for extraction for the entire lifetime of
86  * probe.
87  *
88  * Probing is initialized only once and each INIT request must be
89  * matched by DEINIT call.
90  */
91 static int sof_probes_init(struct sof_client_dev *cdev, u32 stream_tag,
92 			   size_t buffer_size)
93 {
94 	struct sof_ipc_probe_dma_add_params *msg;
95 	size_t size = struct_size(msg, dma, 1);
96 	struct sof_ipc_reply reply;
97 	int ret;
98 
99 	msg = kmalloc(size, GFP_KERNEL);
100 	if (!msg)
101 		return -ENOMEM;
102 	msg->hdr.size = size;
103 	msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_INIT;
104 	msg->num_elems = 1;
105 	msg->dma[0].stream_tag = stream_tag;
106 	msg->dma[0].dma_buffer_size = buffer_size;
107 
108 	ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply));
109 	kfree(msg);
110 	return ret;
111 }
112 
113 /**
114  * sof_probes_deinit - cleanup after data probing
115  * @cdev:		SOF client device
116  *
117  * Host sends DEINIT request to free previously initialized probe
118  * on DSP side once it is no longer needed. DEINIT only when there
119  * are no probes connected and with all injectors detached.
120  */
121 static int sof_probes_deinit(struct sof_client_dev *cdev)
122 {
123 	struct sof_ipc_cmd_hdr msg;
124 	struct sof_ipc_reply reply;
125 
126 	msg.size = sizeof(msg);
127 	msg.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DEINIT;
128 
129 	return sof_client_ipc_tx_message(cdev, &msg, &reply, sizeof(reply));
130 }
131 
132 static int sof_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
133 			   void **params, size_t *num_params)
134 {
135 	struct sof_ipc_probe_info_params msg = {{{0}}};
136 	struct sof_ipc_probe_info_params *reply;
137 	size_t bytes;
138 	int ret;
139 
140 	*params = NULL;
141 	*num_params = 0;
142 
143 	reply = kzalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
144 	if (!reply)
145 		return -ENOMEM;
146 	msg.rhdr.hdr.size = sizeof(msg);
147 	msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd;
148 
149 	ret = sof_client_ipc_tx_message(cdev, &msg, reply, SOF_IPC_MSG_MAX_SIZE);
150 	if (ret < 0 || reply->rhdr.error < 0)
151 		goto exit;
152 
153 	if (!reply->num_elems)
154 		goto exit;
155 
156 	if (cmd == SOF_IPC_PROBE_DMA_INFO)
157 		bytes = sizeof(reply->dma[0]);
158 	else
159 		bytes = sizeof(reply->desc[0]);
160 	bytes *= reply->num_elems;
161 	*params = kmemdup(&reply->dma[0], bytes, GFP_KERNEL);
162 	if (!*params) {
163 		ret = -ENOMEM;
164 		goto exit;
165 	}
166 	*num_params = reply->num_elems;
167 
168 exit:
169 	kfree(reply);
170 	return ret;
171 }
172 
173 /**
174  * sof_probes_points_info - retrieve list of active probe points
175  * @cdev:		SOF client device
176  * @desc:	Returned list of active probes
177  * @num_desc:	Returned count of active probes
178  *
179  * Host sends PROBE_POINT_INFO request to obtain list of active probe
180  * points, valid for disconnection when given probe is no longer
181  * required.
182  */
183 static int sof_probes_points_info(struct sof_client_dev *cdev,
184 				  struct sof_probe_point_desc **desc,
185 				  size_t *num_desc)
186 {
187 	return sof_probes_info(cdev, SOF_IPC_PROBE_POINT_INFO,
188 			       (void **)desc, num_desc);
189 }
190 
191 /**
192  * sof_probes_points_add - connect specified probes
193  * @cdev:		SOF client device
194  * @desc:	List of probe points to connect
195  * @num_desc:	Number of elements in @desc
196  *
197  * Dynamically connects to provided set of endpoints. Immediately
198  * after connection is established, host must be prepared to
199  * transfer data from or to target stream given the probing purpose.
200  *
201  * Each probe point should be removed using PROBE_POINT_REMOVE
202  * request when no longer needed.
203  */
204 static int sof_probes_points_add(struct sof_client_dev *cdev,
205 				 struct sof_probe_point_desc *desc,
206 				 size_t num_desc)
207 {
208 	struct sof_ipc_probe_point_add_params *msg;
209 	size_t size = struct_size(msg, desc, num_desc);
210 	struct sof_ipc_reply reply;
211 	int ret;
212 
213 	msg = kmalloc(size, GFP_KERNEL);
214 	if (!msg)
215 		return -ENOMEM;
216 	msg->hdr.size = size;
217 	msg->num_elems = num_desc;
218 	msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_ADD;
219 	memcpy(&msg->desc[0], desc, size - sizeof(*msg));
220 
221 	ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply));
222 	kfree(msg);
223 	return ret;
224 }
225 
226 /**
227  * sof_probes_points_remove - disconnect specified probes
228  * @cdev:		SOF client device
229  * @buffer_id:		List of probe points to disconnect
230  * @num_buffer_id:	Number of elements in @desc
231  *
232  * Removes previously connected probes from list of active probe
233  * points and frees all resources on DSP side.
234  */
235 static int sof_probes_points_remove(struct sof_client_dev *cdev,
236 				    unsigned int *buffer_id, size_t num_buffer_id)
237 {
238 	struct sof_ipc_probe_point_remove_params *msg;
239 	size_t size = struct_size(msg, buffer_id, num_buffer_id);
240 	struct sof_ipc_reply reply;
241 	int ret;
242 
243 	msg = kmalloc(size, GFP_KERNEL);
244 	if (!msg)
245 		return -ENOMEM;
246 	msg->hdr.size = size;
247 	msg->num_elems = num_buffer_id;
248 	msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE;
249 	memcpy(&msg->buffer_id[0], buffer_id, size - sizeof(*msg));
250 
251 	ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply));
252 	kfree(msg);
253 	return ret;
254 }
255 
256 static int sof_probes_compr_startup(struct snd_compr_stream *cstream,
257 				    struct snd_soc_dai *dai)
258 {
259 	struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
260 	struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
261 	struct sof_probes_priv *priv = cdev->data;
262 	const struct sof_probes_host_ops *ops = priv->host_ops;
263 	int ret;
264 
265 	if (sof_client_get_fw_state(cdev) == SOF_FW_CRASHED)
266 		return -ENODEV;
267 
268 	ret = sof_client_core_module_get(cdev);
269 	if (ret)
270 		return ret;
271 
272 	ret = ops->assign(cdev, cstream, dai, &priv->extractor_stream_tag);
273 	if (ret) {
274 		dev_err(dai->dev, "Failed to assign probe stream: %d\n", ret);
275 		priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
276 		sof_client_core_module_put(cdev);
277 	}
278 
279 	return ret;
280 }
281 
282 static int sof_probes_compr_shutdown(struct snd_compr_stream *cstream,
283 				     struct snd_soc_dai *dai)
284 {
285 	struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
286 	struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
287 	struct sof_probes_priv *priv = cdev->data;
288 	const struct sof_probes_host_ops *ops = priv->host_ops;
289 	struct sof_probe_point_desc *desc;
290 	size_t num_desc;
291 	int i, ret;
292 
293 	/* disconnect all probe points */
294 	ret = sof_probes_points_info(cdev, &desc, &num_desc);
295 	if (ret < 0) {
296 		dev_err(dai->dev, "Failed to get probe points: %d\n", ret);
297 		goto exit;
298 	}
299 
300 	for (i = 0; i < num_desc; i++)
301 		sof_probes_points_remove(cdev, &desc[i].buffer_id, 1);
302 	kfree(desc);
303 
304 exit:
305 	ret = sof_probes_deinit(cdev);
306 	if (ret < 0)
307 		dev_err(dai->dev, "Failed to deinit probe: %d\n", ret);
308 
309 	priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
310 	snd_compr_free_pages(cstream);
311 
312 	ret = ops->free(cdev, cstream, dai);
313 
314 	sof_client_core_module_put(cdev);
315 
316 	return ret;
317 }
318 
319 static int sof_probes_compr_set_params(struct snd_compr_stream *cstream,
320 				       struct snd_compr_params *params,
321 				       struct snd_soc_dai *dai)
322 {
323 	struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
324 	struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
325 	struct snd_compr_runtime *rtd = cstream->runtime;
326 	struct sof_probes_priv *priv = cdev->data;
327 	const struct sof_probes_host_ops *ops = priv->host_ops;
328 	int ret;
329 
330 	cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG;
331 	cstream->dma_buffer.dev.dev = sof_client_get_dma_dev(cdev);
332 	ret = snd_compr_malloc_pages(cstream, rtd->buffer_size);
333 	if (ret < 0)
334 		return ret;
335 
336 	ret = ops->set_params(cdev, cstream, params, dai);
337 	if (ret)
338 		return ret;
339 
340 	ret = sof_probes_init(cdev, priv->extractor_stream_tag, rtd->dma_bytes);
341 	if (ret < 0) {
342 		dev_err(dai->dev, "Failed to init probe: %d\n", ret);
343 		return ret;
344 	}
345 
346 	return 0;
347 }
348 
349 static int sof_probes_compr_trigger(struct snd_compr_stream *cstream, int cmd,
350 				    struct snd_soc_dai *dai)
351 {
352 	struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
353 	struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
354 	struct sof_probes_priv *priv = cdev->data;
355 	const struct sof_probes_host_ops *ops = priv->host_ops;
356 
357 	return ops->trigger(cdev, cstream, cmd, dai);
358 }
359 
360 static int sof_probes_compr_pointer(struct snd_compr_stream *cstream,
361 				    struct snd_compr_tstamp *tstamp,
362 				    struct snd_soc_dai *dai)
363 {
364 	struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
365 	struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
366 	struct sof_probes_priv *priv = cdev->data;
367 	const struct sof_probes_host_ops *ops = priv->host_ops;
368 
369 	return ops->pointer(cdev, cstream, tstamp, dai);
370 }
371 
372 static const struct snd_soc_cdai_ops sof_probes_compr_ops = {
373 	.startup = sof_probes_compr_startup,
374 	.shutdown = sof_probes_compr_shutdown,
375 	.set_params = sof_probes_compr_set_params,
376 	.trigger = sof_probes_compr_trigger,
377 	.pointer = sof_probes_compr_pointer,
378 };
379 
380 static int sof_probes_compr_copy(struct snd_soc_component *component,
381 				 struct snd_compr_stream *cstream,
382 				 char __user *buf, size_t count)
383 {
384 	struct snd_compr_runtime *rtd = cstream->runtime;
385 	unsigned int offset, n;
386 	void *ptr;
387 	int ret;
388 
389 	if (count > rtd->buffer_size)
390 		count = rtd->buffer_size;
391 
392 	div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset);
393 	ptr = rtd->dma_area + offset;
394 	n = rtd->buffer_size - offset;
395 
396 	if (count < n) {
397 		ret = copy_to_user(buf, ptr, count);
398 	} else {
399 		ret = copy_to_user(buf, ptr, n);
400 		ret += copy_to_user(buf + n, rtd->dma_area, count - n);
401 	}
402 
403 	if (ret)
404 		return count - ret;
405 	return count;
406 }
407 
408 static const struct snd_compress_ops sof_probes_compressed_ops = {
409 	.copy = sof_probes_compr_copy,
410 };
411 
412 /**
413  * strsplit_u32 - Split string into sequence of u32 tokens
414  * @buf:	String to split into tokens.
415  * @delim:	String containing delimiter characters.
416  * @tkns:	Returned u32 sequence pointer.
417  * @num_tkns:	Returned number of tokens obtained.
418  */
419 static int strsplit_u32(char *buf, const char *delim, u32 **tkns, size_t *num_tkns)
420 {
421 	char *s;
422 	u32 *data, *tmp;
423 	size_t count = 0;
424 	size_t cap = 32;
425 	int ret = 0;
426 
427 	*tkns = NULL;
428 	*num_tkns = 0;
429 	data = kcalloc(cap, sizeof(*data), GFP_KERNEL);
430 	if (!data)
431 		return -ENOMEM;
432 
433 	while ((s = strsep(&buf, delim)) != NULL) {
434 		ret = kstrtouint(s, 0, data + count);
435 		if (ret)
436 			goto exit;
437 		if (++count >= cap) {
438 			cap *= 2;
439 			tmp = krealloc(data, cap * sizeof(*data), GFP_KERNEL);
440 			if (!tmp) {
441 				ret = -ENOMEM;
442 				goto exit;
443 			}
444 			data = tmp;
445 		}
446 	}
447 
448 	if (!count)
449 		goto exit;
450 	*tkns = kmemdup(data, count * sizeof(*data), GFP_KERNEL);
451 	if (!(*tkns)) {
452 		ret = -ENOMEM;
453 		goto exit;
454 	}
455 	*num_tkns = count;
456 
457 exit:
458 	kfree(data);
459 	return ret;
460 }
461 
462 static int tokenize_input(const char __user *from, size_t count,
463 			  loff_t *ppos, u32 **tkns, size_t *num_tkns)
464 {
465 	char *buf;
466 	int ret;
467 
468 	buf = kmalloc(count + 1, GFP_KERNEL);
469 	if (!buf)
470 		return -ENOMEM;
471 
472 	ret = simple_write_to_buffer(buf, count, ppos, from, count);
473 	if (ret != count) {
474 		ret = ret >= 0 ? -EIO : ret;
475 		goto exit;
476 	}
477 
478 	buf[count] = '\0';
479 	ret = strsplit_u32(buf, ",", tkns, num_tkns);
480 exit:
481 	kfree(buf);
482 	return ret;
483 }
484 
485 static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
486 					  size_t count, loff_t *ppos)
487 {
488 	struct sof_client_dev *cdev = file->private_data;
489 	struct sof_probes_priv *priv = cdev->data;
490 	struct device *dev = &cdev->auxdev.dev;
491 	struct sof_probe_point_desc *desc;
492 	int remaining, offset;
493 	size_t num_desc;
494 	char *buf;
495 	int i, ret, err;
496 
497 	if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) {
498 		dev_warn(dev, "no extractor stream running\n");
499 		return -ENOENT;
500 	}
501 
502 	buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
503 	if (!buf)
504 		return -ENOMEM;
505 
506 	ret = pm_runtime_get_sync(dev);
507 	if (ret < 0 && ret != -EACCES) {
508 		dev_err_ratelimited(dev, "debugfs read failed to resume %d\n", ret);
509 		pm_runtime_put_noidle(dev);
510 		goto exit;
511 	}
512 
513 	ret = sof_probes_points_info(cdev, &desc, &num_desc);
514 	if (ret < 0)
515 		goto exit;
516 
517 	pm_runtime_mark_last_busy(dev);
518 	err = pm_runtime_put_autosuspend(dev);
519 	if (err < 0)
520 		dev_err_ratelimited(dev, "debugfs read failed to idle %d\n", err);
521 
522 	for (i = 0; i < num_desc; i++) {
523 		offset = strlen(buf);
524 		remaining = PAGE_SIZE - offset;
525 		ret = snprintf(buf + offset, remaining,
526 			       "Id: %#010x  Purpose: %u  Node id: %#x\n",
527 				desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag);
528 		if (ret < 0 || ret >= remaining) {
529 			/* truncate the output buffer at the last full line */
530 			buf[offset] = '\0';
531 			break;
532 		}
533 	}
534 
535 	ret = simple_read_from_buffer(to, count, ppos, buf, strlen(buf));
536 
537 	kfree(desc);
538 exit:
539 	kfree(buf);
540 	return ret;
541 }
542 
543 static ssize_t
544 sof_probes_dfs_points_write(struct file *file, const char __user *from,
545 			    size_t count, loff_t *ppos)
546 {
547 	struct sof_client_dev *cdev = file->private_data;
548 	struct sof_probes_priv *priv = cdev->data;
549 	struct device *dev = &cdev->auxdev.dev;
550 	struct sof_probe_point_desc *desc;
551 	size_t num_tkns, bytes;
552 	u32 *tkns;
553 	int ret, err;
554 
555 	if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) {
556 		dev_warn(dev, "no extractor stream running\n");
557 		return -ENOENT;
558 	}
559 
560 	ret = tokenize_input(from, count, ppos, &tkns, &num_tkns);
561 	if (ret < 0)
562 		return ret;
563 	bytes = sizeof(*tkns) * num_tkns;
564 	if (!num_tkns || (bytes % sizeof(*desc))) {
565 		ret = -EINVAL;
566 		goto exit;
567 	}
568 
569 	desc = (struct sof_probe_point_desc *)tkns;
570 
571 	ret = pm_runtime_get_sync(dev);
572 	if (ret < 0 && ret != -EACCES) {
573 		dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
574 		pm_runtime_put_noidle(dev);
575 		goto exit;
576 	}
577 
578 	ret = sof_probes_points_add(cdev, desc, bytes / sizeof(*desc));
579 	if (!ret)
580 		ret = count;
581 
582 	pm_runtime_mark_last_busy(dev);
583 	err = pm_runtime_put_autosuspend(dev);
584 	if (err < 0)
585 		dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err);
586 exit:
587 	kfree(tkns);
588 	return ret;
589 }
590 
591 static const struct file_operations sof_probes_points_fops = {
592 	.open = simple_open,
593 	.read = sof_probes_dfs_points_read,
594 	.write = sof_probes_dfs_points_write,
595 	.llseek = default_llseek,
596 
597 	.owner = THIS_MODULE,
598 };
599 
600 static ssize_t
601 sof_probes_dfs_points_remove_write(struct file *file, const char __user *from,
602 				   size_t count, loff_t *ppos)
603 {
604 	struct sof_client_dev *cdev = file->private_data;
605 	struct sof_probes_priv *priv = cdev->data;
606 	struct device *dev = &cdev->auxdev.dev;
607 	size_t num_tkns;
608 	u32 *tkns;
609 	int ret, err;
610 
611 	if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) {
612 		dev_warn(dev, "no extractor stream running\n");
613 		return -ENOENT;
614 	}
615 
616 	ret = tokenize_input(from, count, ppos, &tkns, &num_tkns);
617 	if (ret < 0)
618 		return ret;
619 	if (!num_tkns) {
620 		ret = -EINVAL;
621 		goto exit;
622 	}
623 
624 	ret = pm_runtime_get_sync(dev);
625 	if (ret < 0) {
626 		dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
627 		pm_runtime_put_noidle(dev);
628 		goto exit;
629 	}
630 
631 	ret = sof_probes_points_remove(cdev, tkns, num_tkns);
632 	if (!ret)
633 		ret = count;
634 
635 	pm_runtime_mark_last_busy(dev);
636 	err = pm_runtime_put_autosuspend(dev);
637 	if (err < 0)
638 		dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err);
639 exit:
640 	kfree(tkns);
641 	return ret;
642 }
643 
644 static const struct file_operations sof_probes_points_remove_fops = {
645 	.open = simple_open,
646 	.write = sof_probes_dfs_points_remove_write,
647 	.llseek = default_llseek,
648 
649 	.owner = THIS_MODULE,
650 };
651 
652 static struct snd_soc_dai_driver sof_probes_dai_drv[] = {
653 {
654 	.name = "Probe Extraction CPU DAI",
655 	.compress_new = snd_soc_new_compress,
656 	.cops = &sof_probes_compr_ops,
657 	.capture = {
658 		.stream_name = "Probe Extraction",
659 		.channels_min = 1,
660 		.channels_max = 8,
661 		.rates = SNDRV_PCM_RATE_48000,
662 		.rate_min = 48000,
663 		.rate_max = 48000,
664 	},
665 },
666 };
667 
668 static const struct snd_soc_component_driver sof_probes_component = {
669 	.name = "sof-probes-component",
670 	.compress_ops = &sof_probes_compressed_ops,
671 	.module_get_upon_open = 1,
672 };
673 
674 SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY()));
675 
676 static int sof_probes_client_probe(struct auxiliary_device *auxdev,
677 				   const struct auxiliary_device_id *id)
678 {
679 	struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
680 	struct dentry *dfsroot = sof_client_get_debugfs_root(cdev);
681 	struct device *dev = &auxdev->dev;
682 	struct snd_soc_dai_link_component platform_component[] = {
683 		{
684 			.name = dev_name(dev),
685 		}
686 	};
687 	struct snd_soc_card *card;
688 	struct sof_probes_priv *priv;
689 	struct snd_soc_dai_link_component *cpus;
690 	struct sof_probes_host_ops *ops;
691 	struct snd_soc_dai_link *links;
692 	int ret;
693 
694 	/* do not set up the probes support if it is not enabled */
695 	if (!sof_probes_enabled)
696 		return -ENXIO;
697 
698 	if (!dev->platform_data) {
699 		dev_err(dev, "missing platform data\n");
700 		return -ENODEV;
701 	}
702 
703 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
704 	if (!priv)
705 		return -ENOMEM;
706 
707 	ops = dev->platform_data;
708 
709 	if (!ops->assign || !ops->free || !ops->set_params || !ops->trigger ||
710 	    !ops->pointer) {
711 		dev_err(dev, "missing platform callback(s)\n");
712 		return -ENODEV;
713 	}
714 
715 	priv->host_ops = ops;
716 	cdev->data = priv;
717 
718 	/* register probes component driver and dai */
719 	ret = devm_snd_soc_register_component(dev, &sof_probes_component,
720 					      sof_probes_dai_drv,
721 					      ARRAY_SIZE(sof_probes_dai_drv));
722 	if (ret < 0) {
723 		dev_err(dev, "failed to register SOF probes DAI driver %d\n", ret);
724 		return ret;
725 	}
726 
727 	/* set client data */
728 	priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
729 
730 	/* create read-write probes_points debugfs entry */
731 	priv->dfs_points = debugfs_create_file("probe_points", 0644, dfsroot,
732 					       cdev, &sof_probes_points_fops);
733 
734 	/* create read-write probe_points_remove debugfs entry */
735 	priv->dfs_points_remove = debugfs_create_file("probe_points_remove", 0644,
736 						      dfsroot, cdev,
737 						      &sof_probes_points_remove_fops);
738 
739 	links = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*links), GFP_KERNEL);
740 	cpus = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*cpus), GFP_KERNEL);
741 	if (!links || !cpus) {
742 		debugfs_remove(priv->dfs_points);
743 		debugfs_remove(priv->dfs_points_remove);
744 		return -ENOMEM;
745 	}
746 
747 	/* extraction DAI link */
748 	links[0].name = "Compress Probe Capture";
749 	links[0].id = 0;
750 	links[0].cpus = &cpus[0];
751 	links[0].num_cpus = 1;
752 	links[0].cpus->dai_name = "Probe Extraction CPU DAI";
753 	links[0].codecs = dummy;
754 	links[0].num_codecs = 1;
755 	links[0].platforms = platform_component;
756 	links[0].num_platforms = ARRAY_SIZE(platform_component);
757 	links[0].nonatomic = 1;
758 
759 	card = &priv->card;
760 
761 	card->dev = dev;
762 	card->name = "sof-probes";
763 	card->owner = THIS_MODULE;
764 	card->num_links = SOF_PROBES_NUM_DAI_LINKS;
765 	card->dai_link = links;
766 
767 	/* set idle_bias_off to prevent the core from resuming the card->dev */
768 	card->dapm.idle_bias_off = true;
769 
770 	snd_soc_card_set_drvdata(card, cdev);
771 
772 	ret = devm_snd_soc_register_card(dev, card);
773 	if (ret < 0) {
774 		debugfs_remove(priv->dfs_points);
775 		debugfs_remove(priv->dfs_points_remove);
776 		dev_err(dev, "Probes card register failed %d\n", ret);
777 		return ret;
778 	}
779 
780 	/* enable runtime PM */
781 	pm_runtime_set_autosuspend_delay(dev, SOF_PROBES_SUSPEND_DELAY_MS);
782 	pm_runtime_use_autosuspend(dev);
783 	pm_runtime_enable(dev);
784 	pm_runtime_mark_last_busy(dev);
785 	pm_runtime_idle(dev);
786 
787 	return 0;
788 }
789 
790 static void sof_probes_client_remove(struct auxiliary_device *auxdev)
791 {
792 	struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
793 	struct sof_probes_priv *priv = cdev->data;
794 
795 	if (!sof_probes_enabled)
796 		return;
797 
798 	pm_runtime_disable(&auxdev->dev);
799 	debugfs_remove(priv->dfs_points);
800 	debugfs_remove(priv->dfs_points_remove);
801 }
802 
803 static const struct auxiliary_device_id sof_probes_client_id_table[] = {
804 	{ .name = "snd_sof.hda-probes", },
805 	{},
806 };
807 MODULE_DEVICE_TABLE(auxiliary, sof_probes_client_id_table);
808 
809 /* driver name will be set based on KBUILD_MODNAME */
810 static struct auxiliary_driver sof_probes_client_drv = {
811 	.probe = sof_probes_client_probe,
812 	.remove = sof_probes_client_remove,
813 
814 	.id_table = sof_probes_client_id_table,
815 };
816 
817 module_auxiliary_driver(sof_probes_client_drv);
818 
819 MODULE_DESCRIPTION("SOF Probes Client Driver");
820 MODULE_LICENSE("GPL v2");
821 MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);
822