int drm_mode_page_flip_ioctl(struct drm_device *dev,
                             void *data, struct drm_file *file_priv)
 {
-       struct drm_mode_crtc_page_flip *page_flip = data;
+       struct drm_mode_crtc_page_flip_target *page_flip = data;
        struct drm_crtc *crtc;
        struct drm_framebuffer *fb = NULL;
        struct drm_pending_vblank_event *e = NULL;
-       u32 target_vblank = 0;
+       u32 target_vblank = page_flip->sequence;
        int ret = -EINVAL;
 
-       if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
-           page_flip->reserved != 0)
+       if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS)
+               return -EINVAL;
+
+       if (page_flip->sequence != 0 && !(page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET))
+               return -EINVAL;
+
+       /* Only one of the DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags
+        * can be specified
+        */
+       if ((page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET) == DRM_MODE_PAGE_FLIP_TARGET)
                return -EINVAL;
 
        if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip)
                return -ENOENT;
 
        if (crtc->funcs->page_flip_target) {
+               u32 current_vblank;
                int r;
 
                r = drm_crtc_vblank_get(crtc);
                if (r)
                        return r;
 
-               target_vblank = drm_crtc_vblank_count(crtc) +
-                       !(page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC);
-       } else if (crtc->funcs->page_flip == NULL) {
+               current_vblank = drm_crtc_vblank_count(crtc);
+
+               switch (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET) {
+               case DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE:
+                       if ((int)(target_vblank - current_vblank) > 1) {
+                               DRM_DEBUG("Invalid absolute flip target %u, "
+                                         "must be <= %u\n", target_vblank,
+                                         current_vblank + 1);
+                               drm_crtc_vblank_put(crtc);
+                               return -EINVAL;
+                       }
+                       break;
+               case DRM_MODE_PAGE_FLIP_TARGET_RELATIVE:
+                       if (target_vblank != 0 && target_vblank != 1) {
+                               DRM_DEBUG("Invalid relative flip target %u, "
+                                         "must be 0 or 1\n", target_vblank);
+                               drm_crtc_vblank_put(crtc);
+                               return -EINVAL;
+                       }
+                       target_vblank += current_vblank;
+                       break;
+               default:
+                       target_vblank = current_vblank +
+                               !(page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC);
+                       break;
+               }
+       } else if (crtc->funcs->page_flip == NULL ||
+                  (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET)) {
                return -EINVAL;
        }
 
 
 static int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
 {
        struct drm_get_cap *req = data;
+       struct drm_crtc *crtc;
 
        req->value = 0;
        switch (req->capability) {
        case DRM_CAP_ASYNC_PAGE_FLIP:
                req->value = dev->mode_config.async_page_flip;
                break;
+       case DRM_CAP_PAGE_FLIP_TARGET:
+               req->value = 1;
+               drm_for_each_crtc(crtc, dev) {
+                       if (!crtc->funcs->page_flip_target)
+                               req->value = 0;
+               }
+               break;
        case DRM_CAP_CURSOR_WIDTH:
                if (dev->mode_config.cursor_width)
                        req->value = dev->mode_config.cursor_width;
 
 #define DRM_CAP_CURSOR_WIDTH           0x8
 #define DRM_CAP_CURSOR_HEIGHT          0x9
 #define DRM_CAP_ADDFB2_MODIFIERS       0x10
+#define DRM_CAP_PAGE_FLIP_TARGET       0x11
 
 /** DRM_IOCTL_GET_CAP ioctl argument type */
 struct drm_get_cap {
 
 
 #define DRM_MODE_PAGE_FLIP_EVENT 0x01
 #define DRM_MODE_PAGE_FLIP_ASYNC 0x02
-#define DRM_MODE_PAGE_FLIP_FLAGS (DRM_MODE_PAGE_FLIP_EVENT|DRM_MODE_PAGE_FLIP_ASYNC)
+#define DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE 0x4
+#define DRM_MODE_PAGE_FLIP_TARGET_RELATIVE 0x8
+#define DRM_MODE_PAGE_FLIP_TARGET (DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE | \
+                                  DRM_MODE_PAGE_FLIP_TARGET_RELATIVE)
+#define DRM_MODE_PAGE_FLIP_FLAGS (DRM_MODE_PAGE_FLIP_EVENT | \
+                                 DRM_MODE_PAGE_FLIP_ASYNC | \
+                                 DRM_MODE_PAGE_FLIP_TARGET)
 
 /*
  * Request a page flip on the specified crtc.
  * 'as soon as possible', meaning that it not delay waiting for vblank.
  * This may cause tearing on the screen.
  *
- * The reserved field must be zero until we figure out something
- * clever to use it for.
+ * The reserved field must be zero.
  */
 
 struct drm_mode_crtc_page_flip {
        __u64 user_data;
 };
 
+/*
+ * Request a page flip on the specified crtc.
+ *
+ * Same as struct drm_mode_crtc_page_flip, but supports new flags and
+ * re-purposes the reserved field:
+ *
+ * The sequence field must be zero unless either of the
+ * DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags is specified. When
+ * the ABSOLUTE flag is specified, the sequence field denotes the absolute
+ * vblank sequence when the flip should take effect. When the RELATIVE
+ * flag is specified, the sequence field denotes the relative (to the
+ * current one when the ioctl is called) vblank sequence when the flip
+ * should take effect. NOTE: DRM_IOCTL_WAIT_VBLANK must still be used to
+ * make sure the vblank sequence before the target one has passed before
+ * calling this ioctl. The purpose of the
+ * DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags is merely to clarify
+ * the target for when code dealing with a page flip runs during a
+ * vertical blank period.
+ */
+
+struct drm_mode_crtc_page_flip_target {
+       __u32 crtc_id;
+       __u32 fb_id;
+       __u32 flags;
+       __u32 sequence;
+       __u64 user_data;
+};
+
 /* create a dumb scanout buffer */
 struct drm_mode_create_dumb {
        __u32 height;