[PATCH 4/4] modesetting: Add support for DRI2 with glamor.

Eric Anholt eric at anholt.net
Fri Oct 10 02:09:40 PDT 2014


This is derived from the intel driver DRI2 code, with swapchain
dropped, functions renamed, and pageflipping shared code moved to a
pageflip.c for reuse by Present.

This allows AIGLX to load, which means that you get appropriate
visuals exposed in GL, along with many extensions under
direct-rendering that require presence in GLX (which aren't supported
in glxdriswrast.c).

Signed-off-by: Eric Anholt <eric at anholt.net>
---
 hw/xfree86/drivers/modesetting/Makefile.am       |    6 +-
 hw/xfree86/drivers/modesetting/dri2.c            | 1122 ++++++++++++++++++++++
 hw/xfree86/drivers/modesetting/driver.c          |   26 +-
 hw/xfree86/drivers/modesetting/driver.h          |   70 ++
 hw/xfree86/drivers/modesetting/drmmode_display.c |    5 +
 hw/xfree86/drivers/modesetting/drmmode_display.h |   26 +
 hw/xfree86/drivers/modesetting/pageflip.c        |  678 +++++++++++++
 7 files changed, 1931 insertions(+), 2 deletions(-)
 create mode 100644 hw/xfree86/drivers/modesetting/dri2.c
 create mode 100644 hw/xfree86/drivers/modesetting/pageflip.c

diff --git a/hw/xfree86/drivers/modesetting/Makefile.am b/hw/xfree86/drivers/modesetting/Makefile.am
index e6834e2..63fdd8f 100644
--- a/hw/xfree86/drivers/modesetting/Makefile.am
+++ b/hw/xfree86/drivers/modesetting/Makefile.am
@@ -30,6 +30,7 @@ AM_CPPFLAGS = \
 	$(XORG_INCS) \
 	-I$(top_srcdir)/glamor \
 	-I$(srcdir)/../../ddc \
+	-I$(srcdir)/../../dri2 \
 	-I$(srcdir)/../../i2c \
 	-I$(srcdir)/../../modes \
 	-I$(srcdir)/../../parser \
@@ -42,10 +43,13 @@ modesetting_drv_la_LIBADD = $(UDEV_LIBS) $(DRM_LIBS)
 modesetting_drv_ladir = @moduledir@/drivers
 
 modesetting_drv_la_SOURCES = \
+	 dri2.c \
 	 driver.c \
 	 driver.h \
 	 drmmode_display.c \
-	 drmmode_display.h
+	 drmmode_display.h \
+	 pageflip.c \
+	 $(NULL)
 
 drivermandir = $(DRIVER_MAN_DIR)
 driverman_PRE = modesetting.man
diff --git a/hw/xfree86/drivers/modesetting/dri2.c b/hw/xfree86/drivers/modesetting/dri2.c
new file mode 100644
index 0000000..33be405
--- /dev/null
+++ b/hw/xfree86/drivers/modesetting/dri2.c
@@ -0,0 +1,1122 @@
+/*
+ * Copyright © 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifdef HAVE_DIX_CONFIG_H
+#include "dix-config.h"
+#endif
+
+#include <time.h>
+#include "list.h"
+#include "xf86.h"
+#include "driver.h"
+#include "dri2.h"
+
+#ifdef GLAMOR
+#define GLAMOR_FOR_XORG 1
+#include "glamor.h"
+
+enum ms_dri2_frame_event_type {
+    DRI2_SWAP,
+    DRI2_FLIP,
+    DRI2_WAITMSC,
+};
+
+typedef struct ms_dri2_frame_event {
+    ScreenPtr screen;
+
+    DrawablePtr drawable;
+    ClientPtr client;
+    enum ms_dri2_frame_event_type type;
+    int frame;
+    int pipe;
+
+    struct xorg_list drawable_resource, client_resource;
+
+    /* for swaps & flips only */
+    DRI2SwapEventPtr event_complete;
+    void *event_data;
+    DRI2BufferPtr front;
+    DRI2BufferPtr back;
+} ms_dri2_frame_event_rec, *ms_dri2_frame_event_ptr;
+
+typedef struct {
+    int refcnt;
+    PixmapPtr pixmap;
+} ms_dri2_buffer_private_rec, *ms_dri2_buffer_private_ptr;
+
+static DevPrivateKeyRec ms_dri2_client_key;
+static RESTYPE frame_event_client_type, frame_event_drawable_type;
+static int ms_dri2_server_generation;
+
+struct ms_dri2_resource {
+    XID id;
+    RESTYPE type;
+    struct xorg_list list;
+};
+
+static struct ms_dri2_resource *
+ms_get_resource(XID id, RESTYPE type)
+{
+    struct ms_dri2_resource *resource;
+    void *ptr;
+
+    ptr = NULL;
+    dixLookupResourceByType(&ptr, id, type, NULL, DixWriteAccess);
+    if (ptr)
+        return ptr;
+
+    resource = malloc(sizeof(*resource));
+    if (resource == NULL)
+        return NULL;
+
+    if (!AddResource(id, type, resource)) {
+        free(resource);
+        return NULL;
+    }
+
+    resource->id = id;
+    resource->type = type;
+    xorg_list_init(&resource->list);
+    return resource;
+}
+
+static inline PixmapPtr
+get_drawable_pixmap(DrawablePtr drawable)
+{
+    ScreenPtr screen = drawable->pScreen;
+
+    if (drawable->type == DRAWABLE_PIXMAP)
+        return (PixmapPtr) drawable;
+    else
+        return screen->GetWindowPixmap((WindowPtr) drawable);
+}
+
+static PixmapPtr
+get_front_buffer(DrawablePtr drawable)
+{
+    PixmapPtr pixmap;
+
+    pixmap = get_drawable_pixmap(drawable);
+    pixmap->refcnt++;
+
+    return pixmap;
+}
+
+static DRI2Buffer2Ptr
+ms_dri2_create_buffer(DrawablePtr drawable, unsigned int attachment,
+                      unsigned int format)
+{
+    ScreenPtr screen = drawable->pScreen;
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    DRI2Buffer2Ptr buffer;
+    PixmapPtr pixmap;
+    uint32_t size;
+    uint16_t pitch;
+    ms_dri2_buffer_private_ptr private;
+
+    buffer = calloc(1, sizeof *buffer);
+    if (buffer == NULL)
+        return NULL;
+
+    private = calloc(1, sizeof(*private));
+    if (private == NULL) {
+        free(buffer);
+        return NULL;
+    }
+
+    pixmap = NULL;
+    if (attachment == DRI2BufferFrontLeft)
+        pixmap = get_front_buffer(drawable);
+
+    if (pixmap == NULL) {
+        int pixmap_width = drawable->width;
+        int pixmap_height = drawable->height;
+        int pixmap_cpp = (format != 0) ? format : drawable->depth;
+
+        /* Assume that non-color-buffers require special
+         * device-specific handling.  Mesa currently makes no requests
+         * for non-color aux buffers.
+         */
+        switch (attachment) {
+        case DRI2BufferAccum:
+        case DRI2BufferBackLeft:
+        case DRI2BufferBackRight:
+        case DRI2BufferFakeFrontLeft:
+        case DRI2BufferFakeFrontRight:
+        case DRI2BufferFrontLeft:
+        case DRI2BufferFrontRight:
+            break;
+
+        case DRI2BufferStencil:
+        case DRI2BufferDepth:
+        case DRI2BufferDepthStencil:
+        case DRI2BufferHiz:
+        default:
+            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+                       "Request for DRI2 buffer attachment %d unsupported\n",
+                       attachment);
+            free(private);
+            free(buffer);
+            return NULL;
+        }
+
+        pixmap = screen->CreatePixmap(screen,
+                                      pixmap_width,
+                                      pixmap_height,
+                                      pixmap_cpp,
+                                      0);
+        if (pixmap == NULL) {
+            if (pixmap)
+                screen->DestroyPixmap(pixmap);
+            free(private);
+            free(buffer);
+            return NULL;
+        }
+    }
+
+    buffer->attachment = attachment;
+    buffer->cpp = pixmap->drawable.bitsPerPixel / 8;
+    buffer->format = format;
+    /* The buffer's flags field is unused by the client drivers in
+     * Mesa currently.
+     */
+    buffer->flags = 0;
+
+    buffer->name = glamor_name_from_pixmap(pixmap, &pitch, &size);
+    buffer->pitch = pitch;
+    if (buffer->name == -1) {
+        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+                   "Failed to get DRI2 name for pixmap\n");
+        screen->DestroyPixmap(pixmap);
+        free(private);
+        free(buffer);
+        return NULL;
+    }
+
+    buffer->driverPrivate = private;
+    private->refcnt = 1;
+    private->pixmap = pixmap;
+
+    return buffer;
+}
+
+static void
+ms_dri2_reference_buffer(DRI2Buffer2Ptr buffer)
+{
+    if (buffer) {
+        ms_dri2_buffer_private_ptr private = buffer->driverPrivate;
+        private->refcnt++;
+    }
+}
+
+static void ms_dri2_destroy_buffer(DrawablePtr drawable, DRI2Buffer2Ptr buffer)
+{
+    if (!buffer)
+        return;
+
+    if (buffer->driverPrivate) {
+        ms_dri2_buffer_private_ptr private = buffer->driverPrivate;
+        if (--private->refcnt == 0) {
+            ScreenPtr screen = private->pixmap->drawable.pScreen;
+            screen->DestroyPixmap(private->pixmap);
+            free(private);
+            free(buffer);
+        }
+    } else {
+        free(buffer);
+    }
+}
+
+static void
+ms_dri2_copy_region(DrawablePtr drawable, RegionPtr pRegion,
+                    DRI2BufferPtr destBuffer, DRI2BufferPtr sourceBuffer)
+{
+    ms_dri2_buffer_private_ptr src_priv = sourceBuffer->driverPrivate;
+    ms_dri2_buffer_private_ptr dst_priv = destBuffer->driverPrivate;
+    PixmapPtr src_pixmap = src_priv->pixmap;
+    PixmapPtr dst_pixmap = dst_priv->pixmap;
+    ScreenPtr screen = drawable->pScreen;
+    DrawablePtr src = (sourceBuffer->attachment == DRI2BufferFrontLeft)
+        ? drawable : &src_pixmap->drawable;
+    DrawablePtr dst = (destBuffer->attachment == DRI2BufferFrontLeft)
+        ? drawable : &dst_pixmap->drawable;
+    RegionPtr pCopyClip;
+    GCPtr gc;
+
+    gc = GetScratchGC(dst->depth, screen);
+    if (!gc)
+        return;
+
+    pCopyClip = REGION_CREATE(screen, NULL, 0);
+    REGION_COPY(screen, pCopyClip, pRegion);
+    (*gc->funcs->ChangeClip) (gc, CT_REGION, pCopyClip, 0);
+    ValidateGC(dst, gc);
+
+    /* It's important that this copy gets submitted before the direct
+     * rendering client submits rendering for the next frame, but we
+     * don't actually need to submit right now.  The client will wait
+     * for the DRI2CopyRegion reply or the swap buffer event before
+     * rendering, and we'll hit the flush callback chain before those
+     * messages are sent.  We submit our batch buffers from the flush
+     * callback chain so we know that will happen before the client
+     * tries to render again.
+     */
+    gc->ops->CopyArea(src, dst, gc,
+                      0, 0,
+                      drawable->width, drawable->height,
+                      0, 0);
+
+    FreeScratchGC(gc);
+}
+
+static uint64_t
+gettime_us(void)
+{
+    struct timespec tv;
+
+    if (clock_gettime(CLOCK_MONOTONIC, &tv))
+        return 0;
+
+    return (uint64_t)tv.tv_sec * 1000000 + tv.tv_nsec / 1000;
+}
+
+/**
+ * Get current frame count and frame count timestamp, based on drawable's
+ * crtc.
+ */
+static int
+ms_dri2_get_msc(DrawablePtr draw, CARD64 *ust, CARD64 *msc)
+{
+    int ret;
+    xf86CrtcPtr crtc = ms_dri2_crtc_covering_drawable(draw);
+
+    /* Drawable not displayed, make up a *monotonic* value */
+    if (crtc == NULL) {
+        *ust = gettime_us();
+        *msc = 0;
+        return TRUE;
+    }
+
+    ret = ms_get_crtc_ust_msc(crtc, ust, msc);
+
+    if (ret)
+        return FALSE;
+
+    return TRUE;
+}
+
+static XID
+get_client_id(ClientPtr client)
+{
+    XID *ptr = dixGetPrivateAddr(&client->devPrivates, &ms_dri2_client_key);
+    if (*ptr == 0)
+        *ptr = FakeClientID(client->index);
+    return *ptr;
+}
+
+/*
+ * Hook this frame event into the server resource
+ * database so we can clean it up if the drawable or
+ * client exits while the swap is pending
+ */
+static Bool
+ms_dri2_add_frame_event(ms_dri2_frame_event_ptr info)
+{
+    struct ms_dri2_resource *resource;
+
+    resource = ms_get_resource(get_client_id(info->client),
+                               frame_event_client_type);
+    if (resource == NULL)
+        return FALSE;
+
+    xorg_list_add(&info->client_resource, &resource->list);
+
+    resource = ms_get_resource(info->drawable->id, frame_event_drawable_type);
+    if (resource == NULL) {
+        xorg_list_del(&info->client_resource);
+        return FALSE;
+    }
+
+    xorg_list_add(&info->drawable_resource, &resource->list);
+
+    return TRUE;
+}
+
+static void
+ms_dri2_del_frame_event(ms_dri2_frame_event_rec *info)
+{
+    xorg_list_del(&info->client_resource);
+    xorg_list_del(&info->drawable_resource);
+
+    if (info->front)
+        ms_dri2_destroy_buffer(NULL, info->front);
+    if (info->back)
+        ms_dri2_destroy_buffer(NULL, info->back);
+
+    free(info);
+}
+
+static void
+ms_dri2_fallback_blit_swap(DrawablePtr drawable,
+                           DRI2BufferPtr dst,
+                           DRI2BufferPtr src)
+{
+    BoxRec box;
+    RegionRec region;
+
+    box.x1 = 0;
+    box.y1 = 0;
+    box.x2 = drawable->width;
+    box.y2 = drawable->height;
+    REGION_INIT(pScreen, &region, &box, 0);
+
+    ms_dri2_copy_region(drawable, &region, dst, src);
+}
+
+static void
+ms_exchange_pixmap_buffers(PixmapPtr front, PixmapPtr back,
+                           uint32_t *front_name, uint32_t *back_name)
+{
+    uint32_t tmp_name;
+
+    glamor_egl_exchange_buffers(front, back);
+
+    tmp_name = *front_name;
+    *front_name = *back_name;
+    *back_name = tmp_name;
+}
+
+static void
+ms_dri2_exchange_buffers(modesettingPtr ms,
+                         DRI2BufferPtr front, DRI2BufferPtr back)
+{
+    RegionRec region;
+    ms_dri2_buffer_private_ptr front_priv, back_priv;
+    DrawablePtr drawable;
+    front_priv = front->driverPrivate;
+    back_priv = back->driverPrivate;
+
+    /* Swap pixmap bos */
+    ms_exchange_pixmap_buffers(front_priv->pixmap, back_priv->pixmap,
+                               &front->name, &back->name);
+
+    /* Post damage on the front buffer so that listeners, such
+     * as DisplayLink know take a copy and shove it over the USB.
+     * also for sw cursors.
+     */
+    drawable = &front_priv->pixmap->drawable;
+    region.extents.x1 = region.extents.y1 = 0;
+    region.extents.x2 = drawable->width;
+    region.extents.y2 = drawable->height;
+    region.data = NULL;
+    DamageRegionAppend(drawable, &region);
+    DamageRegionProcessPending(drawable);
+}
+
+static void
+ms_dri2_flip_handler(uint64_t msc,
+                     uint64_t usec,
+                     void *data)
+{
+    ms_dri2_frame_event_ptr flip_info = data;
+    ScreenPtr screen = flip_info->screen;
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    uint32_t frame = msc;
+    uint32_t tv_sec = usec / 1000000;
+    uint32_t tv_usec = usec % 1000000;
+
+    switch (flip_info->type) {
+    case DRI2_SWAP:
+        if (!flip_info->drawable)
+            break;
+
+        /* Check for too small vblank count of pageflip completion,
+         * taking wraparound into account. This usually means some
+         * defective kms pageflip completion, causing wrong (msc, ust)
+         * return values and possible visual corruption.
+         */
+        if ((frame < flip_info->frame) && (flip_info->frame - frame < 5)) {
+            static int limit = 5;
+
+            /* XXX we are currently hitting this path with older
+             * kernels, so make it quieter.
+             */
+            if (limit) {
+                xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+                           "%s: Pageflip completion has impossible msc %d < "
+                           "target_msc %d\n",
+                           __func__, frame, flip_info->frame);
+                limit--;
+            }
+
+            /* All-0 values signal timestamping failure. */
+            frame = tv_sec = tv_usec = 0;
+        }
+
+        DRI2SwapComplete(flip_info->client, flip_info->drawable,
+                         frame, tv_sec, tv_usec,
+                         DRI2_FLIP_COMPLETE,
+                         flip_info->client ? flip_info->event_complete : NULL,
+                         flip_info->event_data);
+        break;
+
+    default:
+        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+                   "%s: unknown flip event (type %d) received\n", __func__,
+                   flip_info->type);
+        break;
+    }
+
+    ms_dri2_del_frame_event(flip_info);
+}
+
+static void
+ms_dri2_flip_abort(void *data)
+{
+    ms_dri2_frame_event_ptr flip_info = data;
+
+    ms_dri2_del_frame_event(flip_info);
+}
+
+/**
+ * Called either from ScheduleSwap when we just need to present the
+ * backbuffer at the next vblank, or from the vblank event handler if
+ * ScheduleSwap needed to wait for a later vblank before we could
+ * swap.
+ *
+ * When this call is done, there will be a flip request outstanding to
+ * the hardware which will start getting scanned out on the next
+ * vblank.
+ */
+static Bool
+ms_dri2_do_pageflip(DrawablePtr draw, ms_dri2_frame_event_ptr info)
+{
+    ScreenPtr screen = draw->pScreen;
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    modesettingPtr ms = modesettingPTR(scrn);
+    ms_dri2_buffer_private_ptr back_priv = info->back->driverPrivate;
+
+    if (!ms_do_pageflip(back_priv->pixmap, info->pipe, FALSE, info,
+                        ms_dri2_flip_handler, ms_dri2_flip_abort)) {
+        return FALSE;
+    }
+
+    /* Flip the front and back pointers.  Now we have:
+     * front:  backbuffer with flip scheduled to screen
+     * back:   old front buffer still being scanned out
+     * triple: idle
+     */
+    ms_dri2_exchange_buffers(ms, info->front, info->back);
+    DRI2SwapComplete(info->client, draw, 0, 0, 0,
+                     DRI2_EXCHANGE_COMPLETE,
+                     info->event_complete,
+                     info->event_data);
+
+    /* Try to create a third screen-size pixmap, to avoid stalling on
+     * the GPU.
+     *
+     * For a vblank-synchronized pageflip, there's a period when the
+     * pageflip is queued but the old front buffer is still being
+     * scanned out.  To avoid blocking rendering to the backbuffer for
+     * the next frame (which the kernel would do so as to keep the WIP
+     * new frame from appearing on screen), we make a third
+     * screen-sized buffer and make *that* the new back.
+     */
+    if (ms->drmmode.triple_buffer_pixmap == NULL) {
+        ms->drmmode.triple_buffer_pixmap =
+            screen->CreatePixmap(screen,
+                                 back_priv->pixmap->drawable.width,
+                                 back_priv->pixmap->drawable.height,
+                                 back_priv->pixmap->drawable.depth,
+                                 0);
+        if (ms->drmmode.triple_buffer_pixmap) {
+            uint32_t size;
+            uint16_t pitch;
+            ms->drmmode.triple_buffer_name =
+                glamor_name_from_pixmap(ms->drmmode.triple_buffer_pixmap,
+                                        &pitch, &size);
+
+            assert(pitch == info->back->pitch);
+        }
+    }
+
+    if (ms->drmmode.triple_buffer_pixmap) {
+        /* Exchange the triple buffer pixmap in. Now we have:
+         * front:  backbuffer with flip scheduled to screen
+         * back:   idle
+         * triple: old front buffer still being scanned out
+         */
+        ms_exchange_pixmap_buffers(back_priv->pixmap,
+                                   ms->drmmode.triple_buffer_pixmap,
+                                   &info->back->name,
+                                   &ms->drmmode.triple_buffer_name);
+    }
+
+    return TRUE;
+}
+
+static Bool
+can_exchange(DrawablePtr drawable, DRI2BufferPtr front, DRI2BufferPtr back)
+{
+    ScrnInfoPtr scrn = xf86ScreenToScrn(drawable->pScreen);
+    modesettingPtr ms = modesettingPTR(scrn);
+    ms_dri2_buffer_private_ptr front_priv = front->driverPrivate;
+    ms_dri2_buffer_private_ptr back_priv = back->driverPrivate;
+    PixmapPtr front_pixmap = front_priv->pixmap;
+    PixmapPtr back_pixmap = back_priv->pixmap;
+
+    if (!scrn->vtSema)
+        return FALSE;
+
+    if (ms_dri2_crtc_covering_drawable(drawable) == NULL)
+        return FALSE;
+
+    if (!DRI2CanFlip(drawable))
+        return FALSE;
+
+    if (ms->drmmode.shadow_enable)
+        return FALSE;
+
+    if (front_pixmap->drawable.width != back_pixmap->drawable.width)
+        return FALSE;
+
+    if (front_pixmap->drawable.height != back_pixmap->drawable.height)
+        return FALSE;
+
+    /* XXX should we be checking depth instead of bpp? */
+#if 0
+    if (front_pixmap->drawable.depth != back_pixmap->drawable.depth)
+        return FALSE;
+#else
+    if (front_pixmap->drawable.bitsPerPixel != back_pixmap->drawable.bitsPerPixel)
+        return FALSE;
+#endif
+
+    return TRUE;
+}
+
+static void
+ms_dri2_vblank_handler(ScrnInfoPtr scrn,
+                       xf86CrtcPtr crtc,
+                       uint64_t msc,
+                       uint64_t usec,
+                       void *data)
+{
+    ms_dri2_frame_event_ptr swap_info = data;
+    DrawablePtr drawable = swap_info->drawable;
+    uint32_t tv_sec = usec / 1000000;
+    uint32_t tv_usec = usec % 1000000;
+
+    if (!drawable) {
+        ms_dri2_del_frame_event(swap_info);
+        return;
+    }
+
+    switch (swap_info->type) {
+    case DRI2_FLIP:
+        if (can_exchange(drawable, swap_info->front, swap_info->back) &&
+            ms_dri2_do_pageflip(drawable, swap_info)) {
+            return;
+        }
+
+        /* else fall through to exchange/blit */
+    case DRI2_SWAP: {
+        ms_dri2_fallback_blit_swap(drawable, swap_info->front, swap_info->back);
+        DRI2SwapComplete(swap_info->client, drawable, msc, tv_sec, tv_usec,
+                         DRI2_BLIT_COMPLETE,
+                         swap_info->client ? swap_info->event_complete : NULL,
+                         swap_info->event_data);
+        break;
+    }
+    case DRI2_WAITMSC:
+        if (swap_info->client)
+            DRI2WaitMSCComplete(swap_info->client, drawable,
+                                msc, tv_sec, tv_usec);
+        break;
+    default:
+        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+                   "%s: unknown vblank event (type %d) received\n", __func__,
+                   swap_info->type);
+        break;
+    }
+
+    ms_dri2_del_frame_event(swap_info);
+}
+
+static void
+ms_dri2_vblank_abort(ScrnInfoPtr scrn, xf86CrtcPtr crtc, void *data)
+{
+    ms_dri2_frame_event_ptr swap_info = data;
+
+    ms_dri2_del_frame_event(swap_info);
+}
+
+/**
+ * Request a DRM event when the requested conditions will be satisfied.
+ *
+ * We need to handle the event and ask the server to wake up the client when
+ * we receive it.
+ */
+static int
+ms_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
+                          CARD64 divisor, CARD64 remainder)
+{
+    ScreenPtr screen = draw->pScreen;
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    modesettingPtr ms = modesettingPTR(scrn);
+    ms_dri2_frame_event_ptr wait_info;
+    drmVBlank vbl;
+    int ret;
+    xf86CrtcPtr crtc = ms_dri2_crtc_covering_drawable(draw);
+    int pipe = crtc ? ms_crtc_pipe(crtc) : -1;
+    CARD64 current_msc, current_ust, request_msc;
+    uint32_t seq;
+
+    /* Drawable not visible, return immediately */
+    if (pipe == -1)
+        goto out_complete;
+
+    wait_info = calloc(1, sizeof(*wait_info));
+    if (!wait_info)
+        goto out_complete;
+
+    wait_info->screen = screen;
+    wait_info->drawable = draw;
+    wait_info->client = client;
+    wait_info->type = DRI2_WAITMSC;
+
+    if (!ms_dri2_add_frame_event(wait_info)) {
+        free(wait_info);
+        wait_info = NULL;
+        goto out_complete;
+    }
+
+    /* Get current count */
+    ret = ms_get_crtc_ust_msc(crtc, &current_ust, &current_msc);
+
+    /*
+     * If divisor is zero, or current_msc is smaller than target_msc,
+     * we just need to make sure target_msc passes  before waking up the
+     * client.
+     */
+    if (divisor == 0 || current_msc < target_msc) {
+        /* If target_msc already reached or passed, set it to
+         * current_msc to ensure we return a reasonable value back
+         * to the caller. This keeps the client from continually
+         * sending us MSC targets from the past by forcibly updating
+         * their count on this call.
+         */
+        seq = ms_drm_queue_alloc(crtc, wait_info,
+                                 ms_dri2_vblank_handler,
+                                 ms_dri2_vblank_abort);
+        if (!seq)
+            goto out_free;
+
+        if (current_msc >= target_msc)
+            target_msc = current_msc;
+        vbl.request.type = (DRM_VBLANK_ABSOLUTE |
+                            DRM_VBLANK_EVENT |
+                            ms_vblank_pipe_select(pipe));
+        vbl.request.sequence = ms_crtc_msc_to_kernel_msc(crtc, target_msc);
+        vbl.request.signal = (unsigned long)seq;
+
+        ret = drmWaitVBlank(ms->fd, &vbl);
+        if (ret) {
+            static int limit = 5;
+            if (limit) {
+                xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+                           "%s:%d get vblank counter failed: %s\n",
+                           __FUNCTION__, __LINE__,
+                           strerror(errno));
+                limit--;
+            }
+            goto out_free;
+        }
+
+        wait_info->frame = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence);
+        DRI2BlockClient(client, draw);
+        return TRUE;
+    }
+
+    /*
+     * If we get here, target_msc has already passed or we don't have one,
+     * so we queue an event that will satisfy the divisor/remainder equation.
+     */
+    vbl.request.type =
+        DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT | ms_vblank_pipe_select(pipe);
+
+    request_msc = current_msc - (current_msc % divisor) +
+        remainder;
+    /*
+     * If calculated remainder is larger than requested remainder,
+     * it means we've passed the last point where
+     * seq % divisor == remainder, so we need to wait for the next time
+     * that will happen.
+     */
+    if ((current_msc % divisor) >= remainder)
+        request_msc += divisor;
+
+    seq = ms_drm_queue_alloc(crtc, wait_info,
+                             ms_dri2_vblank_handler,
+                             ms_dri2_vblank_abort);
+    if (!seq)
+        goto out_free;
+
+    vbl.request.sequence = ms_crtc_msc_to_kernel_msc(crtc, request_msc);
+    vbl.request.signal = (unsigned long)seq;
+
+    ret = drmWaitVBlank(ms->fd, &vbl);
+    if (ret) {
+        static int limit = 5;
+        if (limit) {
+            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+                       "%s:%d get vblank counter failed: %s\n",
+                       __FUNCTION__, __LINE__,
+                       strerror(errno));
+            limit--;
+        }
+        goto out_free;
+    }
+
+    wait_info->frame = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence);
+    DRI2BlockClient(client, draw);
+
+    return TRUE;
+
+ out_free:
+    ms_dri2_del_frame_event(wait_info);
+ out_complete:
+    DRI2WaitMSCComplete(client, draw, target_msc, 0, 0);
+    return TRUE;
+}
+
+/*
+ * ScheduleSwap is responsible for requesting a DRM vblank event for the
+ * appropriate frame.
+ *
+ * In the case of a blit (e.g. for a windowed swap) or buffer exchange,
+ * the vblank requested can simply be the last queued swap frame + the swap
+ * interval for the drawable.
+ *
+ * In the case of a page flip, we request an event for the last queued swap
+ * frame + swap interval - 1, since we'll need to queue the flip for the frame
+ * immediately following the received event.
+ *
+ * The client will be blocked if it tries to perform further GL commands
+ * after queueing a swap, though in the Intel case after queueing a flip, the
+ * client is free to queue more commands; they'll block in the kernel if
+ * they access buffers busy with the flip.
+ *
+ * When the swap is complete, the driver should call into the server so it
+ * can send any swap complete events that have been requested.
+ */
+static int
+ms_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
+                      DRI2BufferPtr front, DRI2BufferPtr back,
+                      CARD64 *target_msc, CARD64 divisor,
+                      CARD64 remainder, DRI2SwapEventPtr func, void *data)
+{
+    ScreenPtr screen = draw->pScreen;
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    modesettingPtr ms = modesettingPTR(scrn);
+    drmVBlank vbl;
+    int ret;
+    xf86CrtcPtr crtc = ms_dri2_crtc_covering_drawable(draw);
+    int pipe = crtc ? ms_crtc_pipe(crtc) : -1;
+    int flip = 0;
+    ms_dri2_frame_event_ptr swap_info = NULL;
+    enum ms_dri2_frame_event_type swap_type = DRI2_SWAP;
+    uint64_t current_msc, current_ust;
+    uint64_t request_msc;
+    uint32_t seq;
+
+    /* Drawable not displayed... just complete the swap */
+    if (pipe == -1)
+        goto blit_fallback;
+
+    swap_info = calloc(1, sizeof(*swap_info));
+    if (!swap_info)
+        goto blit_fallback;
+
+    swap_info->screen = screen;
+    swap_info->drawable = draw;
+    swap_info->client = client;
+    swap_info->event_complete = func;
+    swap_info->event_data = data;
+    swap_info->front = front;
+    swap_info->back = back;
+    swap_info->pipe = pipe;
+
+    if (!ms_dri2_add_frame_event(swap_info)) {
+        free(swap_info);
+        swap_info = NULL;
+        goto blit_fallback;
+    }
+
+    ms_dri2_reference_buffer(front);
+    ms_dri2_reference_buffer(back);
+
+    ret = ms_get_crtc_ust_msc(crtc, &current_ust, &current_msc);
+
+    /* Flips need to be submitted one frame before */
+    if (can_exchange(draw, front, back)) {
+        swap_type = DRI2_FLIP;
+        flip = 1;
+    }
+
+    swap_info->type = swap_type;
+
+    /* Correct target_msc by 'flip' if swap_type == DRI2_FLIP.
+     * Do it early, so handling of different timing constraints
+     * for divisor, remainder and msc vs. target_msc works.
+     */
+    if (*target_msc > 0)
+        *target_msc -= flip;
+
+    /*
+     * If divisor is zero, or current_msc is smaller than target_msc
+     * we just need to make sure target_msc passes before initiating
+     * the swap.
+     */
+    if (divisor == 0 || current_msc < *target_msc) {
+        /* If we can, schedule the flip directly from here rather
+         * than waiting for an event from the kernel for the current
+         * (or a past) MSC.
+         */
+        if (flip && divisor == 0 && current_msc >= *target_msc &&
+            ms_dri2_do_pageflip(draw, swap_info))
+            return TRUE;
+
+        vbl.request.type = (DRM_VBLANK_ABSOLUTE |
+                            DRM_VBLANK_EVENT |
+                            ms_vblank_pipe_select(pipe));
+
+        /* If non-pageflipping, but blitting/exchanging, we need to use
+         * DRM_VBLANK_NEXTONMISS to avoid unreliable timestamping later
+         * on.
+         */
+        if (flip == 0)
+            vbl.request.type |= DRM_VBLANK_NEXTONMISS;
+
+        /* If target_msc already reached or passed, set it to
+         * current_msc to ensure we return a reasonable value back
+         * to the caller. This makes swap_interval logic more robust.
+         */
+        if (current_msc >= *target_msc)
+            *target_msc = current_msc;
+
+        seq = ms_drm_queue_alloc(crtc, swap_info,
+                                 ms_dri2_vblank_handler,
+                                 ms_dri2_vblank_abort);
+        if (!seq)
+            goto blit_fallback;
+
+        vbl.request.sequence = ms_crtc_msc_to_kernel_msc(crtc, *target_msc);
+        vbl.request.signal = (unsigned long)seq;
+
+        ret = drmWaitVBlank(ms->fd, &vbl);
+        if (ret) {
+            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+                       "divisor 0 get vblank counter failed: %s\n",
+                       strerror(errno));
+            goto blit_fallback;
+        }
+
+        *target_msc = ms_kernel_msc_to_crtc_msc(crtc,
+                                                vbl.reply.sequence + flip);
+        swap_info->frame = *target_msc;
+
+        return TRUE;
+    }
+
+    /*
+     * If we get here, target_msc has already passed or we don't have one,
+     * and we need to queue an event that will satisfy the divisor/remainder
+     * equation.
+     */
+    vbl.request.type = (DRM_VBLANK_ABSOLUTE |
+                        DRM_VBLANK_EVENT |
+                        ms_vblank_pipe_select(pipe));
+    if (flip == 0)
+        vbl.request.type |= DRM_VBLANK_NEXTONMISS;
+
+    request_msc = current_msc - (current_msc % divisor) +
+        remainder;
+
+    /*
+     * If the calculated deadline vbl.request.sequence is smaller than
+     * or equal to current_msc, it means we've passed the last point
+     * when effective onset frame seq could satisfy
+     * seq % divisor == remainder, so we need to wait for the next time
+     * this will happen.
+
+     * This comparison takes the 1 frame swap delay in pageflipping mode
+     * into account, as well as a potential DRM_VBLANK_NEXTONMISS delay
+     * if we are blitting/exchanging instead of flipping.
+     */
+    if (request_msc <= current_msc)
+        request_msc += divisor;
+
+
+    seq = ms_drm_queue_alloc(crtc, swap_info,
+                             ms_dri2_vblank_handler, ms_dri2_vblank_abort);
+    if (!seq)
+        goto blit_fallback;
+
+    /* Account for 1 frame extra pageflip delay if flip > 0 */
+    vbl.request.sequence = ms_crtc_msc_to_kernel_msc(crtc, request_msc) - flip;
+    vbl.request.signal = (unsigned long)seq;
+
+    ret = drmWaitVBlank(ms->fd, &vbl);
+    if (ret) {
+        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+                   "final get vblank counter failed: %s\n",
+                   strerror(errno));
+        goto blit_fallback;
+    }
+
+    /* Adjust returned value for 1 fame pageflip offset of flip > 0 */
+    *target_msc = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence + flip);
+    swap_info->frame = *target_msc;
+
+    return TRUE;
+
+ blit_fallback:
+    ms_dri2_fallback_blit_swap(draw, front, back);
+    DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data);
+    if (swap_info)
+        ms_dri2_del_frame_event(swap_info);
+    *target_msc = 0; /* offscreen, so zero out target vblank count */
+    return TRUE;
+}
+
+static int
+ms_dri2_frame_event_client_gone(void *data, XID id)
+{
+    struct ms_dri2_resource *resource = data;
+
+    while (!xorg_list_is_empty(&resource->list)) {
+        ms_dri2_frame_event_ptr info =
+            xorg_list_first_entry(&resource->list,
+                                  ms_dri2_frame_event_rec,
+                                  client_resource);
+
+        xorg_list_del(&info->client_resource);
+        info->client = NULL;
+    }
+    free(resource);
+
+    return Success;
+}
+
+static int
+ms_dri2_frame_event_drawable_gone(void *data, XID id)
+{
+    struct ms_dri2_resource *resource = data;
+
+    while (!xorg_list_is_empty(&resource->list)) {
+        ms_dri2_frame_event_ptr info =
+            xorg_list_first_entry(&resource->list,
+                                  ms_dri2_frame_event_rec,
+                                  drawable_resource);
+
+        xorg_list_del(&info->drawable_resource);
+        info->drawable = NULL;
+    }
+    free(resource);
+
+    return Success;
+}
+
+static Bool
+ms_dri2_register_frame_event_resource_types(void)
+{
+    frame_event_client_type =
+        CreateNewResourceType(ms_dri2_frame_event_client_gone,
+                              "Frame Event Client");
+    if (!frame_event_client_type)
+        return FALSE;
+
+    frame_event_drawable_type =
+        CreateNewResourceType(ms_dri2_frame_event_drawable_gone,
+                              "Frame Event Drawable");
+    if (!frame_event_drawable_type)
+        return FALSE;
+
+    return TRUE;
+}
+
+Bool
+ms_dri2_screen_init(ScreenPtr screen)
+{
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    modesettingPtr ms = modesettingPTR(scrn);
+    DRI2InfoRec info;
+
+    if (!glamor_supports_pixmap_import_export(screen)) {
+        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+                   "DRI2: glamor lacks support for pixmap import/export\n");
+    }
+
+    if (!xf86LoaderCheckSymbol("DRI2Version"))
+        return FALSE;
+
+    if (!dixRegisterPrivateKey(&ms_dri2_client_key,
+                               PRIVATE_CLIENT, sizeof(XID)))
+        return FALSE;
+
+    if (serverGeneration != ms_dri2_server_generation) {
+        ms_dri2_server_generation = serverGeneration;
+        if (!ms_dri2_register_frame_event_resource_types()) {
+            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+                       "Cannot register DRI2 frame event resources\n");
+            return FALSE;
+        }
+    }
+
+    memset(&info, '\0', sizeof(info));
+    info.fd = ms->fd;
+    info.driverName = NULL; /* Compat field, unused. */
+    info.deviceName = drmGetDeviceNameFromFd(ms->fd);
+
+    info.version = 4;
+    info.CreateBuffer = ms_dri2_create_buffer;
+    info.DestroyBuffer = ms_dri2_destroy_buffer;
+    info.CopyRegion = ms_dri2_copy_region;
+    info.ScheduleSwap = ms_dri2_schedule_swap;
+    info.GetMSC = ms_dri2_get_msc;
+    info.ScheduleWaitMSC = ms_dri2_schedule_wait_msc;
+
+    /* These two will be filled in by dri2.c */
+    info.numDrivers = 0;
+    info.driverNames = NULL;
+
+    return DRI2ScreenInit(screen, &info);
+}
+
+void
+ms_dri2_close_screen(ScreenPtr screen)
+{
+    DRI2CloseScreen(screen);
+}
+#endif
diff --git a/hw/xfree86/drivers/modesetting/driver.c b/hw/xfree86/drivers/modesetting/driver.c
index c62147a..d0b9095 100644
--- a/hw/xfree86/drivers/modesetting/driver.c
+++ b/hw/xfree86/drivers/modesetting/driver.c
@@ -788,7 +788,9 @@ PreInit(ScrnInfoPtr pScrn, int flags)
 
     try_enable_glamor(pScrn);
 
