a86f9a6
From 424f582d0ec7f206dcc59d34c9a2fa1c8087a8aa Mon Sep 17 00:00:00 2001
a86f9a6
From: Daniel Vetter <daniel.vetter@ffwll.ch>
a86f9a6
Date: Tue, 10 Nov 2015 17:37:31 +0100
a86f9a6
Subject: [PATCH] drm/nouveau: Fix pre-nv50 pageflip events (v4)
a86f9a6
MIME-Version: 1.0
a86f9a6
Content-Type: text/plain; charset=UTF-8
a86f9a6
Content-Transfer-Encoding: 8bit
a86f9a6
a86f9a6
Apparently pre-nv50 pageflip events happen before the actual vblank
a86f9a6
period. Therefore that functionality got semi-disabled in
a86f9a6
a86f9a6
commit af4870e406126b7ac0ae7c7ce5751f25ebe60f28
a86f9a6
Author: Mario Kleiner <mario.kleiner.de@gmail.com>
a86f9a6
Date:   Tue May 13 00:42:08 2014 +0200
a86f9a6
a86f9a6
    drm/nouveau/kms/nv04-nv40: fix pageflip events via special case.
a86f9a6
a86f9a6
Unfortunately that hack got uprooted in
a86f9a6
a86f9a6
commit cc1ef118fc099295ae6aabbacc8af94d8d8885eb
a86f9a6
Author: Thierry Reding <treding@nvidia.com>
a86f9a6
Date:   Wed Aug 12 17:00:31 2015 +0200
a86f9a6
a86f9a6
    drm/irq: Make pipe unsigned and name consistent
a86f9a6
a86f9a6
Triggering a warning when trying to sample the vblank timestamp for a
a86f9a6
non-existing pipe. There's a few ways to fix this:
a86f9a6
a86f9a6
- Open-code the old behaviour, which just enshrines this slight
a86f9a6
  breakage of the userspace ABI.
a86f9a6
a86f9a6
- Revert Mario's commit and again inflict broken timestamps, again not
a86f9a6
  pretty.
a86f9a6
a86f9a6
- Fix this for real by delaying the pageflip TS until the next vblank
a86f9a6
  interrupt, thereby making it accurate.
a86f9a6
a86f9a6
This patch implements the third option. Since having a page flip
a86f9a6
interrupt that happens when the pageflip gets armed and not when it
a86f9a6
completes in the next vblank seems to be fairly common (older i915 hw
a86f9a6
works very similarly) create a new helper to arm vblank events for
a86f9a6
such drivers.
a86f9a6
a86f9a6
v2 (Mario Kleiner):
a86f9a6
- Fix function prototypes in drmP.h
a86f9a6
- Add missing vblank_put() for pageflip completion without
a86f9a6
  pageflip event.
a86f9a6
- Initialize sequence number for queued pageflip event to avoid
a86f9a6
  trouble in drm_handle_vblank_events().
a86f9a6
- Remove dead code and spelling fix.
a86f9a6
a86f9a6
v3 (Mario Kleiner):
a86f9a6
- Add a signed-off-by and cc stable tag per Ilja's advice.
a86f9a6
a86f9a6
v4 (Thierry Reding):
a86f9a6
- Fix kerneldoc typo, discovered by Michel Dänzer
a86f9a6
- Rearrange tags and changelog
a86f9a6
a86f9a6
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=106431
a86f9a6
Cc: Thierry Reding <treding@nvidia.com>
a86f9a6
Cc: Mario Kleiner <mario.kleiner.de@gmail.com>
a86f9a6
Acked-by: Ben Skeggs <bskeggs@redhat.com>
a86f9a6
Cc: Ilia Mirkin <imirkin@alum.mit.edu>
a86f9a6
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
a86f9a6
Reviewed-by: Mario Kleiner <mario.kleiner.de@gmail.com>
a86f9a6
Cc: stable@vger.kernel.org # v4.3
a86f9a6
Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com>
a86f9a6
Signed-off-by: Thierry Reding <treding@nvidia.com>
a86f9a6
Signed-off-by: Dave Airlie <airlied@redhat.com>
a86f9a6
---
a86f9a6
 drivers/gpu/drm/drm_irq.c                 | 54 ++++++++++++++++++++++++++++++-
