aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c48
-rw-r--r--include/video/sh_mobile_lcdc.h2
2 files changed, 49 insertions, 1 deletions
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index a69830d26f7f..37aeced2dc18 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -19,6 +19,7 @@
19#include <linux/dma-mapping.h> 19#include <linux/dma-mapping.h>
20#include <linux/interrupt.h> 20#include <linux/interrupt.h>
21#include <linux/vmalloc.h> 21#include <linux/vmalloc.h>
22#include <linux/ioctl.h>
22#include <video/sh_mobile_lcdc.h> 23#include <video/sh_mobile_lcdc.h>
23#include <asm/atomic.h> 24#include <asm/atomic.h>
24 25
@@ -106,6 +107,7 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = {
106#define LDRCNTR_SRC 0x00010000 107#define LDRCNTR_SRC 0x00010000
107#define LDRCNTR_MRS 0x00000002 108#define LDRCNTR_MRS 0x00000002
108#define LDRCNTR_MRC 0x00000001 109#define LDRCNTR_MRC 0x00000001
110#define LDSR_MRS 0x00000100
109 111
110struct sh_mobile_lcdc_priv; 112struct sh_mobile_lcdc_priv;
111struct sh_mobile_lcdc_chan { 113struct sh_mobile_lcdc_chan {
@@ -124,6 +126,7 @@ struct sh_mobile_lcdc_chan {
124 unsigned long pan_offset; 126 unsigned long pan_offset;
125 unsigned long new_pan_offset; 127 unsigned long new_pan_offset;
126 wait_queue_head_t frame_end_wait; 128 wait_queue_head_t frame_end_wait;
129 struct completion vsync_completion;
127}; 130};
128 131
129struct sh_mobile_lcdc_priv { 132struct sh_mobile_lcdc_priv {
@@ -366,7 +369,8 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
366 } 369 }
367 370
368 /* VSYNC End */ 371 /* VSYNC End */
369 if (ldintr & LDINTR_VES) { 372 if ((ldintr & LDINTR_VES) &&
373 (ch->pan_offset != ch->new_pan_offset)) {
370 unsigned long ldrcntr = lcdc_read(priv, _LDRCNTR); 374 unsigned long ldrcntr = lcdc_read(priv, _LDRCNTR);
371 /* Set the source address for the next refresh */ 375 /* Set the source address for the next refresh */
372 lcdc_write_chan_mirror(ch, LDSA1R, ch->dma_handle + 376 lcdc_write_chan_mirror(ch, LDSA1R, ch->dma_handle +
@@ -379,6 +383,9 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
379 ldrcntr ^ LDRCNTR_MRS); 383 ldrcntr ^ LDRCNTR_MRS);
380 ch->pan_offset = ch->new_pan_offset; 384 ch->pan_offset = ch->new_pan_offset;
381 } 385 }
386
387 if (ldintr & LDINTR_VES)
388 complete(&ch->vsync_completion);
382 } 389 }
383 390
384 return IRQ_HANDLED; 391 return IRQ_HANDLED;
@@ -786,6 +793,43 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
786 return 0; 793 return 0;
787} 794}
788 795
796static int sh_mobile_wait_for_vsync(struct fb_info *info)
797{
798 struct sh_mobile_lcdc_chan *ch = info->par;
799 unsigned long ldintr;
800 int ret;
801
802 /* Enable VSync End interrupt */
803 ldintr = lcdc_read(ch->lcdc, _LDINTR);
804 ldintr |= LDINTR_VEE;
805 lcdc_write(ch->lcdc, _LDINTR, ldintr);
806
807 ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion,
808 msecs_to_jiffies(100));
809 if (!ret)
810 return -ETIMEDOUT;
811
812 return 0;
813}
814
815static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd,
816 unsigned long arg)
817{
818 int retval;
819
820 switch (cmd) {
821 case FBIO_WAITFORVSYNC:
822 retval = sh_mobile_wait_for_vsync(info);
823 break;
824
825 default:
826 retval = -ENOIOCTLCMD;
827 break;
828 }
829 return retval;
830}
831
832
789static struct fb_ops sh_mobile_lcdc_ops = { 833static struct fb_ops sh_mobile_lcdc_ops = {
790 .owner = THIS_MODULE, 834 .owner = THIS_MODULE,
791 .fb_setcolreg = sh_mobile_lcdc_setcolreg, 835 .fb_setcolreg = sh_mobile_lcdc_setcolreg,
@@ -795,6 +839,7 @@ static struct fb_ops sh_mobile_lcdc_ops = {
795 .fb_copyarea = sh_mobile_lcdc_copyarea, 839 .fb_copyarea = sh_mobile_lcdc_copyarea,
796 .fb_imageblit = sh_mobile_lcdc_imageblit, 840 .fb_imageblit = sh_mobile_lcdc_imageblit,
797 .fb_pan_display = sh_mobile_fb_pan_display, 841 .fb_pan_display = sh_mobile_fb_pan_display,
842 .fb_ioctl = sh_mobile_ioctl,
798}; 843};
799 844
800static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp) 845static 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)
962 goto err1; 1007 goto err1;
963 } 1008 }
964 init_waitqueue_head(&priv->ch[i].frame_end_wait); 1009 init_waitqueue_head(&priv->ch[i].frame_end_wait);
1010 init_completion(&priv->ch[i].vsync_completion);
965 priv->ch[j].pan_offset = 0; 1011 priv->ch[j].pan_offset = 0;
966 priv->ch[j].new_pan_offset = 0; 1012 priv->ch[j].new_pan_offset = 0;
967 1013
diff --git a/include/video/sh_mobile_lcdc.h b/include/video/sh_mobile_lcdc.h
index 288205457713..2cc893fc1f85 100644
--- a/include/video/sh_mobile_lcdc.h
+++ b/include/video/sh_mobile_lcdc.h
@@ -34,6 +34,8 @@ enum { LCDC_CLK_BUS, LCDC_CLK_PERIPHERAL, LCDC_CLK_EXTERNAL };
34#define LCDC_FLAGS_HSCNT (1 << 3) /* Disable HSYNC during VBLANK */ 34#define LCDC_FLAGS_HSCNT (1 << 3) /* Disable HSYNC during VBLANK */
35#define LCDC_FLAGS_DWCNT (1 << 4) /* Disable dotclock during blanking */ 35#define LCDC_FLAGS_DWCNT (1 << 4) /* Disable dotclock during blanking */
36 36
37#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)
38
37struct sh_mobile_lcdc_sys_bus_cfg { 39struct sh_mobile_lcdc_sys_bus_cfg {
38 unsigned long ldmt2r; 40 unsigned long ldmt2r;
39 unsigned long ldmt3r; 41 unsigned long ldmt3r;