@@ -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;
}
@@ -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);
@@ -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, ®ion, &box, 0);
+
+ I830DRI2CopyRegion(pDraw, ®ion, 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, ®ion, &box, 0);
+
+ I830DRI2CopyRegion(draw, ®ion, 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);
}
@@ -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