summaryrefslogtreecommitdiff
path: root/hw/xfree86/drivers/modesetting/dri2.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/xfree86/drivers/modesetting/dri2.c')
-rw-r--r--hw/xfree86/drivers/modesetting/dri2.c863
1 files changed, 863 insertions, 0 deletions
diff --git a/hw/xfree86/drivers/modesetting/dri2.c b/hw/xfree86/drivers/modesetting/dri2.c
new file mode 100644
index 000000000..6c88060b0
--- /dev/null
+++ b/hw/xfree86/drivers/modesetting/dri2.c
@@ -0,0 +1,863 @@
+/*
+ * Copyright © 2013 Intel Corporation
+ * Copyright © 2014 Broadcom
+ *
+ * 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.
+ */
+
+/**
+ * @file dri2.c
+ *
+ * Implements generic support for DRI2 on KMS, using glamor pixmaps
+ * for color buffer management (no support for other aux buffers), and
+ * the DRM vblank ioctls.
+ *
+ * This doesn't implement pageflipping yet.
+ */
+
+#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 {
+ MS_DRI2_QUEUE_SWAP,
+ MS_DRI2_WAIT_MSC,
+};
+
+typedef struct ms_dri2_frame_event {
+ ScreenPtr screen;
+
+ DrawablePtr drawable;
+ ClientPtr client;
+ enum ms_dri2_frame_event_type type;
+ int frame;
+ xf86CrtcPtr crtc;
+
+ 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_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_dri2_frame_event_handler(uint64_t msc,
+ uint64_t usec,
+ void *data)
+{
+ ms_dri2_frame_event_ptr frame_info = data;
+ DrawablePtr drawable = frame_info->drawable;
+ ScreenPtr screen = frame_info->screen;
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ uint32_t tv_sec = usec / 1000000;
+ uint32_t tv_usec = usec % 1000000;
+
+ if (!drawable) {
+ ms_dri2_del_frame_event(frame_info);
+ return;
+ }
+
+ switch (frame_info->type) {
+ case MS_DRI2_QUEUE_SWAP:
+ ms_dri2_blit_swap(drawable, frame_info->front, frame_info->back);
+ DRI2SwapComplete(frame_info->client, drawable, msc, tv_sec, tv_usec,
+ DRI2_BLIT_COMPLETE,
+ frame_info->client ? frame_info->event_complete : NULL,
+ frame_info->event_data);
+ break;
+
+ case MS_DRI2_WAIT_MSC:
+ if (frame_info->client)
+ DRI2WaitMSCComplete(frame_info->client, drawable,
+ msc, tv_sec, tv_usec);
+ break;
+
+ default:
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "%s: unknown vblank event (type %d) received\n", __func__,
+ frame_info->type);
+ break;
+ }
+
+ ms_dri2_del_frame_event(frame_info);
+}
+
+static void
+ms_dri2_frame_event_abort(void *data)
+{
+ ms_dri2_frame_event_ptr frame_info = data;
+
+ ms_dri2_del_frame_event(frame_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);
+ drmmode_crtc_private_ptr drmmode_crtc;
+ CARD64 current_msc, current_ust, request_msc;
+ uint32_t seq;
+
+ /* Drawable not visible, return immediately */
+ if (!crtc)
+ goto out_complete;
+ drmmode_crtc = crtc->driver_private;
+
+ 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 = MS_DRI2_WAIT_MSC;
+
+ 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_frame_event_handler,
+ ms_dri2_frame_event_abort);
+ if (!seq)
+ goto out_free;
+
+ if (current_msc >= target_msc)
+ target_msc = current_msc;
+ vbl.request.type = (DRM_VBLANK_ABSOLUTE |
+ DRM_VBLANK_EVENT |
+ drmmode_crtc->vblank_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 | drmmode_crtc->vblank_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_frame_event_handler,
+ ms_dri2_frame_event_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, or executing the swap immediately if it
+ * doesn't need to wait.
+ *
+ * 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);
+ drmmode_crtc_private_ptr drmmode_crtc;
+ ms_dri2_frame_event_ptr frame_info = NULL;
+ uint64_t current_msc, current_ust;
+ uint64_t request_msc;
+ uint32_t seq;
+
+ /* Drawable not displayed... just complete the swap */
+ if (!crtc)
+ goto blit_fallback;
+ drmmode_crtc = crtc->driver_private;
+
+ frame_info = calloc(1, sizeof(*frame_info));
+ if (!frame_info)
+ goto blit_fallback;
+
+ frame_info->screen = screen;
+ frame_info->drawable = draw;
+ frame_info->client = client;
+ frame_info->event_complete = func;
+ frame_info->event_data = data;
+ frame_info->front = front;
+ frame_info->back = back;
+ frame_info->crtc = crtc;
+ frame_info->type = MS_DRI2_QUEUE_SWAP;
+
+ if (!ms_dri2_add_frame_event(frame_info)) {
+ free(frame_info);
+ frame_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);
+
+ /*
+ * 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) {
+ /* We need to use DRM_VBLANK_NEXTONMISS to avoid unreliable
+ * timestamping later on.
+ */
+ vbl.request.type = (DRM_VBLANK_ABSOLUTE |
+ DRM_VBLANK_NEXTONMISS |
+ DRM_VBLANK_EVENT |
+ drmmode_crtc->vblank_pipe);
+
+ /* 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, frame_info,
+ ms_dri2_frame_event_handler,
+ ms_dri2_frame_event_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);
+ frame_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_NEXTONMISS |
+ DRM_VBLANK_EVENT |
+ drmmode_crtc->vblank_pipe);
+
+ 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 DRM_VBLANK_NEXTONMISS delay into account.
+ */
+ if (request_msc <= current_msc)
+ request_msc += divisor;
+
+
+ seq = ms_drm_queue_alloc(crtc, frame_info,
+ ms_dri2_frame_event_handler,
+ ms_dri2_frame_event_abort);
+ if (!seq)
+ goto blit_fallback;
+
+ 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) {
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "final get vblank counter failed: %s\n",
+ strerror(errno));
+ goto blit_fallback;
+ }
+
+ *target_msc = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence);
+ frame_info->frame = *target_msc;
+
+ return TRUE;
+
+ blit_fallback:
+ ms_dri2_blit_swap(draw, front, back);
+ DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data);
+ if (frame_info)
+ ms_dri2_del_frame_event(frame_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 /* GLAMOR */