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