diff options
-rw-r--r-- | drivers/video/sh_mobile_lcdcfb.c | 48 | ||||
-rw-r--r-- | include/video/sh_mobile_lcdc.h | 2 |
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 | ||
110 | struct sh_mobile_lcdc_priv; | 112 | struct sh_mobile_lcdc_priv; |
111 | struct sh_mobile_lcdc_chan { | 113 | struct 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 | ||
129 | struct sh_mobile_lcdc_priv { | 132 | struct 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 | ||
796 | static 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 | |||
815 | static 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 | |||
789 | static struct fb_ops sh_mobile_lcdc_ops = { | 833 | static 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 | ||
800 | static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp) | 845 | 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) | |||
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 | |||
37 | struct sh_mobile_lcdc_sys_bus_cfg { | 39 | struct sh_mobile_lcdc_sys_bus_cfg { |
38 | unsigned long ldmt2r; | 40 | unsigned long ldmt2r; |
39 | unsigned long ldmt3r; | 41 | unsigned long ldmt3r; |