From: Tomi Valkeinen <tomi.valkei...@ti.com>

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 at ti.com>
[Added description to the patch]
Signed-off-by: Jyri Sarha <jsarha at ti.com>
---
 drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 61 +++++++++++++++++++++++++++++++-----
 1 file changed, 53 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c 
b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
index 7514d40..7445356 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
@@ -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);

-       set_scanout(crtc, fb);
+       next_vblank = ktime_add_us(tilcdc_crtc->last_vblank,
+               1000000 / crtc->hwmode.vrefresh);
+
+       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;
+
+                       spin_lock_irqsave(&dev->event_lock, flags);

-               if (tilcdc_crtc->event) {
-                       drm_send_vblank_event(dev, 0, tilcdc_crtc->event);
+                       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) {
@@ -716,6 +759,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;
-- 
1.9.1

Reply via email to