drm/nouveau/bios: cache ramcfg strap on later chipsets
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / drm_irq.c
index 44602ef55afc765c6bb968276cfd1905d18e364b..19c01ca3cc76aaa2f5fe7cae45f8759104f74190 100644 (file)
@@ -106,6 +106,7 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc)
        s64 diff_ns;
        int vblrc;
        struct timeval tvblank;
+       int count = DRM_TIMESTAMP_MAXRETRIES;
 
        /* Prevent vblank irq processing while disabling vblank irqs,
         * so no updates of timestamps or count can happen after we've
@@ -131,7 +132,10 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc)
        do {
                dev->last_vblank[crtc] = dev->driver->get_vblank_counter(dev, crtc);
                vblrc = drm_get_last_vbltimestamp(dev, crtc, &tvblank, 0);
-       } while (dev->last_vblank[crtc] != dev->driver->get_vblank_counter(dev, crtc));
+       } while (dev->last_vblank[crtc] != dev->driver->get_vblank_counter(dev, crtc) && (--count) && vblrc);
+
+       if (!count)
+               vblrc = 0;
 
        /* Compute time difference to stored timestamp of last vblank
         * as updated by last invocation of drm_handle_vblank() in vblank irq.
@@ -576,7 +580,8 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
                                          unsigned flags,
                                          struct drm_crtc *refcrtc)
 {
-       struct timeval stime, raw_time;
+       ktime_t stime, etime, mono_time_offset;
+       struct timeval tv_etime;
        struct drm_display_mode *mode;
        int vbl_status, vtotal, vdisplay;
        int vpos, hpos, i;
@@ -625,13 +630,15 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
                preempt_disable();
 
                /* Get system timestamp before query. */
-               do_gettimeofday(&stime);
+               stime = ktime_get();
 
                /* Get vertical and horizontal scanout pos. vpos, hpos. */
                vbl_status = dev->driver->get_scanout_position(dev, crtc, &vpos, &hpos);
 
                /* Get system timestamp after query. */
-               do_gettimeofday(&raw_time);
+               etime = ktime_get();
+               if (!drm_timestamp_monotonic)
+                       mono_time_offset = ktime_get_monotonic_offset();
 
                preempt_enable();
 
@@ -642,7 +649,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
                        return -EIO;
                }
 
-               duration_ns = timeval_to_ns(&raw_time) - timeval_to_ns(&stime);
+               duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime);
 
                /* Accept result with <  max_error nsecs timing uncertainty. */
                if (duration_ns <= (s64) *max_error)
@@ -689,14 +696,20 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
                vbl_status |= 0x8;
        }
 
+       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
         * vblank_time timestamp for end of vblank.
         */
-       *vblank_time = ns_to_timeval(timeval_to_ns(&raw_time) - delta_ns);
+       etime = ktime_sub_ns(etime, delta_ns);
+       *vblank_time = ktime_to_timeval(etime);
 
        DRM_DEBUG("crtc %d : v %d p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
                  crtc, (int)vbl_status, hpos, vpos,
-                 (long)raw_time.tv_sec, (long)raw_time.tv_usec,
+                 (long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
                  (long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
                  (int)duration_ns/1000, i);
 
@@ -708,6 +721,17 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
 }
 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.
@@ -745,9 +769,9 @@ u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc,
        }
 
        /* 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;
 }
@@ -836,7 +860,8 @@ void drm_send_vblank_event(struct drm_device *dev, int crtc,
                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);
 }
@@ -996,6 +1021,8 @@ void drm_vblank_off(struct drm_device *dev, int crtc)
 
        /* Send any queued vblank events, lest the natives grow disquiet */
        seq = drm_vblank_count_and_time(dev, crtc, &now);
+
+       spin_lock(&dev->event_lock);
        list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
                if (e->pipe != crtc)
                        continue;
@@ -1006,6 +1033,7 @@ void drm_vblank_off(struct drm_device *dev, int crtc)
                drm_vblank_put(dev, e->pipe);
                send_vblank_event(dev, e, seq, &now);
        }
+       spin_unlock(&dev->event_lock);
 
        spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
 }