From 7195cb14440127f4bcda912dd51cfc2760f9f25e Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Fri, 29 Jun 2012 16:07:08 +0100 Subject: ARM HDLCD: Add useful functions to HDLCD driver for system integration 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 --- drivers/video/arm-hdlcd.c | 90 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 3 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/arm-hdlcd.c b/drivers/video/arm-hdlcd.c index 9b5b21ab136..8d5409db6b1 100644 --- a/drivers/video/arm-hdlcd.c +++ b/drivers/video/arm-hdlcd.c @@ -26,6 +26,10 @@ #include #include #include +#ifdef HDLCD_COUNT_BUFFERUNDERRUNS +#include +#include +#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); } -- cgit v1.2.3