diff mbox

DRI2: support new DRI2 APIs

Message ID 20100111125445.2b322e8a@jbarnes-piketon (mailing list archive)
State Accepted
Headers show

Commit Message

Jesse Barnes Jan. 11, 2010, 8:54 p.m. UTC
None
diff mbox

Patch

diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index a469f6c..4c6c78b 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -34,6 +34,7 @@ 
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
+#include <poll.h>
 
 #include "xorgVersion.h"
 
@@ -47,6 +48,11 @@  typedef struct {
     uint32_t fb_id;
     drmModeResPtr mode_res;
     int cpp;
+    
+    drmEventContext event_context;
+    void *event_data;
+    int old_fb_id;
+    int flip_count;
 } drmmode_rec, *drmmode_ptr;
 
 typedef struct {
@@ -501,6 +507,7 @@  static PixmapPtr
 drmmode_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
 {
 	ScrnInfoPtr scrn = crtc->scrn;
+	intel_screen_private *intel = intel_get_screen_private(scrn);
 	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
 	drmmode_ptr drmmode = drmmode_crtc->drmmode;
 	unsigned long rotate_pitch;
@@ -533,12 +540,16 @@  drmmode_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
 	if (drmmode_crtc->rotate_bo)
 		i830_set_pixmap_bo(rotate_pixmap, drmmode_crtc->rotate_bo);
 
+	intel->shadow_present = TRUE;
+
 	return rotate_pixmap;
 }
 
 static void
 drmmode_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *data)
 {
+	ScrnInfoPtr scrn = crtc->scrn;
+	intel_screen_private *intel = intel_get_screen_private(scrn);
 	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
 	drmmode_ptr drmmode = drmmode_crtc->drmmode;
 
@@ -556,6 +567,7 @@  drmmode_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *dat
 		dri_bo_unreference(drmmode_crtc->rotate_bo);
 		drmmode_crtc->rotate_bo = NULL;
 	}
+	intel->shadow_present = FALSE;
 }
 
 static void
@@ -1377,15 +1389,119 @@  drmmode_xf86crtc_resize (ScrnInfoPtr scrn, int width, int height)
 	return FALSE;
 }
 
+Bool
+drmmode_do_pageflip(ScreenPtr screen, dri_bo *new_front, dri_bo *old_front,
+		    void *data)
+{
+	ScrnInfoPtr scrn = xf86Screens[screen->myNum];
+	intel_screen_private *intel = intel_get_screen_private(scrn);
+	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
+	drmmode_crtc_private_ptr drmmode_crtc = config->crtc[0]->driver_private;
+	drmmode_ptr drmmode = drmmode_crtc->drmmode;
+	unsigned int pitch = scrn->displayWidth * intel->cpp;
+	int i, old_fb_id;
+	unsigned int crtc_id;
+
+	/*
+	 * Create a new handle for the back buffer
+	 */
+	old_fb_id = drmmode->fb_id;
+	if (drmModeAddFB(drmmode->fd, scrn->virtualX, scrn->virtualY,
+			 scrn->depth, scrn->bitsPerPixel, pitch,
+			 new_front->handle, &drmmode->fb_id))
+		goto error_out;
+
+	/*
+	 * 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.
+	 */
+	for (i = 0; i < config->num_crtc; i++) {
+		xf86CrtcPtr crtc = config->crtc[i];
+
+		if (!crtc->enabled)
+			continue;
+
+		drmmode_crtc = crtc->driver_private;
+		crtc_id = drmmode_crtc->mode_crtc->crtc_id;
+		drmmode->event_data = data;
+		drmmode->flip_count++;
+		if (drmModePageFlip(drmmode->fd, crtc_id, drmmode->fb_id,
+				    DRM_MODE_PAGE_FLIP_EVENT, drmmode)) {
+			xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+				   "flip queue failed: %s\n", strerror(errno));
+			goto error_undo;
+		}
+	}
+
+	dri_bo_pin(new_front, 0);
+	dri_bo_unpin(new_front);
+
+	scrn->fbOffset = new_front->offset;
+	intel->front_buffer->bo = new_front;
+	intel->front_buffer->offset = new_front->offset;
+	drmmode->old_fb_id = old_fb_id;
+
+	return TRUE;
+
+error_undo:
+	drmModeRmFB(drmmode->fd, drmmode->fb_id);
+	drmmode->fb_id = old_fb_id;
+
+error_out:
+	xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Page flip failed: %s\n",
+		   strerror(errno));
+	return FALSE;
+}
+
 static const xf86CrtcConfigFuncsRec drmmode_xf86crtc_config_funcs = {
 	drmmode_xf86crtc_resize
 };
 
