1 // SPDX-License-Identifier: GPL-2.0-only
3 // Copyright(c) 2019-2022 Intel Corporation
5 // Author: Cezary Rojewski <cezary.rojewski@intel.com>
8 // Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
9 // Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
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>
18 #include <sound/soc.h>
19 #include <sound/sof/header.h>
20 #include "sof-client.h"
21 #include "sof-client-probes.h"
23 #define SOF_PROBES_SUSPEND_DELAY_MS 3000
24 /* only extraction supported for now */
25 #define SOF_PROBES_NUM_DAI_LINKS 1
27 #define SOF_PROBES_INVALID_NODE_ID UINT_MAX
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");
33 static int sof_probes_compr_startup(struct snd_compr_stream
*cstream
,
34 struct snd_soc_dai
*dai
)
36 struct snd_soc_card
*card
= snd_soc_component_get_drvdata(dai
->component
);
37 struct sof_client_dev
*cdev
= snd_soc_card_get_drvdata(card
);
38 struct sof_probes_priv
*priv
= cdev
->data
;
39 const struct sof_probes_host_ops
*ops
= priv
->host_ops
;
42 if (sof_client_get_fw_state(cdev
) == SOF_FW_CRASHED
)
45 ret
= sof_client_core_module_get(cdev
);
49 ret
= ops
->startup(cdev
, cstream
, dai
, &priv
->extractor_stream_tag
);
51 dev_err(dai
->dev
, "Failed to startup probe stream: %d\n", ret
);
52 priv
->extractor_stream_tag
= SOF_PROBES_INVALID_NODE_ID
;
53 sof_client_core_module_put(cdev
);
59 static int sof_probes_compr_shutdown(struct snd_compr_stream
*cstream
,
60 struct snd_soc_dai
*dai
)
62 struct snd_soc_card
*card
= snd_soc_component_get_drvdata(dai
->component
);
63 struct sof_client_dev
*cdev
= snd_soc_card_get_drvdata(card
);
64 struct sof_probes_priv
*priv
= cdev
->data
;
65 const struct sof_probes_host_ops
*ops
= priv
->host_ops
;
66 const struct sof_probes_ipc_ops
*ipc
= priv
->ipc_ops
;
67 struct sof_probe_point_desc
*desc
;
71 /* disconnect all probe points */
72 ret
= ipc
->points_info(cdev
, &desc
, &num_desc
);
74 dev_err(dai
->dev
, "Failed to get probe points: %d\n", ret
);
78 for (i
= 0; i
< num_desc
; i
++)
79 ipc
->points_remove(cdev
, &desc
[i
].buffer_id
, 1);
83 ret
= ipc
->deinit(cdev
);
85 dev_err(dai
->dev
, "Failed to deinit probe: %d\n", ret
);
87 priv
->extractor_stream_tag
= SOF_PROBES_INVALID_NODE_ID
;
88 snd_compr_free_pages(cstream
);
90 ret
= ops
->shutdown(cdev
, cstream
, dai
);
92 sof_client_core_module_put(cdev
);
97 static int sof_probes_compr_set_params(struct snd_compr_stream
*cstream
,
98 struct snd_compr_params
*params
,
99 struct snd_soc_dai
*dai
)
101 struct snd_soc_card
*card
= snd_soc_component_get_drvdata(dai
->component
);
102 struct sof_client_dev
*cdev
= snd_soc_card_get_drvdata(card
);
103 struct snd_compr_runtime
*rtd
= cstream
->runtime
;
104 struct sof_probes_priv
*priv
= cdev
->data
;
105 const struct sof_probes_host_ops
*ops
= priv
->host_ops
;
106 const struct sof_probes_ipc_ops
*ipc
= priv
->ipc_ops
;
109 cstream
->dma_buffer
.dev
.type
= SNDRV_DMA_TYPE_DEV_SG
;
110 cstream
->dma_buffer
.dev
.dev
= sof_client_get_dma_dev(cdev
);
111 ret
= snd_compr_malloc_pages(cstream
, rtd
->buffer_size
);
115 ret
= ops
->set_params(cdev
, cstream
, params
, dai
);
119 ret
= ipc
->init(cdev
, priv
->extractor_stream_tag
, rtd
->dma_bytes
);
121 dev_err(dai
->dev
, "Failed to init probe: %d\n", ret
);
128 static int sof_probes_compr_trigger(struct snd_compr_stream
*cstream
, int cmd
,
129 struct snd_soc_dai
*dai
)
131 struct snd_soc_card
*card
= snd_soc_component_get_drvdata(dai
->component
);
132 struct sof_client_dev
*cdev
= snd_soc_card_get_drvdata(card
);
133 struct sof_probes_priv
*priv
= cdev
->data
;
134 const struct sof_probes_host_ops
*ops
= priv
->host_ops
;
136 return ops
->trigger(cdev
, cstream
, cmd
, dai
);
139 static int sof_probes_compr_pointer(struct snd_compr_stream
*cstream
,
140 struct snd_compr_tstamp
*tstamp
,
141 struct snd_soc_dai
*dai
)
143 struct snd_soc_card
*card
= snd_soc_component_get_drvdata(dai
->component
);
144 struct sof_client_dev
*cdev
= snd_soc_card_get_drvdata(card
);
145 struct sof_probes_priv
*priv
= cdev
->data
;
146 const struct sof_probes_host_ops
*ops
= priv
->host_ops
;
148 return ops
->pointer(cdev
, cstream
, tstamp
, dai
);
151 static const struct snd_soc_cdai_ops sof_probes_compr_ops
= {
152 .startup
= sof_probes_compr_startup
,
153 .shutdown
= sof_probes_compr_shutdown
,
154 .set_params
= sof_probes_compr_set_params
,
155 .trigger
= sof_probes_compr_trigger
,
156 .pointer
= sof_probes_compr_pointer
,
159 static int sof_probes_compr_copy(struct snd_soc_component
*component
,
160 struct snd_compr_stream
*cstream
,
161 char __user
*buf
, size_t count
)
163 struct snd_compr_runtime
*rtd
= cstream
->runtime
;
164 unsigned int offset
, n
;
168 if (count
> rtd
->buffer_size
)
169 count
= rtd
->buffer_size
;
171 div_u64_rem(rtd
->total_bytes_transferred
, rtd
->buffer_size
, &offset
);
172 ptr
= rtd
->dma_area
+ offset
;
173 n
= rtd
->buffer_size
- offset
;
176 ret
= copy_to_user(buf
, ptr
, count
);
178 ret
= copy_to_user(buf
, ptr
, n
);
179 ret
+= copy_to_user(buf
+ n
, rtd
->dma_area
, count
- n
);
187 static const struct snd_compress_ops sof_probes_compressed_ops
= {
188 .copy
= sof_probes_compr_copy
,
191 static ssize_t
sof_probes_dfs_points_read(struct file
*file
, char __user
*to
,
192 size_t count
, loff_t
*ppos
)
194 struct sof_client_dev
*cdev
= file
->private_data
;
195 struct sof_probes_priv
*priv
= cdev
->data
;
196 struct device
*dev
= &cdev
->auxdev
.dev
;
197 struct sof_probe_point_desc
*desc
;
198 const struct sof_probes_ipc_ops
*ipc
= priv
->ipc_ops
;
199 int remaining
, offset
;
204 if (priv
->extractor_stream_tag
== SOF_PROBES_INVALID_NODE_ID
) {
205 dev_warn(dev
, "no extractor stream running\n");
209 buf
= kzalloc(PAGE_SIZE
, GFP_KERNEL
);
213 ret
= pm_runtime_resume_and_get(dev
);
214 if (ret
< 0 && ret
!= -EACCES
) {
215 dev_err_ratelimited(dev
, "debugfs read failed to resume %d\n", ret
);
219 ret
= ipc
->points_info(cdev
, &desc
, &num_desc
);
223 for (i
= 0; i
< num_desc
; i
++) {
224 offset
= strlen(buf
);
225 remaining
= PAGE_SIZE
- offset
;
226 ret
= snprintf(buf
+ offset
, remaining
,
227 "Id: %#010x Purpose: %u Node id: %#x\n",
228 desc
[i
].buffer_id
, desc
[i
].purpose
, desc
[i
].stream_tag
);
229 if (ret
< 0 || ret
>= remaining
) {
230 /* truncate the output buffer at the last full line */
236 ret
= simple_read_from_buffer(to
, count
, ppos
, buf
, strlen(buf
));
241 pm_runtime_mark_last_busy(dev
);
242 err
= pm_runtime_put_autosuspend(dev
);
244 dev_err_ratelimited(dev
, "debugfs read failed to idle %d\n", err
);
252 sof_probes_dfs_points_write(struct file
*file
, const char __user
*from
,
253 size_t count
, loff_t
*ppos
)
255 struct sof_client_dev
*cdev
= file
->private_data
;
256 struct sof_probes_priv
*priv
= cdev
->data
;
257 const struct sof_probes_ipc_ops
*ipc
= priv
->ipc_ops
;
258 struct device
*dev
= &cdev
->auxdev
.dev
;
259 struct sof_probe_point_desc
*desc
;
260 u32 num_elems
, *array
;
264 if (priv
->extractor_stream_tag
== SOF_PROBES_INVALID_NODE_ID
) {
265 dev_warn(dev
, "no extractor stream running\n");
269 ret
= parse_int_array_user(from
, count
, (int **)&array
);
274 bytes
= sizeof(*array
) * num_elems
;
275 if (bytes
% sizeof(*desc
)) {
280 desc
= (struct sof_probe_point_desc
*)&array
[1];
282 ret
= pm_runtime_resume_and_get(dev
);
283 if (ret
< 0 && ret
!= -EACCES
) {
284 dev_err_ratelimited(dev
, "debugfs write failed to resume %d\n", ret
);
288 ret
= ipc
->points_add(cdev
, desc
, bytes
/ sizeof(*desc
));
292 pm_runtime_mark_last_busy(dev
);
293 err
= pm_runtime_put_autosuspend(dev
);
295 dev_err_ratelimited(dev
, "debugfs write failed to idle %d\n", err
);
301 static const struct file_operations sof_probes_points_fops
= {
303 .read
= sof_probes_dfs_points_read
,
304 .write
= sof_probes_dfs_points_write
,
305 .llseek
= default_llseek
,
307 .owner
= THIS_MODULE
,
311 sof_probes_dfs_points_remove_write(struct file
*file
, const char __user
*from
,
312 size_t count
, loff_t
*ppos
)
314 struct sof_client_dev
*cdev
= file
->private_data
;
315 struct sof_probes_priv
*priv
= cdev
->data
;
316 const struct sof_probes_ipc_ops
*ipc
= priv
->ipc_ops
;
317 struct device
*dev
= &cdev
->auxdev
.dev
;
321 if (priv
->extractor_stream_tag
== SOF_PROBES_INVALID_NODE_ID
) {
322 dev_warn(dev
, "no extractor stream running\n");
326 ret
= parse_int_array_user(from
, count
, (int **)&array
);
330 ret
= pm_runtime_resume_and_get(dev
);
332 dev_err_ratelimited(dev
, "debugfs write failed to resume %d\n", ret
);
336 ret
= ipc
->points_remove(cdev
, &array
[1], array
[0]);
340 pm_runtime_mark_last_busy(dev
);
341 err
= pm_runtime_put_autosuspend(dev
);
343 dev_err_ratelimited(dev
, "debugfs write failed to idle %d\n", err
);
349 static const struct file_operations sof_probes_points_remove_fops
= {
351 .write
= sof_probes_dfs_points_remove_write
,
352 .llseek
= default_llseek
,
354 .owner
= THIS_MODULE
,
357 static const struct snd_soc_dai_ops sof_probes_dai_ops
= {
358 .compress_new
= snd_soc_new_compress
,
361 static struct snd_soc_dai_driver sof_probes_dai_drv
[] = {
363 .name
= "Probe Extraction CPU DAI",
364 .ops
= &sof_probes_dai_ops
,
365 .cops
= &sof_probes_compr_ops
,
367 .stream_name
= "Probe Extraction",
370 .rates
= SNDRV_PCM_RATE_48000
,
377 static const struct snd_soc_component_driver sof_probes_component
= {
378 .name
= "sof-probes-component",
379 .compress_ops
= &sof_probes_compressed_ops
,
380 .module_get_upon_open
= 1,
381 .legacy_dai_naming
= 1,
384 static int sof_probes_client_probe(struct auxiliary_device
*auxdev
,
385 const struct auxiliary_device_id
*id
)
387 struct sof_client_dev
*cdev
= auxiliary_dev_to_sof_client_dev(auxdev
);
388 struct dentry
*dfsroot
= sof_client_get_debugfs_root(cdev
);
389 struct device
*dev
= &auxdev
->dev
;
390 struct snd_soc_dai_link_component platform_component
[] = {
392 .name
= dev_name(dev
),
395 struct snd_soc_card
*card
;
396 struct sof_probes_priv
*priv
;
397 struct snd_soc_dai_link_component
*cpus
;
398 struct sof_probes_host_ops
*ops
;
399 struct snd_soc_dai_link
*links
;
402 /* do not set up the probes support if it is not enabled */
403 if (!sof_probes_enabled
)
406 ops
= dev_get_platdata(dev
);
408 dev_err(dev
, "missing platform data\n");
411 if (!ops
->startup
|| !ops
->shutdown
|| !ops
->set_params
|| !ops
->trigger
||
413 dev_err(dev
, "missing platform callback(s)\n");
417 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
421 priv
->host_ops
= ops
;
423 switch (sof_client_get_ipc_type(cdev
)) {
424 #ifdef CONFIG_SND_SOC_SOF_IPC4
426 priv
->ipc_ops
= &ipc4_probe_ops
;
429 #ifdef CONFIG_SND_SOC_SOF_IPC3
431 priv
->ipc_ops
= &ipc3_probe_ops
;
435 dev_err(dev
, "Matching IPC ops not found.");
441 /* register probes component driver and dai */
442 ret
= devm_snd_soc_register_component(dev
, &sof_probes_component
,
444 ARRAY_SIZE(sof_probes_dai_drv
));
446 dev_err(dev
, "failed to register SOF probes DAI driver %d\n", ret
);
450 /* set client data */
451 priv
->extractor_stream_tag
= SOF_PROBES_INVALID_NODE_ID
;
453 /* create read-write probes_points debugfs entry */
454 priv
->dfs_points
= debugfs_create_file("probe_points", 0644, dfsroot
,
455 cdev
, &sof_probes_points_fops
);
457 /* create read-write probe_points_remove debugfs entry */
458 priv
->dfs_points_remove
= debugfs_create_file("probe_points_remove", 0644,
460 &sof_probes_points_remove_fops
);
462 links
= devm_kcalloc(dev
, SOF_PROBES_NUM_DAI_LINKS
, sizeof(*links
), GFP_KERNEL
);
463 cpus
= devm_kcalloc(dev
, SOF_PROBES_NUM_DAI_LINKS
, sizeof(*cpus
), GFP_KERNEL
);
464 if (!links
|| !cpus
) {
465 debugfs_remove(priv
->dfs_points
);
466 debugfs_remove(priv
->dfs_points_remove
);
470 /* extraction DAI link */
471 links
[0].name
= "Compress Probe Capture";
473 links
[0].cpus
= &cpus
[0];
474 links
[0].num_cpus
= 1;
475 links
[0].cpus
->dai_name
= "Probe Extraction CPU DAI";
476 links
[0].codecs
= &snd_soc_dummy_dlc
;
477 links
[0].num_codecs
= 1;
478 links
[0].platforms
= platform_component
;
479 links
[0].num_platforms
= ARRAY_SIZE(platform_component
);
480 links
[0].nonatomic
= 1;
485 card
->name
= "sof-probes";
486 card
->owner
= THIS_MODULE
;
487 card
->num_links
= SOF_PROBES_NUM_DAI_LINKS
;
488 card
->dai_link
= links
;
490 /* set idle_bias_off to prevent the core from resuming the card->dev */
491 card
->dapm
.idle_bias_off
= true;
493 snd_soc_card_set_drvdata(card
, cdev
);
495 ret
= devm_snd_soc_register_card(dev
, card
);
497 debugfs_remove(priv
->dfs_points
);
498 debugfs_remove(priv
->dfs_points_remove
);
499 dev_err(dev
, "Probes card register failed %d\n", ret
);
503 /* enable runtime PM */
504 pm_runtime_set_autosuspend_delay(dev
, SOF_PROBES_SUSPEND_DELAY_MS
);
505 pm_runtime_use_autosuspend(dev
);
506 pm_runtime_enable(dev
);
507 pm_runtime_mark_last_busy(dev
);
508 pm_runtime_idle(dev
);
513 static void sof_probes_client_remove(struct auxiliary_device
*auxdev
)
515 struct sof_client_dev
*cdev
= auxiliary_dev_to_sof_client_dev(auxdev
);
516 struct sof_probes_priv
*priv
= cdev
->data
;
518 if (!sof_probes_enabled
)
521 pm_runtime_disable(&auxdev
->dev
);
522 debugfs_remove(priv
->dfs_points
);
523 debugfs_remove(priv
->dfs_points_remove
);
526 static const struct auxiliary_device_id sof_probes_client_id_table
[] = {
527 { .name
= "snd_sof.hda-probes", },
528 { .name
= "snd_sof.acp-probes", },
531 MODULE_DEVICE_TABLE(auxiliary
, sof_probes_client_id_table
);
533 /* driver name will be set based on KBUILD_MODNAME */
534 static struct auxiliary_driver sof_probes_client_drv
= {
535 .probe
= sof_probes_client_probe
,
536 .remove
= sof_probes_client_remove
,
538 .id_table
= sof_probes_client_id_table
,
541 module_auxiliary_driver(sof_probes_client_drv
);
543 MODULE_LICENSE("GPL v2");
544 MODULE_DESCRIPTION("SOF Probes Client Driver");
545 MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT");