drm: add support for monotonic vblank timestamps

Jumps in the vblank and page flip event timestamps cause trouble for
clients, so we should avoid them. The timestamp we get currently with
gettimeofday can jump, so use instead monotonic timestamps.

For backward compatibility use a module flag to revert back to using
gettimeofday timestamps. Add also a DRM_CAP_TIMESTAMP_MONOTONIC flag
that is simply a read only version of the module flag, so that clients
can query this without depending on sysfs.

Signed-off-by: Imre Deak <imre.deak@intel.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index ef5b5f7..c0f0046 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -633,7 +633,8 @@
 
 		/* Get system timestamp after query. */
 		etime = ktime_get();
-		mono_time_offset = ktime_get_monotonic_offset();
+		if (!drm_timestamp_monotonic)
+			mono_time_offset = ktime_get_monotonic_offset();
 
 		preempt_enable();
 
@@ -691,7 +692,9 @@
 		vbl_status |= 0x8;
 	}
 
-	etime = ktime_sub(etime, mono_time_offset);
+	if (!drm_timestamp_monotonic)
+		etime = ktime_sub(etime, mono_time_offset);
+
 	/* save this only for debugging purposes */
 	tv_etime = ktime_to_timeval(etime);
 	/* Subtract time delta from raw timestamp to get final
@@ -714,6 +717,17 @@
 }
 EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
 
+static struct timeval get_drm_timestamp(void)
+{
+	ktime_t now;
+
+	now = ktime_get();
+	if (!drm_timestamp_monotonic)
+		now = ktime_sub(now, ktime_get_monotonic_offset());
+
+	return ktime_to_timeval(now);
+}
+
 /**
  * drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent
  * vblank interval.
@@ -751,9 +765,9 @@
 	}
 
 	/* GPU high precision timestamp query unsupported or failed.
-	 * Return gettimeofday timestamp as best estimate.
+	 * Return current monotonic/gettimeofday timestamp as best estimate.
 	 */
-	do_gettimeofday(tvblank);
+	*tvblank = get_drm_timestamp();
 
 	return 0;
 }
@@ -842,7 +856,8 @@
 		seq = drm_vblank_count_and_time(dev, crtc, &now);
 	} else {
 		seq = 0;
-		do_gettimeofday(&now);
+
+		now = get_drm_timestamp();
 	}
 	send_vblank_event(dev, e, seq, &now);
 }