aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorMario Kleiner <mario.kleiner.de@gmail.com>2016-05-18 08:02:46 -0400
committerEric Anholt <eric@anholt.net>2016-06-06 16:00:40 -0400
commit56d1fe0979dc9b73c1c12ee07722ac380d42a0c4 (patch)
tree1da6ad0c54668dcaa4905304fde0e65a2513c943 /drivers/gpu
parentb10c22e5f9902a329450c2027e9291b71e9f1602 (diff)
drm/vc4: Make pageflip completion handling more robust.
Protect both the setup of the pageflip event and the latching of the new requested displaylist head pointer by the event lock, so we can't get into a situation where vc4_atomic_flush latches the new display list via HVS_WRITE, then immediately gets preempted before queueing the pageflip event, then the page-flip completes in hw and the vc4_crtc_handle_page_flip() runs and no-ops due to lack of a pending pageflip event, then vc4_atomic_flush continues and only then queues the pageflip event - after the page flip handling already no-oped. This would cause flip completion handling only at the next vblank - one frame too late. In vc4_crtc_handle_page_flip() check the actual DL head pointer in SCALER_DISPLACTX against the requested pointer for page flip to make sure that the flip actually really completed in the current vblank and doesn't get deferred to the next one because the DL head pointer was written a bit too late into SCALER_DISPLISTX, after start of vblank, and missed the boat. This avoids handling a pageflip completion too early - one frame too early. According to Eric, DL head pointer updates which were written into the HVS DISPLISTX reg get committed to hardware at the last pixel of active scanout. Our vblank interrupt handler, as triggered by PV_INT_VFP_START irq, gets to run earliest at the first pixel of HBLANK at the end of the last scanline of active scanout, ie. vblank irq handling runs at least 1 pixel duration after a potential pageflip completion happened in hardware. This ordering of events in the hardware, together with the lock protection and SCALER_DISPLACTX sampling of this patch, guarantees that pageflip completion handling only runs at exactly the vblank irq of actual pageflip completion in all cases. Background info from Eric about the relative timing of HVS, PV's and trigger points for interrupts, DL updates: https://lists.freedesktop.org/archives/dri-devel/2016-May/107510.html Tested on RPi 2B with hardware timing measurement equipment and shown to no longer complete flips too early or too late. Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com> Reviewed-by: Eric Anholt <eric@anholt.net>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/vc4/vc4_crtc.c28
-rw-r--r--drivers/gpu/drm/vc4/vc4_regs.h4
2 files changed, 22 insertions, 10 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index e9befb6d10fd..0f18b76c7906 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -456,14 +456,6 @@ static void vc4_crtc_atomic_flush(struct drm_crtc *crtc,
456 456
457 WARN_ON_ONCE(dlist_next - dlist_start != vc4_state->mm.size); 457 WARN_ON_ONCE(dlist_next - dlist_start != vc4_state->mm.size);
458 458
459 HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel),
460 vc4_state->mm.start);
461
462 if (debug_dump_regs) {
463 DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc));
464 vc4_hvs_dump_state(dev);
465 }
466
467 if (crtc->state->event) { 459 if (crtc->state->event) {
468 unsigned long flags; 460 unsigned long flags;
469 461
@@ -473,8 +465,20 @@ static void vc4_crtc_atomic_flush(struct drm_crtc *crtc,
473 465
474 spin_lock_irqsave(&dev->event_lock, flags); 466 spin_lock_irqsave(&dev->event_lock, flags);
475 vc4_crtc->event = crtc->state->event; 467 vc4_crtc->event = crtc->state->event;
476 spin_unlock_irqrestore(&dev->event_lock, flags);
477 crtc->state->event = NULL; 468 crtc->state->event = NULL;
469
470 HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel),
471 vc4_state->mm.start);
472
473 spin_unlock_irqrestore(&dev->event_lock, flags);
474 } else {
475 HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel),
476 vc4_state->mm.start);
477 }
478
479 if (debug_dump_regs) {
480 DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc));
481 vc4_hvs_dump_state(dev);
478 } 482 }
479} 483}
480 484
@@ -500,10 +504,14 @@ static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc)
500{ 504{
501 struct drm_crtc *crtc = &vc4_crtc->base; 505 struct drm_crtc *crtc = &vc4_crtc->base;
502 struct drm_device *dev = crtc->dev; 506 struct drm_device *dev = crtc->dev;
507 struct vc4_dev *vc4 = to_vc4_dev(dev);
508 struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
509 u32 chan = vc4_crtc->channel;
503 unsigned long flags; 510 unsigned long flags;
504 511
505 spin_lock_irqsave(&dev->event_lock, flags); 512 spin_lock_irqsave(&dev->event_lock, flags);
506 if (vc4_crtc->event) { 513 if (vc4_crtc->event &&
514 (vc4_state->mm.start == HVS_READ(SCALER_DISPLACTX(chan)))) {
507 drm_crtc_send_vblank_event(crtc, vc4_crtc->event); 515 drm_crtc_send_vblank_event(crtc, vc4_crtc->event);
508 vc4_crtc->event = NULL; 516 vc4_crtc->event = NULL;
509 drm_crtc_vblank_put(crtc); 517 drm_crtc_vblank_put(crtc);
diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h
index 6163b95c5411..f99eece4cc97 100644
--- a/drivers/gpu/drm/vc4/vc4_regs.h
+++ b/drivers/gpu/drm/vc4/vc4_regs.h
@@ -341,6 +341,10 @@
341#define SCALER_DISPLACT0 0x00000030 341#define SCALER_DISPLACT0 0x00000030
342#define SCALER_DISPLACT1 0x00000034 342#define SCALER_DISPLACT1 0x00000034
343#define SCALER_DISPLACT2 0x00000038 343#define SCALER_DISPLACT2 0x00000038
344#define SCALER_DISPLACTX(x) (SCALER_DISPLACT0 + \
345 (x) * (SCALER_DISPLACT1 - \
346 SCALER_DISPLACT0))
347
344#define SCALER_DISPCTRL0 0x00000040 348#define SCALER_DISPCTRL0 0x00000040
345# define SCALER_DISPCTRLX_ENABLE BIT(31) 349# define SCALER_DISPCTRLX_ENABLE BIT(31)
346# define SCALER_DISPCTRLX_RESET BIT(30) 350# define SCALER_DISPCTRLX_RESET BIT(30)