diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 8678972b00adc6..df8a4698dfa8ff 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -446,6 +446,7 @@ struct snd_sof_ipc { enum sof_dtrace_state { SOF_DTRACE_DISABLED, SOF_DTRACE_STOPPED, + SOF_DTRACE_INITIALIZING, SOF_DTRACE_ENABLED, }; diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index 5d171bf8a5ea1c..52434f3b84f698 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -18,6 +18,15 @@ #define TRACE_FILTER_ELEMENTS_PER_ENTRY 4 #define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024 +static bool trace_pos_update_expected(struct snd_sof_dev *sdev) +{ + if (sdev->dtrace_state == SOF_DTRACE_ENABLED || + sdev->dtrace_state == SOF_DTRACE_INITIALIZING) + return true; + + return false; +} + static int trace_filter_append_elem(struct snd_sof_dev *sdev, uint32_t key, uint32_t value, struct sof_ipc_trace_filter_elem *elem_list, int capacity, int *counter) @@ -233,6 +242,23 @@ static int trace_debugfs_filter_create(struct snd_sof_dev *sdev) return 0; } +static bool sof_trace_set_host_offset(struct snd_sof_dev *sdev, u32 new_offset) +{ + u32 host_offset = READ_ONCE(sdev->host_offset); + + if (host_offset != new_offset) { + /* This is a bit paranoid and unlikely that it is needed */ + u32 ret = cmpxchg(&sdev->host_offset, host_offset, new_offset); + + if (ret == host_offset) { + dev_dbg(sdev->dev, "trace: new host_offset: %#x\n", new_offset); + return true; + } + } + + return false; +} + static size_t sof_trace_avail(struct snd_sof_dev *sdev, loff_t pos, size_t buffer_size) { @@ -263,7 +289,7 @@ static size_t sof_wait_trace_avail(struct snd_sof_dev *sdev, if (ret) return ret; - if (sdev->dtrace_state != SOF_DTRACE_ENABLED && sdev->dtrace_draining) { + if (sdev->dtrace_draining && !trace_pos_update_expected(sdev)) { /* * tracing has ended and all traces have been * read by client, return EOF @@ -296,6 +322,8 @@ static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, size_t avail, buffer_size = dfse->size; u64 lpos_64; + dev_dbg(sdev->dev, "trace: %s: ENTER (count: %zu, lpos: %llu)\n", __func__, count, lpos); + /* make sure we know about any failures on the DSP side */ sdev->dtrace_error = false; @@ -316,6 +344,12 @@ static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, return -EIO; } + /* no new trace data */ + if (!avail) { + dev_dbg(sdev->dev, "trace: nothing to copy\n"); + return 0; + } + /* make sure count is <= avail */ if (count > avail) count = avail; @@ -328,6 +362,8 @@ static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, */ snd_dma_buffer_sync(&sdev->dmatb, SNDRV_DMA_SYNC_CPU); /* copy available trace data to debugfs */ + dev_dbg(sdev->dev, "trace: copy to user from offset : %#llx, count: %#zx\n", + lpos, count); rem = copy_to_user(buffer, ((u8 *)(dfse->buf) + lpos), count); if (rem) return -EFAULT; @@ -338,20 +374,31 @@ static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, return count; } +static int sof_dfsentry_trace_open(struct inode *inode, struct file *file) +{ + struct snd_sof_dfsentry *dfse = inode->i_private; + struct snd_sof_dev *sdev = dfse->sdev; + + dev_dbg(sdev->dev, "trace: %s: ENTER\n", __func__); + + return simple_open(inode, file); +} + static int sof_dfsentry_trace_release(struct inode *inode, struct file *file) { struct snd_sof_dfsentry *dfse = inode->i_private; struct snd_sof_dev *sdev = dfse->sdev; + dev_dbg(sdev->dev, "trace: %s: ENTER\n", __func__); /* avoid duplicate traces at next open */ if (sdev->dtrace_state != SOF_DTRACE_ENABLED) - sdev->host_offset = 0; + sof_trace_set_host_offset(sdev, 0); return 0; } static const struct file_operations sof_dfs_trace_fops = { - .open = simple_open, + .open = sof_dfsentry_trace_open, .read = sof_dfsentry_trace_read, .llseek = default_llseek, .release = sof_dfsentry_trace_release, @@ -417,7 +464,7 @@ static int snd_sof_enable_trace(struct snd_sof_dev *sdev) params.buffer.pages = sdev->dma_trace_pages; params.stream_tag = 0; - sdev->host_offset = 0; + sof_trace_set_host_offset(sdev, 0); sdev->dtrace_draining = false; ret = snd_sof_dma_trace_init(sdev, ¶ms); @@ -429,6 +476,7 @@ static int snd_sof_enable_trace(struct snd_sof_dev *sdev) dev_dbg(sdev->dev, "%s: stream_tag: %d\n", __func__, params.stream_tag); /* send IPC to the DSP */ + sdev->dtrace_state = SOF_DTRACE_INITIALIZING; ret = sof_ipc_tx_message(sdev->ipc, ¶ms, sizeof(params), &ipc_reply, sizeof(ipc_reply)); if (ret < 0) { dev_err(sdev->dev, @@ -437,6 +485,8 @@ static int snd_sof_enable_trace(struct snd_sof_dev *sdev) } start: + sdev->dtrace_state = SOF_DTRACE_ENABLED; + ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_START); if (ret < 0) { dev_err(sdev->dev, @@ -444,8 +494,6 @@ static int snd_sof_enable_trace(struct snd_sof_dev *sdev) goto trace_release; } - sdev->dtrace_state = SOF_DTRACE_ENABLED; - return 0; trace_release: @@ -524,10 +572,9 @@ int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, if (!sdev->dtrace_is_supported) return 0; - if (sdev->dtrace_state == SOF_DTRACE_ENABLED && - sdev->host_offset != posn->host_offset) { - sdev->host_offset = posn->host_offset; - wake_up(&sdev->trace_sleep); + if (trace_pos_update_expected(sdev)) { + if (sof_trace_set_host_offset(sdev, posn->host_offset)) + wake_up(&sdev->trace_sleep); } if (posn->overflow != 0)