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 	size_t max_msg_size = sof_client_get_ipc_max_payload_size(cdev);
136 	struct sof_ipc_probe_info_params msg = {{{0}}};
137 	struct sof_ipc_probe_info_params *reply;
138 	size_t bytes;
139 	int ret;
140 
141 	*params = NULL;
142 	*num_params = 0;
143 
144 	reply = kzalloc(max_msg_size, GFP_KERNEL);
145 	if (!reply)
146 		return -ENOMEM;
147 	msg.rhdr.hdr.size = sizeof(msg);
148 	msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd;
149 
150 	ret = sof_client_ipc_tx_message(cdev, &msg, reply, max_msg_size);
151 	if (ret < 0 || reply->rhdr.error < 0)
152 		goto exit;
153 
154 	if (!reply->num_elems)
155 		goto exit;
156 
157 	if (cmd == SOF_IPC_PROBE_DMA_INFO)
158 		bytes = sizeof(reply->dma[0]);
159 	else
160 		bytes = sizeof(reply->desc[0]);
161 	bytes *= reply->num_elems;
162 	*params = kmemdup(&reply->dma[0], bytes, GFP_KERNEL);
163 	if (!*params) {
164 		ret = -ENOMEM;
165 		goto exit;
166 	}
167 	*num_params = reply->num_elems;
168 
169 exit:
170 	kfree(reply);
171 	return ret;
172 }
173 
174 /**
175  * sof_probes_points_info - retrieve list of active probe points
176  * @cdev:		SOF client device
177  * @desc:	Returned list of active probes
178  * @num_desc:	Returned count of active probes
179  *
180  * Host sends PROBE_POINT_INFO request to obtain list of active probe
181  * points, valid for disconnection when given probe is no longer
182  * required.
183  */
184 static int sof_probes_points_info(struct sof_client_dev *cdev,
185 				  struct sof_probe_point_desc **desc,
186 				  size_t *num_desc)
187 {
188 	return sof_probes_info(cdev, SOF_IPC_PROBE_POINT_INFO,
189 			       (void **)desc, num_desc);
190 }
191 
192 /**
193  * sof_probes_points_add - connect specified probes
194  * @cdev:		SOF client device
195  * @desc:	List of probe points to connect
196  * @num_desc:	Number of elements in @desc
197  *
198  * Dynamically connects to provided set of endpoints. Immediately
199  * after connection is established, host must be prepared to
200  * transfer data from or to target stream given the probing purpose.
201  *
202  * Each probe point should be removed using PROBE_POINT_REMOVE
203  * request when no longer needed.
204  */
205 static int sof_probes_points_add(struct sof_client_dev *cdev,
206 				 struct sof_probe_point_desc *desc,
207 				 size_t num_desc)
208 {
209 	struct sof_ipc_probe_point_add_params *msg;
210 	size_t size = struct_size(msg, desc, num_desc);
211 	struct sof_ipc_reply reply;
212 	int ret;
213 
214 	msg = kmalloc(size, GFP_KERNEL);
215 	if (!msg)
216 		return -ENOMEM;
217 	msg->hdr.size = size;
218 	msg->num_elems = num_desc;
219 	msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_ADD;
220 	memcpy(&msg->desc[0], desc, size - sizeof(*msg));
221 
222 	ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply));
223 	kfree(msg);
224 	return ret;
225 }
226 
227 /**
228  * sof_probes_points_remove - disconnect specified probes
229  * @cdev:		SOF client device
230  * @buffer_id:		List of probe points to disconnect
231  * @num_buffer_id:	Number of elements in @desc
232  *
233  * Removes previously connected probes from list of active probe
234  * points and frees all resources on DSP side.
235  */
236 static int sof_probes_points_remove(struct sof_client_dev *cdev,
237 				    unsigned int *buffer_id, size_t num_buffer_id)
238 {
239 	struct sof_ipc_probe_point_remove_params *msg;
240 	size_t size = struct_size(msg, buffer_id, num_buffer_id);
241 	struct sof_ipc_reply reply;
242 	int ret;
243 
244 	msg = kmalloc(size, GFP_KERNEL);
245 	if (!msg)
246 		return -ENOMEM;
247 	msg->hdr.size = size;
248 	msg->num_elems = num_buffer_id;
249 	msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE;
250 	memcpy(&msg->buffer_id[0], buffer_id, size - sizeof(*msg));
251 
252 	ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply));
253 	kfree(msg);
254 	return ret;
255 }
256 
257 static int sof_probes_compr_startup(struct snd_compr_stream *cstream,
258 				    struct snd_soc_dai *dai)
259 {
260 	struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
261 	struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
262 	struct sof_probes_priv *priv = cdev->data;
263 	const struct sof_probes_host_ops *ops = priv->host_ops;
264 	int ret;
265 
266 	if (sof_client_get_fw_state(cdev) == SOF_FW_CRASHED)
267 		return -ENODEV;
268 
269 	ret = sof_client_core_module_get(cdev);
270 	if (ret)
271 		return ret;
272 
273 	ret = ops->assign(cdev, cstream, dai, &priv->extractor_stream_tag);
274 	if (ret) {
275 		dev_err(dai->dev, "Failed to assign probe stream: %d\n", ret);
276 		priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
277 		sof_client_core_module_put(cdev);
278 	}
279 
280 	return ret;
281 }
282 
283 static int sof_probes_compr_shutdown(struct snd_compr_stream *cstream,
284 				     struct snd_soc_dai *dai)
285 {
286 	struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
287 	struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
288 	struct sof_probes_priv *priv = cdev->data;
289 	const struct sof_probes_host_ops *ops = priv->host_ops;
290 	struct sof_probe_point_desc *desc;
291 	size_t num_desc;
292 	int i, ret;
293 
294 	/* disconnect all probe points */
295 	ret = sof_probes_points_info(cdev, &desc, &num_desc);
296 	if (ret < 0) {
297 		dev_err(dai->dev, "Failed to get probe points: %d\n", ret);
298 		goto exit;
299 	}
300 
301 	for (i = 0; i < num_desc; i++)
302 		sof_probes_points_remove(cdev, &desc[i].buffer_id, 1);
303 	kfree(desc);
304 
305 exit:
306 	ret = sof_probes_deinit(cdev);
307 	if (ret < 0)
308 		dev_err(dai->dev, "Failed to deinit probe: %d\n", ret);
309 
310 	priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
311 	snd_compr_free_pages(cstream);
312 
313 	ret = ops->free(cdev, cstream, dai);
314 
315 	sof_client_core_module_put(cdev);
316 
317 	return ret;
318 }
319 
320 static int sof_probes_compr_set_params(struct snd_compr_stream *cstream,
321 				       struct snd_compr_params *params,
322 				       struct snd_soc_dai *dai)
323 {
324 	struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
325 	struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
326 	struct snd_compr_runtime *rtd = cstream->runtime;
327 	struct sof_probes_priv *priv = cdev->data;
328 	const struct sof_probes_host_ops *ops = priv->host_ops;
329 	int ret;
330 
331 	cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG;
332 	cstream->dma_buffer.dev.dev = sof_client_get_dma_dev(cdev);
333 	ret = snd_compr_malloc_pages(cstream, rtd->buffer_size);
334 	if (ret < 0)
335 		return ret;
336 
337 	ret = ops->set_params(cdev, cstream, params, dai);
338 	if (ret)
339 		return ret;
340 
341 	ret = sof_probes_init(cdev, priv->extractor_stream_tag, rtd->dma_bytes);
342 	if (ret < 0) {
343 		dev_err(dai->dev, "Failed to init probe: %d\n", ret);
344 		return ret;
345 	}
346 
347 	return 0;
348 }
349 
350 static int sof_probes_compr_trigger(struct snd_compr_stream *cstream, int cmd,
351 				    struct snd_soc_dai *dai)
352 {
353 	struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
354 	struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
355 	struct sof_probes_priv *priv = cdev->data;
356 	const struct sof_probes_host_ops *ops = priv->host_ops;
357 
358 	return ops->trigger(cdev, cstream, cmd, dai);
359 }
360 
361 static int sof_probes_compr_pointer(struct snd_compr_stream *cstream,
362 				    struct snd_compr_tstamp *tstamp,
363 				    struct snd_soc_dai *dai)
364 {
365 	struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
366 	struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
367 	struct sof_probes_priv *priv = cdev->data;
368 	const struct sof_probes_host_ops *ops = priv->host_ops;
369 
370 	return ops->pointer(cdev, cstream, tstamp, dai);
371 }
372 
373 static const struct snd_soc_cdai_ops sof_probes_compr_ops = {
374 	.startup = sof_probes_compr_startup,
375 	.shutdown = sof_probes_compr_shutdown,
376 	.set_params = sof_probes_compr_set_params,
377 	.trigger = sof_probes_compr_trigger,
378 	.pointer = sof_probes_compr_pointer,
379 };
380 
381 static int sof_probes_compr_copy(struct snd_soc_component *component,
382 				 struct snd_compr_stream *cstream,
383 				 char __user *buf, size_t count)
384 {
385 	struct snd_compr_runtime *rtd = cstream->runtime;
386 	unsigned int offset, n;
387 	void *ptr;
388 	int ret;
389 
390 	if (count > rtd->buffer_size)
391 		count = rtd->buffer_size;
392 
393 	div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset);
394 	ptr = rtd->dma_area + offset;
395 	n = rtd->buffer_size - offset;
396 
397 	if (count < n) {
398 		ret = copy_to_user(buf, ptr, count);
399 	} else {
400 		ret = copy_to_user(buf, ptr, n);
401 		ret += copy_to_user(buf + n, rtd->dma_area, count - n);
402 	}
403 
404 	if (ret)
405 		return count - ret;
406 	return count;
407 }
408 
409 static const struct snd_compress_ops sof_probes_compressed_ops = {
410 	.copy = sof_probes_compr_copy,
411 };
412 
413 /**
414  * strsplit_u32 - Split string into sequence of u32 tokens
415  * @buf:	String to split into tokens.
416  * @delim:	String containing delimiter characters.
417  * @tkns:	Returned u32 sequence pointer.
418  * @num_tkns:	Returned number of tokens obtained.
419  */
420 static int strsplit_u32(char *buf, const char *delim, u32 **tkns, size_t *num_tkns)
421 {
422 	char *s;
423 	u32 *data, *tmp;
424 	size_t count = 0;
425 	size_t cap = 32;
426 	int ret = 0;
427 
428 	*tkns = NULL;
429 	*num_tkns = 0;
430 	data = kcalloc(cap, sizeof(*data), GFP_KERNEL);
431 	if (!data)
432 		return -ENOMEM;
433 
434 	while ((s = strsep(&buf, delim)) != NULL) {
435 		ret = kstrtouint(s, 0, data + count);
436 		if (ret)
437 			goto exit;
438 		if (++count >= cap) {
439 			cap *= 2;
440 			tmp = krealloc(data, cap * sizeof(*data), GFP_KERNEL);
441 			if (!tmp) {
442 				ret = -ENOMEM;
443 				goto exit;
444 			}
445 			data = tmp;
446 		}
447 	}
448 
449 	if (!count)
450 		goto exit;
451 	*tkns = kmemdup(data, count * sizeof(*data), GFP_KERNEL);
452 	if (!(*tkns)) {
453 		ret = -ENOMEM;
454 		goto exit;
455 	}
456 	*num_tkns = count;
457 
458 exit:
459 	kfree(data);
460 	return ret;
461 }
462 
463 static int tokenize_input(const char __user *from, size_t count,
464 			  loff_t *ppos, u32 **tkns, size_t *num_tkns)
465 {
466 	char *buf;
467 	int ret;
468 
469 	buf = kmalloc(count + 1, GFP_KERNEL);
470 	if (!buf)
471 		return -ENOMEM;
472 
473 	ret = simple_write_to_buffer(buf, count, ppos, from, count);
474 	if (ret != count) {
475 		ret = ret >= 0 ? -EIO : ret;
476 		goto exit;
477 	}
478 
479 	buf[count] = '\0';
480 	ret = strsplit_u32(buf, ",", tkns, num_tkns);
481 exit:
482 	kfree(buf);
483 	return ret;
484 }
485 
486 static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
487 					  size_t count, loff_t *ppos)
488 {
489 	struct sof_client_dev *cdev = file->private_data;
490 	struct sof_probes_priv *priv = cdev->data;
491 	struct device *dev = &cdev->auxdev.dev;
492 	struct sof_probe_point_desc *desc;
493 	int remaining, offset;
494 	size_t num_desc;
495 	char *buf;
496 	int i, ret, err;
497 
498 	if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) {
499 		dev_warn(dev, "no extractor stream running\n");
500 		return -ENOENT;
501 	}
502 
503 	buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
504 	if (!buf)
505 		return -ENOMEM;
506 
507 	ret = pm_runtime_resume_and_get(dev);
508 	if (ret < 0 && ret != -EACCES) {
509 		dev_err_ratelimited(dev, "debugfs read failed to resume %d\n", ret);
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_resume_and_get(dev);
572 	if (ret < 0 && ret != -EACCES) {
573 		dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
574 		goto exit;
575 	}
576 
577 	ret = sof_probes_points_add(cdev, desc, bytes / sizeof(*desc));
578 	if (!ret)
579 		ret = count;
580 
581 	pm_runtime_mark_last_busy(dev);
582 	err = pm_runtime_put_autosuspend(dev);
583 	if (err < 0)
584 		dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err);
585 exit:
586 	kfree(tkns);
587 	return ret;
588 }
589 
590 static const struct file_operations sof_probes_points_fops = {
591 	.open = simple_open,
592 	.read = sof_probes_dfs_points_read,
593 	.write = sof_probes_dfs_points_write,
594 	.llseek = default_llseek,
595 
596 	.owner = THIS_MODULE,
597 };
598 
599 static ssize_t
600 sof_probes_dfs_points_remove_write(struct file *file, const char __user *from,
601 				   size_t count, loff_t *ppos)
602 {
603 	struct sof_client_dev *cdev = file->private_data;
604 	struct sof_probes_priv *priv = cdev->data;
605 	struct device *dev = &cdev->auxdev.dev;
606 	size_t num_tkns;
607 	u32 *tkns;
608 	int ret, err;
609 
610 	if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) {
611 		dev_warn(dev, "no extractor stream running\n");
612 		return -ENOENT;
613 	}
614 
615 	ret = tokenize_input(from, count, ppos, &tkns, &num_tkns);
616 	if (ret < 0)
617 		return ret;
618 	if (!num_tkns) {
619 		ret = -EINVAL;
620 		goto exit;
621 	}
622 
623 	ret = pm_runtime_resume_and_get(dev);
624 	if (ret < 0) {
625 		dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
626 		goto exit;
627 	}
628 
629 	ret = sof_probes_points_remove(cdev, tkns, num_tkns);
630 	if (!ret)
631 		ret = count;
632 
633 	pm_runtime_mark_last_busy(dev);
634 	err = pm_runtime_put_autosuspend(dev);
635 	if (err < 0)
636 		dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err);
637 exit:
638 	kfree(tkns);
639 	return ret;
640 }
641 
642 static const struct file_operations sof_probes_points_remove_fops = {
643 	.open = simple_open,
644 	.write = sof_probes_dfs_points_remove_write,
645 	.llseek = default_llseek,
646 
647 	.owner = THIS_MODULE,
648 };
649 
650 static struct snd_soc_dai_driver sof_probes_dai_drv[] = {
651 {
652 	.name = "Probe Extraction CPU DAI",
653 	.compress_new = snd_soc_new_compress,
654 	.cops = &sof_probes_compr_ops,
655 	.capture = {
656 		.stream_name = "Probe Extraction",
657 		.channels_min = 1,
658 		.channels_max = 8,
659 		.rates = SNDRV_PCM_RATE_48000,
660 		.rate_min = 48000,
661 		.rate_max = 48000,
662 	},
663 },
664 };
665 
666 static const struct snd_soc_component_driver sof_probes_component = {
667 	.name = "sof-probes-component",
668 	.compress_ops = &sof_probes_compressed_ops,
669 	.module_get_upon_open = 1,
670 };
671 
672 SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY()));
673 
674 static int sof_probes_client_probe(struct auxiliary_device *auxdev,
675 				   const struct auxiliary_device_id *id)
676 {
677 	struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
678 	struct dentry *dfsroot = sof_client_get_debugfs_root(cdev);
679 	struct device *dev = &auxdev->dev;
680 	struct snd_soc_dai_link_component platform_component[] = {
681 		{
682 			.name = dev_name(dev),
683 		}
684 	};
685 	struct snd_soc_card *card;
686 	struct sof_probes_priv *priv;
687 	struct snd_soc_dai_link_component *cpus;
688 	struct sof_probes_host_ops *ops;
689 	struct snd_soc_dai_link *links;
690 	int ret;
691 
692 	/* do not set up the probes support if it is not enabled */
693 	if (!sof_probes_enabled)
694 		return -ENXIO;
695 
696 	if (!dev->platform_data) {
697 		dev_err(dev, "missing platform data\n");
698 		return -ENODEV;
699 	}
700 
701 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
702 	if (!priv)
703 		return -ENOMEM;
704 
705 	ops = dev->platform_data;
706 
707 	if (!ops->assign || !ops->free || !ops->set_params || !ops->trigger ||
708 	    !ops->pointer) {
709 		dev_err(dev, "missing platform callback(s)\n");
710 		return -ENODEV;
711 	}
712 
713 	priv->host_ops = ops;
714 	cdev->data = priv;
715 
716 	/* register probes component driver and dai */
717 	ret = devm_snd_soc_register_component(dev, &sof_probes_component,
718 					      sof_probes_dai_drv,
719 					      ARRAY_SIZE(sof_probes_dai_drv));
720 	if (ret < 0) {
721 		dev_err(dev, "failed to register SOF probes DAI driver %d\n", ret);
722 		return ret;
723 	}
724 
725 	/* set client data */
726 	priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
727 
728 	/* create read-write probes_points debugfs entry */
729 	priv->dfs_points = debugfs_create_file("probe_points", 0644, dfsroot,
730 					       cdev, &sof_probes_points_fops);
731 
732 	/* create read-write probe_points_remove debugfs entry */
733 	priv->dfs_points_remove = debugfs_create_file("probe_points_remove", 0644,
734 						      dfsroot, cdev,
735 						      &sof_probes_points_remove_fops);
736 
737 	links = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*links), GFP_KERNEL);
738 	cpus = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*cpus), GFP_KERNEL);
739 	if (!links || !cpus) {
740 		debugfs_remove(priv->dfs_points);
741 		debugfs_remove(priv->dfs_points_remove);
742 		return -ENOMEM;
743 	}
744 
745 	/* extraction DAI link */
746 	links[0].name = "Compress Probe Capture";
747 	links[0].id = 0;
748 	links[0].cpus = &cpus[0];
749 	links[0].num_cpus = 1;
750 	links[0].cpus->dai_name = "Probe Extraction CPU DAI";
751 	links[0].codecs = dummy;
752 	links[0].num_codecs = 1;
753 	links[0].platforms = platform_component;
754 	links[0].num_platforms = ARRAY_SIZE(platform_component);
755 	links[0].nonatomic = 1;
756 
757 	card = &priv->card;
758 
759 	card->dev = dev;
760 	card->name = "sof-probes";
761 	card->owner = THIS_MODULE;
762 	card->num_links = SOF_PROBES_NUM_DAI_LINKS;
763 	card->dai_link = links;
764 
765 	/* set idle_bias_off to prevent the core from resuming the card->dev */
766 	card->dapm.idle_bias_off = true;
767 
768 	snd_soc_card_set_drvdata(card, cdev);
769 
770 	ret = devm_snd_soc_register_card(dev, card);
771 	if (ret < 0) {
772 		debugfs_remove(priv->dfs_points);
773 		debugfs_remove(priv->dfs_points_remove);
774 		dev_err(dev, "Probes card register failed %d\n", ret);
775 		return ret;
776 	}
777 
778 	/* enable runtime PM */
779 	pm_runtime_set_autosuspend_delay(dev, SOF_PROBES_SUSPEND_DELAY_MS);
780 	pm_runtime_use_autosuspend(dev);
781 	pm_runtime_enable(dev);
782 	pm_runtime_mark_last_busy(dev);
783 	pm_runtime_idle(dev);
784 
785 	return 0;
786 }
787 
788 static void sof_probes_client_remove(struct auxiliary_device *auxdev)
789 {
790 	struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
791 	struct sof_probes_priv *priv = cdev->data;
792 
793 	if (!sof_probes_enabled)
794 		return;
795 
796 	pm_runtime_disable(&auxdev->dev);
797 	debugfs_remove(priv->dfs_points);
798 	debugfs_remove(priv->dfs_points_remove);
799 }
800 
801 static const struct auxiliary_device_id sof_probes_client_id_table[] = {
802 	{ .name = "snd_sof.hda-probes", },
803 	{},
804 };
805 MODULE_DEVICE_TABLE(auxiliary, sof_probes_client_id_table);
806 
807 /* driver name will be set based on KBUILD_MODNAME */
808 static struct auxiliary_driver sof_probes_client_drv = {
809 	.probe = sof_probes_client_probe,
810 	.remove = sof_probes_client_remove,
811 
812 	.id_table = sof_probes_client_id_table,
813 };
814 
815 module_auxiliary_driver(sof_probes_client_drv);
816 
817 MODULE_DESCRIPTION("SOF Probes Client Driver");
818 MODULE_LICENSE("GPL v2");
819 MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);
820