a86f9a6
 drivers/gpu/drm/nouveau/nouveau_display.c | 19 ++++++-----
a86f9a6
 include/drm/drmP.h                        |  4 +++
a86f9a6
 3 files changed, 68 insertions(+), 9 deletions(-)
a86f9a6
a86f9a6
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
a86f9a6
index 22d207e211e7..c5f20e41dcc6 100644
a86f9a6
--- a/drivers/gpu/drm/drm_irq.c
a86f9a6
+++ b/drivers/gpu/drm/drm_irq.c
a86f9a6
@@ -944,7 +944,8 @@ static void send_vblank_event(struct drm_device *dev,
a86f9a6
 		struct drm_pending_vblank_event *e,
a86f9a6
 		unsigned long seq, struct timeval *now)
a86f9a6
 {
a86f9a6
-	WARN_ON_SMP(!spin_is_locked(&dev->event_lock));
a86f9a6
+	assert_spin_locked(&dev->event_lock);
a86f9a6
+
a86f9a6
 	e->event.sequence = seq;
a86f9a6
 	e->event.tv_sec = now->tv_sec;
a86f9a6
 	e->event.tv_usec = now->tv_usec;
a86f9a6
@@ -957,6 +958,57 @@ static void send_vblank_event(struct drm_device *dev,
a86f9a6
 }
a86f9a6
 
a86f9a6
 /**
a86f9a6
+ * drm_arm_vblank_event - arm vblank event after pageflip
a86f9a6
+ * @dev: DRM device
a86f9a6
+ * @pipe: CRTC index
a86f9a6
+ * @e: the event to prepare to send
a86f9a6
+ *
a86f9a6
+ * A lot of drivers need to generate vblank events for the very next vblank
a86f9a6
+ * interrupt. For example when the page flip interrupt happens when the page
a86f9a6
+ * flip gets armed, but not when it actually executes within the next vblank
a86f9a6
+ * period. This helper function implements exactly the required vblank arming
a86f9a6
+ * behaviour.
a86f9a6
+ *
a86f9a6
+ * Caller must hold event lock. Caller must also hold a vblank reference for
a86f9a6
+ * the event @e, which will be dropped when the next vblank arrives.
a86f9a6
+ *
a86f9a6
+ * This is the legacy version of drm_crtc_arm_vblank_event().
a86f9a6
+ */
a86f9a6
+void drm_arm_vblank_event(struct drm_device *dev, unsigned int pipe,
a86f9a6
+			  struct drm_pending_vblank_event *e)
a86f9a6
+{
a86f9a6
+	assert_spin_locked(&dev->event_lock);
a86f9a6
+
a86f9a6
+	e->pipe = pipe;
a86f9a6
+	e->event.sequence = drm_vblank_count(dev, pipe);
a86f9a6
+	list_add_tail(&e->base.link, &dev->vblank_event_list);
a86f9a6
+}
a86f9a6
+EXPORT_SYMBOL(drm_arm_vblank_event);
a86f9a6
+
a86f9a6
+/**
a86f9a6
+ * drm_crtc_arm_vblank_event - arm vblank event after pageflip
a86f9a6
+ * @crtc: the source CRTC of the vblank event
a86f9a6
+ * @e: the event to send
a86f9a6
+ *
a86f9a6
+ * A lot of drivers need to generate vblank events for the very next vblank
a86f9a6
+ * interrupt. For example when the page flip interrupt happens when the page
a86f9a6
+ * flip gets armed, but not when it actually executes within the next vblank
a86f9a6
+ * period. This helper function implements exactly the required vblank arming
a86f9a6
+ * behaviour.
a86f9a6
+ *
a86f9a6
+ * Caller must hold event lock. Caller must also hold a vblank reference for
a86f9a6
+ * the event @e, which will be dropped when the next vblank arrives.
a86f9a6
+ *
a86f9a6
+ * This is the native KMS version of drm_arm_vblank_event().
a86f9a6
+ */
a86f9a6
+void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
a86f9a6
+			       struct drm_pending_vblank_event *e)
a86f9a6
+{
a86f9a6
+	drm_arm_vblank_event(crtc->dev, drm_crtc_index(crtc), e);
a86f9a6
+}
a86f9a6
+EXPORT_SYMBOL(drm_crtc_arm_vblank_event);
a86f9a6
+
a86f9a6
+/**
a86f9a6
  * drm_send_vblank_event - helper to send vblank event after pageflip
a86f9a6
  * @dev: DRM device
a86f9a6
  * @pipe: CRTC index
a86f9a6
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
a86f9a6
index e905c00acf1a..54183bcca48f 100644
a86f9a6
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
a86f9a6
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
a86f9a6
@@ -827,7 +827,6 @@ nouveau_finish_page_flip(struct nouveau_channel *chan,
a86f9a6
 	struct drm_device *dev = drm->dev;
a86f9a6
 	struct nouveau_page_flip_state *s;
a86f9a6
 	unsigned long flags;
a86f9a6
-	int crtcid = -1;
a86f9a6
 
a86f9a6
 	spin_lock_irqsave(&dev->event_lock, flags);
a86f9a6
 
a86f9a6
@@ -839,15 +838,19 @@ nouveau_finish_page_flip(struct nouveau_channel *chan,
a86f9a6
 
a86f9a6
 	s = list_first_entry(&fctx->flip, struct nouveau_page_flip_state, head);
a86f9a6
 	if (s->event) {
a86f9a6
-		/* Vblank timestamps/counts are only correct on >= NV-50 */
a86f9a6
-		if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA)
a86f9a6
-			crtcid = s->crtc;
a86f9a6
+		if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) {
a86f9a6
+			drm_arm_vblank_event(dev, s->crtc, s->event);
a86f9a6
+		} else {
a86f9a6
+			drm_send_vblank_event(dev, s->crtc, s->event);
a86f9a6
 
a86f9a6
-		drm_send_vblank_event(dev, crtcid, s->event);
a86f9a6
+			/* Give up ownership of vblank for page-flipped crtc */
a86f9a6
+			drm_vblank_put(dev, s->crtc);
a86f9a6
+		}
a86f9a6
+	}
a86f9a6
+	else {
a86f9a6
+		/* Give up ownership of vblank for page-flipped crtc */
a86f9a6
+		drm_vblank_put(dev, s->crtc);
a86f9a6
 	}
