MLK-18680-1: drm: imx: dcss: low latency tracing mechanism
authorLaurentiu Palcu <laurentiu.palcu@nxp.com>
Mon, 25 Jun 2018 10:43:05 +0000 (13:43 +0300)
committerLeonard Crestez <leonard.crestez@nxp.com>
Wed, 17 Apr 2019 23:51:34 +0000 (02:51 +0300)
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>
drivers/gpu/imx/dcss/dcss-common.c
include/video/imx-dcss.h

index 757c5b9..e55da66 100644 (file)
@@ -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)
index c0d5dd8..ee90bf2 100644 (file)
@@ -45,6 +45,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);