drm/armada: update planes after the dumb frame is complete
authorRussell King <rmk+kernel@armlinux.org.uk>
Mon, 30 Jul 2018 10:53:06 +0000 (11:53 +0100)
committerRussell King <rmk+kernel@armlinux.org.uk>
Mon, 30 Jul 2018 10:53:06 +0000 (11:53 +0100)
Write out the plane updates after the dumb frame has completed, but
just before the blank period.  This allows all the plane updates to
be performed in a flicker-free non-tearing manner.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
drivers/gpu/drm/armada/armada_crtc.c
drivers/gpu/drm/armada/armada_crtc.h

index ebcb99316c94a10d9634496285aed369969a0ae6..bb1e13b4516b77b7e179ba18ae88c10ddcee86ad 100644 (file)
@@ -197,21 +197,27 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
                writel_relaxed(val, base + LCD_SPU_ADV_REG);
        }
 
-       if (stat & DUMB_FRAMEDONE && dcrtc->cursor_update) {
-               writel_relaxed(dcrtc->cursor_hw_pos,
-                              base + LCD_SPU_HWC_OVSA_HPXL_VLN);
-               writel_relaxed(dcrtc->cursor_hw_sz,
-                              base + LCD_SPU_HWC_HPXL_VLN);
-               armada_updatel(CFG_HWC_ENA,
-                              CFG_HWC_ENA | CFG_HWC_1BITMOD | CFG_HWC_1BITENA,
-                              base + LCD_SPU_DMA_CTRL0);
-               dcrtc->cursor_update = false;
+       if (stat & dcrtc->irq_ena & DUMB_FRAMEDONE) {
+               if (dcrtc->update_pending) {
+                       armada_drm_crtc_update_regs(dcrtc, dcrtc->regs);
+                       dcrtc->update_pending = false;
+               }
+               if (dcrtc->cursor_update) {
+                       writel_relaxed(dcrtc->cursor_hw_pos,
+                                      base + LCD_SPU_HWC_OVSA_HPXL_VLN);
+                       writel_relaxed(dcrtc->cursor_hw_sz,
+                                      base + LCD_SPU_HWC_HPXL_VLN);
+                       armada_updatel(CFG_HWC_ENA,
+                                      CFG_HWC_ENA | CFG_HWC_1BITMOD |
+                                      CFG_HWC_1BITENA,
+                                      base + LCD_SPU_DMA_CTRL0);
+                       dcrtc->cursor_update = false;
+               }
                armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
        }
-
        spin_unlock(&dcrtc->irq_lock);
 
-       if (stat & VSYNC_IRQ) {
+       if (stat & VSYNC_IRQ && !dcrtc->update_pending) {
                event = xchg(&dcrtc->event, NULL);
                if (event) {
                        spin_lock(&dcrtc->crtc.dev->event_lock);
@@ -360,22 +366,26 @@ static void armada_drm_crtc_atomic_flush(struct drm_crtc *crtc,
                                         struct drm_crtc_state *old_crtc_state)
 {
        struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
-       unsigned long flags;
 
        DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
 
        armada_reg_queue_end(dcrtc->regs, dcrtc->regs_idx);
 
-       spin_lock_irqsave(&dcrtc->irq_lock, flags);
-       armada_drm_crtc_update_regs(dcrtc, dcrtc->regs);
-       spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
-
        /*
         * If we aren't doing a full modeset, then we need to queue
         * the event here.
         */
-       if (!drm_atomic_crtc_needs_modeset(crtc->state))
+       if (!drm_atomic_crtc_needs_modeset(crtc->state)) {
+               dcrtc->update_pending = true;
                armada_drm_crtc_queue_state_event(crtc);
+               spin_lock_irq(&dcrtc->irq_lock);
+               armada_drm_crtc_enable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
+               spin_unlock_irq(&dcrtc->irq_lock);
+       } else {
+               spin_lock_irq(&dcrtc->irq_lock);
+               armada_drm_crtc_update_regs(dcrtc, dcrtc->regs);
+               spin_unlock_irq(&dcrtc->irq_lock);
+       }
 }
 
 static void armada_drm_crtc_atomic_disable(struct drm_crtc *crtc,
@@ -532,7 +542,6 @@ static int armada_drm_crtc_cursor_update(struct armada_crtc *dcrtc, bool reload)
 
        if (!dcrtc->cursor_obj || !h || !w) {
                spin_lock_irq(&dcrtc->irq_lock);
-               armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
                dcrtc->cursor_update = false;
                armada_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
                spin_unlock_irq(&dcrtc->irq_lock);
@@ -556,7 +565,6 @@ static int armada_drm_crtc_cursor_update(struct armada_crtc *dcrtc, bool reload)
 
        if (dcrtc->cursor_hw_sz != (h << 16 | w)) {
                spin_lock_irq(&dcrtc->irq_lock);
-               armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
                dcrtc->cursor_update = false;
                armada_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
                spin_unlock_irq(&dcrtc->irq_lock);
index 5b607d45f469310cecf52b2addde3d9b23c71915..b95ea13d0705de771b5cba5da40fa67c27d5fca0 100644 (file)
@@ -70,6 +70,7 @@ struct armada_crtc {
        spinlock_t              irq_lock;
        uint32_t                irq_ena;
 
+       bool                    update_pending;
        struct drm_pending_vblank_event *event;
        struct armada_regs      atomic_regs[32];
        struct armada_regs      *regs;