drm: hdlcd: Fix the picture tear when flipping framebuffers.
authorLiviu Dudau <Liviu.Dudau@arm.com>
Fri, 18 Jul 2014 16:31:38 +0000 (17:31 +0100)
committerLiviu Dudau <Liviu.Dudau@arm.com>
Fri, 18 Jul 2014 16:31:38 +0000 (17:31 +0100)
HDLCD contains logic in hardware to wait for VSYNC before updating
the start of framebuffer register, so we don't need to do it in
software. More than that, to remove any tearing we need to wait
for VSYNC after setting up a new framebuffer as releasing the old
fb while the hardware is still using it can cause artifacts.

Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
drivers/gpu/drm/arm/hdlcd_crtc.c
drivers/gpu/drm/arm/hdlcd_drv.c
drivers/gpu/drm/arm/hdlcd_drv.h
drivers/gpu/drm/arm/hdlcd_fb.c

index 39cf6c0deec59ac3da5610e1d252b6bc5214867c..17c006ee3522b41e94d4dfd528d7de8cf5f45fbc 100644 (file)
@@ -31,12 +31,13 @@ static void hdlcd_crtc_destroy(struct drm_crtc *crtc)
        drm_crtc_cleanup(crtc);
 }
 
-void hdlcd_set_scanout(struct hdlcd_drm_private *hdlcd)
+void hdlcd_set_scanout(struct hdlcd_drm_private *hdlcd, bool wait)
 {
        struct drm_framebuffer *fb = hdlcd->crtc.primary->fb;
        struct hdlcd_bo *bo;
        unsigned int depth, bpp;
        dma_addr_t scanout_start;
+       int ret;
 
        drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp);
        bo = hdlcd->bo;
@@ -44,9 +45,16 @@ void hdlcd_set_scanout(struct hdlcd_drm_private *hdlcd)
        scanout_start = bo->dma_addr + fb->offsets[0] +
                (hdlcd->crtc.y * fb->pitches[0]) + (hdlcd->crtc.x * bpp/8);
 
-       if (scanout_start != hdlcd->scanout_buf) {
-               hdlcd_write(hdlcd, HDLCD_REG_FB_BASE, scanout_start);
-               hdlcd->scanout_buf = scanout_start;
+       hdlcd_write(hdlcd, HDLCD_REG_FB_BASE, scanout_start);
+
+       if (wait) {
+               drm_vblank_get(fb->dev, 0);
+               reinit_completion(&hdlcd->vsync_completion);
+               do {
+                       ret = wait_for_completion_interruptible_timeout(&hdlcd->vsync_completion,
+                                                       msecs_to_jiffies(1000));
+               } while (ret <= 0);
+               drm_vblank_put(fb->dev, 0);
        }
 }
 
@@ -72,11 +80,13 @@ static int hdlcd_crtc_page_flip(struct drm_crtc *crtc,
                unsigned long flags;
 
                /* not active, update registers immediately */
-               hdlcd_set_scanout(hdlcd);
+               hdlcd_set_scanout(hdlcd, false);
                spin_lock_irqsave(&crtc->dev->event_lock, flags);
                if (event)
                        drm_send_vblank_event(crtc->dev, 0, event);
                spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+       } else {
+               hdlcd_set_scanout(hdlcd, true);
        }
 
        return 0;
@@ -149,7 +159,7 @@ static int hdlcd_crtc_mode_set(struct drm_crtc *crtc,
        /* This function gets called when the only change is the start of
           the scanout buffer. Detect that and bail out early */
        if (hdlcd->initialised && hdlcd_fb_mode_equal(oldfb, crtc->primary->fb)) {
-               hdlcd_set_scanout(hdlcd);
+               hdlcd_set_scanout(hdlcd, true);
                return 0;
        }
 
@@ -226,7 +236,7 @@ static int hdlcd_crtc_mode_set(struct drm_crtc *crtc,
        clk_set_rate(hdlcd->clk, mode->crtc_clock * 1000);
        clk_enable(hdlcd->clk);
 
-       hdlcd_set_scanout(hdlcd);
+       hdlcd_set_scanout(hdlcd, false);
        hdlcd->initialised = true;
 
        return 0;
@@ -237,7 +247,7 @@ int hdlcd_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
 {
        struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);
 
-       hdlcd_set_scanout(hdlcd);
+       hdlcd_set_scanout(hdlcd, true);
        return 0;
 }
 
index 62f34a01be5635518f6ed4a57b333095d98b4636..a831b8670395828a82d18c5b45d12f0052b0f2bd 100644 (file)
@@ -214,8 +214,6 @@ static irqreturn_t hdlcd_irq(int irq, void *arg)
                struct drm_pending_vblank_event *event;
                unsigned long flags;
 
-               hdlcd_set_scanout(hdlcd);
-
                drm_handle_vblank(dev, 0);
 
                spin_lock_irqsave(&dev->event_lock, flags);
@@ -226,7 +224,7 @@ static irqreturn_t hdlcd_irq(int irq, void *arg)
                        drm_vblank_put(dev, 0);
                }
                spin_unlock_irqrestore(&dev->event_lock, flags);
