Browse Source

drm/armada: update planes after the dumb frame is complete

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>
Russell King 7 years ago
parent
commit
3cb13ac97b
2 changed files with 28 additions and 19 deletions
  1. 27 19
      drivers/gpu/drm/armada/armada_crtc.c
  2. 1 0
      drivers/gpu/drm/armada/armada_crtc.h

+ 27 - 19
drivers/gpu/drm/armada/armada_crtc.c

@@ -197,21 +197,27 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
 		writel_relaxed(val, base + LCD_SPU_ADV_REG);
 		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);
 		armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
 	}
 	}
-
 	spin_unlock(&dcrtc->irq_lock);
 	spin_unlock(&dcrtc->irq_lock);
 
 
-	if (stat & VSYNC_IRQ) {
+	if (stat & VSYNC_IRQ && !dcrtc->update_pending) {
 		event = xchg(&dcrtc->event, NULL);
 		event = xchg(&dcrtc->event, NULL);
 		if (event) {
 		if (event) {
 			spin_lock(&dcrtc->crtc.dev->event_lock);
 			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 drm_crtc_state *old_crtc_state)
 {
 {
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
-	unsigned long flags;
 
 
 	DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
 	DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
 
 
 	armada_reg_queue_end(dcrtc->regs, dcrtc->regs_idx);
 	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
 	 * If we aren't doing a full modeset, then we need to queue
 	 * the event here.
 	 * 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);
 		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,
 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) {
 	if (!dcrtc->cursor_obj || !h || !w) {
 		spin_lock_irq(&dcrtc->irq_lock);
 		spin_lock_irq(&dcrtc->irq_lock);
-		armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
 		dcrtc->cursor_update = false;
 		dcrtc->cursor_update = false;
 		armada_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
 		armada_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
 		spin_unlock_irq(&dcrtc->irq_lock);
 		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)) {
 	if (dcrtc->cursor_hw_sz != (h << 16 | w)) {
 		spin_lock_irq(&dcrtc->irq_lock);
 		spin_lock_irq(&dcrtc->irq_lock);
-		armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
 		dcrtc->cursor_update = false;
 		dcrtc->cursor_update = false;
 		armada_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
 		armada_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
 		spin_unlock_irq(&dcrtc->irq_lock);
 		spin_unlock_irq(&dcrtc->irq_lock);

+ 1 - 0
drivers/gpu/drm/armada/armada_crtc.h

@@ -70,6 +70,7 @@ struct armada_crtc {
 	spinlock_t		irq_lock;
 	spinlock_t		irq_lock;
 	uint32_t		irq_ena;
 	uint32_t		irq_ena;
 
 
+	bool			update_pending;
 	struct drm_pending_vblank_event *event;
 	struct drm_pending_vblank_event *event;
 	struct armada_regs	atomic_regs[32];
 	struct armada_regs	atomic_regs[32];
 	struct armada_regs	*regs;
 	struct armada_regs	*regs;