summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurentiu Palcu <laurentiu.palcu@nxp.com>2018-06-25 13:43:05 +0300
committerJason Liu <jason.hui.liu@nxp.com>2019-02-12 10:32:28 +0800
commitcbf778aa33e271baf305e2cb039be1b16a274228 (patch)
tree23beeea23f9adfb7c7500f02f5b837aa3ea1224d
parent5791818fc076c24f661949c3a0f56276552ee2a6 (diff)
MLK-18680-1: drm: imx: dcss: low latency tracing mechanism
This patch adds a DCSS tracing mechanism that introduces as low latency as possible, so that it does not affect timings. Instead of text, 64 bit tags will be logged, together with the system time in nanoseconds. Based on these, post-processing can be done on any PC to compute deltas, delays, missed buffers, etc. Example usage: echo 1 > /sys/module/imx_dcss_core/parameters/tracing gplay-1.0 movie.mpg echo 0 > /sys/module/imx_dcss_core/parameters/tracing To dump the trace: cat /sys/kernel/debug/imx-dcss/dump_trace_log > trace.txt With the help of a scripting language (awk), the trace can then be post-processed and analyzed on the PC. Signed-off-by: Laurentiu Palcu <laurentiu.palcu@nxp.com>
-rw-r--r--drivers/gpu/imx/dcss/dcss-common.c136
-rw-r--r--include/video/imx-dcss.h22
2 files changed, 158 insertions, 0 deletions
diff --git a/drivers/gpu/imx/dcss/dcss-common.c b/drivers/gpu/imx/dcss/dcss-common.c
index 757c5b9b729f..e55da669292a 100644
--- a/drivers/gpu/imx/dcss/dcss-common.c
+++ b/drivers/gpu/imx/dcss/dcss-common.c
@@ -326,6 +326,125 @@ static void dcss_clocks_enable(struct dcss_soc *dcss, bool en)
#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/sched/clock.h>
+
+static unsigned int dcss_tracing;
+EXPORT_SYMBOL(dcss_tracing);
+
+module_param_named(tracing, dcss_tracing, int, 0600);
+
+struct dcss_trace {
+ u64 seq;
+ u64 time_ns;
+ u64 tag;
+ struct list_head node;
+};
+
+static LIST_HEAD(dcss_trace_list);
+static spinlock_t lock;
+static u64 seq;
+
+void dcss_trace_write(u64 tag)
+{
+ struct dcss_trace *trace;
+ unsigned long flags;
+
+ if (!dcss_tracing)
+ return;
+
+ trace = kzalloc(sizeof(*trace), GFP_KERNEL);
+ if (!trace)
+ return;
+
+ trace->time_ns = local_clock();
+ trace->tag = tag;
+ trace->seq = seq;
+
+ spin_lock_irqsave(&lock, flags);
+ list_add_tail(&trace->node, &dcss_trace_list);
+ seq++;
+ spin_unlock_irqrestore(&lock, flags);
+}
+EXPORT_SYMBOL(dcss_trace_write);
+
+static int dcss_trace_dump_show(struct seq_file *s, void *data)
+{
+ struct dcss_trace *trace = data;
+
+ if (trace)
+ seq_printf(s, "%lld %lld %lld\n",
+ trace->seq, trace->time_ns, trace->tag);
+
+ return 0;
+}
+
+static void *dcss_trace_dump_start(struct seq_file *s, loff_t *pos)
+{
+ unsigned long flags;
+ struct dcss_trace *trace = NULL;
+
+ spin_lock_irqsave(&lock, flags);
+ if (!list_empty(&dcss_trace_list)) {
+ trace = list_first_entry(&dcss_trace_list,
+ struct dcss_trace, node);
+ goto exit;
+ }
+
+exit:
+ spin_unlock_irqrestore(&lock, flags);
+ return trace;
+}
+
+static void *dcss_trace_dump_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ unsigned long flags;
+ struct dcss_trace *next_trace = NULL;
+ struct dcss_trace *trace = v;
+
+ ++*pos;
+ spin_lock_irqsave(&lock, flags);
+ if (!list_is_last(&trace->node, &dcss_trace_list)) {
+ next_trace = list_entry(trace->node.next,
+ struct dcss_trace, node);
+ goto exit;
+ }
+
+exit:
+ spin_unlock_irqrestore(&lock, flags);
+ return next_trace;
+}
+
+static void dcss_trace_dump_stop(struct seq_file *s, void *v)
+{
+ unsigned long flags;
+ struct dcss_trace *trace, *tmp;
+ struct dcss_trace *last_trace = v;
+
+ spin_lock_irqsave(&lock, flags);
+ if (!list_empty(&dcss_trace_list)) {
+ list_for_each_entry_safe(trace, tmp, &dcss_trace_list, node) {
+ if (last_trace && trace->seq >= last_trace->seq)
+ break;
+
+ list_del(&trace->node);
+ kfree(trace);
+ }
+ }
+ spin_unlock_irqrestore(&lock, flags);
+}
+
+static const struct seq_operations dcss_trace_seq_ops = {
+ .start = dcss_trace_dump_start,
+ .next = dcss_trace_dump_next,
+ .stop = dcss_trace_dump_stop,
+ .show = dcss_trace_dump_show,
+};
+
+static int dcss_trace_dump_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &dcss_trace_seq_ops);
+}
static int dcss_dump_regs_show(struct seq_file *s, void *data)
{
@@ -374,6 +493,13 @@ static const struct file_operations dcss_dump_ctx_fops = {
.release = single_release,
};
+static const struct file_operations dcss_dump_trace_fops = {
+ .open = dcss_trace_dump_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
static void dcss_debugfs_init(struct dcss_soc *dcss)
{
struct dentry *d, *root;
@@ -392,6 +518,11 @@ static void dcss_debugfs_init(struct dcss_soc *dcss)
if (!d)
goto err;
+ d = debugfs_create_file("dump_trace_log", 0444, root, dcss,
+ &dcss_dump_trace_fops);
+ if (!d)
+ goto err;
+
return;
err:
@@ -401,6 +532,11 @@ err:
static void dcss_debugfs_init(struct dcss_soc *dcss)
{
}
+
+void dcss_trace_write(u64 tag)
+{
+}
+EXPORT_SYMBOL(dcss_trace_write);
#endif
static void dcss_bus_freq(struct dcss_soc *dcss, bool en)
diff --git a/include/video/imx-dcss.h b/include/video/imx-dcss.h
index 2c6476da2231..2236fe59b56f 100644
--- a/include/video/imx-dcss.h
+++ b/include/video/imx-dcss.h
@@ -30,6 +30,28 @@ int dcss_vblank_irq_get(struct dcss_soc *dcss);
void dcss_vblank_irq_enable(struct dcss_soc *dcss, bool en);
void dcss_vblank_irq_clear(struct dcss_soc *dcss);
enum dcss_color_space dcss_drm_fourcc_to_colorspace(u32 drm_fourcc);
+void dcss_trace_write(u64 tag);
+
+
+#define TAG(x) ((x) << 56)
+
+#define TRACE_COMMON TAG(0LL)
+#define TRACE_DTG TAG(1LL)
+#define TRACE_SS TAG(2LL)
+#define TRACE_DPR TAG(3LL)
+#define TRACE_SCALER TAG(4LL)
+#define TRACE_CTXLD TAG(5LL)
+#define TRACE_DEC400D TAG(6LL)
+#define TRACE_DTRC TAG(7LL)
+#define TRACE_HDR10 TAG(8LL)
+#define TRACE_RDSRC TAG(9LL)
+#define TRACE_WRSCL TAG(10LL)
+
+#define TRACE_DRM_CRTC TAG(11LL)
+#define TRACE_DRM_PLANE TAG(12LL)
+#define TRACE_DRM_KMS TAG(13LL)
+
+#define dcss_trace_module(mod_tag, val) dcss_trace_write((mod_tag) | (val));
/* BLKCTL */
void dcss_blkctl_hdmi_secure_src_en(struct dcss_soc *dcss);