File Index
Files and directories
Sign in
FILE

sof-client-probes.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/sof/sof-client-probes.c C
  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
 35static bool __read_mostly sof_probes_enabled;
 36module_param_named(enable, sof_probes_enabled, bool, 0444);
 37MODULE_PARM_DESC(enable, "Enable SOF probes support");
 38
 39static 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
 65static 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
 89exit:
 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
104static 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
135static 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
146static 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
158static 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
166static 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
194static const struct snd_compress_ops sof_probes_compressed_ops = {
195	.copy = sof_probes_compr_copy,
196};
197
198static 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
252pm_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
257exit:
258	kfree(buf);
259	return ret;
260}
261
262static 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
270static 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
278static ssize_t
279sof_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);
322exit:
323	kfree(array);
324	return ret;
325}
326
327static 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
336static 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
344static ssize_t
345sof_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);
377exit:
378	kfree(array);
379	return ret;
380}
381
382static 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
390static const struct snd_soc_dai_ops sof_probes_dai_ops = {
391	.compress_new = snd_soc_new_compress,
392};
393
394static 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
410static 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
417static 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	snd_soc_card_set_drvdata(card, cdev);
528
529	ret = devm_snd_soc_register_card(dev, card);
530	if (ret < 0) {
531		debugfs_remove(priv->dfs_points);
532		debugfs_remove(priv->dfs_points_remove);
533		dev_err(dev, "Probes card register failed %d\n", ret);
534		return ret;
535	}
536
537	/*
538	 * set idle_bias_off to prevent the core from resuming the card->dev
539	 * call it after snd_soc_register_card()
540	 */
541	struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
542
543	snd_soc_dapm_set_idle_bias(dapm, false);
544
545	/* enable runtime PM */
546	pm_runtime_set_autosuspend_delay(dev, SOF_PROBES_SUSPEND_DELAY_MS);
547	pm_runtime_use_autosuspend(dev);
548	pm_runtime_enable(dev);
549	pm_runtime_mark_last_busy(dev);
550	pm_runtime_idle(dev);
551
552	return 0;
553}
554
555static void sof_probes_client_remove(struct auxiliary_device *auxdev)
556{
557	struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
558	struct sof_probes_priv *priv = cdev->data;
559
560	if (!sof_probes_enabled)
561		return;
562
563	pm_runtime_disable(&auxdev->dev);
564	debugfs_remove(priv->dfs_points);
565	debugfs_remove(priv->dfs_points_remove);
566}
567
568static const struct auxiliary_device_id sof_probes_client_id_table[] = {
569	{ .name = "snd_sof.hda-probes", },
570	{ .name = "snd_sof.acp-probes", },
571	{},
572};
573MODULE_DEVICE_TABLE(auxiliary, sof_probes_client_id_table);
574
575/* driver name will be set based on KBUILD_MODNAME */
576static struct auxiliary_driver sof_probes_client_drv = {
577	.probe = sof_probes_client_probe,
578	.remove = sof_probes_client_remove,
579
580	.id_table = sof_probes_client_id_table,
581};
582
583module_auxiliary_driver(sof_probes_client_drv);
584
585MODULE_LICENSE("GPL v2");
586MODULE_DESCRIPTION("SOF Probes Client Driver");
587MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT");
~/ Read-only file preview.