Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / drm_irq.c
index 15f748c7fa7ebfd2bcaf4688b2a6a7a7f008f8e9..5ef03c216a2740818468ef64ebb1a056f1644235 100644 (file)
@@ -34,6 +34,7 @@
 
 #include <drm/drmP.h>
 #include "drm_trace.h"
+#include "drm_internal.h"
 
 #include <linux/interrupt.h>   /* For task queue support */
 #include <linux/slab.h>
@@ -59,6 +60,20 @@ static bool
 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc,
                          struct timeval *tvblank, unsigned flags);
 
+static unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
+
+/*
+ * Default to use monotonic timestamps for wait-for-vblank and page-flip
+ * complete events.
+ */
+unsigned int drm_timestamp_monotonic = 1;
+
+static int drm_vblank_offdelay = 5000;    /* Default to 5000 msecs. */
+
+module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
+module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
+module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
+
 /**
  * drm_update_vblank_count - update the master vblank counter
  * @dev: DRM device
@@ -111,6 +126,9 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc)
        DRM_DEBUG("updating vblank count on crtc %d, missed %d\n",
                  crtc, diff);
 
+       if (diff == 0)
+               return;
+
        /* Reinitialize corresponding vblank timestamp if high-precision query
         * available. Skip this step if query unsupported or failed. Will
         * reinitialize delayed at next vblank interrupt in that case.
@@ -639,8 +657,8 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
                                          const struct drm_crtc *refcrtc,
                                          const struct drm_display_mode *mode)
 {
-       ktime_t stime, etime, mono_time_offset;
        struct timeval tv_etime;
+       ktime_t stime, etime;
        int vbl_status;
        int vpos, hpos, i;
        int framedur_ns, linedur_ns, pixeldur_ns, delta_ns, duration_ns;
@@ -685,13 +703,6 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
                vbl_status = dev->driver->get_scanout_position(dev, crtc, flags, &vpos,
                                                               &hpos, &stime, &etime);
 
-               /*
-                * Get correction for CLOCK_MONOTONIC -> CLOCK_REALTIME if
-                * CLOCK_REALTIME is requested.
-                */
-               if (!drm_timestamp_monotonic)
-                       mono_time_offset = ktime_get_monotonic_offset();
-
                /* Return as no-op if scanout query unsupported or failed. */
                if (!(vbl_status & DRM_SCANOUTPOS_VALID)) {
                        DRM_DEBUG("crtc %d : scanoutpos query failed [%d].\n",
@@ -721,7 +732,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
         * within vblank area, counting down the number of lines until
         * start of scanout.
         */
-       invbl = vbl_status & DRM_SCANOUTPOS_INVBL;
+       invbl = vbl_status & DRM_SCANOUTPOS_IN_VBLANK;
 
        /* Convert scanout position into elapsed time at raw_time query
         * since start of scanout at first display scanline. delta_ns
@@ -730,7 +741,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
        delta_ns = vpos * linedur_ns + hpos * pixeldur_ns;
 
        if (!drm_timestamp_monotonic)
-               etime = ktime_sub(etime, mono_time_offset);
+               etime = ktime_mono_to_real(etime);
 
        /* save this only for debugging purposes */
        tv_etime = ktime_to_timeval(etime);
@@ -751,7 +762,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
 
        vbl_status = DRM_VBLANKTIME_SCANOUTPOS_METHOD;
        if (invbl)
-               vbl_status |= DRM_VBLANKTIME_INVBL;
+               vbl_status |= DRM_VBLANKTIME_IN_VBLANK;
 
        return vbl_status;
 }
@@ -761,10 +772,7 @@ 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());
-
+       now = drm_timestamp_monotonic ? ktime_get() : ktime_get_real();
        return ktime_to_timeval(now);
 }
 
@@ -829,6 +837,8 @@ u32 drm_vblank_count(struct drm_device *dev, int crtc)
 {
        struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
 
+       if (WARN_ON(crtc >= dev->num_crtcs))
+               return 0;
        return atomic_read(&vblank->count);
 }
 EXPORT_SYMBOL(drm_vblank_count);
@@ -852,6 +862,9 @@ u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc,
        struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
        u32 cur_vblank;
 
