ARM HDLCD: Add useful functions to HDLCD driver for system integration
authorChris Redpath <chris.redpath@arm.com>
Fri, 29 Jun 2012 15:07:08 +0000 (16:07 +0100)
committerJon Medhurst <tixy@linaro.org>
Mon, 1 Jul 2013 10:04:25 +0000 (11:04 +0100)
During TC2 integration a bad config option resulted in HDLCD memory reads
not being serviced often enough. This lead to unsightly screen blanking.

These options allow the developer to count the number of underruns and
to control the color used by HDLCD when an underrun prevents accessing
pixel data. The combination of these two options allow easy diagnosis
of HDLCD underrun conditions.

Signed-off-by: Chris Redpath <chris.redpath@arm.com>
drivers/video/arm-hdlcd.c
include/linux/arm-hdlcd.h

index 9b5b21ab136ffa963edf653cb09ac3c10e8001b2..8d5409db6b17bf5feecae07bf79aaa8bef8e5ec4 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/memblock.h>
 #include <linux/arm-hdlcd.h>
+#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#endif
 
 #include "edid.h"
 
@@ -47,6 +51,63 @@ static struct of_device_id  hdlcd_of_matches[] = {
 /* Framebuffer size.  */
 static unsigned long framebuffer_size;
 
+#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
+static unsigned long buffer_underrun_events;
+static DEFINE_SPINLOCK(hdlcd_underrun_lock);
+
+static void hdlcd_underrun_set(unsigned long val)
+{
+       spin_lock(&hdlcd_underrun_lock);
+       buffer_underrun_events = val;
+       spin_unlock(&hdlcd_underrun_lock);
+}
+
+static unsigned long hdlcd_underrun_get(void)
+{
+       unsigned long val;
+       spin_lock(&hdlcd_underrun_lock);
+       val = buffer_underrun_events;
+       spin_unlock(&hdlcd_underrun_lock);
+       return val;
+}
+
+#ifdef CONFIG_PROC_FS
+static int hdlcd_underrun_show(struct seq_file *m, void *v)
+{
+       unsigned char underrun_string[32];
+       snprintf(underrun_string, 32, "%lu\n", hdlcd_underrun_get());
+       seq_puts(m, underrun_string);
+       return 0;
+}
+
+static int proc_hdlcd_underrun_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, hdlcd_underrun_show, NULL);
+}
+
+static const struct file_operations proc_hdlcd_underrun_operations = {
+       .open           = proc_hdlcd_underrun_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int hdlcd_underrun_init(void)
+{
+       hdlcd_underrun_set(0);
+       proc_create("hdlcd_underrun", 0, NULL, &proc_hdlcd_underrun_operations);
+       return 0;
+}
+static void hdlcd_underrun_close(void)
+{
+       remove_proc_entry("hdlcd_underrun", NULL);
+}
+#else
+static int hdlcd_underrun_init(void) { return 0; }
+static void hdlcd_underrun_close(void) { }
+#endif
+#endif
+
 static char *fb_mode = "1680x1050-32@60\0\0\0\0\0";
 
 static struct fb_var_screeninfo cached_var_screeninfo;
@@ -66,7 +127,6 @@ static struct fb_videomode hdlcd_default_mode = {
        .vmode          = FB_VMODE_NONINTERLACED
 };
 
-
 static inline void hdlcd_enable(struct hdlcd_device *hdlcd)
 {
        dev_dbg(hdlcd->dev, "HDLCD: output enabled\n");
@@ -243,7 +303,12 @@ static int hdlcd_set_par(struct fb_info *info)
        WRITE_HDLCD_REG(HDLCD_REG_H_FRONT_PORCH, hdlcd->fb.var.right_margin - 1);
        WRITE_HDLCD_REG(HDLCD_REG_POLARITIES, polarities);
        WRITE_HDLCD_REG(HDLCD_REG_PIXEL_FORMAT, (bytes_per_pixel - 1) << 3);
+#ifdef HDLCD_RED_DEFAULT_COLOUR
+       WRITE_HDLCD_REG(HDLCD_REG_RED_SELECT, (0x00ff0000 | (hdlcd->fb.var.red.length & 0xf) << 8) \
+                                                                                                         | hdlcd->fb.var.red.offset);
+#else
        WRITE_HDLCD_REG(HDLCD_REG_RED_SELECT, ((hdlcd->fb.var.red.length & 0xf) << 8) | hdlcd->fb.var.red.offset);
+#endif
        WRITE_HDLCD_REG(HDLCD_REG_GREEN_SELECT, ((hdlcd->fb.var.green.length & 0xf) << 8) | hdlcd->fb.var.green.offset);
        WRITE_HDLCD_REG(HDLCD_REG_BLUE_SELECT, ((hdlcd->fb.var.blue.length & 0xf) << 8) | hdlcd->fb.var.blue.offset);
 
@@ -282,7 +347,12 @@ static irqreturn_t hdlcd_irq(int irq, void *data)
 
        /* acknowledge interrupt(s) */
        WRITE_HDLCD_REG(HDLCD_REG_INT_CLEAR, irq_status);
-
+#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
+       if (irq_status & HDLCD_INTERRUPT_UNDERRUN) {
+               /* increment the count */
+               hdlcd_underrun_set(hdlcd_underrun_get() + 1);
+       }
+#endif
        if (irq_status & HDLCD_INTERRUPT_VSYNC) {
                /* disable future VSYNC interrupts */
                WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, irq_mask & ~HDLCD_INTERRUPT_VSYNC);
@@ -514,9 +584,13 @@ static int hdlcd_setup(struct hdlcd_device *hdlcd)
        WRITE_HDLCD_REG(HDLCD_REG_BUS_OPTIONS, HDLCD_BUS_MAX_OUTSTAND | HDLCD_BUS_BURST_16);
        /* Set the framebuffer base to start of allocated memory */
        WRITE_HDLCD_REG(HDLCD_REG_FB_BASE, hdlcd->fb.fix.smem_start);
+#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
+       /* turn on underrun interrupt for counting */
+       WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, HDLCD_INTERRUPT_UNDERRUN);
+#else
        /* Ensure interrupts are disabled */
        WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, 0);
