Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 //
0003 // Copyright(c) 2022 Intel Corporation. All rights reserved.
0004 //
0005 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
0006 
0007 #include <linux/debugfs.h>
0008 #include <linux/sched/signal.h>
0009 #include "sof-priv.h"
0010 #include "sof-audio.h"
0011 #include "ops.h"
0012 #include "sof-utils.h"
0013 #include "ipc3-priv.h"
0014 
0015 #define TRACE_FILTER_ELEMENTS_PER_ENTRY 4
0016 #define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024
0017 
0018 enum sof_dtrace_state {
0019     SOF_DTRACE_DISABLED,
0020     SOF_DTRACE_STOPPED,
0021     SOF_DTRACE_INITIALIZING,
0022     SOF_DTRACE_ENABLED,
0023 };
0024 
0025 struct sof_dtrace_priv {
0026     struct snd_dma_buffer dmatb;
0027     struct snd_dma_buffer dmatp;
0028     int dma_trace_pages;
0029     wait_queue_head_t trace_sleep;
0030     u32 host_offset;
0031     bool dtrace_error;
0032     bool dtrace_draining;
0033     enum sof_dtrace_state dtrace_state;
0034 };
0035 
0036 static bool trace_pos_update_expected(struct sof_dtrace_priv *priv)
0037 {
0038     if (priv->dtrace_state == SOF_DTRACE_ENABLED ||
0039         priv->dtrace_state == SOF_DTRACE_INITIALIZING)
0040         return true;
0041 
0042     return false;
0043 }
0044 
0045 static int trace_filter_append_elem(struct snd_sof_dev *sdev, u32 key, u32 value,
0046                     struct sof_ipc_trace_filter_elem *elem_list,
0047                     int capacity, int *counter)
0048 {
0049     if (*counter >= capacity)
0050         return -ENOMEM;
0051 
0052     elem_list[*counter].key = key;
0053     elem_list[*counter].value = value;
0054     ++*counter;
0055 
0056     return 0;
0057 }
0058 
0059 static int trace_filter_parse_entry(struct snd_sof_dev *sdev, const char *line,
0060                     struct sof_ipc_trace_filter_elem *elem,
0061                     int capacity, int *counter)
0062 {
0063     int log_level, pipe_id, comp_id, read, ret;
0064     int len = strlen(line);
0065     int cnt = *counter;
0066     u32 uuid_id;
0067 
0068     /* ignore empty content */
0069     ret = sscanf(line, " %n", &read);
0070     if (!ret && read == len)
0071         return len;
0072 
0073     ret = sscanf(line, " %d %x %d %d %n", &log_level, &uuid_id, &pipe_id, &comp_id, &read);
0074     if (ret != TRACE_FILTER_ELEMENTS_PER_ENTRY || read != len) {
0075         dev_err(sdev->dev, "Invalid trace filter entry '%s'\n", line);
0076         return -EINVAL;
0077     }
0078 
0079     if (uuid_id > 0) {
0080         ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_UUID,
0081                            uuid_id, elem, capacity, &cnt);
0082         if (ret)
0083             return ret;
0084     }
0085     if (pipe_id >= 0) {
0086         ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_PIPE,
0087                            pipe_id, elem, capacity, &cnt);
0088         if (ret)
0089             return ret;
0090     }
0091     if (comp_id >= 0) {
0092         ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_COMP,
0093                            comp_id, elem, capacity, &cnt);
0094         if (ret)
0095             return ret;
0096     }
0097 
0098     ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_SET_LEVEL |
0099                        SOF_IPC_TRACE_FILTER_ELEM_FIN,
0100                        log_level, elem, capacity, &cnt);
0101     if (ret)
0102         return ret;
0103 
0104     /* update counter only when parsing whole entry passed */
0105     *counter = cnt;
0106 
0107     return len;
0108 }
0109 
0110 static int trace_filter_parse(struct snd_sof_dev *sdev, char *string,
0111                   int *out_elem_cnt,
0112                   struct sof_ipc_trace_filter_elem **out)
0113 {
0114     static const char entry_delimiter[] = ";";
0115     char *entry = string;
0116     int capacity = 0;
0117     int entry_len;
0118     int cnt = 0;
0119 
0120     /*
0121      * Each entry contains at least 1, up to TRACE_FILTER_ELEMENTS_PER_ENTRY
0122      * IPC elements, depending on content. Calculate IPC elements capacity
0123      * for the input string where each element is set.
0124      */
0125     while (entry) {
0126         capacity += TRACE_FILTER_ELEMENTS_PER_ENTRY;
0127         entry = strchr(entry + 1, entry_delimiter[0]);
0128     }
0129     *out = kmalloc(capacity * sizeof(**out), GFP_KERNEL);
0130     if (!*out)
0131         return -ENOMEM;
0132 
0133     /* split input string by ';', and parse each entry separately in trace_filter_parse_entry */
0134     while ((entry = strsep(&string, entry_delimiter))) {
0135         entry_len = trace_filter_parse_entry(sdev, entry, *out, capacity, &cnt);
0136         if (entry_len < 0) {
0137             dev_err(sdev->dev,
0138                 "Parsing filter entry '%s' failed with %d\n",
0139                 entry, entry_len);
0140             return -EINVAL;
0141         }
0142     }
0143 
0144     *out_elem_cnt = cnt;
0145 
0146     return 0;
0147 }
0148 
0149 static int ipc3_trace_update_filter(struct snd_sof_dev *sdev, int num_elems,
0150                     struct sof_ipc_trace_filter_elem *elems)
0151 {
0152     struct sof_ipc_trace_filter *msg;
0153     struct sof_ipc_reply reply;
0154     size_t size;
0155     int ret;
0156 
0157     size = struct_size(msg, elems, num_elems);
0158     if (size > SOF_IPC_MSG_MAX_SIZE)
0159         return -EINVAL;
0160 
0161     msg = kmalloc(size, GFP_KERNEL);
0162     if (!msg)
0163         return -ENOMEM;
0164 
0165     msg->hdr.size = size;
0166     msg->hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_FILTER_UPDATE;
0167     msg->elem_cnt = num_elems;
0168     memcpy(&msg->elems[0], elems, num_elems * sizeof(*elems));
0169 
0170     ret = pm_runtime_resume_and_get(sdev->dev);
0171     if (ret < 0 && ret != -EACCES) {
0172         dev_err(sdev->dev, "enabling device failed: %d\n", ret);
0173         goto error;
0174     }
0175     ret = sof_ipc_tx_message(sdev->ipc, msg, msg->hdr.size, &reply, sizeof(reply));
0176     pm_runtime_mark_last_busy(sdev->dev);
0177     pm_runtime_put_autosuspend(sdev->dev);
0178 
0179 error:
0180     kfree(msg);
0181     return ret ? ret : reply.error;
0182 }
0183 
0184 static ssize_t dfsentry_trace_filter_write(struct file *file, const char __user *from,
0185                        size_t count, loff_t *ppos)
0186 {
0187     struct snd_sof_dfsentry *dfse = file->private_data;
0188     struct sof_ipc_trace_filter_elem *elems = NULL;
0189     struct snd_sof_dev *sdev = dfse->sdev;
0190     loff_t pos = 0;
0191     int num_elems;
0192     char *string;
0193     int ret;
0194 
0195     if (count > TRACE_FILTER_MAX_CONFIG_STRING_LENGTH) {
0196         dev_err(sdev->dev, "%s too long input, %zu > %d\n", __func__, count,
0197             TRACE_FILTER_MAX_CONFIG_STRING_LENGTH);
0198         return -EINVAL;
0199     }
0200 
0201     string = kmalloc(count + 1, GFP_KERNEL);
0202     if (!string)
0203         return -ENOMEM;
0204 
0205     /* assert null termination */
0206     string[count] = 0;
0207     ret = simple_write_to_buffer(string, count, &pos, from, count);
0208     if (ret < 0)
0209         goto error;
0210 
0211     ret = trace_filter_parse(sdev, string, &num_elems, &elems);
0212     if (ret < 0)
0213         goto error;
0214 
0215     if (num_elems) {
0216         ret = ipc3_trace_update_filter(sdev, num_elems, elems);
0217         if (ret < 0) {
0218             dev_err(sdev->dev, "Filter update failed: %d\n", ret);
0219             goto error;
0220         }
0221     }
0222     ret = count;
0223 error:
0224     kfree(string);
0225     kfree(elems);
0226     return ret;
0227 }
0228 
0229 static const struct file_operations sof_dfs_trace_filter_fops = {
0230     .open = simple_open,
0231     .write = dfsentry_trace_filter_write,
0232     .llseek = default_llseek,
0233 };
0234 
0235 static int debugfs_create_trace_filter(struct snd_sof_dev *sdev)
0236 {
0237     struct snd_sof_dfsentry *dfse;
0238 
0239     dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
0240     if (!dfse)
0241         return -ENOMEM;
0242 
0243     dfse->sdev = sdev;
0244     dfse->type = SOF_DFSENTRY_TYPE_BUF;
0245 
0246     debugfs_create_file("filter", 0200, sdev->debugfs_root, dfse,
0247                 &sof_dfs_trace_filter_fops);
0248     /* add to dfsentry list */
0249     list_add(&dfse->list, &sdev->dfsentry_list);
0250 
0251     return 0;
0252 }
0253 
0254 static bool sof_dtrace_set_host_offset(struct sof_dtrace_priv *priv, u32 new_offset)
0255 {
0256     u32 host_offset = READ_ONCE(priv->host_offset);
0257 
0258     if (host_offset != new_offset) {
0259         /* This is a bit paranoid and unlikely that it is needed */
0260         u32 ret = cmpxchg(&priv->host_offset, host_offset, new_offset);
0261 
0262         if (ret == host_offset)
0263             return true;
0264     }
0265 
0266     return false;
0267 }
0268 
0269 static size_t sof_dtrace_avail(struct snd_sof_dev *sdev,
0270                    loff_t pos, size_t buffer_size)
0271 {
0272     struct sof_dtrace_priv *priv = sdev->fw_trace_data;
0273     loff_t host_offset = READ_ONCE(priv->host_offset);
0274 
0275     /*
0276      * If host offset is less than local pos, it means write pointer of
0277      * host DMA buffer has been wrapped. We should output the trace data
0278      * at the end of host DMA buffer at first.
0279      */
0280     if (host_offset < pos)
0281         return buffer_size - pos;
0282 
0283     /* If there is available trace data now, it is unnecessary to wait. */
0284     if (host_offset > pos)
0285         return host_offset - pos;
0286 
0287     return 0;
0288 }
0289 
0290 static size_t sof_wait_dtrace_avail(struct snd_sof_dev *sdev, loff_t pos,
0291                     size_t buffer_size)
0292 {
0293     size_t ret = sof_dtrace_avail(sdev, pos, buffer_size);
0294     struct sof_dtrace_priv *priv = sdev->fw_trace_data;
0295     wait_queue_entry_t wait;
0296 
0297     /* data immediately available */
0298     if (ret)
0299         return ret;
0300 
0301     if (priv->dtrace_draining && !trace_pos_update_expected(priv)) {
0302         /*
0303          * tracing has ended and all traces have been
0304          * read by client, return EOF
0305          */
0306         priv->dtrace_draining = false;
0307         return 0;
0308     }
0309 
0310     /* wait for available trace data from FW */
0311     init_waitqueue_entry(&wait, current);
0312     set_current_state(TASK_INTERRUPTIBLE);
0313     add_wait_queue(&priv->trace_sleep, &wait);
0314 
0315     if (!signal_pending(current)) {
0316         /* set timeout to max value, no error code */
0317         schedule_timeout(MAX_SCHEDULE_TIMEOUT);
0318     }
0319     remove_wait_queue(&priv->trace_sleep, &wait);
0320 
0321     return sof_dtrace_avail(sdev, pos, buffer_size);
0322 }
0323 
0324 static ssize_t dfsentry_dtrace_read(struct file *file, char __user *buffer,
0325                     size_t count, loff_t *ppos)
0326 {
0327     struct snd_sof_dfsentry *dfse = file->private_data;
0328     struct snd_sof_dev *sdev = dfse->sdev;
0329     struct sof_dtrace_priv *priv = sdev->fw_trace_data;
0330     unsigned long rem;
0331     loff_t lpos = *ppos;
0332     size_t avail, buffer_size = dfse->size;
0333     u64 lpos_64;
0334 
0335     /* make sure we know about any failures on the DSP side */
0336     priv->dtrace_error = false;
0337 
0338     /* check pos and count */
0339     if (lpos < 0)
0340         return -EINVAL;
0341     if (!count)
0342         return 0;
0343 
0344     /* check for buffer wrap and count overflow */
0345     lpos_64 = lpos;
0346     lpos = do_div(lpos_64, buffer_size);
0347 
0348     /* get available count based on current host offset */
0349     avail = sof_wait_dtrace_avail(sdev, lpos, buffer_size);
0350     if (priv->dtrace_error) {
0351         dev_err(sdev->dev, "trace IO error\n");
0352         return -EIO;
0353     }
0354 
0355     /* no new trace data */
0356     if (!avail)
0357         return 0;
0358 
0359     /* make sure count is <= avail */
0360     if (count > avail)
0361         count = avail;
0362 
0363     /*
0364      * make sure that all trace data is available for the CPU as the trace
0365      * data buffer might be allocated from non consistent memory.
0366      * Note: snd_dma_buffer_sync() is called for normal audio playback and
0367      *   capture streams also.
0368      */
0369     snd_dma_buffer_sync(&priv->dmatb, SNDRV_DMA_SYNC_CPU);
0370     /* copy available trace data to debugfs */
0371     rem = copy_to_user(buffer, ((u8 *)(dfse->buf) + lpos), count);
0372     if (rem)
0373         return -EFAULT;
0374 
0375     *ppos += count;
0376 
0377     /* move debugfs reading position */
0378     return count;
0379 }
0380 
0381 static int dfsentry_dtrace_release(struct inode *inode, struct file *file)
0382 {
0383     struct snd_sof_dfsentry *dfse = inode->i_private;
0384     struct snd_sof_dev *sdev = dfse->sdev;
0385     struct sof_dtrace_priv *priv = sdev->fw_trace_data;
0386 
0387     /* avoid duplicate traces at next open */
0388     if (priv->dtrace_state != SOF_DTRACE_ENABLED)
0389         sof_dtrace_set_host_offset(priv, 0);
0390 
0391     return 0;
0392 }
0393 
0394 static const struct file_operations sof_dfs_dtrace_fops = {
0395     .open = simple_open,
0396     .read = dfsentry_dtrace_read,
0397     .llseek = default_llseek,
0398     .release = dfsentry_dtrace_release,
0399 };
0400 
0401 static int debugfs_create_dtrace(struct snd_sof_dev *sdev)
0402 {
0403     struct sof_dtrace_priv *priv;
0404     struct snd_sof_dfsentry *dfse;
0405     int ret;
0406 
0407     if (!sdev)
0408         return -EINVAL;
0409 
0410     priv = sdev->fw_trace_data;
0411 
0412     ret = debugfs_create_trace_filter(sdev);
0413     if (ret < 0)
0414         dev_warn(sdev->dev, "failed to create filter debugfs file: %d", ret);
0415 
0416     dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
0417     if (!dfse)
0418         return -ENOMEM;
0419 
0420     dfse->type = SOF_DFSENTRY_TYPE_BUF;
0421     dfse->buf = priv->dmatb.area;
0422     dfse->size = priv->dmatb.bytes;
0423     dfse->sdev = sdev;
0424 
0425     debugfs_create_file("trace", 0444, sdev->debugfs_root, dfse,
0426                 &sof_dfs_dtrace_fops);
0427 
0428     return 0;
0429 }
0430 
0431 static int ipc3_dtrace_enable(struct snd_sof_dev *sdev)
0432 {
0433     struct sof_dtrace_priv *priv = sdev->fw_trace_data;
0434     struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
0435     struct sof_ipc_fw_version *v = &ready->version;
0436     struct sof_ipc_dma_trace_params_ext params;
0437     struct sof_ipc_reply ipc_reply;
0438     int ret;
0439 
0440     if (!sdev->fw_trace_is_supported)
0441         return 0;
0442 
0443     if (priv->dtrace_state == SOF_DTRACE_ENABLED || !priv->dma_trace_pages)
0444         return -EINVAL;
0445 
0446     if (priv->dtrace_state == SOF_DTRACE_STOPPED)
0447         goto start;
0448 
0449     /* set IPC parameters */
0450     params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG;
0451     /* PARAMS_EXT is only supported from ABI 3.7.0 onwards */
0452     if (v->abi_version >= SOF_ABI_VER(3, 7, 0)) {
0453         params.hdr.size = sizeof(struct sof_ipc_dma_trace_params_ext);
0454         params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS_EXT;
0455         params.timestamp_ns = ktime_get(); /* in nanosecond */
0456     } else {
0457         params.hdr.size = sizeof(struct sof_ipc_dma_trace_params);
0458         params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS;
0459     }
0460     params.buffer.phy_addr = priv->dmatp.addr;
0461     params.buffer.size = priv->dmatb.bytes;
0462     params.buffer.pages = priv->dma_trace_pages;
0463     params.stream_tag = 0;
0464 
0465     sof_dtrace_set_host_offset(priv, 0);
0466     priv->dtrace_draining = false;
0467 
0468     ret = sof_dtrace_host_init(sdev, &priv->dmatb, &params);
0469     if (ret < 0) {
0470         dev_err(sdev->dev, "Host dtrace init failed: %d\n", ret);
0471         return ret;
0472     }
0473     dev_dbg(sdev->dev, "stream_tag: %d\n", params.stream_tag);
0474 
0475     /* send IPC to the DSP */
0476     priv->dtrace_state = SOF_DTRACE_INITIALIZING;
0477     ret = sof_ipc_tx_message(sdev->ipc, &params, sizeof(params), &ipc_reply, sizeof(ipc_reply));
0478     if (ret < 0) {
0479         dev_err(sdev->dev, "can't set params for DMA for trace %d\n", ret);
0480         goto trace_release;
0481     }
0482 
0483 start:
0484     priv->dtrace_state = SOF_DTRACE_ENABLED;
0485 
0486     ret = sof_dtrace_host_trigger(sdev, SNDRV_PCM_TRIGGER_START);
0487     if (ret < 0) {
0488         dev_err(sdev->dev, "Host dtrace trigger start failed: %d\n", ret);
0489         goto trace_release;
0490     }
0491 
0492     return 0;
0493 
0494 trace_release:
0495     priv->dtrace_state = SOF_DTRACE_DISABLED;
0496     sof_dtrace_host_release(sdev);
0497     return ret;
0498 }
0499 
0500 static int ipc3_dtrace_init(struct snd_sof_dev *sdev)
0501 {
0502     struct sof_dtrace_priv *priv;
0503     int ret;
0504 
0505     /* dtrace is only supported with SOF_IPC */
0506     if (sdev->pdata->ipc_type != SOF_IPC)
0507         return -EOPNOTSUPP;
0508 
0509     if (sdev->fw_trace_data) {
0510         dev_err(sdev->dev, "fw_trace_data has been already allocated\n");
0511         return -EBUSY;
0512     }
0513 
0514     priv = devm_kzalloc(sdev->dev, sizeof(*priv), GFP_KERNEL);
0515     if (!priv)
0516         return -ENOMEM;
0517 
0518     sdev->fw_trace_data = priv;
0519 
0520     /* set false before start initialization */
0521     priv->dtrace_state = SOF_DTRACE_DISABLED;
0522 
0523     /* allocate trace page table buffer */
0524     ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev,
0525                   PAGE_SIZE, &priv->dmatp);
0526     if (ret < 0) {
0527         dev_err(sdev->dev, "can't alloc page table for trace %d\n", ret);
0528         return ret;
0529     }
0530 
0531     /* allocate trace data buffer */
0532     ret = snd_dma_alloc_dir_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->dev,
0533                       DMA_FROM_DEVICE, DMA_BUF_SIZE_FOR_TRACE,
0534                       &priv->dmatb);
0535     if (ret < 0) {
0536         dev_err(sdev->dev, "can't alloc buffer for trace %d\n", ret);
0537         goto page_err;
0538     }
0539 
0540     /* create compressed page table for audio firmware */
0541     ret = snd_sof_create_page_table(sdev->dev, &priv->dmatb,
0542                     priv->dmatp.area, priv->dmatb.bytes);
0543     if (ret < 0)
0544         goto table_err;
0545 
0546     priv->dma_trace_pages = ret;
0547     dev_dbg(sdev->dev, "dma_trace_pages: %d\n", priv->dma_trace_pages);
0548 
0549     if (sdev->first_boot) {
0550         ret = debugfs_create_dtrace(sdev);
0551         if (ret < 0)
0552             goto table_err;
0553     }
0554 
0555     init_waitqueue_head(&priv->trace_sleep);
0556 
0557     ret = ipc3_dtrace_enable(sdev);
0558     if (ret < 0)
0559         goto table_err;
0560 
0561     return 0;
0562 table_err:
0563     priv->dma_trace_pages = 0;
0564     snd_dma_free_pages(&priv->dmatb);
0565 page_err:
0566     snd_dma_free_pages(&priv->dmatp);
0567     return ret;
0568 }
0569 
0570 int ipc3_dtrace_posn_update(struct snd_sof_dev *sdev,
0571                 struct sof_ipc_dma_trace_posn *posn)
0572 {
0573     struct sof_dtrace_priv *priv = sdev->fw_trace_data;
0574 
0575     if (!sdev->fw_trace_is_supported)
0576         return 0;
0577 
0578     if (trace_pos_update_expected(priv) &&
0579         sof_dtrace_set_host_offset(priv, posn->host_offset))
0580         wake_up(&priv->trace_sleep);
0581 
0582     if (posn->overflow != 0)
0583         dev_err(sdev->dev,
0584             "DSP trace buffer overflow %u bytes. Total messages %d\n",
0585             posn->overflow, posn->messages);
0586 
0587     return 0;
0588 }
0589 
0590 /* an error has occurred within the DSP that prevents further trace */
0591 static void ipc3_dtrace_fw_crashed(struct snd_sof_dev *sdev)
0592 {
0593     struct sof_dtrace_priv *priv = sdev->fw_trace_data;
0594 
0595     if (priv->dtrace_state == SOF_DTRACE_ENABLED) {
0596         priv->dtrace_error = true;
0597         wake_up(&priv->trace_sleep);
0598     }
0599 }
0600 
0601 static void ipc3_dtrace_release(struct snd_sof_dev *sdev, bool only_stop)
0602 {
0603     struct sof_dtrace_priv *priv = sdev->fw_trace_data;
0604     struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
0605     struct sof_ipc_fw_version *v = &ready->version;
0606     struct sof_ipc_cmd_hdr hdr;
0607     struct sof_ipc_reply ipc_reply;
0608     int ret;
0609 
0610     if (!sdev->fw_trace_is_supported || priv->dtrace_state == SOF_DTRACE_DISABLED)
0611         return;
0612 
0613     ret = sof_dtrace_host_trigger(sdev, SNDRV_PCM_TRIGGER_STOP);
0614     if (ret < 0)
0615         dev_err(sdev->dev, "Host dtrace trigger stop failed: %d\n", ret);
0616     priv->dtrace_state = SOF_DTRACE_STOPPED;
0617 
0618     /*
0619      * stop and free trace DMA in the DSP. TRACE_DMA_FREE is only supported from
0620      * ABI 3.20.0 onwards
0621      */
0622     if (v->abi_version >= SOF_ABI_VER(3, 20, 0)) {
0623         hdr.size = sizeof(hdr);
0624         hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_FREE;
0625 
0626         ret = sof_ipc_tx_message(sdev->ipc, &hdr, hdr.size,
0627                      &ipc_reply, sizeof(ipc_reply));
0628         if (ret < 0)
0629             dev_err(sdev->dev, "DMA_TRACE_FREE failed with error: %d\n", ret);
0630     }
0631 
0632     if (only_stop)
0633         goto out;
0634 
0635     ret = sof_dtrace_host_release(sdev);
0636     if (ret < 0)
0637         dev_err(sdev->dev, "Host dtrace release failed %d\n", ret);
0638 
0639     priv->dtrace_state = SOF_DTRACE_DISABLED;
0640 
0641 out:
0642     priv->dtrace_draining = true;
0643     wake_up(&priv->trace_sleep);
0644 }
0645 
0646 static void ipc3_dtrace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state)
0647 {
0648     ipc3_dtrace_release(sdev, pm_state.event == SOF_DSP_PM_D0);
0649 }
0650 
0651 static int ipc3_dtrace_resume(struct snd_sof_dev *sdev)
0652 {
0653     return ipc3_dtrace_enable(sdev);
0654 }
0655 
0656 static void ipc3_dtrace_free(struct snd_sof_dev *sdev)
0657 {
0658     struct sof_dtrace_priv *priv = sdev->fw_trace_data;
0659 
0660     /* release trace */
0661     ipc3_dtrace_release(sdev, false);
0662 
0663     if (priv->dma_trace_pages) {
0664         snd_dma_free_pages(&priv->dmatb);
0665         snd_dma_free_pages(&priv->dmatp);
0666         priv->dma_trace_pages = 0;
0667     }
0668 }
0669 
0670 const struct sof_ipc_fw_tracing_ops ipc3_dtrace_ops = {
0671     .init = ipc3_dtrace_init,
0672     .free = ipc3_dtrace_free,
0673     .fw_crashed = ipc3_dtrace_fw_crashed,
0674     .suspend = ipc3_dtrace_suspend,
0675     .resume = ipc3_dtrace_resume,
0676 };