drm/i915: Add support for precise vblank timestamping (v2)

v2: Change IS_IRONLAKE to IS_GEN5 to adapt to 2.6.37

This patch adds new functions for use by the drm core:

.get_vblank_timestamp() provides a precise timestamp
for the end of the most recent (or current) vblank
interval of a given crtc, as needed for the DRI2
implementation of the OML_sync_control extension.
It is a thin wrapper around the drm function
drm_calc_vbltimestamp_from_scanoutpos() which does
almost all the work.

.get_scanout_position() provides the current horizontal
and vertical video scanout position and "in vblank"
status of a given crtc, as needed by the drm for use by
drm_calc_vbltimestamp_from_scanoutpos().

The patch modifies the pageflip completion routine
to use these precise vblank timestamps as the timestamps
for pageflip completion events.

This code has been only tested on a HP-Mini Netbook with
Atom processor and Intel 945GME gpu. The codepath for
(IS_G4X(dev) || IS_GEN5(dev) || IS_GEN6(dev)) gpu's
has not been tested so far due to lack of hardware.

Signed-off-by: Mario Kleiner <mario.kleiner@tuebingen.mpg.de>
Acked-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 8645a97..0c201d6 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -5252,7 +5252,8 @@
 }
 
 static void do_intel_finish_page_flip(struct drm_device *dev,
-				      struct drm_crtc *crtc)
+				      struct drm_crtc *crtc,
+				      int called_before_vblirq)
 {
 	drm_i915_private_t *dev_priv = dev->dev_private;
 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
@@ -5274,19 +5275,33 @@
 	}
 
 	intel_crtc->unpin_work = NULL;
-	drm_vblank_put(dev, intel_crtc->pipe);
 
 	if (work->event) {
 		e = work->event;
-		do_gettimeofday(&now);
-		e->event.sequence = drm_vblank_count(dev, intel_crtc->pipe);
+		e->event.sequence = drm_vblank_count_and_time(dev, intel_crtc->pipe, &now);
+
+		/* Called before vblank count and timestamps have
+		 * been updated for the vblank interval of flip
+		 * completion? Need to increment vblank count and
+		 * add one videorefresh duration to returned timestamp
+		 * to account for this.
+		 */
+		if (called_before_vblirq) {
+			e->event.sequence++;
+			now = ns_to_timeval(timeval_to_ns(&now) +
+					    crtc->framedur_ns);
+		}
+
 		e->event.tv_sec = now.tv_sec;
 		e->event.tv_usec = now.tv_usec;
+
 		list_add_tail(&e->base.link,
 			      &e->base.file_priv->event_list);
 		wake_up_interruptible(&e->base.file_priv->event_wait);
 	}
 
+	drm_vblank_put(dev, intel_crtc->pipe);
+
 	spin_unlock_irqrestore(&dev->event_lock, flags);
 
 	obj = work->old_fb_obj;
@@ -5306,7 +5321,8 @@
 	drm_i915_private_t *dev_priv = dev->dev_private;
 	struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
 
-	do_intel_finish_page_flip(dev, crtc);
+	/* Called after drm_handle_vblank has run for finish vblank. */
+	do_intel_finish_page_flip(dev, crtc, 0);
 }
 
 void intel_finish_page_flip_plane(struct drm_device *dev, int plane)
@@ -5314,7 +5330,8 @@
 	drm_i915_private_t *dev_priv = dev->dev_private;
 	struct drm_crtc *crtc = dev_priv->plane_to_crtc_mapping[plane];
 
-	do_intel_finish_page_flip(dev, crtc);
+	/* Called before drm_handle_vblank has run for finish vblank. */
+	do_intel_finish_page_flip(dev, crtc, 1);
 }
 
 void intel_prepare_page_flip(struct drm_device *dev, int plane)