+static void
+drmmode_vblank_handler(int fd, unsigned int frame, unsigned int tv_sec,
+		       unsigned int tv_usec, void *event_data)
+{
+	I830DRI2FrameEventHandler(frame, tv_sec, tv_usec, event_data);
+}
+
+static void
+drmmode_page_flip_handler(int fd, unsigned int frame, unsigned int tv_sec,
+			  unsigned int tv_usec, void *event_data)
+{
+	drmmode_ptr drmmode = event_data;
+
+	drmmode->flip_count--;
+	if (drmmode->flip_count > 0)
+		return;
+
+	drmModeRmFB(drmmode->fd, drmmode->old_fb_id);
+
+	I830DRI2FlipEventHandler(frame, tv_sec, tv_usec, drmmode->event_data);
+}
+
+static void
+drm_wakeup_handler(pointer data, int err, pointer p)
+{
+    drmmode_ptr drmmode = data;
+    fd_set *read_mask = p;
+
+    if (err >= 0 && FD_ISSET(drmmode->fd, read_mask))
+	drmHandleEvent(drmmode->fd, &drmmode->event_context);
+}
+
 Bool drmmode_pre_init(ScrnInfoPtr scrn, int fd, int cpp)
 {
+	intel_screen_private *intel = intel_get_screen_private(scrn);
 	xf86CrtcConfigPtr   xf86_config;
+	struct drm_i915_getparam gp;
 	drmmode_ptr drmmode;
-	int i;
+	unsigned int i;
+	int has_flipping = 0;
 
 	drmmode = xnfalloc(sizeof *drmmode);
 	drmmode->fd = fd;
@@ -1412,6 +1528,23 @@  Bool drmmode_pre_init(ScrnInfoPtr scrn, int fd, int cpp)
 
 	xf86InitialConfiguration(scrn, TRUE);
 
+	gp.param = I915_PARAM_HAS_PAGEFLIPPING;
+	gp.value = &has_flipping;
+	(void)drmCommandWriteRead(intel->drmSubFD, DRM_I915_GETPARAM, &gp,
+				  sizeof(gp));
+	if (has_flipping) {
+		xf86DrvMsg(scrn->scrnIndex, X_INFO,
+			   "Kernel page flipping support detected, enabling\n");
+		intel->use_pageflipping = TRUE;
+		drmmode->event_context.version = DRM_EVENT_CONTEXT_VERSION;
+		drmmode->event_context.vblank_handler = drmmode_vblank_handler;
+		drmmode->event_context.page_flip_handler =
+		    drmmode_page_flip_handler;
+		AddGeneralSocket(fd);
+		RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
+					       drm_wakeup_handler, drmmode);
+	}
+
 	return TRUE;
 }
 
diff --git a/src/i830.h b/src/i830.h
index a66038a..cdce6e5 100644
--- a/src/i830.h
+++ b/src/i830.h
@@ -65,6 +65,7 @@  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include "sarea.h"
 #define _XF86DRI_SERVER_
 #include "dri.h"
+#include "dri2.h"
 #include "GL/glxint.h"
 #include "i830_dri.h"
 #include "intel_bufmgr.h"
