Index: s3c2412/linux-2.6.22/drivers/video/s3c2410fb.c =================================================================== --- s3c2412/linux-2.6.22/drivers/video/s3c2410fb.c (revision 2100) +++ s3c2412/linux-2.6.22/drivers/video/s3c2410fb.c (working copy) @@ -599,7 +599,13 @@ struct s3c2410fb_info *fbi = info->par; unsigned long saddr1, saddr2, val, flags; void __iomem *regs = fbi->io; + unsigned long irqen; + void __iomem *irq_base = fbi->irq_base; + unsigned int cnt; + wait_queue_t __wait; + int ret; + saddr1 = info->fix.smem_start; saddr1 += var->xoffset; saddr1 += info->fix.line_length * var->yoffset; @@ -607,14 +613,14 @@ saddr2 = saddr1; saddr2 += info->fix.line_length * info->var.yres; + local_irq_save(flags); + /* Users must not change the value of the LCDBASEU and LCDBASEL * registers at the end of FRAME by refering to the LINECNT field * in LCDCON1 register, for the LCD FIFO fetches the next frame * data prior to the change in frame. To check the LINECNT, * interrupts should be masked. */ - local_irq_save(flags); - do { val = readl(regs + S3C2410_LCDCON1) >> 18; } while (val == 0); @@ -622,13 +628,30 @@ writel(saddr1 >> 1, regs + S3C2410_LCDSADDR1); writel(saddr2 >> 1, regs + S3C2410_LCDSADDR2); + fbi->vsync_cnt++; + + /* Wait until the start of the next frame to prevent flicker. */ + + /* enable IRQ */ + irqen = readl(irq_base + S3C24XX_LCDINTMSK); + irqen &= ~S3C2410_LCDINT_FRSYNC; + writel(irqen, irq_base + S3C24XX_LCDINTMSK); + + writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND); + writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND); + local_irq_restore(flags); - /* FIXME we should wait for the start of the next frame here but - * the Jive application process events after flipping the - * framebuffer, so by not delaying we save a few ms per frame. - */ + init_waitqueue_entry(&__wait, current); + ret = wait_event_interruptible_timeout(fbi->vsync_wait, fbi->vsync_cnt == 0, HZ/10); + if (ret < 0) { + return ret; + } + if (ret == 0) { + return -ETIMEDOUT; + } + return 0; } @@ -842,6 +865,9 @@ writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND); writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND); + + fbi->vsync_cnt = 0; + wake_up_interruptible(&fbi->vsync_wait); } return IRQ_HANDLED; @@ -961,8 +987,12 @@ goto release_mem; } + init_waitqueue_head(&info->vsync_wait); + info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE); + printk("LCD IRQ BASE %p\n", info->irq_base); + dprintk("devinit\n"); strcpy(fbinfo->fix.id, driver_name); Index: s3c2412/linux-2.6.22/drivers/video/s3c2410fb.h =================================================================== --- s3c2412/linux-2.6.22/drivers/video/s3c2410fb.h (revision 2100) +++ s3c2412/linux-2.6.22/drivers/video/s3c2410fb.h (working copy) @@ -56,6 +56,11 @@ /* keep these registers in case we need to re-write palette */ u32 palette_buffer[256]; u32 pseudo_pal[16]; + + /* vertical sync interrupt */ + unsigned int vsync_cnt; + wait_queue_head_t vsync_wait; + unsigned long irq_flags; }; #define PALETTE_BUFF_CLEAR (0x80000000) /* entry is clear/invalid */