1
2
3
4
5
6
7
8
9
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
22#define SOF_PROBES_NUM_DAI_LINKS 1
23
24#define SOF_PROBES_INVALID_NODE_ID UINT_MAX
25
26static bool __read_mostly sof_probes_enabled;
27module_param_named(enable, sof_probes_enabled, bool, 0444);
28MODULE_PARM_DESC(enable, "Enable SOF probes support");
29
30struct 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
39struct sof_probe_point_desc {
40 unsigned int buffer_id;
41 unsigned int purpose;
42 unsigned int stream_tag;
43} __packed;
44
45struct sof_probe_dma {
46 unsigned int stream_tag;
47 unsigned int dma_buffer_size;
48} __packed;
49
50struct 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
56struct 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
65struct 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
71struct 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
79
80
81
82
83
84
85
86
87
88
89
90
91static 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
115
116
117
118
119
120
121static 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
132static 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
168exit:
169 kfree(reply);
170 return ret;
171}
172
173
174
175
176
177
178
179
180
181
182
183static 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
193
194
195
196
197
198
199
200
201
202
203
204static 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
228
229
230
231
232
233
234
235static 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
256static 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
282static 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
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
304exit:
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
319static 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
349static 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
360static 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
372static 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
380static 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
408static const struct snd_compress_ops sof_probes_compressed_ops = {
409 .copy = sof_probes_compr_copy,
410};
411
412
413
414
415
416
417
418
419static 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
457exit:
458 kfree(data);
459 return ret;
460}
461
462static 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);
480exit:
481 kfree(buf);
482 return ret;
483}
484
485static 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
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);
538exit:
539 kfree(buf);
540 return ret;
541}
542
543static ssize_t
544sof_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);
586exit:
587 kfree(tkns);
588 return ret;
589}
590
591static 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
600static ssize_t
601sof_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);
639exit:
640 kfree(tkns);
641 return ret;
642}
643
644static 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
652static 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
668static 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
674SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY()));
675
676static 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
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
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
728 priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
729
730
731 priv->dfs_points = debugfs_create_file("probe_points", 0644, dfsroot,
732 cdev, &sof_probes_points_fops);
733
734
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
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
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
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
790static 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
803static const struct auxiliary_device_id sof_probes_client_id_table[] = {
804 { .name = "snd_sof.hda-probes", },
805 {},
806};
807MODULE_DEVICE_TABLE(auxiliary, sof_probes_client_id_table);
808
809
810static 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
817module_auxiliary_driver(sof_probes_client_drv);
818
819MODULE_DESCRIPTION("SOF Probes Client Driver");
820MODULE_LICENSE("GPL v2");
821MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);
822