@@ -287,6 +288,8 @@  typedef struct intel_screen_private {
 
 	CreateScreenResourcesProcPtr CreateScreenResources;
 
+	Bool shadow_present;
+
 	Bool need_mi_flush;
 
 	Bool tiling;
@@ -372,6 +375,8 @@  typedef struct intel_screen_private {
 	int drmSubFD;
 	char *deviceName;
 
+	Bool use_pageflipping;
+
 	/* Broken-out options. */
 	OptionInfoPtr Options;
 
@@ -393,6 +398,11 @@  enum {
 	DEBUG_FLUSH_WAIT = 0x4,
 };
 
+extern Bool drmmode_pre_init(ScrnInfoPtr pScrn, int fd, int cpp);
+extern int drmmode_get_pipe_from_crtc_id(drm_intel_bufmgr *bufmgr, xf86CrtcPtr crtc);
+extern int drmmode_output_dpms_status(xf86OutputPtr output);
+extern Bool drmmode_do_pageflip(ScreenPtr screen, dri_bo *new_front,
+				dri_bo *old_front, void *data);
 
 static inline intel_screen_private *
 intel_get_screen_private(ScrnInfoPtr scrn)
@@ -424,6 +434,10 @@  extern xf86CrtcPtr i830_pipe_to_crtc(ScrnInfoPtr scrn, int pipe);
 
 Bool I830DRI2ScreenInit(ScreenPtr pScreen);
 void I830DRI2CloseScreen(ScreenPtr pScreen);
+void I830DRI2FrameEventHandler(unsigned int frame, unsigned int tv_sec,
+			       unsigned int tv_usec, void *user_data);
+void I830DRI2FlipEventHandler(unsigned int frame, unsigned int tv_sec,
+			      unsigned int tv_usec, void *user_data);
 
 extern Bool drmmode_pre_init(ScrnInfoPtr scrn, int fd, int cpp);
 extern void drmmode_closefb(ScrnInfoPtr scrn);
diff --git a/src/i830_dri.c b/src/i830_dri.c
index 0246e61..e1c1470 100644
--- a/src/i830_dri.c
+++ b/src/i830_dri.c
@@ -44,6 +44,9 @@  USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include <sys/ioctl.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <sys/time.h>
+#include <time.h>
+#include <errno.h>
 
 #include "xf86.h"
 #include "xf86_OSproc.h"
@@ -72,11 +75,6 @@  USE OR OTHER DEALINGS IN THE SOFTWARE.
 extern XF86ModuleData dri2ModuleData;
 #endif
 
-typedef struct {
-	PixmapPtr pixmap;
-	unsigned int attachment;
-} I830DRI2BufferPrivateRec, *I830DRI2BufferPrivatePtr;
-
 #ifndef USE_DRI2_1_1_0
 static DRI2BufferPtr
 I830DRI2CreateBuffers(DrawablePtr drawable, unsigned int *attachments,
@@ -271,6 +269,29 @@  static void I830DRI2DestroyBuffer(DrawablePtr drawable, DRI2Buffer2Ptr buffer)
 
 #endif
 
+static int
+I830DRI2DrawablePipe(DrawablePtr pDraw)
+{
+    ScreenPtr pScreen = pDraw->pScreen;
+    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
+    BoxRec box, crtcbox;
+    xf86CrtcPtr crtc;
+    int pipe = -1;
+
+    box.x1 = pDraw->x;
+    box.y1 = pDraw->y;
+    box.x2 = box.x1 + pDraw->width;
+    box.y2 = box.y1 + pDraw->height;
+
+    crtc = i830_covering_crtc(pScrn, &box, NULL, &crtcbox);
+
+    /* Make sure the CRTC is valid and this is the real front buffer */
+    if (crtc != NULL && !crtc->rotatedData)
+	pipe = i830_crtc_to_pipe(crtc);
+
+    return pipe;
+}
+
 static void
 I830DRI2CopyRegion(DrawablePtr drawable, RegionPtr pRegion,
 		   DRI2BufferPtr destBuffer, DRI2BufferPtr sourceBuffer)
@@ -359,6 +380,466 @@  I830DRI2CopyRegion(DrawablePtr drawable, RegionPtr pRegion,
 
 }
 
+#if DRI2INFOREC_VERSION >= 4
+
+enum DRI2FrameEventType {
+    DRI2_SWAP,
+    DRI2_FLIP,
+    DRI2_WAITMSC,
+};
+
+typedef struct _DRI2FrameEvent {
+    DrawablePtr		pDraw;
+    ClientPtr		client;
+    enum DRI2FrameEventType type;
+    int			frame;
+
+    /* for swaps & flips only */
+    DRI2SwapEventPtr	event_complete;
+    void		*event_data;
+    DRI2BufferPtr	front;
+    DRI2BufferPtr	back;
+} DRI2FrameEventRec, *DRI2FrameEventPtr;
+
+static void
+I830DRI2ExchangeBuffers(DrawablePtr draw, DRI2BufferPtr front,
+			DRI2BufferPtr back)
+{
+	I830DRI2BufferPrivatePtr front_priv, back_priv;
+	dri_bo *tmp_bo;
+	int tmp;
+
+	front_priv = front->driverPrivate;
+	back_priv = back->driverPrivate;
+
+	/* Swap BO names so DRI works */
+	tmp = front->name;
+	front->name = back->name;
+	back->name = tmp;
+
+	/* Swap pixmap bos */
+	dri_bo_reference(i830_get_pixmap_bo(front_priv->pixmap));
+
+	tmp_bo = i830_get_pixmap_bo(front_priv->pixmap);
+	i830_set_pixmap_bo(front_priv->pixmap,
+			   i830_get_pixmap_bo(back_priv->pixmap));
+	i830_set_pixmap_bo(back_priv->pixmap, tmp_bo); /* should be screen */
+}
+
+/*
+ * Our internal swap routine takes care of actually exchanging, blitting, or
+ * flipping buffers as necessary.
+ */
+static Bool
+I830DRI2ScheduleFlip(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
+		     DRI2BufferPtr back, DRI2SwapEventPtr func, void *data)
+{
+	ScreenPtr screen = draw->pScreen;
+	I830DRI2BufferPrivatePtr front_priv, back_priv;
+	dri_bo *tmp_bo;
+	DRI2FrameEventPtr flip_info;
+	Bool ret;
+
+	flip_info = xcalloc(1, sizeof(DRI2FrameEventRec));
+	if (!flip_info)
+	    return FALSE;
+
+	flip_info->pDraw = draw;
+	flip_info->client = client;
+	flip_info->type = DRI2_SWAP;
+	flip_info->event_complete = func;
+	flip_info->event_data = data;
+
+	front_priv = front->driverPrivate;
+	back_priv = back->driverPrivate;
+	tmp_bo = i830_get_pixmap_bo(front_priv->pixmap);
+
+	I830DRI2ExchangeBuffers(draw, front, back);
+
+	/* Page flip the full screen buffer */
+	ret = drmmode_do_pageflip(screen,
+				  i830_get_pixmap_bo(front_priv->pixmap),
+				  i830_get_pixmap_bo(back_priv->pixmap),
+				  flip_info);
+
+	/* Unwind in case of failure */
+	if (!ret) {
+	    i830_set_pixmap_bo(back_priv->pixmap,
+			       i830_get_pixmap_bo(front_priv->pixmap));
+	    i830_set_pixmap_bo(front_priv->pixmap, tmp_bo);
+	    return FALSE;
+	}
+
+	return ret;
+}
+
+void I830DRI2FrameEventHandler(unsigned int frame, unsigned int tv_sec,
+			       unsigned int tv_usec, void *event_data)
+{
+    DRI2FrameEventPtr event = event_data;
+    DrawablePtr pDraw = event->pDraw;
+    ScreenPtr screen = pDraw->pScreen;
+    ScrnInfoPtr scrn = xf86Screens[screen->myNum];
+    intel_screen_private *intel = intel_get_screen_private(scrn);
+
+    switch (event->type) {
+    case DRI2_FLIP:
+	/* If we can still flip... */
+	if (DRI2CanFlip(pDraw) && !intel->shadow_present &&
+	    intel->use_pageflipping &&
+	    I830DRI2ScheduleFlip(event->client, pDraw, event->front,
+				 event->back, event->event_complete,
+				 event->event_data)) {
+	    break;
+	}
+	/* else fall through to exchange/blit */
+    case DRI2_SWAP: {
+	int swap_type;
+
+	if (DRI2CanExchange(pDraw)) {
+	    I830DRI2ExchangeBuffers(pDraw, event->front, event->back);
+	    swap_type = DRI2_EXCHANGE_COMPLETE;
+	} else {
+	    BoxRec	    box;
+	    RegionRec	    region;
+
+	    box.x1 = 0;
+	    box.y1 = 0;
+	    box.x2 = pDraw->width;
+	    box.y2 = pDraw->height;
+	    REGION_INIT(pScreen, &region, &box, 0);
+
+	    I830DRI2CopyRegion(pDraw, &region, event->front, event->back);
+	    swap_type = DRI2_BLIT_COMPLETE;
+	}
+	DRI2SwapComplete(event->client, pDraw, frame, tv_sec, tv_usec,
+			 swap_type, event->event_complete, event->event_data);
+	break;
+    }
+    case DRI2_WAITMSC:
+	DRI2WaitMSCComplete(event->client, pDraw, frame, tv_sec, tv_usec);
+	break;
+    default:
+	xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+		   "%s: unknown vblank event received\n", __func__);
+	/* Unknown type */
+	break;
+    }
+
+    xfree(event);
+}
+
+void I830DRI2FlipEventHandler(unsigned int frame, unsigned int tv_sec,
+			      unsigned int tv_usec, void *event_data)
+{
+    DRI2FrameEventPtr flip = event_data;
+    DrawablePtr pDraw = flip->pDraw;
+    ScreenPtr screen = pDraw->pScreen;
+    ScrnInfoPtr scrn = xf86Screens[screen->myNum];
+
+    /* We assume our flips arrive in order, so we don't check the frame */
+    switch (flip->type) {
+    case DRI2_SWAP:
+	DRI2SwapComplete(flip->client, pDraw, frame, tv_sec, tv_usec,
+			 DRI2_FLIP_COMPLETE, flip->event_complete,
+			 flip->event_data);
+	break;
+    default:
+	xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+		   "%s: unknown vblank event received\n", __func__);
+	/* Unknown type */
+	break;
+    }
+
+    xfree(flip);
+}
+
+/*
+ * 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
+I830DRI2ScheduleSwap(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 = xf86Screens[screen->myNum];
+	intel_screen_private *intel = intel_get_screen_private(scrn);
+	drmVBlank vbl;
+	int ret, pipe = I830DRI2DrawablePipe(draw), flip = 0;
+	DRI2FrameEventPtr swap_info;
+	enum DRI2FrameEventType swap_type = DRI2_SWAP;
+
+	swap_info = xcalloc(1, sizeof(DRI2FrameEventRec));
+
+	/* Drawable not displayed... just complete the swap */
+	if (pipe == -1 || !swap_info) {
+	    BoxRec	    box;
+	    RegionRec	    region;
+
+	    box.x1 = 0;
+	    box.y1 = 0;
+	    box.x2 = draw->width;
+	    box.y2 = draw->height;
+	    REGION_INIT(pScreen, &region, &box, 0);
+
+	    I830DRI2CopyRegion(draw, &region, front, back);
+
+	    DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func,
+			     data);
+	    if (swap_info)
+		xfree(swap_info);
+	    return TRUE;
+	}
+
+	swap_info->pDraw = draw;
+	swap_info->client = client;
+	swap_info->event_complete = func;
+	swap_info->event_data = data;
+	swap_info->front = front;
+	swap_info->back = back;
+
+	/* Get current count */
+	vbl.request.type = DRM_VBLANK_RELATIVE;
+	if (pipe > 0)
+		vbl.request.type |= DRM_VBLANK_SECONDARY;
+	vbl.request.sequence = 0;
+	ret = drmWaitVBlank(intel->drmSubFD, &vbl);
+	if (ret) {
+		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+			   "first get vblank counter failed: %s\n",
+			   strerror(errno));
+		return FALSE;
+	}
+
+	/* Flips need to be submitted one frame before */
+	if (DRI2CanFlip(draw) && !intel->shadow_present &&
+	    intel->use_pageflipping) {
+	    swap_type = DRI2_FLIP;
+	    flip = 1;
+	}
+
+	swap_info->type = swap_type;
+
+	/*
+	 * If divisor is zero, we just need to make sure target_msc passes
+	 * before waking up the client.
+	 */
+	if (divisor == 0) {
+		vbl.request.type = DRM_VBLANK_NEXTONMISS |
+		    DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
+		if (pipe > 0)
+			vbl.request.type |= DRM_VBLANK_SECONDARY;
+
+		vbl.request.sequence = *target_msc;
+		vbl.request.sequence -= flip;
+		vbl.request.signal = (unsigned long)swap_info;
+		ret = drmWaitVBlank(intel->drmSubFD, &vbl);
+		if (ret) {
+			xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+				   "divisor 0 get vblank counter failed: %s\n",
+				   strerror(errno));
+			return FALSE;
+		}
+
+		*target_msc = vbl.reply.sequence;
+		swap_info->frame = *target_msc;
+
+		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/remainderequation.
+	 */
+	if ((vbl.reply.sequence % divisor) == remainder)
+		return FALSE;
+
+	vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
+	if (pipe > 0)
+		vbl.request.type |= DRM_VBLANK_SECONDARY;
+
+	/*
+	 * If we have no remainder, and the test above failed, 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 (!remainder)
+		vbl.request.sequence += divisor;
+
+	vbl.request.sequence = vbl.reply.sequence -
+	    (vbl.reply.sequence % divisor) + remainder;
+	vbl.request.sequence -= flip;
+	vbl.request.signal = (unsigned long)swap_info;
+	ret = drmWaitVBlank(intel->drmSubFD, &vbl);
+	if (ret) {
+		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+			   "final get vblank counter failed: %s\n",
+			   strerror(errno));
+		return FALSE;
+	}
+
+	*target_msc = vbl.reply.sequence;
+	swap_info->frame = *target_msc;
+
+	return TRUE;
+}
+
+/*
+ * Get current frame count and frame count timestamp, based on drawable's
+ * crtc.
+ */
+static int
+I830DRI2GetMSC(DrawablePtr draw, CARD64 *ust, CARD64 *msc)
+{
+    ScreenPtr screen = draw->pScreen;
+    ScrnInfoPtr scrn = xf86Screens[screen->myNum];
+    intel_screen_private *intel = intel_get_screen_private(scrn);
+    drmVBlank vbl;
+    int ret, pipe = I830DRI2DrawablePipe(draw);
+
+    /* Drawable not displayed, make up a value */
+    if (pipe == -1) {
+	*ust = 0;
+	*msc = 0;
+	return TRUE;
+    }
+
+    vbl.request.type = DRM_VBLANK_RELATIVE;
+    if (pipe > 0)
+	vbl.request.type |= DRM_VBLANK_SECONDARY;
+    vbl.request.sequence = 0;
+
+    ret = drmWaitVBlank(intel->drmSubFD, &vbl);
+    if (ret) {
+	xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+		   "get vblank counter failed: %s\n", strerror(errno));
+	return FALSE;
+    }
+
+    *ust = ((CARD64)vbl.reply.tval_sec * 1000000) + vbl.reply.tval_usec;
+    *msc = vbl.reply.sequence;
+
+    return TRUE;
+}
+
+/*
+ * 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
+I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
+			CARD64 divisor, CARD64 remainder)
+{
+    ScreenPtr screen = draw->pScreen;
+    ScrnInfoPtr scrn = xf86Screens[screen->myNum];
+    intel_screen_private *intel = intel_get_screen_private(scrn);
+    DRI2FrameEventPtr wait_info;
+    drmVBlank vbl;
+    int ret, pipe = I830DRI2DrawablePipe(draw);
+
+    /* Drawable not visible, return immediately */
+    if (pipe == -1) {
+	DRI2WaitMSCComplete(client, draw, target_msc, 0, 0);
+	return TRUE;
+    }
+
+    wait_info = xcalloc(1, sizeof(DRI2FrameEventRec));
+    if (!wait_info) {
+	DRI2WaitMSCComplete(client, draw, 0, 0, 0);
+	return TRUE;
+    }
+
+    wait_info->pDraw = draw;
+    wait_info->client = client;
+    wait_info->type = DRI2_WAITMSC;
+
+    /* Get current count */
+    vbl.request.type = DRM_VBLANK_RELATIVE;
+    if (pipe > 0)
+	vbl.request.type |= DRM_VBLANK_SECONDARY;
+    vbl.request.sequence = 0;
+    ret = drmWaitVBlank(intel->drmSubFD, &vbl);
+    if (ret) {
+	xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+		   "get vblank counter failed: %s\n", strerror(errno));
+	return FALSE;
+    }
+
+    /*
+     * If divisor is zero, we just need to make sure target_msc passes
+     * before waking up the client.
+     */
+    if (divisor == 0) {
+	vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
+	if (pipe > 0)
+	    vbl.request.type |= DRM_VBLANK_SECONDARY;
+	vbl.request.sequence = target_msc;
+	vbl.request.signal = (unsigned long)wait_info;
+	ret = drmWaitVBlank(intel->drmSubFD, &vbl);
+	if (ret) {
+	    xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+		       "get vblank counter failed: %s\n", strerror(errno));
+	    return FALSE;
+	}
+
+	wait_info->frame = 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;
+    if (pipe > 0)
+	vbl.request.type |= DRM_VBLANK_SECONDARY;
+
+    /*
+     * If we have no remainder and the condition isn't satisified, 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 (((vbl.reply.sequence % divisor) != remainder) && !remainder)
+	vbl.request.sequence += divisor;
+
+    vbl.request.sequence = vbl.reply.sequence - (vbl.reply.sequence % divisor) +
+	remainder;
+    vbl.request.signal = (unsigned long)wait_info;
+    ret = drmWaitVBlank(intel->drmSubFD, &vbl);
+    if (ret) {
+	xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+		   "get vblank counter failed: %s\n", strerror(errno));
+	return FALSE;
+    }
+
+    wait_info->frame = vbl.reply.sequence;
+    DRI2BlockClient(client, draw);
+
+    return TRUE;
+}
+#endif
+
 Bool I830DRI2ScreenInit(ScreenPtr screen)
 {
 	ScrnInfoPtr scrn = xf86Screens[screen->myNum];
@@ -405,6 +886,12 @@  Bool I830DRI2ScreenInit(ScreenPtr screen)
 #endif
 
 	info.CopyRegion = I830DRI2CopyRegion;
+#if DRI2INFOREC_VERSION >= 4
+	info.version = 4;
+	info.ScheduleSwap = I830DRI2ScheduleSwap;
+	info.GetMSC = I830DRI2GetMSC;
+	info.ScheduleWaitMSC = I830DRI2ScheduleWaitMSC;
+#endif
 
 	return DRI2ScreenInit(screen, &info);
 }
diff --git a/src/i830_dri.h b/src/i830_dri.h
index 9802356..babcac3 100644
--- a/src/i830_dri.h
+++ b/src/i830_dri.h
@@ -2,6 +2,7 @@ 
 #ifndef _I830_DRI_H
 #define _I830_DRI_H
 
+#include "xorg-server.h"
 #include "xf86drm.h"
 #include "i830_common.h"
 
@@ -58,4 +59,9 @@  typedef struct {
 	int dummy;
 } I830DRIContextRec, *I830DRIContextPtr;
 
+typedef struct {
+    PixmapPtr pixmap;
+    unsigned int attachment;
+} I830DRI2BufferPrivateRec, *I830DRI2BufferPrivatePtr;
+
 #endif