a86f9a6
-
a86f9a6
-	/* Give up ownership of vblank for page-flipped crtc */
a86f9a6
-	drm_vblank_put(dev, s->crtc);
a86f9a6
 
a86f9a6
 	list_del(&s->head);
a86f9a6
 	if (ps)
a86f9a6
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
a86f9a6
index 8b5ce7c5d9bb..c98f01046bd0 100644
a86f9a6
--- a/include/drm/drmP.h
a86f9a6
+++ b/include/drm/drmP.h
a86f9a6
@@ -932,6 +932,10 @@ extern void drm_send_vblank_event(struct drm_device *dev, unsigned int pipe,
a86f9a6
 				  struct drm_pending_vblank_event *e);
a86f9a6
 extern void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
a86f9a6
 				       struct drm_pending_vblank_event *e);
a86f9a6
+extern void drm_arm_vblank_event(struct drm_device *dev, unsigned int pipe,
a86f9a6
+				 struct drm_pending_vblank_event *e);
a86f9a6
+extern void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
a86f9a6
+				      struct drm_pending_vblank_event *e);
a86f9a6
 extern bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe);
a86f9a6
 extern bool drm_crtc_handle_vblank(struct drm_crtc *crtc);
a86f9a6
 extern int drm_vblank_get(struct drm_device *dev, unsigned int pipe);
a86f9a6
-- 
a86f9a6
2.5.0
a86f9a6