xref: /linux/sound/soc/sof/sof-client-probes.c (revision 05a54fa773284d1a7923cdfdd8f0c8dabb98bd26)
1 // SPDX-License-Identifier: GPL-2.0-only
2 //
3 // Copyright(c) 2019-2022 Intel Corporation
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 <linux/string_helpers.h>
16 #include <linux/stddef.h>
17 
18 #include <sound/soc.h>
19 #include <sound/sof/header.h>
20 #include <sound/sof/ipc4/header.h>
21 #include "sof-client.h"
22 #include "sof-client-probes.h"
23 #include "sof-audio.h"
24 
25 #ifdef CONFIG_SND_SOC_SOF_IPC4
26 #include "ipc4-priv.h"
27 #endif
28 
29 #define SOF_PROBES_SUSPEND_DELAY_MS 3000
30 /* only extraction supported for now */
31 #define SOF_PROBES_NUM_DAI_LINKS 1
32 
33 #define SOF_PROBES_INVALID_NODE_ID UINT_MAX
34 
35 static bool __read_mostly sof_probes_enabled;
36 module_param_named(enable, sof_probes_enabled, bool, 0444);
37 MODULE_PARM_DESC(enable, "Enable SOF probes support");
38 
39 static int sof_probes_compr_startup(struct snd_compr_stream *cstream,
40 				    struct snd_soc_dai *dai)
41 {
42 	struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
43 	struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
44 	struct sof_probes_priv *priv = cdev->data;
45 	const struct sof_probes_host_ops *ops = priv->host_ops;
46 	int ret;
47 
48 	if (sof_client_get_fw_state(cdev) == SOF_FW_CRASHED)
49 		return -ENODEV;
50 
51 	ret = sof_client_core_module_get(cdev);
52 	if (ret)
53 		return ret;
54 
55 	ret = ops->startup(cdev, cstream, dai, &priv->extractor_stream_tag);
56 	if (ret) {
57 		dev_err(dai->dev, "Failed to startup probe stream: %d\n", ret);
58 		priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
59 		sof_client_core_module_put(cdev);
60 	}
61 
62 	return ret;
63 }
64 
65 static int sof_probes_compr_shutdown(struct snd_compr_stream *cstream,
66 				     struct snd_soc_dai *dai)
67 {
68 	struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
69 	struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
70 	struct sof_probes_priv *priv = cdev->data;
71 	const struct sof_probes_host_ops *ops = priv->host_ops;
72 	const struct sof_probes_ipc_ops *ipc = priv->ipc_ops;
73 	struct sof_probe_point_desc *desc;
74 	size_t num_desc;
75 	int i, ret;
76 
77 	/* disconnect all probe points */
78 	ret = ipc->points_info(cdev, &desc, &num_desc,
79 			       PROBES_INFO_ACTIVE_PROBES);
80 	if (ret < 0) {
81 		dev_err(dai->dev, "Failed to get probe points: %d\n", ret);
82 		goto exit;
83 	}
84 
85 	for (i = 0; i < num_desc; i++)
86 		ipc->points_remove(cdev, &desc[i].buffer_id, 1);
87 	kfree(desc);
88 
89 exit:
90 	ret = ipc->deinit(cdev);
91 	if (ret < 0)
92 		dev_err(dai->dev, "Failed to deinit probe: %d\n", ret);
93 
94 	priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
95 	snd_compr_free_pages(cstream);
96 
97 	ret = ops->shutdown(cdev, cstream, dai);
98 
99 	sof_client_core_module_put(cdev);
100 
101 	return ret;
102 }
103 
104 static int sof_probes_compr_set_params(struct snd_compr_stream *cstream,
105 				       struct snd_compr_params *params,
106 				       struct snd_soc_dai *dai)
107 {
108 	struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
109 	struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
110 	struct snd_compr_runtime *rtd = cstream->runtime;
111 	struct sof_probes_priv *priv = cdev->data;
112 	const struct sof_probes_host_ops *ops = priv->host_ops;
113 	const struct sof_probes_ipc_ops *ipc = priv->ipc_ops;
114 	int ret;
115 
116 	cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG;
117 	cstream->dma_buffer.dev.dev = sof_client_get_dma_dev(cdev);
118 	ret = snd_compr_malloc_pages(cstream, rtd->buffer_size);
119 	if (ret < 0)
120 		return ret;
121 
122 	ret = ops->set_params(cdev, cstream, params, dai);
123 	if (ret)
124 		return ret;
125 
126 	ret = ipc->init(cdev, priv->extractor_stream_tag, rtd->dma_bytes);
127 	if (ret < 0) {
128 		dev_err(dai->dev, "Failed to init probe: %d\n", ret);
129 		return ret;
130 	}
131 
132 	return 0;
133 }
134 
135 static int sof_probes_compr_trigger(struct snd_compr_stream *cstream, int cmd,
136 				    struct snd_soc_dai *dai)
137 {
138 	struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
139 	struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
140 	struct sof_probes_priv *priv = cdev->data;
141 	const struct sof_probes_host_ops *ops = priv->host_ops;
142 
143 	return ops->trigger(cdev, cstream, cmd, dai);
144 }
145 
146 static int sof_probes_compr_pointer(struct snd_compr_stream *cstream,
147 				    struct snd_compr_tstamp64 *tstamp,
148 				    struct snd_soc_dai *dai)
149 {
150 	struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
151 	struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
152 	struct sof_probes_priv *priv = cdev->data;
153 	const struct sof_probes_host_ops *ops = priv->host_ops;
154 
155 	return ops->pointer(cdev, cstream, tstamp, dai);
156 }
157 
158 static const struct snd_soc_cdai_ops sof_probes_compr_ops = {
159 	.startup = sof_probes_compr_startup,
160 	.shutdown = sof_probes_compr_shutdown,
161 	.set_params = sof_probes_compr_set_params,
162 	.trigger = sof_probes_compr_trigger,
163 	.pointer = sof_probes_compr_pointer,
164 };
165 
166 static int sof_probes_compr_copy(struct snd_soc_component *component,
167 				 struct snd_compr_stream *cstream,
168 				 char __user *buf, size_t count)
169 {
170 	struct snd_compr_runtime *rtd = cstream->runtime;
171 	unsigned int offset, n;
172 	void *ptr;
173 	int ret;
174 
175 	if (count > rtd->buffer_size)
176 		count = rtd->buffer_size;
177 
178 	div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset);
179 	ptr = rtd->dma_area + offset;
180 	n = rtd->buffer_size - offset;
181 
182 	if (count < n) {
183 		ret = copy_to_user(buf, ptr, count);
184 	} else {
185 		ret = copy_to_user(buf, ptr, n);
186 		ret += copy_to_user(buf + n, rtd->dma_area, count - n);
187 	}
188 
189 	if (ret)
190 		return count - ret;
191 	return count;
192 }
193 
194 static const struct snd_compress_ops sof_probes_compressed_ops = {
195 	.copy = sof_probes_compr_copy,
196 };
197 
198 static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
199 					  size_t count, loff_t *ppos,
200 					  enum sof_probe_info_type type)
201 {
202 	struct sof_client_dev *cdev = file->private_data;
203 	struct sof_probes_priv *priv = cdev->data;
204 	struct device *dev = &cdev->auxdev.dev;
205 	struct sof_probe_point_desc *desc;
206 	const struct sof_probes_ipc_ops *ipc = priv->ipc_ops;
207 	int remaining, offset;
208 	size_t num_desc;
209 	char *buf;
210 	int i, ret, err;
211 
212 	if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) {
213 		dev_warn(dev, "no extractor stream running\n");
214 		return -ENOENT;
215 	}
216 
217 	buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
218 	if (!buf)
219 		return -ENOMEM;
220 
221 	ret = pm_runtime_resume_and_get(dev);
222 	if (ret < 0 && ret != -EACCES) {
223 		dev_err_ratelimited(dev, "debugfs read failed to resume %d\n", ret);
224 		goto exit;
225 	}
226 
227 	ret = ipc->points_info(cdev, &desc, &num_desc, type);
228 	if (ret < 0)
229 		goto pm_error;
230 
231 	for (i = 0; i < num_desc; i++) {
232 		offset = strlen(buf);
233 		remaining = PAGE_SIZE - offset;
234 		if (ipc->point_print)
235 			ret = ipc->point_print(cdev, buf + offset, remaining, &desc[i]);
236 		else
237 			ret = snprintf(buf + offset, remaining,
238 				       "Id: %#010x  Purpose: %u  Node id: %#x\n",
239 				       desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag);
240 
241 		if (ret < 0 || ret >= remaining) {
242 			/* truncate the output buffer at the last full line */
243 			buf[offset] = '\0';
244 			break;
245 		}
246 	}
247 
248 	ret = simple_read_from_buffer(to, count, ppos, buf, strlen(buf));
249 
250 	kfree(desc);
251 
252 pm_error:
253 	err = pm_runtime_put_autosuspend(dev);
254 	if (err < 0)
255 		dev_err_ratelimited(dev, "debugfs read failed to idle %d\n", err);
256 
257 exit:
258 	kfree(buf);
259 	return ret;
260 }
261 
262 static ssize_t sof_probes_dfs_active_points_read(struct file *file,
263 						 char __user *to,
264 						 size_t count, loff_t *ppos)
265 {
266 	return sof_probes_dfs_points_read(file, to, count, ppos,
267 					  PROBES_INFO_ACTIVE_PROBES);
268 }
269 
270 static ssize_t sof_probes_dfs_available_points_read(struct file *file,
271 						    char __user *to,
272 						    size_t count, loff_t *ppos)
273 {
274 	return sof_probes_dfs_points_read(file, to, count, ppos,
275 					  PROBES_INFO_AVAILABE_PROBES);
276 }
277 
278 static ssize_t
279 sof_probes_dfs_points_write(struct file *file, const char __user *from,
280 			    size_t count, loff_t *ppos)
281 {
282 	struct sof_client_dev *cdev = file->private_data;
283 	struct sof_probes_priv *priv = cdev->data;
284 	const struct sof_probes_ipc_ops *ipc = priv->ipc_ops;
285 	struct device *dev = &cdev->auxdev.dev;
286 	struct sof_probe_point_desc *desc;
287 	u32 num_elems, *array;
288 	size_t bytes;
289 	int ret, err;
290 
291 	if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) {
292 		dev_warn(dev, "no extractor stream running\n");
293 		return -ENOENT;
294 	}
295 
296 	ret = parse_int_array_user(from, count, (int **)&array);
297 	if (ret < 0)
298 		return ret;
299 
300 	num_elems = *array;
301 	bytes = sizeof(*array) * num_elems;
302 	if (bytes % sizeof(*desc)) {
303 		ret = -EINVAL;
304 		goto exit;
305 	}
306 
307 	desc = (struct sof_probe_point_desc *)&array[1];
308 
309 	ret = pm_runtime_resume_and_get(dev);
310 	if (ret < 0 && ret != -EACCES) {
311 		dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
312 		goto exit;
313 	}
314 
315 	ret = ipc->points_add(cdev, desc, bytes / sizeof(*desc));
316 	if (!ret)
317 		ret = count;
318 
319 	err = pm_runtime_put_autosuspend(dev);
320 	if (err < 0)
321 		dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err);
322 exit:
323 	kfree(array);
324 	return ret;
325 }
326 
327 static const struct file_operations sof_probes_active_points_fops = {
328 	.open = simple_open,
329 	.read = sof_probes_dfs_active_points_read,
330 	.write = sof_probes_dfs_points_write,
331 	.llseek = default_llseek,
332 
333 	.owner = THIS_MODULE,
334 };
335 
336 static const struct file_operations sof_probes_available_points_fops = {
337 	.open = simple_open,
338 	.read = sof_probes_dfs_available_points_read,
339 	.llseek = default_llseek,
340 
341 	.owner = THIS_MODULE,
342 };
343 
344 static ssize_t
345 sof_probes_dfs_points_remove_write(struct file *file, const char __user *from,
346 				   size_t count, loff_t *ppos)
347 {
348 	struct sof_client_dev *cdev = file->private_data;
349 	struct sof_probes_priv *priv = cdev->data;
350 	const struct sof_probes_ipc_ops *ipc = priv->ipc_ops;
351 	struct device *dev = &cdev->auxdev.dev;
352 	int ret, err;
353 	u32 *array;
354 
355 	if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) {
356 		dev_warn(dev, "no extractor stream running\n");
357 		return -ENOENT;
358 	}
359 
360 	ret = parse_int_array_user(from, count, (int **)&array);
361 	if (ret < 0)
362 		return ret;
363 
364 	ret = pm_runtime_resume_and_get(dev);
365 	if (ret < 0) {
366 		dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
367 		goto exit;
368 	}
369 
370 	ret = ipc->points_remove(cdev, &array[1], array[0]);
371 	if (!ret)
372 		ret = count;
373 
374 	err = pm_runtime_put_autosuspend(dev);
375 	if (err < 0)
376 		dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err);
377 exit:
378 	kfree(array);
379 	return ret;
380 }
381 
382 static const struct file_operations sof_probes_points_remove_fops = {
383 	.open = simple_open,
384 	.write = sof_probes_dfs_points_remove_write,
385 	.llseek = default_llseek,
386 
387 	.owner = THIS_MODULE,
388 };
389 
390 static const struct snd_soc_dai_ops sof_probes_dai_ops = {
391 	.compress_new = snd_soc_new_compress,
392 };
393 
394 static struct snd_soc_dai_driver sof_probes_dai_drv[] = {
395 {
396 	.name = "Probe Extraction CPU DAI",
397 	.ops  = &sof_probes_dai_ops,
398 	.cops = &sof_probes_compr_ops,
399 	.capture = {
400 		.stream_name = "Probe Extraction",
401 		.channels_min = 1,
402 		.channels_max = 8,
403 		.rates = SNDRV_PCM_RATE_48000,
404 		.rate_min = 48000,
405 		.rate_max = 48000,
406 	},
407 },
408 };
409 
410 static const struct snd_soc_component_driver sof_probes_component = {
411 	.name = "sof-probes-component",
412 	.compress_ops = &sof_probes_compressed_ops,
413 	.module_get_upon_open = 1,
414 	.legacy_dai_naming = 1,
415 };
416 
417 static int sof_probes_client_probe(struct auxiliary_device *auxdev,
418 				   const struct auxiliary_device_id *id)
419 {
420 	struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
421 	struct dentry *dfsroot = sof_client_get_debugfs_root(cdev);
422 	struct device *dev = &auxdev->dev;
423 	struct snd_soc_dai_link_component platform_component[] = {
424 		{
425 			.name = dev_name(dev),
426 		}
427 	};
428 	struct snd_soc_card *card;
429 	struct sof_probes_priv *priv;
430 	struct snd_soc_dai_link_component *cpus;
431 	struct sof_probes_host_ops *ops;
432 	struct snd_soc_dai_link *links;
433 	int ret;
434 
435 	/* do not set up the probes support if it is not enabled */
436 	if (!sof_probes_enabled)
437 		return -ENXIO;
438 
439 	ops = dev_get_platdata(dev);
440 	if (!ops) {
441 		dev_err(dev, "missing platform data\n");
442 		return -ENODEV;
443 	}
444 	if (!ops->startup || !ops->shutdown || !ops->set_params || !ops->trigger ||
445 	    !ops->pointer) {
446 		dev_err(dev, "missing platform callback(s)\n");
447 		return -ENODEV;
448 	}
449 
450 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
451 	if (!priv)
452 		return -ENOMEM;
453 
454 	priv->host_ops = ops;
455 
456 	switch (sof_client_get_ipc_type(cdev)) {
457 #ifdef CONFIG_SND_SOC_SOF_IPC4
458 	case SOF_IPC_TYPE_4:
459 		priv->ipc_ops = &ipc4_probe_ops;
460 		break;
461 #endif
462 #ifdef CONFIG_SND_SOC_SOF_IPC3
463 	case SOF_IPC_TYPE_3:
464 		priv->ipc_ops = &ipc3_probe_ops;
465 		break;
466 #endif
467 	default:
468 		dev_err(dev, "Matching IPC ops not found.");
469 		return -ENODEV;
470 	}
471 
472 	cdev->data = priv;
473 
474 	/* register probes component driver and dai */
475 	ret = devm_snd_soc_register_component(dev, &sof_probes_component,
476 					      sof_probes_dai_drv,
477 					      ARRAY_SIZE(sof_probes_dai_drv));
478 	if (ret < 0) {
479 		dev_err(dev, "failed to register SOF probes DAI driver %d\n", ret);
480 		return ret;
481 	}
482 
483 	/* set client data */
484 	priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
485 
486 	/* create read-write probes_points debugfs entry */
487 	priv->dfs_points = debugfs_create_file("probe_points", 0644, dfsroot,
488 					       cdev, &sof_probes_active_points_fops);
489 
490 	/* create read-write probe_points_remove debugfs entry */
491 	priv->dfs_points_remove = debugfs_create_file("probe_points_remove", 0644,
492 						      dfsroot, cdev,
493 						      &sof_probes_points_remove_fops);
494 
495 	/* create read-write probes_points debugfs entry */
496 	priv->dfs_points = debugfs_create_file("probe_points_available", 0644, dfsroot,
497 					       cdev, &sof_probes_available_points_fops);
498 
499 	links = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*links), GFP_KERNEL);
500 	cpus = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*cpus), GFP_KERNEL);
501 	if (!links || !cpus) {
502 		debugfs_remove(priv->dfs_points);
503 		debugfs_remove(priv->dfs_points_remove);
504 		return -ENOMEM;
505 	}
506 
507 	/* extraction DAI link */
508 	links[0].name = "Compress Probe Capture";
509 	links[0].id = 0;
510 	links[0].cpus = &cpus[0];
511 	links[0].num_cpus = 1;
512 	links[0].cpus->dai_name = "Probe Extraction CPU DAI";
513 	links[0].codecs = &snd_soc_dummy_dlc;
514 	links[0].num_codecs = 1;
515 	links[0].platforms = platform_component;
516 	links[0].num_platforms = ARRAY_SIZE(platform_component);
517 	links[0].nonatomic = 1;
518 
519 	card = &priv->card;
520 
521 	card->dev = dev;
522 	card->name = "sof-probes";
523 	card->owner = THIS_MODULE;
524 	card->num_links = SOF_PROBES_NUM_DAI_LINKS;
525 	card->dai_link = links;
526 
527 	/* set idle_bias_off to prevent the core from resuming the card->dev */
528 	card->dapm.idle_bias = false;
529 
530 	snd_soc_card_set_drvdata(card, cdev);
531 
532 	ret = devm_snd_soc_register_card(dev, card);
533 	if (ret < 0) {
534 		debugfs_remove(priv->dfs_points);
535 		debugfs_remove(priv->dfs_points_remove);
536 		dev_err(dev, "Probes card register failed %d\n", ret);
537 		return ret;
538 	}
539 
540 	/* enable runtime PM */
541 	pm_runtime_set_autosuspend_delay(dev, SOF_PROBES_SUSPEND_DELAY_MS);
542 	pm_runtime_use_autosuspend(dev);
543 	pm_runtime_enable(dev);
544 	pm_runtime_mark_last_busy(dev);
545 	pm_runtime_idle(dev);
546 
547 	return 0;
548 }
549 
550 static void sof_probes_client_remove(struct auxiliary_device *auxdev)
551 {
552 	struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
553 	struct sof_probes_priv *priv = cdev->data;
554 
555 	if (!sof_probes_enabled)
556 		return;
557 
558 	pm_runtime_disable(&auxdev->dev);
559 	debugfs_remove(priv->dfs_points);
560 	debugfs_remove(priv->dfs_points_remove);
561 }
562 
563 static const struct auxiliary_device_id sof_probes_client_id_table[] = {
564 	{ .name = "snd_sof.hda-probes", },
565 	{ .name = "snd_sof.acp-probes", },
566 	{},
567 };
568 MODULE_DEVICE_TABLE(auxiliary, sof_probes_client_id_table);
569 
570 /* driver name will be set based on KBUILD_MODNAME */
571 static struct auxiliary_driver sof_probes_client_drv = {
572 	.probe = sof_probes_client_probe,
573 	.remove = sof_probes_client_remove,
574 
575 	.id_table = sof_probes_client_id_table,
576 };
577 
578 module_auxiliary_driver(sof_probes_client_drv);
579 
580 MODULE_LICENSE("GPL v2");
581 MODULE_DESCRIPTION("SOF Probes Client Driver");
582 MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT");
583