-               complete(&hdlcd->vsync_completion);
+               complete_all(&hdlcd->vsync_completion);
        }
 
        /* acknowledge interrupt(s) */
index cd2923a335ce44c2aafb30f81ff30e4432e4264c..455da17c250764da19793cc52a54bb483a7b67fd 100644 (file)
@@ -16,7 +16,6 @@ struct hdlcd_drm_private {
        struct clk                      *clk;
        struct drm_fb_helper            *fb_helper;
        struct hdlcd_bo                 *bo;
-       dma_addr_t                      scanout_buf;
        struct drm_pending_vblank_event *event;
        struct drm_crtc                 crtc;
        struct device_node              *slave_node;
@@ -73,7 +72,7 @@ static inline int hdlcd_create_virtual_connector(struct drm_device *dev)
 }
 #endif
 
-void hdlcd_set_scanout(struct hdlcd_drm_private *hdlcd);
+void hdlcd_set_scanout(struct hdlcd_drm_private *hdlcd, bool wait);
 void hdlcd_drm_mode_config_init(struct drm_device *dev);
 int hdlcd_fbdev_init(struct drm_device *dev);
 
index cae947a8726fbb44f3fd3bcd5b686859552d94d8..ca67e6085a921a76e9a6e375e2a37f1bc1d417e7 100644 (file)
@@ -252,11 +252,14 @@ static int hdlcd_wait_for_vsync(struct fb_info *info)
        struct hdlcd_drm_private *hdlcd = helper->dev->dev_private;
        int ret;
 
-       drm_crtc_vblank_on(&hdlcd->crtc);
-       ret = wait_for_completion_interruptible_timeout(&hdlcd->vsync_completion,
-                                                       msecs_to_jiffies(100));
-       drm_crtc_vblank_off(&hdlcd->crtc);
-       if (ret)
+       drm_vblank_get(helper->dev, 0);
+       reinit_completion(&hdlcd->vsync_completion);
+       do {
+               ret = wait_for_completion_interruptible_timeout(&hdlcd->vsync_completion,
+                                                       msecs_to_jiffies(1000));
+       } while (ret == -ERESTARTSYS);
+       drm_vblank_put(helper->dev, 0);
+       if (!ret)
                return -ETIMEDOUT;
 #endif
 
@@ -288,7 +291,7 @@ static int hdlcd_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
 
        dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
 
-       vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
+       vma->vm_flags |= VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP;
        vm_size = vma->vm_end - vma->vm_start;
 
        if (vm_size > bo->gem.size)
@@ -474,11 +477,11 @@ static int hdlcd_fb_probe(struct drm_fb_helper *helper,
 
        cmd.width = sizes->surface_width;
        cmd.height = sizes->surface_height;
-       cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
+       cmd.pitches[0] = ALIGN(sizes->surface_width * bytes_per_pixel, 64);
        cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
                                                     sizes->surface_depth);
 
-       size = cmd.pitches[0] * cmd.height * MAX_FRAMES;
+       size = PAGE_ALIGN(cmd.pitches[0] * cmd.height * MAX_FRAMES);
 
        bo = hdlcd_fb_bo_create(helper->dev, size);
        if (IS_ERR(bo))
@@ -566,7 +569,6 @@ static struct drm_framebuffer *hdlcd_fb_alloc(struct drm_device *dev,
        int err;
        struct drm_framebuffer *fb;
 
-       dev_info(dev->dev, "Linux is here %s", __func__);
        fb = kzalloc(sizeof(*fb), GFP_KERNEL);
        if (!fb)
                return ERR_PTR(-ENOMEM);