-
+#endif 
        if (!register_framebuffer(&hdlcd->fb)) {
                fb_set_var(&hdlcd->fb, &hdlcd->fb.var);
                clk_enable(hdlcd->clk);
@@ -758,11 +832,21 @@ static struct platform_driver hdlcd_driver = {
 
 static int __init hdlcd_init(void)
 {
+#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
+       int err = platform_driver_register(&hdlcd_driver);
+       if (!err)
+               hdlcd_underrun_init();
+       return err;
+#else
        return platform_driver_register(&hdlcd_driver);
+#endif
 }
 
 void __exit hdlcd_exit(void)
 {
+#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
+       hdlcd_underrun_close();
+#endif
        platform_driver_unregister(&hdlcd_driver);
 }
 
index 085fbec1fec7d4cadf17f6c79a7c406c75346fea..f3f4887ac4fad5c6dc90352cf23c50b54be4c150 100644 (file)
 
 #define NR_PALETTE     256
 
+/* OEMs using HDLCD may wish to enable these settings if
+ * display disruption is apparent and you suspect HDLCD
+ * access to RAM may be starved.
+ */
+/* Turn HDLCD default color red instead of black so
+ * that it's easy to see pixel clock data underruns
+ * (compared to other visual disruption)
+ */
+//#define HDLCD_RED_DEFAULT_COLOUR
+/* Add a counter in the IRQ handler to count buffer underruns
+ * and /proc/hdlcd_underrun to read the counter
+ */
+//#define HDLCD_COUNT_BUFFERUNDERRUNS
+
 struct hdlcd_device {
        struct fb_info          fb;
        struct device           *dev;