video: sh_mobile_lcdcfb: Add wait for vsync.
authorPhil Edworthy <Phil.Edworthy@renesas.com>
Mon, 15 Feb 2010 13:57:49 +0000 (13:57 +0000)
committerPaul Mundt <lethal@linux-sh.org>
Tue, 16 Feb 2010 04:30:27 +0000 (13:30 +0900)
Added FBIO_WAITFORVSYNC ioctl for SH-Mobile devices.
Tested on MS7724 and MigoR boards against 2.6.33-rc7.

Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
drivers/video/sh_mobile_lcdcfb.c
include/video/sh_mobile_lcdc.h

index a69830d..37aeced 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/interrupt.h>
 #include <linux/vmalloc.h>
+#include <linux/ioctl.h>
 #include <video/sh_mobile_lcdc.h>
 #include <asm/atomic.h>
 
@@ -106,6 +107,7 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = {
 #define LDRCNTR_SRC    0x00010000
 #define LDRCNTR_MRS    0x00000002
 #define LDRCNTR_MRC    0x00000001
+#define LDSR_MRS       0x00000100
 
 struct sh_mobile_lcdc_priv;
 struct sh_mobile_lcdc_chan {
@@ -124,6 +126,7 @@ struct sh_mobile_lcdc_chan {
        unsigned long pan_offset;
        unsigned long new_pan_offset;
        wait_queue_head_t frame_end_wait;
+       struct completion vsync_completion;
 };
 
 struct sh_mobile_lcdc_priv {
@@ -366,7 +369,8 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
                }
 
                /* VSYNC End */
-               if (ldintr & LDINTR_VES) {
+               if ((ldintr & LDINTR_VES) &&
+                   (ch->pan_offset != ch->new_pan_offset)) {
                        unsigned long ldrcntr = lcdc_read(priv, _LDRCNTR);
                        /* Set the source address for the next refresh */
                        lcdc_write_chan_mirror(ch, LDSA1R, ch->dma_handle +
@@ -379,6 +383,9 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
                                           ldrcntr ^ LDRCNTR_MRS);
                        ch->pan_offset = ch->new_pan_offset;
                }
+
+               if (ldintr & LDINTR_VES)
+                       complete(&ch->vsync_completion);
        }
 
        return IRQ_HANDLED;
@@ -786,6 +793,43 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
        return 0;
 }
 
+static int sh_mobile_wait_for_vsync(struct fb_info *info)
+{
+       struct sh_mobile_lcdc_chan *ch = info->par;
+       unsigned long ldintr;
+       int ret;
+
+       /* Enable VSync End interrupt */
+       ldintr = lcdc_read(ch->lcdc, _LDINTR);
+       ldintr |= LDINTR_VEE;
+       lcdc_write(ch->lcdc, _LDINTR, ldintr);
+
+       ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion,
+                                                       msecs_to_jiffies(100));
+       if (!ret)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd,
+                      unsigned long arg)
+{
+       int retval;
+
+       switch (cmd) {
+       case FBIO_WAITFORVSYNC:
+               retval = sh_mobile_wait_for_vsync(info);
+               break;
+
+       default:
+               retval = -ENOIOCTLCMD;
+               break;
+       }
+       return retval;
+}
+
+
 static struct fb_ops sh_mobile_lcdc_ops = {
        .owner          = THIS_MODULE,
        .fb_setcolreg   = sh_mobile_lcdc_setcolreg,
@@ -795,6 +839,7 @@ static struct fb_ops sh_mobile_lcdc_ops = {
        .fb_copyarea    = sh_mobile_lcdc_copyarea,
        .fb_imageblit   = sh_mobile_lcdc_imageblit,
        .fb_pan_display = sh_mobile_fb_pan_display,
+       .fb_ioctl       = sh_mobile_ioctl,
 };
 
 static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp)
@@ -962,6 +1007,7 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
                        goto err1;
                }
                init_waitqueue_head(&priv->ch[i].frame_end_wait);
+               init_completion(&priv->ch[i].vsync_completion);
                priv->ch[j].pan_offset = 0;
                priv->ch[j].new_pan_offset = 0;
 
index 2882054..2cc893f 100644 (file)
@@ -34,6 +34,8 @@ enum { LCDC_CLK_BUS, LCDC_CLK_PERIPHERAL, LCDC_CLK_EXTERNAL };
 #define LCDC_FLAGS_HSCNT (1 << 3) /* Disable HSYNC during VBLANK */
 #define LCDC_FLAGS_DWCNT (1 << 4) /* Disable dotclock during blanking */
 
+#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)
+
 struct sh_mobile_lcdc_sys_bus_cfg {
        unsigned long ldmt2r;
        unsigned long ldmt3r;