aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/sound/sof/ipc4/header.h5
-rw-r--r--sound/soc/codecs/hdmi-codec.c10
-rw-r--r--sound/soc/fsl/fsl_asrc.c29
-rw-r--r--sound/soc/sof/Makefile3
-rw-r--r--sound/soc/sof/intel/apl.c2
-rw-r--r--sound/soc/sof/intel/cnl.c2
-rw-r--r--sound/soc/sof/intel/icl.c30
-rw-r--r--sound/soc/sof/intel/mtl.c2
-rw-r--r--sound/soc/sof/intel/tgl.c2
-rw-r--r--sound/soc/sof/ipc4-loader.c2
-rw-r--r--sound/soc/sof/ipc4-mtrace.c643
-rw-r--r--sound/soc/sof/ipc4-priv.h18
-rw-r--r--sound/soc/sof/ipc4.c22
13 files changed, 753 insertions, 17 deletions
diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h
index a795deacc2ea..99efe0ef1784 100644
--- a/include/sound/sof/ipc4/header.h
+++ b/include/sound/sof/ipc4/header.h
@@ -427,6 +427,11 @@ struct sof_ipc4_dx_state_info {
#define SOF_IPC4_NOTIFICATION_TYPE_GET(x) (((x) & SOF_IPC4_NOTIFICATION_TYPE_MASK) >> \
SOF_IPC4_NOTIFICATION_TYPE_SHIFT)
+#define SOF_IPC4_LOG_CORE_SHIFT 12
+#define SOF_IPC4_LOG_CORE_MASK GENMASK(15, 12)
+#define SOF_IPC4_LOG_CORE_GET(x) (((x) & SOF_IPC4_LOG_CORE_MASK) >> \
+ SOF_IPC4_LOG_CORE_SHIFT)
+
/* Value of notification type field - must fit into 8 bits */
enum sof_ipc4_notification_type {
/* Phrase detected (notification from WoV module) */
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c
index 863e679d2ac1..0b1cdb2d6049 100644
--- a/sound/soc/codecs/hdmi-codec.c
+++ b/sound/soc/codecs/hdmi-codec.c
@@ -823,7 +823,7 @@ static int hdmi_dai_probe(struct snd_soc_dai *dai)
if (ret)
return ret;
- daifmt = kzalloc(sizeof(*daifmt), GFP_KERNEL);
+ daifmt = devm_kzalloc(dai->dev, sizeof(*daifmt), GFP_KERNEL);
if (!daifmt)
return -ENOMEM;
@@ -890,17 +890,10 @@ static int hdmi_dai_spdif_probe(struct snd_soc_dai *dai)
return 0;
}
-static int hdmi_codec_dai_remove(struct snd_soc_dai *dai)
-{
- kfree(dai->playback_dma_data);
- return 0;
-}
-
static const struct snd_soc_dai_driver hdmi_i2s_dai = {
.name = "i2s-hifi",
.id = DAI_ID_I2S,
.probe = hdmi_dai_probe,
- .remove = hdmi_codec_dai_remove,
.playback = {
.stream_name = "I2S Playback",
.channels_min = 2,
@@ -925,7 +918,6 @@ static const struct snd_soc_dai_driver hdmi_spdif_dai = {
.name = "spdif-hifi",
.id = DAI_ID_SPDIF,
.probe = hdmi_dai_spdif_probe,
- .remove = hdmi_codec_dai_remove,
.playback = {
.stream_name = "SPDIF Playback",
.channels_min = 2,
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index aa5edf32d988..936aef5d2767 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -20,6 +20,7 @@
#define IDEAL_RATIO_DECIMAL_DEPTH 26
#define DIVIDER_NUM 64
+#define INIT_RETRY_NUM 50
#define pair_err(fmt, ...) \
dev_err(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
@@ -27,6 +28,9 @@
#define pair_dbg(fmt, ...) \
dev_dbg(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
+#define pair_warn(fmt, ...) \
+ dev_warn(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
+
/* Corresponding to process_option */
static unsigned int supported_asrc_rate[] = {
5512, 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
@@ -579,7 +583,7 @@ static void fsl_asrc_start_pair(struct fsl_asrc_pair *pair)
{
struct fsl_asrc *asrc = pair->asrc;
enum asrc_pair_index index = pair->index;
- int reg, retry = 10, i;
+ int reg, retry = INIT_RETRY_NUM, i;
/* Enable the current pair */
regmap_update_bits(asrc->regmap, REG_ASRCTR,
@@ -592,6 +596,10 @@ static void fsl_asrc_start_pair(struct fsl_asrc_pair *pair)
reg &= ASRCFG_INIRQi_MASK(index);
} while (!reg && --retry);
+ /* NOTE: Doesn't treat initialization timeout as an error */
+ if (!retry)
+ pair_warn("initialization isn't finished\n");
+
/* Make the input fifo to ASRC STALL level */
regmap_read(asrc->regmap, REG_ASRCNCR, &reg);
for (i = 0; i < pair->channels * 4; i++)
@@ -1257,6 +1265,7 @@ static int fsl_asrc_runtime_resume(struct device *dev)
{
struct fsl_asrc *asrc = dev_get_drvdata(dev);
struct fsl_asrc_priv *asrc_priv = asrc->private;
+ int reg, retry = INIT_RETRY_NUM;
int i, ret;
u32 asrctr;
@@ -1295,6 +1304,24 @@ static int fsl_asrc_runtime_resume(struct device *dev)
regmap_update_bits(asrc->regmap, REG_ASRCTR,
ASRCTR_ASRCEi_ALL_MASK, asrctr);
+ /* Wait for status of initialization for all enabled pairs */
+ do {
+ udelay(5);
+ regmap_read(asrc->regmap, REG_ASRCFG, &reg);
+ reg = (reg >> ASRCFG_INIRQi_SHIFT(0)) & 0x7;
+ } while ((reg != ((asrctr >> ASRCTR_ASRCEi_SHIFT(0)) & 0x7)) && --retry);
+
+ /*
+ * NOTE: Doesn't treat initialization timeout as an error
+ * Some of the pairs may success, then still can continue.
+ */
+ if (!retry) {
+ for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) {
+ if ((asrctr & ASRCTR_ASRCEi_MASK(i)) && !(reg & (1 << i)))
+ dev_warn(dev, "Pair %c initialization isn't finished\n", 'A' + i);
+ }
+ }
+
return 0;
disable_asrck_clk:
diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile
index 9a74ed116ed9..eab7cc53f71a 100644
--- a/sound/soc/sof/Makefile
+++ b/sound/soc/sof/Makefile
@@ -9,7 +9,8 @@ snd-sof-objs += ipc3.o ipc3-loader.o ipc3-topology.o ipc3-control.o ipc3-pcm.o\
ipc3-dtrace.o
endif
ifneq ($(CONFIG_SND_SOC_SOF_INTEL_IPC4),)
-snd-sof-objs += ipc4.o ipc4-loader.o ipc4-topology.o ipc4-control.o ipc4-pcm.o
+snd-sof-objs += ipc4.o ipc4-loader.o ipc4-topology.o ipc4-control.o ipc4-pcm.o\
+ ipc4-mtrace.o
endif
# SOF client support
diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c
index 084c245a9522..295df44be271 100644
--- a/sound/soc/sof/intel/apl.c
+++ b/sound/soc/sof/intel/apl.c
@@ -57,6 +57,8 @@ int sof_apl_ops_init(struct snd_sof_dev *sdev)
ipc4_data = sdev->private;
ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
+ ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_1_5;
+
/* doorbell */
sof_apl_ops.irq_thread = hda_dsp_ipc4_irq_thread;
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c
index a064453f0bc3..0bb91df27280 100644
--- a/sound/soc/sof/intel/cnl.c
+++ b/sound/soc/sof/intel/cnl.c
@@ -366,6 +366,8 @@ int sof_cnl_ops_init(struct snd_sof_dev *sdev)
ipc4_data = sdev->private;
ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
+ ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_1_8;
+
/* doorbell */
sof_cnl_ops.irq_thread = cnl_ipc4_irq_thread;
diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c
index 4e37b7fe0627..59ce3132fada 100644
--- a/sound/soc/sof/intel/icl.c
+++ b/sound/soc/sof/intel/icl.c
@@ -13,6 +13,7 @@
#include <linux/kconfig.h>
#include <linux/export.h>
#include <linux/bits.h>
+#include "../ipc4-priv.h"
#include "../ops.h"
#include "hda.h"
#include "hda-ipc.h"
@@ -106,11 +107,32 @@ int sof_icl_ops_init(struct snd_sof_dev *sdev)
/* probe/remove/shutdown */
sof_icl_ops.shutdown = hda_dsp_shutdown;
- /* doorbell */
- sof_icl_ops.irq_thread = cnl_ipc_irq_thread;
+ if (sdev->pdata->ipc_type == SOF_IPC) {
+ /* doorbell */
+ sof_icl_ops.irq_thread = cnl_ipc_irq_thread;
- /* ipc */
- sof_icl_ops.send_msg = cnl_ipc_send_msg;
+ /* ipc */
+ sof_icl_ops.send_msg = cnl_ipc_send_msg;
+ }
+
+ if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
+ struct sof_ipc4_fw_data *ipc4_data;
+
+ sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL);
+ if (!sdev->private)
+ return -ENOMEM;
+
+ ipc4_data = sdev->private;
+ ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
+
+ ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2;
+
+ /* doorbell */
+ sof_icl_ops.irq_thread = cnl_ipc4_irq_thread;
+
+ /* ipc */
+ sof_icl_ops.send_msg = cnl_ipc4_send_msg;
+ }
/* debug */
sof_icl_ops.debug_map = icl_dsp_debugfs;
diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c
index 96239ebb1eed..1cc1398336e1 100644
--- a/sound/soc/sof/intel/mtl.c
+++ b/sound/soc/sof/intel/mtl.c
@@ -764,6 +764,8 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev)
ipc4_data = sdev->private;
ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
+ ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2;
+
/* set DAI ops */
hda_set_dai_drv_ops(sdev, &sof_mtl_ops);
diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c
index 6dfb4786c782..017bf331ed5a 100644
--- a/sound/soc/sof/intel/tgl.c
+++ b/sound/soc/sof/intel/tgl.c
@@ -80,6 +80,8 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev)
ipc4_data = sdev->private;
ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
+ ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2;
+
/* doorbell */
sof_tgl_ops.irq_thread = cnl_ipc4_irq_thread;
diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c
index 8bd2132b4f41..c678f05d0ef5 100644
--- a/sound/soc/sof/ipc4-loader.c
+++ b/sound/soc/sof/ipc4-loader.c
@@ -157,6 +157,7 @@ static int sof_ipc4_validate_firmware(struct snd_sof_dev *sdev)
static int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev)
{
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
const struct sof_ipc_ops *iops = sdev->ipc->ops;
struct sof_ipc4_fw_version *fw_ver;
struct sof_ipc4_tuple *tuple;
@@ -200,6 +201,7 @@ static int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev)
break;
case SOF_IPC4_FW_CFG_TRACE_LOG_BYTES:
dev_vdbg(sdev->dev, "Trace log size: %u\n", *tuple->value);
+ ipc4_data->mtrace_log_bytes = *tuple->value;
break;
default:
break;
diff --git a/sound/soc/sof/ipc4-mtrace.c b/sound/soc/sof/ipc4-mtrace.c
new file mode 100644
index 000000000000..9c7080041d08
--- /dev/null
+++ b/sound/soc/sof/ipc4-mtrace.c
@@ -0,0 +1,643 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+
+#include <linux/debugfs.h>
+#include <linux/sched/signal.h>
+#include <sound/sof/ipc4/header.h>
+#include "sof-priv.h"
+#include "ipc4-priv.h"
+
+/*
+ * debug info window is organized in 16 (equal sized) pages:
+ *
+ * ------------------------
+ * | Page0 - descriptors |
+ * ------------------------
+ * | Page1 - slot0 |
+ * ------------------------
+ * | Page2 - slot1 |
+ * ------------------------
+ * | ... |
+ * ------------------------
+ * | Page14 - slot13 |
+ * ------------------------
+ * | Page15 - slot14 |
+ * ------------------------
+ *
+ * The slot size == page size
+ *
+ * The first page contains descriptors for the remaining 15 cores
+ * The slot descriptor is:
+ * u32 res_id;
+ * u32 type;
+ * u32 vma;
+ *
+ * Log buffer slots have the following layout:
+ * u32 host_read_ptr;
+ * u32 dsp_write_ptr;
+ * u8 buffer[];
+ *
+ * The two pointers are offsets within the buffer.
+ */
+
+#define SOF_MTRACE_DESCRIPTOR_SIZE 12 /* 3 x u32 */
+
+#define FW_EPOCH_DELTA 11644473600LL
+
+#define INVALID_SLOT_OFFSET 0xffffffff
+#define MAX_ALLOWED_LIBRARIES 16
+#define MAX_MTRACE_SLOTS 15
+
+#define SOF_MTRACE_PAGE_SIZE 0x1000
+#define SOF_MTRACE_SLOT_SIZE SOF_MTRACE_PAGE_SIZE
+
+/* debug log slot types */
+#define SOF_MTRACE_SLOT_UNUSED 0x00000000
+#define SOF_MTRACE_SLOT_CRITICAL_LOG 0x54524300 /* byte 0: core ID */
+#define SOF_MTRACE_SLOT_DEBUG_LOG 0x474f4c00 /* byte 0: core ID */
+#define SOF_MTRACE_SLOT_GDB_STUB 0x42444700
+#define SOF_MTRACE_SLOT_TELEMETRY 0x4c455400
+#define SOF_MTRACE_SLOT_BROKEN 0x44414544
+ /* for debug and critical types */
+#define SOF_MTRACE_SLOT_CORE_MASK GENMASK(7, 0)
+#define SOF_MTRACE_SLOT_TYPE_MASK GENMASK(31, 8)
+
+#define DEFAULT_AGING_TIMER_PERIOD_MS 0x100
+#define DEFAULT_FIFO_FULL_TIMER_PERIOD_MS 0x1000
+
+/* ipc4 log level and source definitions for logs_priorities_mask */
+#define SOF_MTRACE_LOG_LEVEL_CRITICAL BIT(0)
+#define SOF_MTRACE_LOG_LEVEL_ERROR BIT(1)
+#define SOF_MTRACE_LOG_LEVEL_WARNING BIT(2)
+#define SOF_MTRACE_LOG_LEVEL_INFO BIT(3)
+#define SOF_MTRACE_LOG_LEVEL_VERBOSE BIT(4)
+#define SOF_MTRACE_LOG_SOURCE_INFRA BIT(5) /* log source 0 */
+#define SOF_MTRACE_LOG_SOURCE_HAL BIT(6)
+#define SOF_MTRACE_LOG_SOURCE_MODULE BIT(7)
+#define SOF_MTRACE_LOG_SOURCE_AUDIO BIT(8)
+#define SOF_MTRACE_LOG_SOURCE_SCHEDULER BIT(9)
+#define SOF_MTRACE_LOG_SOURCE_ULP_INFRA BIT(10)
+#define SOF_MTRACE_LOG_SOURCE_ULP_MODULE BIT(11)
+#define SOF_MTRACE_LOG_SOURCE_VISION BIT(12) /* log source 7 */
+#define DEFAULT_LOGS_PRIORITIES_MASK (SOF_MTRACE_LOG_LEVEL_CRITICAL | \
+ SOF_MTRACE_LOG_LEVEL_ERROR | \
+ SOF_MTRACE_LOG_LEVEL_WARNING | \
+ SOF_MTRACE_LOG_LEVEL_INFO | \
+ SOF_MTRACE_LOG_SOURCE_INFRA | \
+ SOF_MTRACE_LOG_SOURCE_HAL | \
+ SOF_MTRACE_LOG_SOURCE_MODULE | \
+ SOF_MTRACE_LOG_SOURCE_AUDIO)
+
+struct sof_log_state_info {
+ u32 aging_timer_period;
+ u32 fifo_full_timer_period;
+ u32 enable;
+ u32 logs_priorities_mask[MAX_ALLOWED_LIBRARIES];
+} __packed;
+
+enum sof_mtrace_state {
+ SOF_MTRACE_DISABLED,
+ SOF_MTRACE_INITIALIZING,
+ SOF_MTRACE_ENABLED,
+};
+
+struct sof_mtrace_core_data {
+ struct snd_sof_dev *sdev;
+
+ int id;
+ u32 slot_offset;
+ void *log_buffer;
+ u32 host_read_ptr;
+ u32 dsp_write_ptr;
+ /* pos update IPC arrived before the slot offset is known, queried */
+ bool delayed_pos_update;
+ wait_queue_head_t trace_sleep;
+};
+
+struct sof_mtrace_priv {
+ struct snd_sof_dev *sdev;
+ enum sof_mtrace_state mtrace_state;
+ struct sof_log_state_info state_info;
+
+ struct sof_mtrace_core_data cores[];
+};
+
+static int sof_ipc4_mtrace_dfs_open(struct inode *inode, struct file *file)
+{
+ struct sof_mtrace_core_data *core_data = inode->i_private;
+ int ret;
+
+ ret = debugfs_file_get(file->f_path.dentry);
+ if (unlikely(ret))
+ return ret;
+
+ core_data->log_buffer = kmalloc(SOF_MTRACE_SLOT_SIZE, GFP_KERNEL);
+ if (!core_data->log_buffer) {
+ debugfs_file_put(file->f_path.dentry);
+ return -ENOMEM;
+ }
+
+ ret = simple_open(inode, file);
+ if (ret) {
+ kfree(core_data->log_buffer);
+ debugfs_file_put(file->f_path.dentry);
+ }
+
+ return ret;
+}
+
+static bool sof_wait_mtrace_avail(struct sof_mtrace_core_data *core_data)
+{
+ wait_queue_entry_t wait;
+
+ /* data immediately available */
+ if (core_data->host_read_ptr != core_data->dsp_write_ptr)
+ return true;
+
+ /* wait for available trace data from FW */
+ init_waitqueue_entry(&wait, current);
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&core_data->trace_sleep, &wait);
+
+ if (!signal_pending(current)) {
+ /* set timeout to max value, no error code */
+ schedule_timeout(MAX_SCHEDULE_TIMEOUT);
+ }
+ remove_wait_queue(&core_data->trace_sleep, &wait);
+
+ if (core_data->host_read_ptr != core_data->dsp_write_ptr)
+ return true;
+
+ return false;
+}
+
+static ssize_t sof_ipc4_mtrace_dfs_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct sof_mtrace_core_data *core_data = file->private_data;
+ u32 log_buffer_offset, log_buffer_size, read_ptr, write_ptr;
+ struct snd_sof_dev *sdev = core_data->sdev;
+ struct sof_mtrace_priv *priv = sdev->fw_trace_data;
+ void *log_buffer = core_data->log_buffer;
+ loff_t lpos = *ppos;
+ u32 avail;
+ int ret;
+
+ /* check pos and count */
+ if (lpos < 0)
+ return -EINVAL;
+ if (!count || count < sizeof(avail))
+ return 0;
+
+ /* get available count based on current host offset */
+ if (!sof_wait_mtrace_avail(core_data)) {
+ /* No data available */
+ avail = 0;
+ if (copy_to_user(buffer, &avail, sizeof(avail)))
+ return -EFAULT;
+
+ return 0;
+ }
+
+ if (core_data->slot_offset == INVALID_SLOT_OFFSET)
+ return 0;
+
+ /* The log data buffer starts after the two pointer in the slot */
+ log_buffer_offset = core_data->slot_offset + (sizeof(u32) * 2);
+ /* The log data size excludes the pointers */
+ log_buffer_size = SOF_MTRACE_SLOT_SIZE - (sizeof(u32) * 2);
+
+ read_ptr = core_data->host_read_ptr;
+ write_ptr = core_data->dsp_write_ptr;
+
+ if (read_ptr < write_ptr)
+ avail = write_ptr - read_ptr;
+ else
+ avail = log_buffer_size - read_ptr + write_ptr;
+
+ if (!avail)
+ return 0;
+
+ if (avail > log_buffer_size)
+ avail = log_buffer_size;
+
+ /* Need space for the initial u32 of the avail */
+ if (avail > count - sizeof(avail))
+ avail = count - sizeof(avail);
+
+ if (sof_debug_check_flag(SOF_DBG_PRINT_DMA_POSITION_UPDATE_LOGS))
+ dev_dbg(sdev->dev,
+ "core%d, host read: %#x, dsp write: %#x, avail: %#x\n",
+ core_data->id, read_ptr, write_ptr, avail);
+
+ if (read_ptr < write_ptr) {
+ /* Read data between read pointer and write pointer */
+ sof_mailbox_read(sdev, log_buffer_offset + read_ptr, log_buffer, avail);
+ } else {
+ /* read from read pointer to end of the slot */
+ sof_mailbox_read(sdev, log_buffer_offset + read_ptr, log_buffer,
+ avail - write_ptr);
+ /* read from slot start to write pointer */
+ if (write_ptr)
+ sof_mailbox_read(sdev, log_buffer_offset,
+ (u8 *)(log_buffer) + avail - write_ptr,
+ write_ptr);
+ }
+
+ /* first write the number of bytes we have gathered */
+ ret = copy_to_user(buffer, &avail, sizeof(avail));
+ if (ret)
+ return -EFAULT;
+
+ /* Followed by the data itself */
+ ret = copy_to_user(buffer + sizeof(avail), log_buffer, avail);
+ if (ret)
+ return -EFAULT;
+
+ /* Update the host_read_ptr in the slot for this core */
+ read_ptr += avail;
+ if (read_ptr >= log_buffer_size)
+ read_ptr -= log_buffer_size;
+ sof_mailbox_write(sdev, core_data->slot_offset, &read_ptr, sizeof(read_ptr));
+
+ /* Only update the host_read_ptr if mtrace is enabled */
+ if (priv->mtrace_state != SOF_MTRACE_DISABLED)
+ core_data->host_read_ptr = read_ptr;
+
+ /*
+ * Ask for a new buffer from user space for the next chunk, not
+ * streaming due to the heading number of bytes value.
+ */
+ *ppos += count;
+
+ return count;
+}
+
+static int sof_ipc4_mtrace_dfs_release(struct inode *inode, struct file *file)
+{
+ struct sof_mtrace_core_data *core_data = inode->i_private;
+
+ debugfs_file_put(file->f_path.dentry);
+
+ kfree(core_data->log_buffer);
+
+ return 0;
+}
+
+static const struct file_operations sof_dfs_mtrace_fops = {
+ .open = sof_ipc4_mtrace_dfs_open,
+ .read = sof_ipc4_mtrace_dfs_read,
+ .llseek = default_llseek,
+ .release = sof_ipc4_mtrace_dfs_release,
+
+ .owner = THIS_MODULE,
+};
+
+static ssize_t sof_ipc4_priority_mask_dfs_read(struct file *file, char __user *to,
+ size_t count, loff_t *ppos)
+{
+ struct sof_mtrace_priv *priv = file->private_data;
+ int i, ret, offset, remaining;
+ char *buf;
+
+ /*
+ * one entry (14 char + new line = 15):
+ * " 0: 000001ef"
+ *
+ * 16 * 15 + 1 = 241
+ */
+ buf = kzalloc(241, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ for (i = 0; i < MAX_ALLOWED_LIBRARIES; i++) {
+ offset = strlen(buf);
+ remaining = 241 - offset;
+ snprintf(buf + offset, remaining, "%2d: 0x%08x\n", i,
+ priv->state_info.logs_priorities_mask[i]);
+ }
+
+ ret = simple_read_from_buffer(to, count, ppos, buf, strlen(buf));
+
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t sof_ipc4_priority_mask_dfs_write(struct file *file,
+ const char __user *from,
+ size_t count, loff_t *ppos)
+{
+ struct sof_mtrace_priv *priv = file->private_data;
+ int id, ret;
+ char *buf;
+ u32 mask;
+
+ /*
+ * To update Nth mask entry, write:
+ * "N,0x1234" or "N,1234" to the debugfs file
+ * The mask will be interpreted as hexadecimal number
+ */
+ buf = memdup_user_nul(from, count);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ ret = sscanf(buf, "%d,0x%x", &id, &mask);
+ if (ret != 2) {
+ ret = sscanf(buf, "%d,%x", &id, &mask);
+ if (ret != 2) {
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (id >= MAX_ALLOWED_LIBRARIES) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ priv->state_info.logs_priorities_mask[id] = mask;
+ ret = count;
+
+out:
+ kfree(buf);
+ return ret;
+}
+
+static const struct file_operations sof_dfs_priority_mask_fops = {
+ .open = simple_open,
+ .read = sof_ipc4_priority_mask_dfs_read,
+ .write = sof_ipc4_priority_mask_dfs_write,
+ .llseek = default_llseek,
+
+ .owner = THIS_MODULE,
+};
+
+static int mtrace_debugfs_create(struct snd_sof_dev *sdev)
+{
+ struct sof_mtrace_priv *priv = sdev->fw_trace_data;
+ struct dentry *dfs_root;
+ char dfs_name[100];
+ int i;
+
+ dfs_root = debugfs_create_dir("mtrace", sdev->debugfs_root);
+ if (IS_ERR_OR_NULL(dfs_root))
+ return 0;
+
+ /* Create files for the logging parameters */
+ debugfs_create_u32("aging_timer_period", 0644, dfs_root,
+ &priv->state_info.aging_timer_period);
+ debugfs_create_u32("fifo_full_timer_period", 0644, dfs_root,
+ &priv->state_info.fifo_full_timer_period);
+ debugfs_create_file("logs_priorities_mask", 0644, dfs_root, priv,
+ &sof_dfs_priority_mask_fops);
+
+ /* Separate log files per core */
+ for (i = 0; i < sdev->num_cores; i++) {
+ snprintf(dfs_name, sizeof(dfs_name), "core%d", i);
+ debugfs_create_file(dfs_name, 0444, dfs_root, &priv->cores[i],
+ &sof_dfs_mtrace_fops);
+ }
+
+ return 0;
+}
+
+static int ipc4_mtrace_enable(struct snd_sof_dev *sdev)
+{
+ struct sof_mtrace_priv *priv = sdev->fw_trace_data;
+ const struct sof_ipc_ops *iops = sdev->ipc->ops;
+ struct sof_ipc4_msg msg;
+ u64 system_time;
+ ktime_t kt;
+ int ret;
+
+ if (priv->mtrace_state != SOF_MTRACE_DISABLED)
+ return 0;
+
+ msg.primary = SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
+ msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+ msg.primary |= SOF_IPC4_MOD_ID(SOF_IPC4_MOD_INIT_BASEFW_MOD_ID);
+ msg.primary |= SOF_IPC4_MOD_INSTANCE(SOF_IPC4_MOD_INIT_BASEFW_INSTANCE_ID);
+ msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_FW_PARAM_SYSTEM_TIME);
+
+ /* The system time is in usec, UTC, epoch is 1601-01-01 00:00:00 */
+ kt = ktime_add_us(ktime_get_real(), FW_EPOCH_DELTA * USEC_PER_SEC);
+ system_time = ktime_to_us(kt);
+ msg.data_size = sizeof(system_time);
+ msg.data_ptr = &system_time;
+ ret = iops->set_get_data(sdev, &msg, msg.data_size, true);
+ if (ret)
+ return ret;
+
+ msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_FW_PARAM_ENABLE_LOGS);
+
+ priv->state_info.enable = 1;
+
+ msg.data_size = sizeof(priv->state_info);
+ msg.data_ptr = &priv->state_info;
+
+ priv->mtrace_state = SOF_MTRACE_INITIALIZING;
+ ret = iops->set_get_data(sdev, &msg, msg.data_size, true);
+ if (ret) {
+ priv->mtrace_state = SOF_MTRACE_DISABLED;
+ return ret;
+ }
+
+ priv->mtrace_state = SOF_MTRACE_ENABLED;
+
+ return 0;
+}
+
+static void ipc4_mtrace_disable(struct snd_sof_dev *sdev)
+{
+ struct sof_mtrace_priv *priv = sdev->fw_trace_data;
+ const struct sof_ipc_ops *iops = sdev->ipc->ops;
+ struct sof_ipc4_msg msg;
+ int i;
+
+ if (priv->mtrace_state == SOF_MTRACE_DISABLED)
+ return;
+
+ msg.primary = SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
+ msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+ msg.primary |= SOF_IPC4_MOD_ID(SOF_IPC4_MOD_INIT_BASEFW_MOD_ID);
+ msg.primary |= SOF_IPC4_MOD_INSTANCE(SOF_IPC4_MOD_INIT_BASEFW_INSTANCE_ID);
+ msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_FW_PARAM_ENABLE_LOGS);
+
+ priv->state_info.enable = 0;
+
+ msg.data_size = sizeof(priv->state_info);
+ msg.data_ptr = &priv->state_info;
+ iops->set_get_data(sdev, &msg, msg.data_size, true);
+
+ priv->mtrace_state = SOF_MTRACE_DISABLED;
+
+ for (i = 0; i < sdev->num_cores; i++) {
+ struct sof_mtrace_core_data *core_data = &priv->cores[i];
+
+ core_data->host_read_ptr = 0;
+ core_data->dsp_write_ptr = 0;
+ wake_up(&core_data->trace_sleep);
+ }
+}
+
+/*
+ * Each DSP core logs to a dedicated slot.
+ * Parse the slot descriptors at debug_box offset to find the debug log slots
+ * and map them to cores.
+ * There are 15 slots and therefore 15 descriptors to check (MAX_MTRACE_SLOTS)
+ */
+static void sof_mtrace_find_core_slots(struct snd_sof_dev *sdev)
+{
+ struct sof_mtrace_priv *priv = sdev->fw_trace_data;
+ struct sof_mtrace_core_data *core_data;
+ u32 slot_desc_type_offset, type, core;
+ int i;
+
+ for (i = 0; i < MAX_MTRACE_SLOTS; i++) {
+ /* The type is the second u32 in the slot descriptor */
+ slot_desc_type_offset = sdev->debug_box.offset;
+ slot_desc_type_offset += SOF_MTRACE_DESCRIPTOR_SIZE * i + sizeof(u32);
+ sof_mailbox_read(sdev, slot_desc_type_offset, &type, sizeof(type));
+
+ if ((type & SOF_MTRACE_SLOT_TYPE_MASK) == SOF_MTRACE_SLOT_DEBUG_LOG) {
+ core = type & SOF_MTRACE_SLOT_CORE_MASK;
+
+ if (core >= sdev->num_cores) {
+ dev_dbg(sdev->dev, "core%u is invalid for slot%d\n",
+ core, i);
+ continue;
+ }
+
+ core_data = &priv->cores[core];
+ /*
+ * The area reserved for descriptors have the same size
+ * as a slot.
+ * In other words: slot0 starts at
+ * debug_box + SOF_MTRACE_SLOT_SIZE offset
+ */
+ core_data->slot_offset = sdev->debug_box.offset;
+ core_data->slot_offset += SOF_MTRACE_SLOT_SIZE * (i + 1);
+ dev_dbg(sdev->dev, "slot%d is used for core%u\n", i, core);
+ if (core_data->delayed_pos_update) {
+ sof_ipc4_mtrace_update_pos(sdev, core);
+ core_data->delayed_pos_update = false;
+ }
+ } else if (type) {
+ dev_dbg(sdev->dev, "slot%d is not a log slot (%#x)\n", i, type);
+ }
+ }
+}
+
+static int ipc4_mtrace_init(struct snd_sof_dev *sdev)
+{
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ struct sof_mtrace_priv *priv;
+ int i, ret;
+
+ if (sdev->fw_trace_data) {
+ dev_err(sdev->dev, "fw_trace_data has been already allocated\n");
+ return -EBUSY;
+ }
+
+ if (!ipc4_data->mtrace_log_bytes ||
+ ipc4_data->mtrace_type != SOF_IPC4_MTRACE_INTEL_CAVS_2) {
+ sdev->fw_trace_is_supported = false;
+ return 0;
+ }
+
+ priv = devm_kzalloc(sdev->dev, struct_size(priv, cores, sdev->num_cores),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ sdev->fw_trace_data = priv;
+
+ /* Set initial values for mtrace parameters */
+ priv->state_info.aging_timer_period = DEFAULT_AGING_TIMER_PERIOD_MS;
+ priv->state_info.fifo_full_timer_period = DEFAULT_FIFO_FULL_TIMER_PERIOD_MS;
+ /* Only enable basefw logs initially (index 0 is always basefw) */
+ priv->state_info.logs_priorities_mask[0] = DEFAULT_LOGS_PRIORITIES_MASK;
+
+ for (i = 0; i < sdev->num_cores; i++) {
+ struct sof_mtrace_core_data *core_data = &priv->cores[i];
+
+ init_waitqueue_head(&core_data->trace_sleep);
+ core_data->sdev = sdev;
+ core_data->id = i;
+ }
+
+ ret = ipc4_mtrace_enable(sdev);
+ if (ret) {
+ /*
+ * Mark firmware tracing as not supported and return 0 to not
+ * block the whole audio stack
+ */
+ sdev->fw_trace_is_supported = false;
+ dev_dbg(sdev->dev, "initialization failed, fw tracing is disabled\n");
+ return 0;
+ }
+
+ sof_mtrace_find_core_slots(sdev);
+
+ ret = mtrace_debugfs_create(sdev);
+ if (ret)
+ ipc4_mtrace_disable(sdev);
+
+ return ret;
+}
+
+static void ipc4_mtrace_free(struct snd_sof_dev *sdev)
+{
+ ipc4_mtrace_disable(sdev);
+}
+
+int sof_ipc4_mtrace_update_pos(struct snd_sof_dev *sdev, int core)
+{
+ struct sof_mtrace_priv *priv = sdev->fw_trace_data;
+ struct sof_mtrace_core_data *core_data;
+
+ if (!sdev->fw_trace_is_supported ||
+ priv->mtrace_state == SOF_MTRACE_DISABLED)
+ return 0;
+
+ if (core >= sdev->num_cores)
+ return -EINVAL;
+
+ core_data = &priv->cores[core];
+
+ if (core_data->slot_offset == INVALID_SLOT_OFFSET) {
+ core_data->delayed_pos_update = true;
+ return 0;
+ }
+
+ /* Read out the dsp_write_ptr from the slot for this core */
+ sof_mailbox_read(sdev, core_data->slot_offset + sizeof(u32),
+ &core_data->dsp_write_ptr, 4);
+ core_data->dsp_write_ptr -= core_data->dsp_write_ptr % 4;
+
+ if (sof_debug_check_flag(SOF_DBG_PRINT_DMA_POSITION_UPDATE_LOGS))
+ dev_dbg(sdev->dev, "core%d, host read: %#x, dsp write: %#x",
+ core, core_data->host_read_ptr, core_data->dsp_write_ptr);
+
+ wake_up(&core_data->trace_sleep);
+
+ return 0;
+}
+
+static int ipc4_mtrace_resume(struct snd_sof_dev *sdev)
+{
+ return ipc4_mtrace_enable(sdev);
+}
+
+static void ipc4_mtrace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state)
+{
+ ipc4_mtrace_disable(sdev);
+}
+
+const struct sof_ipc_fw_tracing_ops ipc4_mtrace_ops = {
+ .init = ipc4_mtrace_init,
+ .free = ipc4_mtrace_free,
+ .suspend = ipc4_mtrace_suspend,
+ .resume = ipc4_mtrace_resume,
+};
diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h
index 9492fe1796c2..e3b8484a2f1f 100644
--- a/sound/soc/sof/ipc4-priv.h
+++ b/sound/soc/sof/ipc4-priv.h
@@ -13,18 +13,33 @@
#include <sound/sof/ext_manifest4.h>
#include "sof-priv.h"
+/* The DSP window indices are fixed */
+#define SOF_IPC4_OUTBOX_WINDOW_IDX 1
+#define SOF_IPC4_DEBUG_WINDOW_IDX 2
+
+enum sof_ipc4_mtrace_type {
+ SOF_IPC4_MTRACE_NOT_AVAILABLE = 0,
+ SOF_IPC4_MTRACE_INTEL_CAVS_1_5,
+ SOF_IPC4_MTRACE_INTEL_CAVS_1_8,
+ SOF_IPC4_MTRACE_INTEL_CAVS_2,
+};
+
/**
* struct sof_ipc4_fw_data - IPC4-specific data
* @manifest_fw_hdr_offset: FW header offset in the manifest
* @num_fw_modules : Number of modules in base FW
* @fw_modules: Array of base FW modules
* @nhlt: NHLT table either from the BIOS or the topology manifest
+ * @mtrace_type: mtrace type supported on the booted platform
+ * @mtrace_log_bytes: log bytes as reported by the firmware via fw_config reply
*/
struct sof_ipc4_fw_data {
u32 manifest_fw_hdr_offset;
int num_fw_modules;
void *fw_modules;
void *nhlt;
+ enum sof_ipc4_mtrace_type mtrace_type;
+ u32 mtrace_log_bytes;
};
/**
@@ -45,7 +60,8 @@ extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops;
extern const struct sof_ipc_tplg_ops ipc4_tplg_ops;
extern const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops;
extern const struct sof_ipc_pcm_ops ipc4_pcm_ops;
+extern const struct sof_ipc_fw_tracing_ops ipc4_mtrace_ops;
int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state);
-
+int sof_ipc4_mtrace_update_pos(struct snd_sof_dev *sdev, int core);
#endif
diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c
index 432b812bdf9c..0d830020556d 100644
--- a/sound/soc/sof/ipc4.c
+++ b/sound/soc/sof/ipc4.c
@@ -205,6 +205,11 @@ static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_ms
/* Notification message */
u32 notif = SOF_IPC4_NOTIFICATION_TYPE_GET(msg->primary);
+ /* Do not print log buffer notification if not desired */
+ if (notif == SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS &&
+ !sof_debug_check_flag(SOF_DBG_PRINT_DMA_POSITION_UPDATE_LOGS))
+ return;
+
if (notif < SOF_IPC4_NOTIFY_TYPE_LAST)
str2 = ipc4_dbg_notification_type[notif];
if (!str2)
@@ -234,6 +239,13 @@ static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_ms
static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_msg *msg,
bool data_size_valid)
{
+ /* Do not print log buffer notification if not desired */
+ if (!sof_debug_check_flag(SOF_DBG_PRINT_DMA_POSITION_UPDATE_LOGS) &&
+ !SOF_IPC4_MSG_IS_MODULE_MSG(msg->primary) &&
+ SOF_IPC4_MSG_TYPE_GET(msg->primary) == SOF_IPC4_GLB_NOTIFICATION &&
+ SOF_IPC4_NOTIFICATION_TYPE_GET(msg->primary) == SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS)
+ return;
+
if (data_size_valid && msg->data_size)
dev_dbg(dev, "%s: %#x|%#x [data size: %zu]\n", text,
msg->primary, msg->extension, msg->data_size);
@@ -525,7 +537,7 @@ static int ipc4_fw_ready(struct snd_sof_dev *sdev, struct sof_ipc4_msg *ipc4_msg
return inbox_offset;
}
inbox_size = SOF_IPC4_MSG_MAX_SIZE;
- outbox_offset = snd_sof_dsp_get_window_offset(sdev, 1);
+ outbox_offset = snd_sof_dsp_get_window_offset(sdev, SOF_IPC4_OUTBOX_WINDOW_IDX);
outbox_size = SOF_IPC4_MSG_MAX_SIZE;
sdev->dsp_box.offset = inbox_offset;
@@ -533,10 +545,14 @@ static int ipc4_fw_ready(struct snd_sof_dev *sdev, struct sof_ipc4_msg *ipc4_msg
sdev->host_box.offset = outbox_offset;
sdev->host_box.size = outbox_size;
+ sdev->debug_box.offset = snd_sof_dsp_get_window_offset(sdev,
+ SOF_IPC4_DEBUG_WINDOW_IDX);
+
dev_dbg(sdev->dev, "mailbox upstream 0x%x - size 0x%x\n",
inbox_offset, inbox_size);
dev_dbg(sdev->dev, "mailbox downstream 0x%x - size 0x%x\n",
outbox_offset, outbox_size);
+ dev_dbg(sdev->dev, "debug box 0x%x\n", sdev->debug_box.offset);
return sof_ipc4_init_msg_memory(sdev);
}
@@ -573,6 +589,9 @@ static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev)
case SOF_IPC4_NOTIFY_RESOURCE_EVENT:
data_size = sizeof(struct sof_ipc4_notify_resource_data);
break;
+ case SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS:
+ sof_ipc4_mtrace_update_pos(sdev, SOF_IPC4_LOG_CORE_GET(ipc4_msg->primary));
+ break;
default:
dev_dbg(sdev->dev, "Unhandled DSP message: %#x|%#x\n",
ipc4_msg->primary, ipc4_msg->extension);
@@ -646,4 +665,5 @@ const struct sof_ipc_ops ipc4_ops = {
.fw_loader = &ipc4_loader_ops,
.tplg = &ipc4_tplg_ops,
.pcm = &ipc4_pcm_ops,
+ .fw_tracing = &ipc4_mtrace_ops,
};