aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLiviu Dudau <Liviu.Dudau@arm.com>2014-07-18 17:31:38 +0100
committerJon Medhurst <tixy@linaro.org>2015-06-30 10:12:46 +0100
commit9b28ce21aaba8eb74f4a9a439d88de617cc26ca5 (patch)
tree465a7cb7f58e45636aa495bef7a27095631eea1d
parentc6442debee708c5e67080f205d04395add86e49f (diff)
drm: hdlcd: Fix the picture tear when flipping framebuffers.
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> Signed-off-by: Jon Medhurst <tixy@linaro.org>
-rw-r--r--drivers/gpu/drm/arm/hdlcd_crtc.c26
-rw-r--r--drivers/gpu/drm/arm/hdlcd_drv.c4
-rw-r--r--drivers/gpu/drm/arm/hdlcd_drv.h3
-rw-r--r--drivers/gpu/drm/arm/hdlcd_fb.c20
4 files changed, 31 insertions, 22 deletions
diff --git a/drivers/gpu/drm/arm/hdlcd_crtc.c b/drivers/gpu/drm/arm/hdlcd_crtc.c
index 39cf6c0deec5..17c006ee3522 100644
--- a/drivers/gpu/drm/arm/hdlcd_crtc.c
+++ b/drivers/gpu/drm/arm/hdlcd_crtc.c
@@ -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;
}
diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c
index 62f34a01be56..a831b8670395 100644
--- a/drivers/gpu/drm/arm/hdlcd_drv.c
+++ b/drivers/gpu/drm/arm/hdlcd_drv.c
@@ -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) */
diff --git a/drivers/gpu/drm/arm/hdlcd_drv.h b/drivers/gpu/drm/arm/hdlcd_drv.h
index cd2923a335ce..455da17c2507 100644
--- a/drivers/gpu/drm/arm/hdlcd_drv.h
+++ b/drivers/gpu/drm/arm/hdlcd_drv.h
@@ -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);
diff --git a/drivers/gpu/drm/arm/hdlcd_fb.c b/drivers/gpu/drm/arm/hdlcd_fb.c
index cae947a8726f..ca67e6085a92 100644
--- a/drivers/gpu/drm/arm/hdlcd_fb.c
+++ b/drivers/gpu/drm/arm/hdlcd_fb.c
@@ -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);