drm/tilcdc: Do not update the next frame buffer close to vertical blank
authorTomi Valkeinen <tomi.valkeinen@ti.com>
Tue, 3 Nov 2015 10:00:51 +0000 (12:00 +0200)
committerJyri Sarha <jsarha@ti.com>
Thu, 25 Feb 2016 14:39:41 +0000 (16:39 +0200)
Do not update the next frame buffer close to vertical blank. This is
to avoid situation when the frame changes between writing of
LCDC_DMA_FB_BASE_ADDR_0_REG and LCDC_DMA_FB_CEILING_ADDR_0_REG.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
[Added description to the patch]
Signed-off-by: Jyri Sarha <jsarha@ti.com>
drivers/gpu/drm/tilcdc/tilcdc_crtc.c

index 3257228564d9e923fa13a8345d0e646bba2a1972..b1df04625f50a8a4353274dae41a422202b30ab1 100644 (file)
@@ -21,6 +21,8 @@
 #include "tilcdc_drv.h"
 #include "tilcdc_regs.h"
 
+#define TILCDC_VBLANK_SAFETY_THRESHOLD_US 1000
+
 struct tilcdc_crtc {
        struct drm_crtc base;
 
@@ -29,8 +31,12 @@ struct tilcdc_crtc {
        int dpms;
        wait_queue_head_t frame_done_wq;
        bool frame_done;
+       spinlock_t irq_lock;
+
+       ktime_t last_vblank;
 
        struct drm_framebuffer *curr_fb;
+       struct drm_framebuffer *next_fb;
 
        /* for deferred fb unref's: */
        struct drm_flip_work unref_work;
@@ -146,6 +152,8 @@ static int tilcdc_crtc_page_flip(struct drm_crtc *crtc,
        struct drm_device *dev = crtc->dev;
        int r;
        unsigned long flags;
+       s64 tdiff;
+       ktime_t next_vblank;
 
        r = tilcdc_verify_fb(crtc, fb);
        if (r)
@@ -162,12 +170,21 @@ static int tilcdc_crtc_page_flip(struct drm_crtc *crtc,
 
        pm_runtime_get_sync(dev->dev);
 
+       spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags);
+
+       next_vblank = ktime_add_us(tilcdc_crtc->last_vblank,
+               1000000 / crtc->hwmode.vrefresh);
 
-       set_scanout(crtc, fb);
+       tdiff = ktime_to_us(ktime_sub(next_vblank, ktime_get()));
+
+       if (tdiff >= TILCDC_VBLANK_SAFETY_THRESHOLD_US)
+               set_scanout(crtc, fb);
+       else
+               tilcdc_crtc->next_fb = fb;
 
-       spin_lock_irqsave(&dev->event_lock, flags);
        tilcdc_crtc->event = event;
-       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       spin_unlock_irqrestore(&tilcdc_crtc->irq_lock, flags);
 
        pm_runtime_put_sync(dev->dev);
 
@@ -211,6 +228,12 @@ void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode)
 
                pm_runtime_put_sync(dev->dev);
 
+               if (tilcdc_crtc->next_fb) {
+                       drm_flip_work_queue(&tilcdc_crtc->unref_work,
+                                           tilcdc_crtc->next_fb);
+                       tilcdc_crtc->next_fb = NULL;
+               }
+
                if (tilcdc_crtc->curr_fb) {
                        drm_flip_work_queue(&tilcdc_crtc->unref_work,
                                            tilcdc_crtc->curr_fb);
@@ -651,19 +674,39 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc)
 
        if (stat & LCDC_END_OF_FRAME0) {
                unsigned long flags;
+               bool skip_event = false;
+               ktime_t now;
+
+               now = ktime_get();
 
                drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq);
 
+               spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags);
+
+               tilcdc_crtc->last_vblank = now;
+
+               if (tilcdc_crtc->next_fb) {
+                       set_scanout(crtc, tilcdc_crtc->next_fb);
+                       tilcdc_crtc->next_fb = NULL;
+                       skip_event = true;
+               }
+
+               spin_unlock_irqrestore(&tilcdc_crtc->irq_lock, flags);
+
                drm_handle_vblank(dev, 0);
 
-               spin_lock_irqsave(&dev->event_lock, flags);
+               if (!skip_event) {
+                       struct drm_pending_vblank_event *event;
 
-               if (tilcdc_crtc->event) {
-                       drm_send_vblank_event(dev, 0, tilcdc_crtc->event);
+                       spin_lock_irqsave(&dev->event_lock, flags);
+
+                       event = tilcdc_crtc->event;
                        tilcdc_crtc->event = NULL;
-               }
+                       if (event)
+                               drm_send_vblank_event(dev, 0, event);
 
-               spin_unlock_irqrestore(&dev->event_lock, flags);
+                       spin_unlock_irqrestore(&dev->event_lock, flags);
+               }
        }
 
        if (priv->rev == 2) {
@@ -697,6 +740,8 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev)
        drm_flip_work_init(&tilcdc_crtc->unref_work,
                        "unref", unref_worker);
 
+       spin_lock_init(&tilcdc_crtc->irq_lock);
+
        ret = drm_crtc_init(dev, crtc, &tilcdc_crtc_funcs);
        if (ret < 0)
                goto fail;
This page took 0.030255 seconds and 5 git commands to generate.