summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/driver.c7
-rw-r--r--src/driver.h44
-rw-r--r--src/drmmode_display.h13
-rw-r--r--src/present.c874
5 files changed, 939 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 0ced1ff..4b7d8d5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -38,5 +38,6 @@ modesetting_drv_la_SOURCES = \
driver.h \
drmmode_display.c \
drmmode_display.h \
+ present.c \
sync.c \
$()
diff --git a/src/driver.c b/src/driver.c
index a367713..95b7cdc 100644
--- a/src/driver.c
+++ b/src/driver.c
@@ -1159,6 +1159,12 @@ ScreenInit(SCREEN_INIT_ARGS_DECL)
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Failed to initialize the DRI2 extension.\n");
}
+
+ if (!ms_present_screen_init(pScreen)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Failed to initialize PRESENT.\n");
+ return FALSE;
+ }
}
#endif
@@ -1236,6 +1242,7 @@ CloseScreen(CLOSE_SCREEN_ARGS_DECL)
if (ms->glamor) {
ms_dri2_close_screen(pScreen);
ms_sync_close_screen(pScreen);
+ ms_present_close_screen(pScreen);
}
#endif
diff --git a/src/driver.h b/src/driver.h
index e879b48..345252a 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -32,6 +32,8 @@
#include <xf86drm.h>
#include <damage.h>
#include "misync.h"
+#include "xorg-server.h"
+#include "xf86Crtc.h"
#include "drmmode_display.h"
#define DRV_ERROR(msg) xf86DrvMsg(pScrn->scrnIndex, X_ERROR, msg);
@@ -48,6 +50,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;
@@ -78,6 +110,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;
@@ -93,3 +134,6 @@ void ms_dri2_close_screen(ScreenPtr screen);
Bool ms_sync_screen_init(ScreenPtr screen);
void ms_sync_close_screen(ScreenPtr screen);
+
+Bool ms_present_screen_init(ScreenPtr screen);
+void ms_present_close_screen(ScreenPtr screen);
diff --git a/src/drmmode_display.h b/src/drmmode_display.h
index 18a55a6..59e92de 100644
--- a/src/drmmode_display.h
+++ b/src/drmmode_display.h
@@ -54,6 +54,7 @@ struct dumb_bo {
typedef struct {
int fd;
unsigned fb_id;
+ unsigned old_fb_id;
drmModeResPtr mode_res;
drmModeFBPtr mode_fb;
int cpp;
@@ -82,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.
+ */
+ uint32_t vblank_offset;
+ uint32_t msc_prev;
+ uint64_t msc_high;
+ /** @} */
} drmmode_crtc_private_rec, *drmmode_crtc_private_ptr;
typedef struct {
diff --git a/src/present.c b/src/present.c
new file mode 100644
index 0000000..bf91855
--- /dev/null
+++ b/src/present.c
@@ -0,0 +1,874 @@
+/*
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unistd.h>
+#include <xf86.h>
+#include <xf86Crtc.h>
+#include <xorg-server.h>
+#include <present.h>
+#include <glamor.h>
+#include <poll.h>
+#include "compat-api.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_present_vblank_event {
+ uint64_t event_id;
+};
+
+struct ms_pageflip {
+ ScreenPtr screen;
+ Bool crtc_for_msc_ust;
+};
+
+static uint32_t
+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;
+}
+
+static int
+ms_present_crtc_pipe(ScreenPtr screen, RRCrtcPtr randr_crtc)
+{
+ xf86CrtcPtr crtc;
+ drmmode_crtc_private_ptr drmmode_crtc;
+
+ if (randr_crtc == NULL)
+ return 0;
+
+ crtc = randr_crtc->devPrivate;
+ drmmode_crtc = crtc->driver_private;
+
+ return drmmode_crtc->hw_id;
+}
+
+static Bool
+ms_get_kernel_ust_msc(RRCrtcPtr crtc,
+ uint32_t *msc, uint64_t *ust)
+{
+ ScreenPtr screen = crtc->pScreen;
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ modesettingPtr ms = modesettingPTR(scrn);
+ drmVBlank vbl;
+ int pipe = ms_present_crtc_pipe(screen, crtc);
+ int ret;
+
+ /* Get current count */
+ vbl.request.type = DRM_VBLANK_RELATIVE | 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
+ */
+static 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;
+}
+
+static int
+ms_get_crtc_ust_msc(RRCrtcPtr crtc, CARD64 *ust, CARD64 *msc)
+{
+ xf86CrtcPtr xf86_crtc = crtc->devPrivate;
+ uint32_t kernel_msc;
+
+ if (!ms_get_kernel_ust_msc(crtc, &kernel_msc, ust))
+ return BadMatch;
+ *msc = ms_kernel_msc_to_crtc_msc(xf86_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.
+ */
+static uint32_t
+ms_crtc_msc_to_kernel_msc(RRCrtcPtr crtc, uint64_t expect)
+{
+ xf86CrtcPtr xf86_crtc = crtc->devPrivate;
+ drmmode_crtc_private_rec *drmmode_crtc = xf86_crtc->driver_private;
+ uint64_t msc;
+ uint64_t ust;
+ int64_t diff;
+
+ ms_get_crtc_ust_msc(crtc, &msc, &ust);
+ 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 (-MAX_VBLANK_OFFSET < drmmode_crtc->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);
+}
+
+/*
+ * Flush the DRM event queue when full; this
+ * makes space for new requests
+ */
+static Bool
+ms_present_flush_drm_events(ScreenPtr screen)
+{
+ return ms_read_drm_events(screen) >= 0;
+}
+
+/**
+ * Called when the queued vblank event has occurred
+ */
+static void
+ms_present_vblank_handler(ScrnInfoPtr scrn, xf86CrtcPtr crtc,
+ uint64_t msc, uint64_t usec, void *data)
+{
+ struct ms_present_vblank_event *event = data;
+
+ present_event_notify(event->event_id, usec, msc);
+ free(event);
+}
+
+/**
+ * Called when the queued vblank is aborted
+ */
+static void
+ms_present_vblank_abort(ScrnInfoPtr scrn, xf86CrtcPtr crtc, void *data)
+{
+ struct ms_present_vblank_event *event = data;
+
+ free(event);
+}
+
+/*
+ * Enqueue a potential drm response; when the associated response
+ * appears, we've got data to pass to the handler from here
+ */
+static uint32_t
+ms_drm_queue_alloc(RRCrtcPtr crtc,
+ void *data,
+ ms_drm_handler_proc handler,
+ ms_drm_abort_proc abort)
+{
+ xf86CrtcPtr xf86_crtc = crtc->devPrivate;
+ ScreenPtr screen = 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 = xf86_crtc;
+ q->data = data;
+ q->handler = handler;
+ q->abort = abort;
+
+ xorg_list_add(&q->list, &ms_drm_queue);
+
+ return q->seq;
+}
+
+/**
+ * Queue an event to report back to the Present extension when the specified
+ * MSC has passed.
+ */
+static int
+ms_present_queue_vblank(RRCrtcPtr crtc,
+ uint64_t event_id,
+ uint64_t msc)
+{
+ ScreenPtr screen = crtc->pScreen;
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ modesettingPtr ms = modesettingPTR(scrn);
+ int pipe = ms_present_crtc_pipe(screen, crtc);
+ struct ms_present_vblank_event *event;
+ drmVBlank vbl;
+ int ret;
+ uint32_t seq;
+
+ event = calloc(sizeof(struct ms_present_vblank_event), 1);
+ if (!event)
+ return BadAlloc;
+ event->event_id = event_id;
+ seq = ms_drm_queue_alloc(crtc, event,
+ ms_present_vblank_handler,
+ ms_present_vblank_abort);
+ if (!seq) {
+ free(event);
+ return BadAlloc;
+ }
+
+ vbl.request.type = (DRM_VBLANK_ABSOLUTE |
+ DRM_VBLANK_EVENT |
+ vblank_pipe_select(pipe));
+ vbl.request.sequence = ms_crtc_msc_to_kernel_msc(crtc, msc);
+ vbl.request.signal = seq;
+ for (;;) {
+ ret = drmWaitVBlank(ms->fd, &vbl);
+ if (!ret)
+ break;
+ if (errno != EBUSY || !ms_present_flush_drm_events(screen))
+ return BadAlloc;
+ }
+
+ return Success;
+}
+
+static Bool
+ms_present_event_match(void *data, void *match_data)
+{
+ struct ms_present_vblank_event *event = data;
+ uint64_t *match = match_data;
+
+ return *match == event->event_id;
+}
+
+/**
+ * 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);
+}
+
+/**
+ * Externally usable abort function that uses a callback to match a single queued
+ * entry to abort
+ */
+static void
+ms_drm_abort(ScrnInfoPtr scrn,
+ Bool (*match)(void *data, void *match_data), void *match_data)
+{
+ struct ms_drm_queue *q, *tmp;
+
+ xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
+ if (match(q->data, match_data)) {
+ ms_drm_abort_one(q);
+ break;
+ }
+ }
+}
+
+/*
+ * 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;
+ }
+ }
+}
+
+/*
+ * Remove a pending vblank event from the DRM queue so that it is not reported
+ * to the extension
+ */
+static void
+ms_present_abort_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
+{
+ ScreenPtr screen = crtc->pScreen;
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+
+ ms_drm_abort(scrn, ms_present_event_match, &event_id);
+}
+
+/**
+ * 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);
+ }
+}
+
+/*
+ * Test to see if page flipping is possible on the target crtc
+ */
+static Bool
+ms_present_check_flip(RRCrtcPtr crtc,
+ WindowPtr window,
+ PixmapPtr pixmap,
+ Bool sync_flip)
+{
+ xf86CrtcPtr xf86_crtc = crtc->devPrivate;
+ ScreenPtr screen = window->drawable.pScreen;
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ modesettingPtr ms = modesettingPTR(scrn);
+
+ if (!scrn->vtSema)
+ return FALSE;
+
+ if (ms->drmmode.shadow_enable)
+ return FALSE;
+
+ if (!xf86_crtc->enabled)
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * Once the flip has been completed on all pipes, notify the
+ * extension code telling it when that happened
+ */
+static void
+ms_present_flip_event(uint64_t msc, uint64_t ust, void *pageflip_data)
+{
+ struct ms_present_vblank_event *event = pageflip_data;
+
+ present_event_notify(event->event_id, ust, msc);
+ free(event);
+}
+
+/**
+ * The flip has been aborted, free the structure
+ */
+static void
+ms_present_flip_abort(void *pageflip_data)
+{
+ struct ms_present_vblank_event *event = pageflip_data;
+
+ free(event);
+}
+
+
+/**
+ * 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);
+ }
+}
+
+static 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->randr_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->hw_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;
+}
+
+/**
+ * Queue a flip on 'crtc' to 'pixmap' at 'target_msc'. If 'sync_flip' is true,
+ * then wait for vblank. Otherwise, flip immediately
+ */
+static Bool
+ms_present_flip(RRCrtcPtr crtc,
+ uint64_t event_id,
+ uint64_t target_msc,
+ PixmapPtr pixmap,
+ Bool sync_flip)
+{
+ ScreenPtr screen = crtc->pScreen;
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ struct ms_present_vblank_event *event;
+ int pipe = ms_present_crtc_pipe(screen, crtc);
+ Bool ret;
+
+ if (!ms_present_check_flip(crtc, screen->root, pixmap, sync_flip))
+ return FALSE;
+
+ event = calloc(1, sizeof(struct ms_present_vblank_event));
+ if (!event)
+ return FALSE;
+
+ event->event_id = event_id;
+
+ ret = ms_do_pageflip(pixmap, pipe, !sync_flip,
+ event,
+ ms_present_flip_event,
+ ms_present_flip_abort);
+ if (!ret)
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "present flip failed\n");
+ return ret;
+}
+
+/**
+ * Queue a flip back to the normal frame buffer
+ */
+static void
+ms_present_unflip(ScreenPtr screen, uint64_t event_id)
+{
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ struct ms_present_vblank_event *event;
+ PixmapPtr pixmap = screen->GetScreenPixmap(screen);
+ Bool ret;
+
+ event = calloc(1, sizeof(struct ms_present_vblank_event));
+ if (!event)
+ return;
+
+ event->event_id = event_id;
+
+ ret = ms_do_pageflip(pixmap, 0, FALSE, event,
+ ms_present_flip_event, ms_present_flip_abort);
+ if (!ret) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR, "present unflip failed\n");
+ }
+}
+
+static void
+ms_present_flush(WindowPtr window)
+{
+ ScreenPtr screen = window->drawable.pScreen;
+
+ /* XXX: This function is terribly named */
+ glamor_block_handler(screen);
+}
+
+static RRCrtcPtr
+ms_present_get_crtc(WindowPtr window)
+{
+ ScreenPtr screen = window->drawable.pScreen;
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
+ int c;
+
+ /* XXX: fill in */
+ for (c = 0; c < xf86_config->num_crtc; c++) {
+ xf86CrtcPtr crtc = xf86_config->crtc[c];
+
+ if (crtc->enabled)
+ return crtc->randr_crtc;
+ }
+
+ return NULL;
+}
+
+static present_screen_info_rec ms_present_screen_info = {
+ .version = 0,
+
+ .get_crtc = ms_present_get_crtc,
+ .get_ust_msc = ms_get_crtc_ust_msc,
+ .queue_vblank = ms_present_queue_vblank,
+ .abort_vblank = ms_present_abort_vblank,
+ .flush = ms_present_flush,
+
+ .capabilities = PresentCapabilityNone,
+ .check_flip = ms_present_check_flip,
+ .flip = ms_present_flip,
+ .unflip = ms_present_unflip,
+};
+
+static Bool
+has_async_flip(ScreenPtr screen)
+{
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ modesettingPtr ms = modesettingPTR(scrn);
+ int ret;
+ uint64_t value;
+
+ ret = drmGetCap(ms->fd, DRM_CAP_ASYNC_PAGE_FLIP, &value);
+ if (ret == 0)
+ return value == 1;
+
+ 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_present_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);
+
+ if (has_async_flip(screen))
+ ms_present_screen_info.capabilities |= PresentCapabilityAsync;
+
+ return present_screen_init(screen, &ms_present_screen_info);
+}
+
+void
+ms_present_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);
+}