tizen 2.4 release
[kernel/linux-3.0.git] / drivers / gpu / drm / drm_irq.c
index 9a41842..5da4064 100644 (file)
@@ -575,7 +575,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;
@@ -624,13 +625,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();
 
@@ -641,7 +644,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)
@@ -688,14 +691,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);
 
@@ -707,6 +716,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.
@@ -744,9 +764,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;
 }
@@ -801,6 +821,47 @@ u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc,
 }
 EXPORT_SYMBOL(drm_vblank_count_and_time);
 
+static void send_vblank_event(struct drm_device *dev,
+               struct drm_pending_vblank_event *e,
+               unsigned long seq, struct timeval *now)
+{
+       WARN_ON_SMP(!spin_is_locked(&dev->event_lock));
+       e->event.sequence = seq;
+       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);
+       trace_drm_vblank_event_delivered(e->base.pid, e->pipe,
+                                        e->event.sequence);
+}
+
+/**
+ * drm_send_vblank_event - helper to send vblank event after pageflip
+ * @dev: DRM device
+ * @crtc: CRTC in question
+ * @e: the event to send
+ *
+ * Updates sequence # and timestamp on event, and sends it to userspace.
+ * Caller must hold event lock.
+ */
+void drm_send_vblank_event(struct drm_device *dev, int crtc,
+               struct drm_pending_vblank_event *e)
+{
+       struct timeval now;
+       unsigned int seq;
+       if (crtc >= 0) {
+               seq = drm_vblank_count_and_time(dev, crtc, &now);
+       } else {
+               seq = 0;
+
+               now = get_drm_timestamp();
+       }
+       send_vblank_event(dev, e, seq, &now);
+}
+EXPORT_SYMBOL(drm_send_vblank_event);
+
 /**
  * drm_update_vblank_count - update the master vblank counter
  * @dev: DRM device
@@ -935,6 +996,13 @@ void drm_vblank_put(struct drm_device *dev, int crtc)
 }
 EXPORT_SYMBOL(drm_vblank_put);
 
+/**
+ * drm_vblank_off - disable vblank events on a CRTC
+ * @dev: DRM device
+ * @crtc: CRTC in question
+ *
+ * Caller must hold event lock.
+ */
 void drm_vblank_off(struct drm_device *dev, int crtc)
 {
        struct drm_pending_vblank_event *e, *t;
@@ -954,15 +1022,9 @@ void drm_vblank_off(struct drm_device *dev, int crtc)
                DRM_DEBUG("Sending premature vblank event on disable: \
                          wanted %d, current %d\n",
                          e->event.sequence, seq);
-
-               e->event.sequence = seq;
-               e->event.tv_sec = now.tv_sec;
-               e->event.tv_usec = now.tv_usec;
+               list_del(&e->base.link);
                drm_vblank_put(dev, e->pipe);
-               list_move_tail(&e->base.link, &e->base.file_priv->event_list);
-               wake_up_interruptible(&e->base.file_priv->event_wait);
-               trace_drm_vblank_event_delivered(e->base.pid, e->pipe,
-                                                e->event.sequence);
+               send_vblank_event(dev, e, seq, &now);
        }
 
        spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
@@ -1112,15 +1174,9 @@ static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
 
        e->event.sequence = vblwait->request.sequence;
        if ((seq - vblwait->request.sequence) <= (1 << 23)) {
-               e->event.sequence = seq;
-               e->event.tv_sec = now.tv_sec;
-               e->event.tv_usec = now.tv_usec;
                drm_vblank_put(dev, pipe);
-               list_add_tail(&e->base.link, &e->base.file_priv->event_list);
-               wake_up_interruptible(&e->base.file_priv->event_wait);
+               send_vblank_event(dev, e, seq, &now);
                vblwait->reply.sequence = seq;
-               trace_drm_vblank_event_delivered(current->pid, pipe,
-                                                vblwait->request.sequence);
        } else {
                /* drm_handle_vblank_events will call drm_vblank_put */
                list_add_tail(&e->base.link, &dev->vblank_event_list);
@@ -1261,14 +1317,9 @@ void drm_handle_vblank_events(struct drm_device *dev, int crtc)
                DRM_DEBUG("vblank event on %d, current %d\n",
                          e->event.sequence, seq);
 
-               e->event.sequence = seq;
-               e->event.tv_sec = now.tv_sec;
-               e->event.tv_usec = now.tv_usec;
+               list_del(&e->base.link);
                drm_vblank_put(dev, e->pipe);
-               list_move_tail(&e->base.link, &e->base.file_priv->event_list);
-               wake_up_interruptible(&e->base.file_priv->event_wait);
-               trace_drm_vblank_event_delivered(e->base.pid, e->pipe,
-                                                e->event.sequence);
+               send_vblank_event(dev, e, seq, &now);
        }
 
        spin_unlock_irqrestore(&dev->event_lock, flags);