+       if (WARN_ON(crtc >= dev->num_crtcs))
+               return 0;
+
        /* Read timestamp from slot of _vblank_time ringbuffer
         * that corresponds to current vblank count. Retry if
         * count has incremented during readout. This works like
@@ -965,6 +978,9 @@ int drm_vblank_get(struct drm_device *dev, int crtc)
        unsigned long irqflags;
        int ret = 0;
 
+       if (WARN_ON(crtc >= dev->num_crtcs))
+               return -EINVAL;
+
        spin_lock_irqsave(&dev->vbl_lock, irqflags);
        /* Going from 0->1 means we have to enable interrupts again */
        if (atomic_add_return(1, &vblank->refcount) == 1) {
@@ -1015,6 +1031,9 @@ void drm_vblank_put(struct drm_device *dev, int crtc)
 
        BUG_ON(atomic_read(&vblank->refcount) == 0);
 
+       if (WARN_ON(crtc >= dev->num_crtcs))
+               return;
+
        /* Last user schedules interrupt disable */
        if (atomic_dec_and_test(&vblank->refcount)) {
                if (drm_vblank_offdelay == 0)
@@ -1043,6 +1062,50 @@ void drm_crtc_vblank_put(struct drm_crtc *crtc)
 }
 EXPORT_SYMBOL(drm_crtc_vblank_put);
 
+/**
+ * drm_wait_one_vblank - wait for one vblank
+ * @dev: DRM device
+ * @crtc: crtc index
+ *
+ * This waits for one vblank to pass on @crtc, using the irq driver interfaces.
+ * It is a failure to call this when the vblank irq for @crtc is disabled, e.g.
+ * due to lack of driver support or because the crtc is off.
+ */
+void drm_wait_one_vblank(struct drm_device *dev, int crtc)
+{
+       int ret;
+       u32 last;
+
+       ret = drm_vblank_get(dev, crtc);
+       if (WARN(ret, "vblank not available on crtc %i, ret=%i\n", crtc, ret))
+               return;
+
+       last = drm_vblank_count(dev, crtc);
+
+       ret = wait_event_timeout(dev->vblank[crtc].queue,
+                                last != drm_vblank_count(dev, crtc),
+                                msecs_to_jiffies(100));
+
+       WARN(ret == 0, "vblank wait timed out on crtc %i\n", crtc);
+
+       drm_vblank_put(dev, crtc);
+}
+EXPORT_SYMBOL(drm_wait_one_vblank);
+
+/**
+ * drm_crtc_wait_one_vblank - wait for one vblank
+ * @crtc: DRM crtc
+ *
+ * This waits for one vblank to pass on @crtc, using the irq driver interfaces.
+ * It is a failure to call this when the vblank irq for @crtc is disabled, e.g.
+ * due to lack of driver support or because the crtc is off.
+ */
+void drm_crtc_wait_one_vblank(struct drm_crtc *crtc)
+{
+       drm_wait_one_vblank(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_wait_one_vblank);
+
 /**
  * drm_vblank_off - disable vblank events on a CRTC
  * @dev: DRM device
@@ -1065,6 +1128,9 @@ void drm_vblank_off(struct drm_device *dev, int crtc)
        unsigned long irqflags;
        unsigned int seq;
 
+       if (WARN_ON(crtc >= dev->num_crtcs))
+               return;
+
        spin_lock_irqsave(&dev->event_lock, irqflags);
 
        spin_lock(&dev->vbl_lock);
@@ -1134,6 +1200,9 @@ void drm_vblank_on(struct drm_device *dev, int crtc)
        struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
        unsigned long irqflags;
 
+       if (WARN_ON(crtc >= dev->num_crtcs))
+               return;
+
        spin_lock_irqsave(&dev->vbl_lock, irqflags);
        /* Drop our private "prevent drm_vblank_get" refcount */
        if (vblank->inmodeset) {
@@ -1209,6 +1278,10 @@ void drm_vblank_pre_modeset(struct drm_device *dev, int crtc)
        /* vblank is not initialized (IRQ not installed ?), or has been freed */
        if (!dev->num_crtcs)
                return;
+
+       if (WARN_ON(crtc >= dev->num_crtcs))
+               return;
+
        /*
         * To avoid all the problems that might happen if interrupts
         * were enabled/disabled around or between these calls, we just
@@ -1532,6 +1605,9 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc)
        if (!dev->num_crtcs)
                return false;
 
+       if (WARN_ON(crtc >= dev->num_crtcs))
+               return false;
+
        spin_lock_irqsave(&dev->event_lock, irqflags);
 
        /* Need timestamp lock to prevent concurrent execution with