-    if (!ms->glamor) {
+    if (ms->glamor) {
+        xf86LoadSubModule(pScrn, "dri2");
+    } else {
         Bool prefer_shadow = TRUE;
 
         ret = drmGetCap(ms->fd, DRM_CAP_DUMB_PREFER_SHADOW, &value);
@@ -1107,6 +1109,21 @@ ScreenInit(ScreenPtr pScreen, int argc, char **argv)
     if (serverGeneration == 1)
         xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options);
 
+#ifdef GLAMOR
+    if (ms->glamor) {
+        if (!ms_pageflip_screen_init(pScreen)) {
+            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+                       "Failed to initialize pageflipping support.\n");
+            return FALSE;
+        }
+
+        if (!ms_dri2_screen_init(pScreen)) {
+            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+                       "Failed to initialize the DRI2 extension.\n");
+        }
+    }
+#endif
+
     return EnterVT(pScrn);
 }
 
@@ -1172,6 +1189,13 @@ CloseScreen(ScreenPtr pScreen)
     ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
     modesettingPtr ms = modesettingPTR(pScrn);
 
+#ifdef GLAMOR
+    if (ms->glamor) {
+        ms_dri2_close_screen(pScreen);
+        ms_pageflip_close_screen(pScreen);
+    }
+#endif
+
     if (ms->damage) {
         DamageUnregister(ms->damage);
         DamageDestroy(ms->damage);
diff --git a/hw/xfree86/drivers/modesetting/driver.h b/hw/xfree86/drivers/modesetting/driver.h
index 35f2419..c82c7de 100644
--- a/hw/xfree86/drivers/modesetting/driver.h
+++ b/hw/xfree86/drivers/modesetting/driver.h
@@ -30,6 +30,7 @@
 #include <errno.h>
 #include <drm.h>
 #include <xf86drm.h>
+#include <xf86Crtc.h>
 #include <damage.h>
 
 #include "drmmode_display.h"
@@ -42,6 +43,36 @@ typedef struct {
     ScrnInfoPtr pScrn_2;
 } EntRec, *EntPtr;
 
+typedef void (*ms_drm_handler_proc)(ScrnInfoPtr scrn,
+                                    xf86CrtcPtr crtc,
+                                    uint64_t seq,
+                                    uint64_t usec,
+                                    void *data);
+
+typedef void (*ms_drm_abort_proc)(ScrnInfoPtr scrn,
+                                  xf86CrtcPtr crtc,
+                                  void *data);
+
+typedef void (*ms_pageflip_handler_proc)(uint64_t frame,
+                                         uint64_t usec,
+                                         void *data);
+
+typedef void (*ms_pageflip_abort_proc)(void *data);
+
+/**
+ * A tracked handler for an event that will hopefully be generated by
+ * the kernel, and what to do when it is encountered.
+ */
+struct ms_drm_queue {
+    struct xorg_list list;
+    xf86CrtcPtr crtc;
+    uint32_t seq;
+    void *data;
+    ScrnInfoPtr scrn;
+    ms_drm_handler_proc handler;
+    ms_drm_abort_proc abort;
+};
+
 typedef struct _modesettingRec {
     int fd;
 
@@ -70,6 +101,15 @@ typedef struct _modesettingRec {
 
     drmmode_rec drmmode;
 
+    drmEventContext event_context;
+
+    int flip_count;
+    uint64_t fe_msc;
+    uint64_t fe_usec;
+    void *pageflip_data;
+    ms_pageflip_handler_proc pageflip_handler;
+    ms_pageflip_abort_proc pageflip_abort;
+
     DamagePtr damage;
     Bool dirty_enabled;
 
@@ -78,3 +118,33 @@ typedef struct _modesettingRec {
 } modesettingRec, *modesettingPtr;
 
 #define modesettingPTR(p) ((modesettingPtr)((p)->driverPrivate))
+
+Bool ms_do_pageflip(PixmapPtr new_front,
+                    int ref_crtc_hw_id,
+                    Bool async,
+                    void *pageflip_data,
+                    ms_pageflip_handler_proc pageflip_handler,
+                    ms_pageflip_abort_proc pageflip_abort);
+
+uint32_t ms_drm_queue_alloc(xf86CrtcPtr crtc,
+                            void *data,
+                            ms_drm_handler_proc handler,
+                            ms_drm_abort_proc abort);
+
+int ms_crtc_pipe(xf86CrtcPtr randr_crtc);
+xf86CrtcPtr ms_dri2_crtc_covering_drawable(DrawablePtr pDraw);
+xf86CrtcPtr ms_covering_crtc(ScrnInfoPtr scrn, BoxPtr box,
+                             xf86CrtcPtr desired, BoxPtr crtc_box_ret);
+uint32_t ms_vblank_pipe_select(int pipe);
+
+int ms_get_crtc_ust_msc(xf86CrtcPtr crtc, CARD64 *ust, CARD64 *msc);
+
+uint32_t ms_crtc_msc_to_kernel_msc(xf86CrtcPtr crtc, uint64_t expect);
+uint64_t ms_kernel_msc_to_crtc_msc(xf86CrtcPtr crtc, uint32_t sequence);
+
+
+Bool ms_dri2_screen_init(ScreenPtr screen);
+void ms_dri2_close_screen(ScreenPtr screen);
+
+Bool ms_pageflip_screen_init(ScreenPtr screen);
+void ms_pageflip_close_screen(ScreenPtr screen);
diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.c b/hw/xfree86/drivers/modesetting/drmmode_display.c
index d8d1b44..9500888 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.c
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.c
@@ -1183,6 +1183,11 @@ drmmode_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height)
     xf86DrvMsg(scrn->scrnIndex, X_INFO,
                "Allocate new frame buffer %dx%d stride\n", width, height);
 
+    if (drmmode->triple_buffer_pixmap) {
+        screen->DestroyPixmap(drmmode->triple_buffer_pixmap);
+        drmmode->triple_buffer_pixmap = NULL;
+    }
+
     old_width = scrn->virtualX;
     old_height = scrn->virtualY;
     old_pitch = drmmode->front_bo->pitch;
diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.h b/hw/xfree86/drivers/modesetting/drmmode_display.h
index c7e7ef0..f7e3c6b 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.h
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.h
@@ -43,6 +43,7 @@ struct dumb_bo {
 typedef struct {
     int fd;
     unsigned fb_id;
+    unsigned old_fb_id;
     drmModeResPtr mode_res;
     drmModeFBPtr mode_fb;
     int cpp;
@@ -58,6 +59,19 @@ typedef struct {
     Bool shadow_enable;
     void *shadow_fb;
 
+    /**
+     * A screen-sized pixmap when we're doing triple-buffered DRI2
+     * pageflipping.
+     *
+     * One is shared between all drawables that flip to the front
+     * buffer, and it only gets reallocated when root pixmap size
+     * changes.
+     */
+    PixmapPtr triple_buffer_pixmap;
+
+    /** The GEM name for triple_buffer_pixmap */
+    uint32_t triple_buffer_name;
+
     DevPrivateKeyRec pixmapPrivateKeyRec;
 } drmmode_rec, *drmmode_ptr;
 
@@ -69,6 +83,18 @@ typedef struct {
     unsigned rotate_fb_id;
     uint16_t lut_r[256], lut_g[256], lut_b[256];
     DamagePtr slave_damage;
+
+    /**
+     * @{ MSC (vblank count) handling for the PRESENT extension.
+     *
+     * The kernel's vblank counters are 32 bits and apparently full of
+     * lies, and we need to give a reliable 64-bit msc for GL, so we
+     * have to track and convert to a userland-tracked 64-bit msc.
+     */
+    int32_t vblank_offset;
+    uint32_t msc_prev;
+    uint64_t msc_high;
+    /** @} */
 } drmmode_crtc_private_rec, *drmmode_crtc_private_ptr;
 
 typedef struct {
diff --git a/hw/xfree86/drivers/modesetting/pageflip.c b/hw/xfree86/drivers/modesetting/pageflip.c
new file mode 100644
index 0000000..c126a5c
--- /dev/null
+++ b/hw/xfree86/drivers/modesetting/pageflip.c
@@ -0,0 +1,678 @@
+/*
+ * Copyright © 2013 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+/** @file pageflip.c
+ *
+ * Support for tracking VBlank events, emitting requests for
+ * pageflips, and tracking the completion of pageflips.
+ */
+
+#ifdef HAVE_DIX_CONFIG_H
+#include "dix-config.h"
+#endif
+
+#include <unistd.h>
+#include <xf86.h>
+#include <xf86Crtc.h>
+#include <present.h>
+#include <glamor.h>
+#include <poll.h>
+#include "driver.h"
+#include "drmmode_display.h"
+
+/**
+ * Tracking for outstanding events queued to the kernel.
+ *
+ * Each list entry is a struct ms_drm_queue, which has a uint32_t
+ * value generated from drm_seq that identifies the event and a
+ * reference back to the crtc/screen associated with the event.  It's
+ * done this way rather than in the screen because we want to be able
+ * to drain the list of event handlers that should be called at server
+ * regen time, even though we don't close the drm fd and have no way
+ * to actually drain the kernel events.
+ */
+static struct xorg_list ms_drm_queue;
+static uint32_t ms_drm_seq;
+
+struct ms_pageflip {
+    ScreenPtr screen;
+    Bool crtc_for_msc_ust;
+};
+
+static void ms_box_intersect(BoxPtr dest, BoxPtr a, BoxPtr b)
+{
+    dest->x1 = a->x1 > b->x1 ? a->x1 : b->x1;
+    dest->x2 = a->x2 < b->x2 ? a->x2 : b->x2;
+    if (dest->x1 >= dest->x2) {
+        dest->x1 = dest->x2 = dest->y1 = dest->y2 = 0;
+        return;
+    }
+
+    dest->y1 = a->y1 > b->y1 ? a->y1 : b->y1;
+    dest->y2 = a->y2 < b->y2 ? a->y2 : b->y2;
+    if (dest->y1 >= dest->y2)
+        dest->x1 = dest->x2 = dest->y1 = dest->y2 = 0;
+}
+
+static void ms_crtc_box(xf86CrtcPtr crtc, BoxPtr crtc_box)
+{
+    if (crtc->enabled) {
+        crtc_box->x1 = crtc->x;
+        crtc_box->x2 =
+            crtc->x + xf86ModeWidth(&crtc->mode, crtc->rotation);
+        crtc_box->y1 = crtc->y;
+        crtc_box->y2 =
+            crtc->y + xf86ModeHeight(&crtc->mode, crtc->rotation);
+    } else
+        crtc_box->x1 = crtc_box->x2 = crtc_box->y1 = crtc_box->y2 = 0;
+}
+
+static int ms_box_area(BoxPtr box)
+{
+    return (int)(box->x2 - box->x1) * (int)(box->y2 - box->y1);
+}
+
+/*
+ * Return the crtc covering 'box'. If two crtcs cover a portion of
+ * 'box', then prefer 'desired'. If 'desired' is NULL, then prefer the crtc
+ * with greater coverage
+ */
+
+xf86CrtcPtr
+ms_covering_crtc(ScrnInfoPtr scrn,
+                 BoxPtr box, xf86CrtcPtr desired, BoxPtr crtc_box_ret)
+{
+    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
+    xf86CrtcPtr crtc, best_crtc;
+    int coverage, best_coverage;
+    int c;
+    BoxRec crtc_box, cover_box;
+
+    best_crtc = NULL;
+    best_coverage = 0;
+    crtc_box_ret->x1 = 0;
+    crtc_box_ret->x2 = 0;
+    crtc_box_ret->y1 = 0;
+    crtc_box_ret->y2 = 0;
+    for (c = 0; c < xf86_config->num_crtc; c++) {
+        crtc = xf86_config->crtc[c];
+
+        /* If the CRTC is off, treat it as not covering */
+        if (!crtc->enabled)
+            continue;
+
+        ms_crtc_box(crtc, &crtc_box);
+        ms_box_intersect(&cover_box, &crtc_box, box);
+        coverage = ms_box_area(&cover_box);
+        if (coverage && crtc == desired) {
+            *crtc_box_ret = crtc_box;
+            return crtc;
+        }
+        if (coverage > best_coverage) {
+            *crtc_box_ret = crtc_box;
+            best_crtc = crtc;
+            best_coverage = coverage;
+        }
+    }
+    return best_crtc;
+}
+
+xf86CrtcPtr
+ms_dri2_crtc_covering_drawable(DrawablePtr pDraw)
+{
+    ScreenPtr pScreen = pDraw->pScreen;
+    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
+    BoxRec box, crtcbox;
+    xf86CrtcPtr crtc;
+
+    box.x1 = pDraw->x;
+    box.y1 = pDraw->y;
+    box.x2 = box.x1 + pDraw->width;
+    box.y2 = box.y1 + pDraw->height;
+
+    crtc = ms_covering_crtc(pScrn, &box, NULL, &crtcbox);
+
+    /* Make sure the CRTC is valid and this is the real front buffer */
+    if (crtc != NULL && !crtc->rotatedData)
+        return crtc;
+
+    return NULL;
+}
+
+uint32_t
+ms_vblank_pipe_select(int pipe)
+{
+    if (pipe > 1)
+        return pipe << DRM_VBLANK_HIGH_CRTC_SHIFT;
+    else if (pipe > 0)
+        return DRM_VBLANK_SECONDARY;
+    else
+        return 0;
+}
+
+int
+ms_crtc_pipe(xf86CrtcPtr crtc)
+{
+    drmmode_crtc_private_ptr drmmode_crtc;
+
+    if (crtc == NULL)
+        return 0;
+
+    drmmode_crtc = crtc->driver_private;
+
+    return drmmode_crtc->hw_id;
+}
+
+static Bool
+ms_get_kernel_ust_msc(xf86CrtcPtr crtc,
+                      uint32_t *msc, uint64_t *ust)
+{
+    ScreenPtr screen = crtc->randr_crtc->pScreen;
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    modesettingPtr ms = modesettingPTR(scrn);
+    drmVBlank vbl;
+    int pipe = ms_crtc_pipe(crtc);
+    int ret;
+
+    /* Get current count */
+    vbl.request.type = DRM_VBLANK_RELATIVE | ms_vblank_pipe_select(pipe);
+    vbl.request.sequence = 0;
+    vbl.request.signal = 0;
+    ret = drmWaitVBlank(ms->fd, &vbl);
+    if (ret) {
+        *msc = 0;
+        *ust = 0;
+        return FALSE;
+    } else {
+        *msc = vbl.reply.sequence;
+        *ust = (CARD64) vbl.reply.tval_sec * 1000000 + vbl.reply.tval_usec;
+        return TRUE;
+    }
+}
+
+/**
+ * Convert a 32-bit kernel MSC sequence number to a 64-bit local sequence
+ * number, adding in the vblank_offset and high 32 bits, and dealing
+ * with 64-bit wrapping
+ */
+uint64_t
+ms_kernel_msc_to_crtc_msc(xf86CrtcPtr crtc, uint32_t sequence)
+{
+    drmmode_crtc_private_rec *drmmode_crtc = crtc->driver_private;
+    sequence += drmmode_crtc->vblank_offset;
+
+    if ((int32_t) (sequence - drmmode_crtc->msc_prev) < -0x40000000)
+        drmmode_crtc->msc_high += 0x100000000L;
+    drmmode_crtc->msc_prev = sequence;
+    return drmmode_crtc->msc_high + sequence;
+}
+
+int
+ms_get_crtc_ust_msc(xf86CrtcPtr crtc, CARD64 *ust, CARD64 *msc)
+{
+    uint32_t kernel_msc;
+
+    if (!ms_get_kernel_ust_msc(crtc, &kernel_msc, ust))
+        return BadMatch;
+    *msc = ms_kernel_msc_to_crtc_msc(crtc, kernel_msc);
+
+    return Success;
+}
+
+#define MAX_VBLANK_OFFSET       1000
+
+/**
+ * Convert a 64-bit adjusted MSC value into a 32-bit kernel sequence number,
+ * removing the high 32 bits and subtracting out the vblank_offset term.
+ *
+ * This also updates the vblank_offset when it notices that the value should
+ * change.
+ */
+uint32_t
+ms_crtc_msc_to_kernel_msc(xf86CrtcPtr crtc, uint64_t expect)
+{
+    drmmode_crtc_private_rec *drmmode_crtc = crtc->driver_private;
+    uint64_t msc;
+    uint64_t ust;
+    int64_t diff;
+
+    ms_get_crtc_ust_msc(crtc, &ust, &msc);
+    diff = expect - msc;
+
+    /* We're way off here, assume that the kernel has lost its mind
+     * and smack the vblank back to something sensible
+     */
+    if (diff < -MAX_VBLANK_OFFSET || MAX_VBLANK_OFFSET < diff) {
+        drmmode_crtc->vblank_offset += (int32_t) diff;
+        if (drmmode_crtc->vblank_offset > -MAX_VBLANK_OFFSET &&
+            drmmode_crtc->vblank_offset < MAX_VBLANK_OFFSET)
+            drmmode_crtc->vblank_offset = 0;
+    }
+    return (uint32_t) (expect - drmmode_crtc->vblank_offset);
+}
+
+/**
+ * Check for pending DRM events and process them.
+ */
+static void
+ms_drm_wakeup_handler(void *data, int err, void *mask)
+{
+    ScreenPtr screen = data;
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    modesettingPtr ms = modesettingPTR(scrn);
+    fd_set *read_mask = mask;
+
+    if (data == NULL || err < 0)
+        return;
+
+    if (FD_ISSET(ms->fd, read_mask))
+        drmHandleEvent(ms->fd, &ms->event_context);
+}
+
+/*
+ * If there are any available, read drm_events
+ */
+static int
+ms_read_drm_events(ScreenPtr screen)
+{
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    modesettingPtr ms = modesettingPTR(scrn);
+    struct pollfd p = {
+        .fd = ms->fd,
+        .events = POLLIN
+    };
+    int r;
+
+    do {
+        r = poll(&p, 1, 0);
+    } while (r == -1 && (errno == EINTR || errno == EAGAIN));
+
+    if (r <= 0)
+        return 0;
+
+    return drmHandleEvent(ms->fd, &ms->event_context);
+}
+
+/*
+ * Enqueue a potential drm response; when the associated response
+ * appears, we've got data to pass to the handler from here
+ */
+uint32_t
+ms_drm_queue_alloc(xf86CrtcPtr crtc,
+                   void *data,
+                   ms_drm_handler_proc handler,
+                   ms_drm_abort_proc abort)
+{
+    ScreenPtr screen = crtc->randr_crtc->pScreen;
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    struct ms_drm_queue *q;
+
+    q = calloc(1, sizeof(struct ms_drm_queue));
+
+    if (!q)
+        return 0;
+    if (!ms_drm_seq)
+        ++ms_drm_seq;
+    q->seq = ms_drm_seq++;
+    q->scrn = scrn;
+    q->crtc = crtc;
+    q->data = data;
+    q->handler = handler;
+    q->abort = abort;
+
+    xorg_list_add(&q->list, &ms_drm_queue);
+
+    return q->seq;
+}
+
+/**
+ * Abort one queued DRM entry, removing it
+ * from the list, calling the abort function and
+ * freeing the memory
+ */
+static void
+ms_drm_abort_one(struct ms_drm_queue *q)
+{
+        xorg_list_del(&q->list);
+        q->abort(q->scrn, q->crtc, q->data);
+        free(q);
+}
+
+/*
+ * Abort by drm queue sequence number
+ */
+static void
+ms_drm_abort_seq(ScrnInfoPtr scrn, uint32_t seq)
+{
+    struct ms_drm_queue *q, *tmp;
+
+    xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
+        if (q->seq == seq) {
+            ms_drm_abort_one(q);
+            break;
+        }
+    }
+}
+
+/**
+ * Abort all queued entries on a specific scrn, used
+ * when resetting the X server
+ */
+static void
+ms_drm_abort_scrn(ScrnInfoPtr scrn)
+{
+    struct ms_drm_queue *q, *tmp;
+
+    xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
+        if (q->scrn == scrn)
+            ms_drm_abort_one(q);
+    }
+}
+
+/**
+ * Notify the page flip caller that the flip is complete
+ */
+static void
+ms_pageflip_complete(ScreenPtr screen)
+{
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    modesettingPtr ms = modesettingPTR(scrn);
+
+    /* Release framebuffer */
+    drmModeRmFB(ms->fd, ms->drmmode.old_fb_id);
+
+    if (!ms->pageflip_handler)
+        return;
+
+    ms->pageflip_handler(ms->fe_msc, ms->fe_usec, ms->pageflip_data);
+}
+
+/**
+ * Called after processing a pageflip complete event from DRM.
+ *
+ * Update the saved msc/ust values as needed, then check to see if the
+ * whole set of events are complete and notify the application at that
+ * point.
+ */
+static Bool
+ms_handle_pageflip(struct ms_pageflip *flip, uint64_t msc, uint64_t usec)
+{
+    ScreenPtr screen = flip->screen;
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    modesettingPtr ms = modesettingPTR(scrn);
+
+    if (flip->crtc_for_msc_ust) {
+        /* Cache msc, ust for later delivery with a Present event or
+         * GLX reply.
+         */
+        ms->fe_msc = msc;
+        ms->fe_usec = usec;
+    }
+    free(flip);
+
+    ms->flip_count--;
+
+    /* Tell the caller if this was the last DRM flip complete event expected. */
+    return ms->flip_count == 0;
+}
+
+/*
+ * Called from the DRM event queue when a single flip has completed
+ */
+static void
+ms_pageflip_handler(ScrnInfoPtr scrn, xf86CrtcPtr crtc,
+                    uint64_t msc, uint64_t usec, void *data)
+{
+    struct ms_pageflip *flip = data;
+    ScreenPtr screen = flip->screen;
+
+    if (ms_handle_pageflip(flip, msc, usec))
+        ms_pageflip_complete(screen);
+}
+
+/*
+ * Called from the DRM queue abort code when a flip has been aborted
+ */
+static void
+ms_pageflip_abort(ScrnInfoPtr scrn, xf86CrtcPtr crtc, void *data)
+{
+    struct ms_pageflip *flip = data;
+    modesettingPtr ms = modesettingPTR(scrn);
+
+    if (ms_handle_pageflip(flip, 0, 0)) {
+        /* Release framebuffer */
+        drmModeRmFB(ms->fd, ms->drmmode.old_fb_id);
+
+        if (!ms->pageflip_abort)
+            return;
+
+        ms->pageflip_abort(ms->pageflip_data);
+    }
+}
+
+Bool
+ms_do_pageflip(PixmapPtr new_front,
+               int ref_crtc_hw_id,
+               Bool async,
+               void *pageflip_data,
+               ms_pageflip_handler_proc pageflip_handler,
+               ms_pageflip_abort_proc pageflip_abort)
+{
+    ScreenPtr screen = new_front->drawable.pScreen;
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    modesettingPtr ms = modesettingPTR(scrn);
+    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
+    uint16_t pitch;
+    struct ms_pageflip *flip;
+    uint32_t new_fb_id;
+    uint32_t flags;
+    uint32_t seq;
+    uint32_t size;
+    int i;
+    int fd;
+    struct dumb_bo *new_front_bo;
+
+    glamor_block_handler(screen);
+
+    /* Take a detour through a dmabuf fd to get a GEM handle for our
+     * new front, since there's no GL interface to get the GEM handle
+     * (just a dmabuf fd, or an old-style GEM name)
+     */
+    fd = glamor_fd_from_pixmap(screen, new_front, &pitch, &size);
+    if (fd < 0) {
+        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+                   "Failed to get fd for flip to new front.\n");
+        return FALSE;
+    }
+    new_front_bo = dumb_get_bo_from_fd(ms->fd, fd, pitch, size);
+    close(fd);
+    if (!new_front_bo) {
+        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+                   "Failed to open fd for new front.\n");
+        return FALSE;
+    }
+
+    /*
+     * Create a new handle for the back buffer
+     */
+    if (drmModeAddFB(ms->fd, scrn->virtualX, scrn->virtualY,
+                     scrn->depth, scrn->bitsPerPixel, pitch,
+                     new_front_bo->handle, &new_fb_id))
+        goto error_out;
+
+    dumb_bo_destroy(ms->fd, new_front_bo);
+
+    ms->pageflip_data = pageflip_data;
+    ms->pageflip_handler = pageflip_handler;
+    ms->pageflip_abort = pageflip_abort;
+
+    /*
+     * Queue flips on all enabled CRTCs
+     * Note that if/when we get per-CRTC buffers, we'll have to update this.
+     * Right now it assumes a single shared fb across all CRTCs, with the
+     * kernel fixing up the offset of each CRTC as necessary.
+     *
+     * Also, flips queued on disabled or incorrectly configured displays
+     * may never complete; this is a configuration error.
+     */
+    ms->fe_msc = 0;
+    ms->fe_usec = 0;
+
+    flags = DRM_MODE_PAGE_FLIP_EVENT;
+    if (async)
+        flags |= DRM_MODE_PAGE_FLIP_ASYNC;
+    for (i = 0; i < config->num_crtc; i++) {
+        xf86CrtcPtr crtc = config->crtc[i];
+        drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+        int this_fb_id;
+
+        if (!config->crtc[i]->enabled)
+            continue;
+
+        flip = calloc(1, sizeof(struct ms_pageflip));
+        if (flip == NULL) {
+            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+                       "flip queue: carrier alloc failed.\n");
+            goto error_undo;
+        }
+
+        /* Only the reference crtc will finally deliver its page flip
+         * completion event. All other crtc's events will be discarded.
+         */
+        flip->crtc_for_msc_ust = (drmmode_crtc->hw_id == ref_crtc_hw_id);
+        flip->screen = screen;
+
+        seq = ms_drm_queue_alloc(crtc, flip,
+                                 ms_pageflip_handler, ms_pageflip_abort);
+        if (!seq) {
+            free(flip);
+            goto error_undo;
+        }
+
+        this_fb_id = new_fb_id;
+        if (drmmode_crtc->rotate_fb_id)
+            this_fb_id = drmmode_crtc->rotate_fb_id;
+    again:
+        if (drmModePageFlip(ms->fd,
+                            drmmode_crtc->mode_crtc->crtc_id,
+                            this_fb_id,
+                            flags, (void *) (uintptr_t) seq)) {
+            int err = errno;
+            if (ms_read_drm_events(screen)) {
+                xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+                           "flip queue retry\n");
+                goto again;
+            }
+            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+                       "flip queue failed: %s\n", strerror(err));
+            ms_drm_abort_seq(scrn, seq);
+            goto error_undo;
+        }
+        ms->flip_count++;
+    }
+
+    ms->drmmode.old_fb_id = ms->drmmode.fb_id;
+    ms->drmmode.fb_id = new_fb_id;
+
+    if (!ms->flip_count)
+        ms_pageflip_complete(screen);
+
+    return TRUE;
+
+ error_undo:
+    drmModeRmFB(ms->fd, new_fb_id);
+    for (i = 0; i < config->num_crtc; i++) {
+        if (config->crtc[i]->enabled) {
+            ErrorF("XXX: crtc apply\n");
+            /*ms_crtc_apply(config->crtc[i]);*/
+        }
+    }
+
+ error_out:
+    xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Page flip failed: %s\n",
+               strerror(errno));
+
+    ms->flip_count = 0;
+    return FALSE;
+}
+
+/*
+ * General DRM kernel handler. Looks for the matching sequence number in the
+ * drm event queue and calls the handler for it.
+ */
+static void
+ms_drm_handler(int fd, uint32_t frame, uint32_t sec, uint32_t usec,
+               void *user_ptr)
+{
+    struct ms_drm_queue *q, *tmp;
+    uint32_t user_data = (uint32_t) (intptr_t) user_ptr;
+
+    xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
+        if (q->seq == user_data) {
+            uint64_t msc;
+
+            msc = ms_kernel_msc_to_crtc_msc(q->crtc, frame);
+            xorg_list_del(&q->list);
+            q->handler(q->scrn, q->crtc, msc,
+                       (uint64_t) sec * 1000000 + usec, q->data);
+            free(q);
+            break;
+        }
+    }
+}
+
+Bool
+ms_pageflip_screen_init(ScreenPtr screen)
+{
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    modesettingPtr ms = modesettingPTR(scrn);
+
+    xorg_list_init(&ms_drm_queue);
+
+    ms->event_context.version = DRM_EVENT_CONTEXT_VERSION;
+    ms->event_context.vblank_handler = ms_drm_handler;
+    ms->event_context.page_flip_handler = ms_drm_handler;
+    ms->flip_count = 0;
+
+    /* We need to re-register the DRM fd for the synchronisation
+     * feedback on every server generation, so perform the
+     * registration within ScreenInit and not PreInit.
+     */
+    AddGeneralSocket(ms->fd);
+    RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
+                                   ms_drm_wakeup_handler, screen);
+
+    return TRUE;
+}
+
+void
+ms_pageflip_close_screen(ScreenPtr screen)
+{
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    modesettingPtr ms = modesettingPTR(scrn);
+
+    ms_drm_abort_scrn(scrn);
+
+    RemoveBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
+                                 ms_drm_wakeup_handler, screen);
+    RemoveGeneralSocket(ms->fd);
+}
-- 
2.1.1



More information about the xorg-devel mailing list