summaryrefslogtreecommitdiffstats
path: root/sound/soc/sof/trace.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-05-09 08:26:55 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2019-05-09 08:26:55 -0700
commite57ccca1ba33e1d92cc3bbf8b6304a46948844b0 (patch)
treea988a1f7d1d3250f57761dbea365482300a7b3b2 /sound/soc/sof/trace.c
parenta2d635decbfa9c1e4ae15cb05b68b2559f7f827c (diff)
parented97c988bdc61ab6fb5d1f5f02a709844557b68f (diff)
downloadlinux-e57ccca1ba33e1d92cc3bbf8b6304a46948844b0.tar.bz2
Merge tag 'sound-5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai: "The most significant changes at this cycle are the Sound Open Firmware support from Intel for the common DSP framework along with its support for Intel platforms. It's a door opened to a real "free" firmware (in the sense of FOSS), and other parties show interests in it. In addition to SOF, we've got a bunch of updates and fixes as usual. Some highlights are below. ALSA core: - Cleanups and fixes in ALSA timer code to cover some races spotted by syzkaller - Cleanups and fixes in ALSA sequencer code to cover some races, again unsurprisingly, spotted by syzkaller - Optimize the common page allocation helper with alloc_pages_exact() ASoC: - Add SOF core support, as well as Intel SOF platform support - Generic card driver improvements: support for MCLK/sample rate ratio and pin switches - A big set of improvements to TLV320AIC32x4 drivers - New drivers for Freescale audio mixers, several Intel machines, several Mediatek machines, Meson G12A, Spreadtrum compressed audio and DMA devices HD-audio: - A few Realtek codec fixes for reducing pop noises - Quirks for Chromebooks - Workaround for faulty connection report on AMD/Nvidia HDMI Others: - A quirk for Focusrite Scarlett Solo USB-audio - Add support for MOTU 8pre FireWire - 24bit sample format support in aloop - GUS patch format support (finally, over a decade) in native emux synth code" * tag 'sound-5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (375 commits) ASoC: SOF: Fix unused variable warnings ALSA: line6: toneport: Fix broken usage of timer for delayed execution ALSA: aica: Fix a long-time build breakage ALSA: hda/realtek - Support low power consumption for ALC256 ASoC: stm32: i2s: update pcm hardware constraints ASoC: codec: hdac_hdmi: no checking monitor in hw_params ASoC: mediatek: mt6358: save PGA for mixer control ASoC: mediatek: mt6358: save output volume for mixer controls ASoC: mediatek: mt6358: initialize setting when ramping volume ASoC: SOF: core: fix undefined nocodec reference ASoC: SOF: xtensa: fix undefined references ASoC: SOF: Propagate sof_get_ctrl_copy_params() error properly ALSA: hdea/realtek - Headset fixup for System76 Gazelle (gaze14) ALSA: hda/intel: add CometLake PCI IDs ALSA: hda/realtek - Support low power consumption for ALC295 ASoC: rockchip: Fix an uninitialized variable compile warning ASoC: SOF: Fix a compile warning with CONFIG_PCI=n ASoC: da7219: Fix a compile warning at CONFIG_COMMON_CLK=n ASoC: sound/soc/sof/: fix kconfig dependency warning ASoC: stm32: spdifrx: change trace level on iec control ...
Diffstat (limited to 'sound/soc/sof/trace.c')
-rw-r--r--sound/soc/sof/trace.c297
1 files changed, 297 insertions, 0 deletions
diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c
new file mode 100644
index 000000000000..d588e4b70fad
--- /dev/null
+++ b/sound/soc/sof/trace.c
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2018 Intel Corporation. All rights reserved.
+//
+// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
+//
+
+#include <linux/debugfs.h>
+#include <linux/sched/signal.h>
+#include "sof-priv.h"
+#include "ops.h"
+
+static size_t sof_wait_trace_avail(struct snd_sof_dev *sdev,
+ loff_t pos, size_t buffer_size)
+{
+ wait_queue_entry_t wait;
+ loff_t host_offset = READ_ONCE(sdev->host_offset);
+
+ /*
+ * If host offset is less than local pos, it means write pointer of
+ * host DMA buffer has been wrapped. We should output the trace data
+ * at the end of host DMA buffer at first.
+ */
+ if (host_offset < pos)
+ return buffer_size - pos;
+
+ /* If there is available trace data now, it is unnecessary to wait. */
+ if (host_offset > pos)
+ return host_offset - pos;
+
+ /* wait for available trace data from FW */
+ init_waitqueue_entry(&wait, current);
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&sdev->trace_sleep, &wait);
+
+ if (!signal_pending(current)) {
+ /* set timeout to max value, no error code */
+ schedule_timeout(MAX_SCHEDULE_TIMEOUT);
+ }
+ remove_wait_queue(&sdev->trace_sleep, &wait);
+
+ /* return bytes available for copy */
+ host_offset = READ_ONCE(sdev->host_offset);
+ if (host_offset < pos)
+ return buffer_size - pos;
+
+ return host_offset - pos;
+}
+
+static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct snd_sof_dfsentry *dfse = file->private_data;
+ struct snd_sof_dev *sdev = dfse->sdev;
+ unsigned long rem;
+ loff_t lpos = *ppos;
+ size_t avail, buffer_size = dfse->size;
+ u64 lpos_64;
+
+ /* make sure we know about any failures on the DSP side */
+ sdev->dtrace_error = false;
+
+ /* check pos and count */
+ if (lpos < 0)
+ return -EINVAL;
+ if (!count)
+ return 0;
+
+ /* check for buffer wrap and count overflow */
+ lpos_64 = lpos;
+ lpos = do_div(lpos_64, buffer_size);
+
+ if (count > buffer_size - lpos) /* min() not used to avoid sparse warnings */
+ count = buffer_size - lpos;
+
+ /* get available count based on current host offset */
+ avail = sof_wait_trace_avail(sdev, lpos, buffer_size);
+ if (sdev->dtrace_error) {
+ dev_err(sdev->dev, "error: trace IO error\n");
+ return -EIO;
+ }
+
+ /* make sure count is <= avail */
+ count = avail > count ? count : avail;
+
+ /* copy available trace data to debugfs */
+ rem = copy_to_user(buffer, ((u8 *)(dfse->buf) + lpos), count);
+ if (rem)
+ return -EFAULT;
+
+ *ppos += count;
+
+ /* move debugfs reading position */
+ return count;
+}
+
+static const struct file_operations sof_dfs_trace_fops = {
+ .open = simple_open,
+ .read = sof_dfsentry_trace_read,
+ .llseek = default_llseek,
+};
+
+static int trace_debugfs_create(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_dfsentry *dfse;
+
+ if (!sdev)
+ return -EINVAL;
+
+ dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
+ if (!dfse)
+ return -ENOMEM;
+
+ dfse->type = SOF_DFSENTRY_TYPE_BUF;
+ dfse->buf = sdev->dmatb.area;
+ dfse->size = sdev->dmatb.bytes;
+ dfse->sdev = sdev;
+
+ dfse->dfsentry = debugfs_create_file("trace", 0444, sdev->debugfs_root,
+ dfse, &sof_dfs_trace_fops);
+ if (!dfse->dfsentry) {
+ /* can't rely on debugfs, only log error and keep going */
+ dev_err(sdev->dev,
+ "error: cannot create debugfs entry for trace\n");
+ }
+
+ return 0;
+}
+
+int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev)
+{
+ struct sof_ipc_dma_trace_params params;
+ struct sof_ipc_reply ipc_reply;
+ int ret;
+
+ if (sdev->dtrace_is_enabled || !sdev->dma_trace_pages)
+ return -EINVAL;
+
+ /* set IPC parameters */
+ params.hdr.size = sizeof(params);
+ params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_PARAMS;
+ params.buffer.phy_addr = sdev->dmatp.addr;
+ params.buffer.size = sdev->dmatb.bytes;
+ params.buffer.pages = sdev->dma_trace_pages;
+ params.stream_tag = 0;
+
+ sdev->host_offset = 0;
+
+ ret = snd_sof_dma_trace_init(sdev, &params.stream_tag);
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "error: fail in snd_sof_dma_trace_init %d\n", ret);
+ return ret;
+ }
+ dev_dbg(sdev->dev, "stream_tag: %d\n", params.stream_tag);
+
+ /* send IPC to the DSP */
+ ret = sof_ipc_tx_message(sdev->ipc,
+ params.hdr.cmd, &params, sizeof(params),
+ &ipc_reply, sizeof(ipc_reply));
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "error: can't set params for DMA for trace %d\n", ret);
+ goto trace_release;
+ }
+
+ ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_START);
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "error: snd_sof_dma_trace_trigger: start: %d\n", ret);
+ goto trace_release;
+ }
+
+ sdev->dtrace_is_enabled = true;
+
+ return 0;
+
+trace_release:
+ snd_sof_dma_trace_release(sdev);
+ return ret;
+}
+
+int snd_sof_init_trace(struct snd_sof_dev *sdev)
+{
+ int ret;
+
+ /* set false before start initialization */
+ sdev->dtrace_is_enabled = false;
+
+ /* allocate trace page table buffer */
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev,
+ PAGE_SIZE, &sdev->dmatp);
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "error: can't alloc page table for trace %d\n", ret);
+ return ret;
+ }
+
+ /* allocate trace data buffer */
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->dev,
+ DMA_BUF_SIZE_FOR_TRACE, &sdev->dmatb);
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "error: can't alloc buffer for trace %d\n", ret);
+ goto page_err;
+ }
+
+ /* create compressed page table for audio firmware */
+ ret = snd_sof_create_page_table(sdev, &sdev->dmatb, sdev->dmatp.area,
+ sdev->dmatb.bytes);
+ if (ret < 0)
+ goto table_err;
+
+ sdev->dma_trace_pages = ret;
+ dev_dbg(sdev->dev, "dma_trace_pages: %d\n", sdev->dma_trace_pages);
+
+ if (sdev->first_boot) {
+ ret = trace_debugfs_create(sdev);
+ if (ret < 0)
+ goto table_err;
+ }
+
+ init_waitqueue_head(&sdev->trace_sleep);
+
+ ret = snd_sof_init_trace_ipc(sdev);
+ if (ret < 0)
+ goto table_err;
+
+ return 0;
+table_err:
+ sdev->dma_trace_pages = 0;
+ snd_dma_free_pages(&sdev->dmatb);
+page_err:
+ snd_dma_free_pages(&sdev->dmatp);
+ return ret;
+}
+EXPORT_SYMBOL(snd_sof_init_trace);
+
+int snd_sof_trace_update_pos(struct snd_sof_dev *sdev,
+ struct sof_ipc_dma_trace_posn *posn)
+{
+ if (sdev->dtrace_is_enabled && sdev->host_offset != posn->host_offset) {
+ sdev->host_offset = posn->host_offset;
+ wake_up(&sdev->trace_sleep);
+ }
+
+ if (posn->overflow != 0)
+ dev_err(sdev->dev,
+ "error: DSP trace buffer overflow %u bytes. Total messages %d\n",
+ posn->overflow, posn->messages);
+
+ return 0;
+}
+
+/* an error has occurred within the DSP that prevents further trace */
+void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev)
+{
+ if (sdev->dtrace_is_enabled) {
+ dev_err(sdev->dev, "error: waking up any trace sleepers\n");
+ sdev->dtrace_error = true;
+ wake_up(&sdev->trace_sleep);
+ }
+}
+EXPORT_SYMBOL(snd_sof_trace_notify_for_error);
+
+void snd_sof_release_trace(struct snd_sof_dev *sdev)
+{
+ int ret;
+
+ if (!sdev->dtrace_is_enabled)
+ return;
+
+ ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_STOP);
+ if (ret < 0)
+ dev_err(sdev->dev,
+ "error: snd_sof_dma_trace_trigger: stop: %d\n", ret);
+
+ ret = snd_sof_dma_trace_release(sdev);
+ if (ret < 0)
+ dev_err(sdev->dev,
+ "error: fail in snd_sof_dma_trace_release %d\n", ret);
+
+ sdev->dtrace_is_enabled = false;
+}
+EXPORT_SYMBOL(snd_sof_release_trace);
+
+void snd_sof_free_trace(struct snd_sof_dev *sdev)
+{
+ snd_sof_release_trace(sdev);
+
+ snd_dma_free_pages(&sdev->dmatb);
+ snd_dma_free_pages(&sdev->dmatp);
+}
+EXPORT_SYMBOL(snd_sof_free_trace);