the <methodname>page_flip</methodname> operation will be called with a
             non-NULL <parameter>event</parameter> argument pointing to a
             <structname>drm_pending_vblank_event</structname> instance. Upon page
-            flip completion the driver must fill the
-            <parameter>event</parameter>::<structfield>event</structfield>
-            <structfield>sequence</structfield>, <structfield>tv_sec</structfield>
-            and <structfield>tv_usec</structfield> fields with the associated
-            vertical blanking count and timestamp, add the event to the
-            <parameter>drm_file</parameter> list of events to be signaled, and wake
-            up any waiting process. This can be performed with
+            flip completion the driver must call <methodname>drm_send_vblank_event</methodname>
+            to fill in the event and send to wake up any waiting processes.
+            This can be performed with
             <programlisting><![CDATA[
-            struct timeval now;
-
-            event->event.sequence = drm_vblank_count_and_time(..., &now);
-            event->event.tv_sec = now.tv_sec;
-            event->event.tv_usec = now.tv_usec;
-
             spin_lock_irqsave(&dev->event_lock, flags);
-            list_add_tail(&event->base.link, &event->base.file_priv->event_list);
-            wake_up_interruptible(&event->base.file_priv->event_wait);
+            ...
+            drm_send_vblank_event(dev, pipe, event);
             spin_unlock_irqrestore(&dev->event_lock, flags);
             ]]></programlisting>
           </para>
 
 }
 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;
+               do_gettimeofday(&now);
+       }
+       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
 }
 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;
                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);
 
        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);
                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);