1 // SPDX-License-Identifier: GPL-2.0-only
3 // Copyright(c) 2022 Intel Corporation. All rights reserved.
5 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
7 #include <linux/debugfs.h>
8 #include <linux/sched/signal.h>
10 #include "sof-audio.h"
12 #include "sof-utils.h"
13 #include "ipc3-priv.h"
15 #define TRACE_FILTER_ELEMENTS_PER_ENTRY 4
16 #define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024
18 static int trace_filter_append_elem(struct snd_sof_dev *sdev, u32 key, u32 value,
19 struct sof_ipc_trace_filter_elem *elem_list,
20 int capacity, int *counter)
22 if (*counter >= capacity)
25 elem_list[*counter].key = key;
26 elem_list[*counter].value = value;
32 static int trace_filter_parse_entry(struct snd_sof_dev *sdev, const char *line,
33 struct sof_ipc_trace_filter_elem *elem,
34 int capacity, int *counter)
36 int log_level, pipe_id, comp_id, read, ret;
37 int len = strlen(line);
41 /* ignore empty content */
42 ret = sscanf(line, " %n", &read);
43 if (!ret && read == len)
46 ret = sscanf(line, " %d %x %d %d %n", &log_level, &uuid_id, &pipe_id, &comp_id, &read);
47 if (ret != TRACE_FILTER_ELEMENTS_PER_ENTRY || read != len) {
48 dev_err(sdev->dev, "Invalid trace filter entry '%s'\n", line);
53 ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_UUID,
54 uuid_id, elem, capacity, &cnt);
59 ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_PIPE,
60 pipe_id, elem, capacity, &cnt);
65 ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_COMP,
66 comp_id, elem, capacity, &cnt);
71 ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_SET_LEVEL |
72 SOF_IPC_TRACE_FILTER_ELEM_FIN,
73 log_level, elem, capacity, &cnt);
77 /* update counter only when parsing whole entry passed */
83 static int trace_filter_parse(struct snd_sof_dev *sdev, char *string,
85 struct sof_ipc_trace_filter_elem **out)
87 static const char entry_delimiter[] = ";";
94 * Each entry contains at least 1, up to TRACE_FILTER_ELEMENTS_PER_ENTRY
95 * IPC elements, depending on content. Calculate IPC elements capacity
96 * for the input string where each element is set.
99 capacity += TRACE_FILTER_ELEMENTS_PER_ENTRY;
100 entry = strchr(entry + 1, entry_delimiter[0]);
102 *out = kmalloc(capacity * sizeof(**out), GFP_KERNEL);
106 /* split input string by ';', and parse each entry separately in trace_filter_parse_entry */
107 while ((entry = strsep(&string, entry_delimiter))) {
108 entry_len = trace_filter_parse_entry(sdev, entry, *out, capacity, &cnt);
111 "Parsing filter entry '%s' failed with %d\n",
122 static int ipc3_trace_update_filter(struct snd_sof_dev *sdev, int num_elems,
123 struct sof_ipc_trace_filter_elem *elems)
125 struct sof_ipc_trace_filter *msg;
126 struct sof_ipc_reply reply;
130 size = struct_size(msg, elems, num_elems);
131 if (size > SOF_IPC_MSG_MAX_SIZE)
134 msg = kmalloc(size, GFP_KERNEL);
138 msg->hdr.size = size;
139 msg->hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_FILTER_UPDATE;
140 msg->elem_cnt = num_elems;
141 memcpy(&msg->elems[0], elems, num_elems * sizeof(*elems));
143 ret = pm_runtime_get_sync(sdev->dev);
144 if (ret < 0 && ret != -EACCES) {
145 pm_runtime_put_noidle(sdev->dev);
146 dev_err(sdev->dev, "enabling device failed: %d\n", ret);
149 ret = sof_ipc_tx_message(sdev->ipc, msg, msg->hdr.size, &reply, sizeof(reply));
150 pm_runtime_mark_last_busy(sdev->dev);
151 pm_runtime_put_autosuspend(sdev->dev);
155 return ret ? ret : reply.error;
158 static ssize_t dfsentry_trace_filter_write(struct file *file, const char __user *from,
159 size_t count, loff_t *ppos)
161 struct snd_sof_dfsentry *dfse = file->private_data;
162 struct sof_ipc_trace_filter_elem *elems = NULL;
163 struct snd_sof_dev *sdev = dfse->sdev;
169 if (count > TRACE_FILTER_MAX_CONFIG_STRING_LENGTH) {
170 dev_err(sdev->dev, "%s too long input, %zu > %d\n", __func__, count,
171 TRACE_FILTER_MAX_CONFIG_STRING_LENGTH);
175 string = kmalloc(count + 1, GFP_KERNEL);
179 /* assert null termination */
181 ret = simple_write_to_buffer(string, count, &pos, from, count);
185 ret = trace_filter_parse(sdev, string, &num_elems, &elems);
190 ret = ipc3_trace_update_filter(sdev, num_elems, elems);
192 dev_err(sdev->dev, "Filter update failed: %d\n", ret);
203 static const struct file_operations sof_dfs_trace_filter_fops = {
205 .write = dfsentry_trace_filter_write,
206 .llseek = default_llseek,
209 static int debugfs_create_trace_filter(struct snd_sof_dev *sdev)
211 struct snd_sof_dfsentry *dfse;
213 dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
218 dfse->type = SOF_DFSENTRY_TYPE_BUF;
220 debugfs_create_file("filter", 0200, sdev->debugfs_root, dfse,
221 &sof_dfs_trace_filter_fops);
222 /* add to dfsentry list */
223 list_add(&dfse->list, &sdev->dfsentry_list);
228 static size_t sof_dtrace_avail(struct snd_sof_dev *sdev,
229 loff_t pos, size_t buffer_size)
231 loff_t host_offset = READ_ONCE(sdev->host_offset);
234 * If host offset is less than local pos, it means write pointer of
235 * host DMA buffer has been wrapped. We should output the trace data
236 * at the end of host DMA buffer at first.
238 if (host_offset < pos)
239 return buffer_size - pos;
241 /* If there is available trace data now, it is unnecessary to wait. */
242 if (host_offset > pos)
243 return host_offset - pos;
248 static size_t sof_wait_dtrace_avail(struct snd_sof_dev *sdev, loff_t pos,
251 wait_queue_entry_t wait;
252 size_t ret = sof_dtrace_avail(sdev, pos, buffer_size);
254 /* data immediately available */
258 if (sdev->dtrace_state != SOF_DTRACE_ENABLED && sdev->dtrace_draining) {
260 * tracing has ended and all traces have been
261 * read by client, return EOF
263 sdev->dtrace_draining = false;
267 /* wait for available trace data from FW */
268 init_waitqueue_entry(&wait, current);
269 set_current_state(TASK_INTERRUPTIBLE);
270 add_wait_queue(&sdev->trace_sleep, &wait);
272 if (!signal_pending(current)) {
273 /* set timeout to max value, no error code */
274 schedule_timeout(MAX_SCHEDULE_TIMEOUT);
276 remove_wait_queue(&sdev->trace_sleep, &wait);
278 return sof_dtrace_avail(sdev, pos, buffer_size);
281 static ssize_t dfsentry_dtrace_read(struct file *file, char __user *buffer,
282 size_t count, loff_t *ppos)
284 struct snd_sof_dfsentry *dfse = file->private_data;
285 struct snd_sof_dev *sdev = dfse->sdev;
288 size_t avail, buffer_size = dfse->size;
291 /* make sure we know about any failures on the DSP side */
292 sdev->dtrace_error = false;
294 /* check pos and count */
300 /* check for buffer wrap and count overflow */
302 lpos = do_div(lpos_64, buffer_size);
304 /* get available count based on current host offset */
305 avail = sof_wait_dtrace_avail(sdev, lpos, buffer_size);
306 if (sdev->dtrace_error) {
307 dev_err(sdev->dev, "trace IO error\n");
311 /* make sure count is <= avail */
316 * make sure that all trace data is available for the CPU as the trace
317 * data buffer might be allocated from non consistent memory.
318 * Note: snd_dma_buffer_sync() is called for normal audio playback and
319 * capture streams also.
321 snd_dma_buffer_sync(&sdev->dmatb, SNDRV_DMA_SYNC_CPU);
322 /* copy available trace data to debugfs */
323 rem = copy_to_user(buffer, ((u8 *)(dfse->buf) + lpos), count);
329 /* move debugfs reading position */
333 static int dfsentry_dtrace_release(struct inode *inode, struct file *file)
335 struct snd_sof_dfsentry *dfse = inode->i_private;
336 struct snd_sof_dev *sdev = dfse->sdev;
338 /* avoid duplicate traces at next open */
339 if (sdev->dtrace_state != SOF_DTRACE_ENABLED)
340 sdev->host_offset = 0;
345 static const struct file_operations sof_dfs_dtrace_fops = {
347 .read = dfsentry_dtrace_read,
348 .llseek = default_llseek,
349 .release = dfsentry_dtrace_release,
352 static int debugfs_create_dtrace(struct snd_sof_dev *sdev)
354 struct snd_sof_dfsentry *dfse;
360 ret = debugfs_create_trace_filter(sdev);
362 dev_warn(sdev->dev, "failed to create filter debugfs file: %d", ret);
364 dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
368 dfse->type = SOF_DFSENTRY_TYPE_BUF;
369 dfse->buf = sdev->dmatb.area;
370 dfse->size = sdev->dmatb.bytes;
373 debugfs_create_file("trace", 0444, sdev->debugfs_root, dfse,
374 &sof_dfs_dtrace_fops);
379 static int ipc3_dtrace_enable(struct snd_sof_dev *sdev)
381 struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
382 struct sof_ipc_fw_version *v = &ready->version;
383 struct sof_ipc_dma_trace_params_ext params;
384 struct sof_ipc_reply ipc_reply;
387 if (!sdev->fw_trace_is_supported)
390 if (sdev->dtrace_state == SOF_DTRACE_ENABLED || !sdev->dma_trace_pages)
393 if (sdev->dtrace_state == SOF_DTRACE_STOPPED)
396 /* set IPC parameters */
397 params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG;
398 /* PARAMS_EXT is only supported from ABI 3.7.0 onwards */
399 if (v->abi_version >= SOF_ABI_VER(3, 7, 0)) {
400 params.hdr.size = sizeof(struct sof_ipc_dma_trace_params_ext);
401 params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS_EXT;
402 params.timestamp_ns = ktime_get(); /* in nanosecond */
404 params.hdr.size = sizeof(struct sof_ipc_dma_trace_params);
405 params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS;
407 params.buffer.phy_addr = sdev->dmatp.addr;
408 params.buffer.size = sdev->dmatb.bytes;
409 params.buffer.pages = sdev->dma_trace_pages;
410 params.stream_tag = 0;
412 sdev->host_offset = 0;
413 sdev->dtrace_draining = false;
415 ret = snd_sof_dma_trace_init(sdev, ¶ms);
417 dev_err(sdev->dev, "Host dtrace init failed: %d\n", ret);
420 dev_dbg(sdev->dev, "%s: stream_tag: %d\n", __func__, params.stream_tag);
422 /* send IPC to the DSP */
423 ret = sof_ipc_tx_message(sdev->ipc, ¶ms, sizeof(params), &ipc_reply, sizeof(ipc_reply));
425 dev_err(sdev->dev, "can't set params for DMA for trace %d\n", ret);
430 ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_START);
432 dev_err(sdev->dev, "Host dtrace trigger start failed: %d\n", ret);
436 sdev->dtrace_state = SOF_DTRACE_ENABLED;
441 snd_sof_dma_trace_release(sdev);
445 static int ipc3_dtrace_init(struct snd_sof_dev *sdev)
449 /* dtrace is only supported with SOF_IPC */
450 if (sdev->pdata->ipc_type != SOF_IPC)
453 /* set false before start initialization */
454 sdev->dtrace_state = SOF_DTRACE_DISABLED;
456 /* allocate trace page table buffer */
457 ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev,
458 PAGE_SIZE, &sdev->dmatp);
460 dev_err(sdev->dev, "can't alloc page table for trace %d\n", ret);
464 /* allocate trace data buffer */
465 ret = snd_dma_alloc_dir_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->dev,
466 DMA_FROM_DEVICE, DMA_BUF_SIZE_FOR_TRACE,
469 dev_err(sdev->dev, "can't alloc buffer for trace %d\n", ret);
473 /* create compressed page table for audio firmware */
474 ret = snd_sof_create_page_table(sdev->dev, &sdev->dmatb,
475 sdev->dmatp.area, sdev->dmatb.bytes);
479 sdev->dma_trace_pages = ret;
480 dev_dbg(sdev->dev, "%s: dma_trace_pages: %d\n", __func__,
481 sdev->dma_trace_pages);
483 if (sdev->first_boot) {
484 ret = debugfs_create_dtrace(sdev);
489 init_waitqueue_head(&sdev->trace_sleep);
491 ret = ipc3_dtrace_enable(sdev);
497 sdev->dma_trace_pages = 0;
498 snd_dma_free_pages(&sdev->dmatb);
500 snd_dma_free_pages(&sdev->dmatp);
504 int ipc3_dtrace_posn_update(struct snd_sof_dev *sdev,
505 struct sof_ipc_dma_trace_posn *posn)
507 if (!sdev->fw_trace_is_supported)
510 if (sdev->dtrace_state == SOF_DTRACE_ENABLED &&
511 sdev->host_offset != posn->host_offset) {
512 sdev->host_offset = posn->host_offset;
513 wake_up(&sdev->trace_sleep);
516 if (posn->overflow != 0)
518 "DSP trace buffer overflow %u bytes. Total messages %d\n",
519 posn->overflow, posn->messages);
524 /* an error has occurred within the DSP that prevents further trace */
525 static void ipc3_dtrace_fw_crashed(struct snd_sof_dev *sdev)
527 if (sdev->dtrace_state == SOF_DTRACE_ENABLED) {
528 sdev->dtrace_error = true;
529 wake_up(&sdev->trace_sleep);
533 static void ipc3_dtrace_release(struct snd_sof_dev *sdev, bool only_stop)
535 struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
536 struct sof_ipc_fw_version *v = &ready->version;
537 struct sof_ipc_cmd_hdr hdr;
538 struct sof_ipc_reply ipc_reply;
541 if (!sdev->fw_trace_is_supported || sdev->dtrace_state == SOF_DTRACE_DISABLED)
544 ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_STOP);
546 dev_err(sdev->dev, "Host dtrace trigger stop failed: %d\n", ret);
547 sdev->dtrace_state = SOF_DTRACE_STOPPED;
550 * stop and free trace DMA in the DSP. TRACE_DMA_FREE is only supported from
553 if (v->abi_version >= SOF_ABI_VER(3, 20, 0)) {
554 hdr.size = sizeof(hdr);
555 hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_FREE;
557 ret = sof_ipc_tx_message(sdev->ipc, &hdr, hdr.size,
558 &ipc_reply, sizeof(ipc_reply));
560 dev_err(sdev->dev, "DMA_TRACE_FREE failed with error: %d\n", ret);
566 ret = snd_sof_dma_trace_release(sdev);
568 dev_err(sdev->dev, "Host dtrace release failed %d\n", ret);
570 sdev->dtrace_state = SOF_DTRACE_DISABLED;
573 sdev->dtrace_draining = true;
574 wake_up(&sdev->trace_sleep);
577 static void ipc3_dtrace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state)
579 ipc3_dtrace_release(sdev, pm_state.event == SOF_DSP_PM_D0);
582 static int ipc3_dtrace_resume(struct snd_sof_dev *sdev)
584 return ipc3_dtrace_enable(sdev);
587 static void ipc3_dtrace_free(struct snd_sof_dev *sdev)
590 ipc3_dtrace_release(sdev, false);
592 if (sdev->dma_trace_pages) {
593 snd_dma_free_pages(&sdev->dmatb);
594 snd_dma_free_pages(&sdev->dmatp);
595 sdev->dma_trace_pages = 0;
599 const struct sof_ipc_fw_tracing_ops ipc3_dtrace_ops = {
600 .init = ipc3_dtrace_init,
601 .free = ipc3_dtrace_free,
602 .fw_crashed = ipc3_dtrace_fw_crashed,
603 .suspend = ipc3_dtrace_suspend,
604 .resume = ipc3_dtrace_resume,