eb246b8234614cde8f18b6ab8f6a3cbba5f64ebc
[sfrench/cifs-2.6.git] / sound / soc / sof / sof-client-probes.c
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->startup(cdev, cstream, dai, &priv->extractor_stream_tag);
274         if (ret) {
275                 dev_err(dai->dev, "Failed to startup 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->shutdown(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         .legacy_dai_naming = 1,
671 };
672
673 SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY()));
674
675 static int sof_probes_client_probe(struct auxiliary_device *auxdev,
676                                    const struct auxiliary_device_id *id)
677 {
678         struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
679         struct dentry *dfsroot = sof_client_get_debugfs_root(cdev);
680         struct device *dev = &auxdev->dev;
681         struct snd_soc_dai_link_component platform_component[] = {
682                 {
683                         .name = dev_name(dev),
684                 }
685         };
686         struct snd_soc_card *card;
687         struct sof_probes_priv *priv;
688         struct snd_soc_dai_link_component *cpus;
689         struct sof_probes_host_ops *ops;
690         struct snd_soc_dai_link *links;
691         int ret;
692
693         /* do not set up the probes support if it is not enabled */
694         if (!sof_probes_enabled)
695                 return -ENXIO;
696
697         /* only ipc3 is supported */
698         if (sof_client_get_ipc_type(cdev) != SOF_IPC)
699                 return -ENXIO;
700
701         if (!dev->platform_data) {
702                 dev_err(dev, "missing platform data\n");
703                 return -ENODEV;
704         }
705
706         priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
707         if (!priv)
708                 return -ENOMEM;
709
710         ops = dev->platform_data;
711
712         if (!ops->startup || !ops->shutdown || !ops->set_params || !ops->trigger ||
713             !ops->pointer) {
714                 dev_err(dev, "missing platform callback(s)\n");
715                 return -ENODEV;
716         }
717
718         priv->host_ops = ops;
719         cdev->data = priv;
720
721         /* register probes component driver and dai */
722         ret = devm_snd_soc_register_component(dev, &sof_probes_component,
723                                               sof_probes_dai_drv,
724                                               ARRAY_SIZE(sof_probes_dai_drv));
725         if (ret < 0) {
726                 dev_err(dev, "failed to register SOF probes DAI driver %d\n", ret);
727                 return ret;
728         }
729
730         /* set client data */
731         priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
732
733         /* create read-write probes_points debugfs entry */
734         priv->dfs_points = debugfs_create_file("probe_points", 0644, dfsroot,
735                                                cdev, &sof_probes_points_fops);
736
737         /* create read-write probe_points_remove debugfs entry */
738         priv->dfs_points_remove = debugfs_create_file("probe_points_remove", 0644,
739                                                       dfsroot, cdev,
740                                                       &sof_probes_points_remove_fops);
741
742         links = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*links), GFP_KERNEL);
743         cpus = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*cpus), GFP_KERNEL);
744         if (!links || !cpus) {
745                 debugfs_remove(priv->dfs_points);
746                 debugfs_remove(priv->dfs_points_remove);
747                 return -ENOMEM;
748         }
749
750         /* extraction DAI link */
751         links[0].name = "Compress Probe Capture";
752         links[0].id = 0;
753         links[0].cpus = &cpus[0];
754         links[0].num_cpus = 1;
755         links[0].cpus->dai_name = "Probe Extraction CPU DAI";
756         links[0].codecs = dummy;
757         links[0].num_codecs = 1;
758         links[0].platforms = platform_component;
759         links[0].num_platforms = ARRAY_SIZE(platform_component);
760         links[0].nonatomic = 1;
761
762         card = &priv->card;
763
764         card->dev = dev;
765         card->name = "sof-probes";
766         card->owner = THIS_MODULE;
767         card->num_links = SOF_PROBES_NUM_DAI_LINKS;
768         card->dai_link = links;
769
770         /* set idle_bias_off to prevent the core from resuming the card->dev */
771         card->dapm.idle_bias_off = true;
772
773         snd_soc_card_set_drvdata(card, cdev);
774
775         ret = devm_snd_soc_register_card(dev, card);
776         if (ret < 0) {
777                 debugfs_remove(priv->dfs_points);
778                 debugfs_remove(priv->dfs_points_remove);
779                 dev_err(dev, "Probes card register failed %d\n", ret);
780                 return ret;
781         }
782
783         /* enable runtime PM */
784         pm_runtime_set_autosuspend_delay(dev, SOF_PROBES_SUSPEND_DELAY_MS);
785         pm_runtime_use_autosuspend(dev);
786         pm_runtime_enable(dev);
787         pm_runtime_mark_last_busy(dev);
788         pm_runtime_idle(dev);
789
790         return 0;
791 }
792
793 static void sof_probes_client_remove(struct auxiliary_device *auxdev)
794 {
795         struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
796         struct sof_probes_priv *priv = cdev->data;
797
798         if (!sof_probes_enabled)
799                 return;
800
801         pm_runtime_disable(&auxdev->dev);
802         debugfs_remove(priv->dfs_points);
803         debugfs_remove(priv->dfs_points_remove);
804 }
805
806 static const struct auxiliary_device_id sof_probes_client_id_table[] = {
807         { .name = "snd_sof.hda-probes", },
808         {},
809 };
810 MODULE_DEVICE_TABLE(auxiliary, sof_probes_client_id_table);
811
812 /* driver name will be set based on KBUILD_MODNAME */
813 static struct auxiliary_driver sof_probes_client_drv = {
814         .probe = sof_probes_client_probe,
815         .remove = sof_probes_client_remove,
816
817         .id_table = sof_probes_client_id_table,
818 };
819
820 module_auxiliary_driver(sof_probes_client_drv);
821
822 MODULE_DESCRIPTION("SOF Probes Client Driver");
823 MODULE_LICENSE("GPL v2");
824 MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);