135c4bbdfSmrg/*
235c4bbdfSmrg * Copyright © 2013 Intel Corporation
335c4bbdfSmrg * Copyright © 2014 Broadcom
435c4bbdfSmrg *
535c4bbdfSmrg * Permission is hereby granted, free of charge, to any person obtaining a
635c4bbdfSmrg * copy of this software and associated documentation files (the "Software"),
735c4bbdfSmrg * to deal in the Software without restriction, including without limitation
835c4bbdfSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
935c4bbdfSmrg * and/or sell copies of the Software, and to permit persons to whom the
1035c4bbdfSmrg * Software is furnished to do so, subject to the following conditions:
1135c4bbdfSmrg *
1235c4bbdfSmrg * The above copyright notice and this permission notice (including the next
1335c4bbdfSmrg * paragraph) shall be included in all copies or substantial portions of the
1435c4bbdfSmrg * Software.
1535c4bbdfSmrg *
1635c4bbdfSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1735c4bbdfSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1835c4bbdfSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1935c4bbdfSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2035c4bbdfSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2135c4bbdfSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2235c4bbdfSmrg * IN THE SOFTWARE.
2335c4bbdfSmrg */
2435c4bbdfSmrg
2535c4bbdfSmrg/**
2635c4bbdfSmrg * @file dri2.c
2735c4bbdfSmrg *
2835c4bbdfSmrg * Implements generic support for DRI2 on KMS, using glamor pixmaps
2935c4bbdfSmrg * for color buffer management (no support for other aux buffers), and
3035c4bbdfSmrg * the DRM vblank ioctls.
3135c4bbdfSmrg *
3235c4bbdfSmrg * This doesn't implement pageflipping yet.
3335c4bbdfSmrg */
3435c4bbdfSmrg
3535c4bbdfSmrg#ifdef HAVE_DIX_CONFIG_H
3635c4bbdfSmrg#include "dix-config.h"
3735c4bbdfSmrg#endif
3835c4bbdfSmrg
3935c4bbdfSmrg#include <time.h>
4035c4bbdfSmrg#include "list.h"
4135c4bbdfSmrg#include "xf86.h"
4235c4bbdfSmrg#include "driver.h"
4335c4bbdfSmrg#include "dri2.h"
4435c4bbdfSmrg
451b5d61b8Smrg#ifdef GLAMOR_HAS_GBM
4635c4bbdfSmrg
4735c4bbdfSmrgenum ms_dri2_frame_event_type {
4835c4bbdfSmrg    MS_DRI2_QUEUE_SWAP,
491b5d61b8Smrg    MS_DRI2_QUEUE_FLIP,
5035c4bbdfSmrg    MS_DRI2_WAIT_MSC,
5135c4bbdfSmrg};
5235c4bbdfSmrg
5335c4bbdfSmrgtypedef struct ms_dri2_frame_event {
5435c4bbdfSmrg    ScreenPtr screen;
5535c4bbdfSmrg
5635c4bbdfSmrg    DrawablePtr drawable;
5735c4bbdfSmrg    ClientPtr client;
5835c4bbdfSmrg    enum ms_dri2_frame_event_type type;
5935c4bbdfSmrg    int frame;
6035c4bbdfSmrg    xf86CrtcPtr crtc;
6135c4bbdfSmrg
6235c4bbdfSmrg    struct xorg_list drawable_resource, client_resource;
6335c4bbdfSmrg
6435c4bbdfSmrg    /* for swaps & flips only */
6535c4bbdfSmrg    DRI2SwapEventPtr event_complete;
6635c4bbdfSmrg    void *event_data;
6735c4bbdfSmrg    DRI2BufferPtr front;
6835c4bbdfSmrg    DRI2BufferPtr back;
6935c4bbdfSmrg} ms_dri2_frame_event_rec, *ms_dri2_frame_event_ptr;
7035c4bbdfSmrg
7135c4bbdfSmrgtypedef struct {
7235c4bbdfSmrg    int refcnt;
7335c4bbdfSmrg    PixmapPtr pixmap;
7435c4bbdfSmrg} ms_dri2_buffer_private_rec, *ms_dri2_buffer_private_ptr;
7535c4bbdfSmrg
7635c4bbdfSmrgstatic DevPrivateKeyRec ms_dri2_client_key;
7735c4bbdfSmrgstatic RESTYPE frame_event_client_type, frame_event_drawable_type;
7835c4bbdfSmrgstatic int ms_dri2_server_generation;
7935c4bbdfSmrg
8035c4bbdfSmrgstruct ms_dri2_resource {
8135c4bbdfSmrg    XID id;
8235c4bbdfSmrg    RESTYPE type;
8335c4bbdfSmrg    struct xorg_list list;
8435c4bbdfSmrg};
8535c4bbdfSmrg
8635c4bbdfSmrgstatic struct ms_dri2_resource *
8735c4bbdfSmrgms_get_resource(XID id, RESTYPE type)
8835c4bbdfSmrg{
8935c4bbdfSmrg    struct ms_dri2_resource *resource;
9035c4bbdfSmrg    void *ptr;
9135c4bbdfSmrg
9235c4bbdfSmrg    ptr = NULL;
9335c4bbdfSmrg    dixLookupResourceByType(&ptr, id, type, NULL, DixWriteAccess);
9435c4bbdfSmrg    if (ptr)
9535c4bbdfSmrg        return ptr;
9635c4bbdfSmrg
9735c4bbdfSmrg    resource = malloc(sizeof(*resource));
9835c4bbdfSmrg    if (resource == NULL)
9935c4bbdfSmrg        return NULL;
10035c4bbdfSmrg
10135c4bbdfSmrg    if (!AddResource(id, type, resource))
10235c4bbdfSmrg        return NULL;
10335c4bbdfSmrg
10435c4bbdfSmrg    resource->id = id;
10535c4bbdfSmrg    resource->type = type;
10635c4bbdfSmrg    xorg_list_init(&resource->list);
10735c4bbdfSmrg    return resource;
10835c4bbdfSmrg}
10935c4bbdfSmrg
11035c4bbdfSmrgstatic inline PixmapPtr
11135c4bbdfSmrgget_drawable_pixmap(DrawablePtr drawable)
11235c4bbdfSmrg{
11335c4bbdfSmrg    ScreenPtr screen = drawable->pScreen;
11435c4bbdfSmrg
11535c4bbdfSmrg    if (drawable->type == DRAWABLE_PIXMAP)
11635c4bbdfSmrg        return (PixmapPtr) drawable;
11735c4bbdfSmrg    else
11835c4bbdfSmrg        return screen->GetWindowPixmap((WindowPtr) drawable);
11935c4bbdfSmrg}
12035c4bbdfSmrg
12135c4bbdfSmrgstatic DRI2Buffer2Ptr
1221b5d61b8Smrgms_dri2_create_buffer2(ScreenPtr screen, DrawablePtr drawable,
1231b5d61b8Smrg                       unsigned int attachment, unsigned int format)
12435c4bbdfSmrg{
12535c4bbdfSmrg    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
126ed6184dfSmrg    modesettingPtr ms = modesettingPTR(scrn);
12735c4bbdfSmrg    DRI2Buffer2Ptr buffer;
12835c4bbdfSmrg    PixmapPtr pixmap;
12935c4bbdfSmrg    CARD32 size;
13035c4bbdfSmrg    CARD16 pitch;
13135c4bbdfSmrg    ms_dri2_buffer_private_ptr private;
13235c4bbdfSmrg
13335c4bbdfSmrg    buffer = calloc(1, sizeof *buffer);
13435c4bbdfSmrg    if (buffer == NULL)
13535c4bbdfSmrg        return NULL;
13635c4bbdfSmrg
13735c4bbdfSmrg    private = calloc(1, sizeof(*private));
13835c4bbdfSmrg    if (private == NULL) {
13935c4bbdfSmrg        free(buffer);
14035c4bbdfSmrg        return NULL;
14135c4bbdfSmrg    }
14235c4bbdfSmrg
14335c4bbdfSmrg    pixmap = NULL;
1441b5d61b8Smrg    if (attachment == DRI2BufferFrontLeft) {
1451b5d61b8Smrg        pixmap = get_drawable_pixmap(drawable);
1461b5d61b8Smrg        if (pixmap && pixmap->drawable.pScreen != screen)
1471b5d61b8Smrg            pixmap = NULL;
1481b5d61b8Smrg        if (pixmap)
1491b5d61b8Smrg            pixmap->refcnt++;
1501b5d61b8Smrg    }
15135c4bbdfSmrg
15235c4bbdfSmrg    if (pixmap == NULL) {
15335c4bbdfSmrg        int pixmap_width = drawable->width;
15435c4bbdfSmrg        int pixmap_height = drawable->height;
15535c4bbdfSmrg        int pixmap_cpp = (format != 0) ? format : drawable->depth;
15635c4bbdfSmrg
15735c4bbdfSmrg        /* Assume that non-color-buffers require special
15835c4bbdfSmrg         * device-specific handling.  Mesa currently makes no requests
15935c4bbdfSmrg         * for non-color aux buffers.
16035c4bbdfSmrg         */
16135c4bbdfSmrg        switch (attachment) {
16235c4bbdfSmrg        case DRI2BufferAccum:
16335c4bbdfSmrg        case DRI2BufferBackLeft:
16435c4bbdfSmrg        case DRI2BufferBackRight:
16535c4bbdfSmrg        case DRI2BufferFakeFrontLeft:
16635c4bbdfSmrg        case DRI2BufferFakeFrontRight:
16735c4bbdfSmrg        case DRI2BufferFrontLeft:
16835c4bbdfSmrg        case DRI2BufferFrontRight:
16935c4bbdfSmrg            break;
17035c4bbdfSmrg
17135c4bbdfSmrg        case DRI2BufferStencil:
17235c4bbdfSmrg        case DRI2BufferDepth:
17335c4bbdfSmrg        case DRI2BufferDepthStencil:
17435c4bbdfSmrg        case DRI2BufferHiz:
17535c4bbdfSmrg        default:
17635c4bbdfSmrg            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
17735c4bbdfSmrg                       "Request for DRI2 buffer attachment %d unsupported\n",
17835c4bbdfSmrg                       attachment);
17935c4bbdfSmrg            free(private);
18035c4bbdfSmrg            free(buffer);
18135c4bbdfSmrg            return NULL;
18235c4bbdfSmrg        }
18335c4bbdfSmrg
18435c4bbdfSmrg        pixmap = screen->CreatePixmap(screen,
18535c4bbdfSmrg                                      pixmap_width,
18635c4bbdfSmrg                                      pixmap_height,
18735c4bbdfSmrg                                      pixmap_cpp,
18835c4bbdfSmrg                                      0);
18935c4bbdfSmrg        if (pixmap == NULL) {
19035c4bbdfSmrg            free(private);
19135c4bbdfSmrg            free(buffer);
19235c4bbdfSmrg            return NULL;
19335c4bbdfSmrg        }
19435c4bbdfSmrg    }
19535c4bbdfSmrg
19635c4bbdfSmrg    buffer->attachment = attachment;
19735c4bbdfSmrg    buffer->cpp = pixmap->drawable.bitsPerPixel / 8;
19835c4bbdfSmrg    buffer->format = format;
19935c4bbdfSmrg    /* The buffer's flags field is unused by the client drivers in
20035c4bbdfSmrg     * Mesa currently.
20135c4bbdfSmrg     */
20235c4bbdfSmrg    buffer->flags = 0;
20335c4bbdfSmrg
204ed6184dfSmrg    buffer->name = ms->glamor.name_from_pixmap(pixmap, &pitch, &size);
20535c4bbdfSmrg    buffer->pitch = pitch;
20635c4bbdfSmrg    if (buffer->name == -1) {
20735c4bbdfSmrg        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
20835c4bbdfSmrg                   "Failed to get DRI2 name for pixmap\n");
20935c4bbdfSmrg        screen->DestroyPixmap(pixmap);
21035c4bbdfSmrg        free(private);
21135c4bbdfSmrg        free(buffer);
21235c4bbdfSmrg        return NULL;
21335c4bbdfSmrg    }
21435c4bbdfSmrg
21535c4bbdfSmrg    buffer->driverPrivate = private;
21635c4bbdfSmrg    private->refcnt = 1;
21735c4bbdfSmrg    private->pixmap = pixmap;
21835c4bbdfSmrg
21935c4bbdfSmrg    return buffer;
22035c4bbdfSmrg}
22135c4bbdfSmrg
2221b5d61b8Smrgstatic DRI2Buffer2Ptr
2231b5d61b8Smrgms_dri2_create_buffer(DrawablePtr drawable, unsigned int attachment,
2241b5d61b8Smrg                      unsigned int format)
2251b5d61b8Smrg{
2261b5d61b8Smrg    return ms_dri2_create_buffer2(drawable->pScreen, drawable, attachment,
2271b5d61b8Smrg                                  format);
2281b5d61b8Smrg}
2291b5d61b8Smrg
23035c4bbdfSmrgstatic void
23135c4bbdfSmrgms_dri2_reference_buffer(DRI2Buffer2Ptr buffer)
23235c4bbdfSmrg{
23335c4bbdfSmrg    if (buffer) {
23435c4bbdfSmrg        ms_dri2_buffer_private_ptr private = buffer->driverPrivate;
23535c4bbdfSmrg        private->refcnt++;
23635c4bbdfSmrg    }
23735c4bbdfSmrg}
23835c4bbdfSmrg
2391b5d61b8Smrgstatic void ms_dri2_destroy_buffer2(ScreenPtr unused, DrawablePtr unused2,
2401b5d61b8Smrg                                    DRI2Buffer2Ptr buffer)
24135c4bbdfSmrg{
24235c4bbdfSmrg    if (!buffer)
24335c4bbdfSmrg        return;
24435c4bbdfSmrg
24535c4bbdfSmrg    if (buffer->driverPrivate) {
24635c4bbdfSmrg        ms_dri2_buffer_private_ptr private = buffer->driverPrivate;
24735c4bbdfSmrg        if (--private->refcnt == 0) {
24835c4bbdfSmrg            ScreenPtr screen = private->pixmap->drawable.pScreen;
24935c4bbdfSmrg            screen->DestroyPixmap(private->pixmap);
25035c4bbdfSmrg            free(private);
25135c4bbdfSmrg            free(buffer);
25235c4bbdfSmrg        }
25335c4bbdfSmrg    } else {
25435c4bbdfSmrg        free(buffer);
25535c4bbdfSmrg    }
25635c4bbdfSmrg}
25735c4bbdfSmrg
2581b5d61b8Smrgstatic void ms_dri2_destroy_buffer(DrawablePtr drawable, DRI2Buffer2Ptr buffer)
2591b5d61b8Smrg{
2601b5d61b8Smrg    ms_dri2_destroy_buffer2(NULL, drawable, buffer);
2611b5d61b8Smrg}
2621b5d61b8Smrg
26335c4bbdfSmrgstatic void
2641b5d61b8Smrgms_dri2_copy_region2(ScreenPtr screen, DrawablePtr drawable, RegionPtr pRegion,
2651b5d61b8Smrg                     DRI2BufferPtr destBuffer, DRI2BufferPtr sourceBuffer)
26635c4bbdfSmrg{
26735c4bbdfSmrg    ms_dri2_buffer_private_ptr src_priv = sourceBuffer->driverPrivate;
26835c4bbdfSmrg    ms_dri2_buffer_private_ptr dst_priv = destBuffer->driverPrivate;
26935c4bbdfSmrg    PixmapPtr src_pixmap = src_priv->pixmap;
27035c4bbdfSmrg    PixmapPtr dst_pixmap = dst_priv->pixmap;
27135c4bbdfSmrg    DrawablePtr src = (sourceBuffer->attachment == DRI2BufferFrontLeft)
27235c4bbdfSmrg        ? drawable : &src_pixmap->drawable;
27335c4bbdfSmrg    DrawablePtr dst = (destBuffer->attachment == DRI2BufferFrontLeft)
27435c4bbdfSmrg        ? drawable : &dst_pixmap->drawable;
2751b5d61b8Smrg    int off_x = 0, off_y = 0;
2761b5d61b8Smrg    Bool translate = FALSE;
27735c4bbdfSmrg    RegionPtr pCopyClip;
27835c4bbdfSmrg    GCPtr gc;
27935c4bbdfSmrg
2801b5d61b8Smrg    if (destBuffer->attachment == DRI2BufferFrontLeft &&
2811b5d61b8Smrg             drawable->pScreen != screen) {
2821b5d61b8Smrg        dst = DRI2UpdatePrime(drawable, destBuffer);
2831b5d61b8Smrg        if (!dst)
2841b5d61b8Smrg            return;
2851b5d61b8Smrg        if (dst != drawable)
2861b5d61b8Smrg            translate = TRUE;
2871b5d61b8Smrg    }
2881b5d61b8Smrg
2891b5d61b8Smrg    if (translate && drawable->type == DRAWABLE_WINDOW) {
2901b5d61b8Smrg#ifdef COMPOSITE
2911b5d61b8Smrg        PixmapPtr pixmap = get_drawable_pixmap(drawable);
2921b5d61b8Smrg        off_x = -pixmap->screen_x;
2931b5d61b8Smrg        off_y = -pixmap->screen_y;
2941b5d61b8Smrg#endif
2951b5d61b8Smrg        off_x += drawable->x;
2961b5d61b8Smrg        off_y += drawable->y;
2971b5d61b8Smrg    }
2981b5d61b8Smrg
29935c4bbdfSmrg    gc = GetScratchGC(dst->depth, screen);
30035c4bbdfSmrg    if (!gc)
30135c4bbdfSmrg        return;
30235c4bbdfSmrg
30335c4bbdfSmrg    pCopyClip = REGION_CREATE(screen, NULL, 0);
30435c4bbdfSmrg    REGION_COPY(screen, pCopyClip, pRegion);
3051b5d61b8Smrg    if (translate)
3061b5d61b8Smrg        REGION_TRANSLATE(screen, pCopyClip, off_x, off_y);
30735c4bbdfSmrg    (*gc->funcs->ChangeClip) (gc, CT_REGION, pCopyClip, 0);
30835c4bbdfSmrg    ValidateGC(dst, gc);
30935c4bbdfSmrg
31035c4bbdfSmrg    /* It's important that this copy gets submitted before the direct
31135c4bbdfSmrg     * rendering client submits rendering for the next frame, but we
31235c4bbdfSmrg     * don't actually need to submit right now.  The client will wait
31335c4bbdfSmrg     * for the DRI2CopyRegion reply or the swap buffer event before
31435c4bbdfSmrg     * rendering, and we'll hit the flush callback chain before those
31535c4bbdfSmrg     * messages are sent.  We submit our batch buffers from the flush
31635c4bbdfSmrg     * callback chain so we know that will happen before the client
31735c4bbdfSmrg     * tries to render again.
31835c4bbdfSmrg     */
31935c4bbdfSmrg    gc->ops->CopyArea(src, dst, gc,
32035c4bbdfSmrg                      0, 0,
32135c4bbdfSmrg                      drawable->width, drawable->height,
3221b5d61b8Smrg                      off_x, off_y);
32335c4bbdfSmrg
32435c4bbdfSmrg    FreeScratchGC(gc);
32535c4bbdfSmrg}
32635c4bbdfSmrg
3271b5d61b8Smrgstatic void
3281b5d61b8Smrgms_dri2_copy_region(DrawablePtr drawable, RegionPtr pRegion,
3291b5d61b8Smrg                    DRI2BufferPtr destBuffer, DRI2BufferPtr sourceBuffer)
3301b5d61b8Smrg{
3311b5d61b8Smrg    ms_dri2_copy_region2(drawable->pScreen, drawable, pRegion, destBuffer,
3321b5d61b8Smrg                         sourceBuffer);
3331b5d61b8Smrg}
3341b5d61b8Smrg
33535c4bbdfSmrgstatic uint64_t
33635c4bbdfSmrggettime_us(void)
33735c4bbdfSmrg{
33835c4bbdfSmrg    struct timespec tv;
33935c4bbdfSmrg
34035c4bbdfSmrg    if (clock_gettime(CLOCK_MONOTONIC, &tv))
34135c4bbdfSmrg        return 0;
34235c4bbdfSmrg
34335c4bbdfSmrg    return (uint64_t)tv.tv_sec * 1000000 + tv.tv_nsec / 1000;
34435c4bbdfSmrg}
34535c4bbdfSmrg
34635c4bbdfSmrg/**
34735c4bbdfSmrg * Get current frame count and frame count timestamp, based on drawable's
34835c4bbdfSmrg * crtc.
34935c4bbdfSmrg */
35035c4bbdfSmrgstatic int
35135c4bbdfSmrgms_dri2_get_msc(DrawablePtr draw, CARD64 *ust, CARD64 *msc)
35235c4bbdfSmrg{
35335c4bbdfSmrg    int ret;
35435c4bbdfSmrg    xf86CrtcPtr crtc = ms_dri2_crtc_covering_drawable(draw);
35535c4bbdfSmrg
35635c4bbdfSmrg    /* Drawable not displayed, make up a *monotonic* value */
35735c4bbdfSmrg    if (crtc == NULL) {
35835c4bbdfSmrg        *ust = gettime_us();
35935c4bbdfSmrg        *msc = 0;
36035c4bbdfSmrg        return TRUE;
36135c4bbdfSmrg    }
36235c4bbdfSmrg
36335c4bbdfSmrg    ret = ms_get_crtc_ust_msc(crtc, ust, msc);
36435c4bbdfSmrg
36535c4bbdfSmrg    if (ret)
36635c4bbdfSmrg        return FALSE;
36735c4bbdfSmrg
36835c4bbdfSmrg    return TRUE;
36935c4bbdfSmrg}
37035c4bbdfSmrg
37135c4bbdfSmrgstatic XID
37235c4bbdfSmrgget_client_id(ClientPtr client)
37335c4bbdfSmrg{
37435c4bbdfSmrg    XID *ptr = dixGetPrivateAddr(&client->devPrivates, &ms_dri2_client_key);
37535c4bbdfSmrg    if (*ptr == 0)
37635c4bbdfSmrg        *ptr = FakeClientID(client->index);
37735c4bbdfSmrg    return *ptr;
37835c4bbdfSmrg}
37935c4bbdfSmrg
38035c4bbdfSmrg/*
38135c4bbdfSmrg * Hook this frame event into the server resource
38235c4bbdfSmrg * database so we can clean it up if the drawable or
38335c4bbdfSmrg * client exits while the swap is pending
38435c4bbdfSmrg */
38535c4bbdfSmrgstatic Bool
38635c4bbdfSmrgms_dri2_add_frame_event(ms_dri2_frame_event_ptr info)
38735c4bbdfSmrg{
38835c4bbdfSmrg    struct ms_dri2_resource *resource;
38935c4bbdfSmrg
39035c4bbdfSmrg    resource = ms_get_resource(get_client_id(info->client),
39135c4bbdfSmrg                               frame_event_client_type);
39235c4bbdfSmrg    if (resource == NULL)
39335c4bbdfSmrg        return FALSE;
39435c4bbdfSmrg
39535c4bbdfSmrg    xorg_list_add(&info->client_resource, &resource->list);
39635c4bbdfSmrg
39735c4bbdfSmrg    resource = ms_get_resource(info->drawable->id, frame_event_drawable_type);
39835c4bbdfSmrg    if (resource == NULL) {
39935c4bbdfSmrg        xorg_list_del(&info->client_resource);
40035c4bbdfSmrg        return FALSE;
40135c4bbdfSmrg    }
40235c4bbdfSmrg
40335c4bbdfSmrg    xorg_list_add(&info->drawable_resource, &resource->list);
40435c4bbdfSmrg
40535c4bbdfSmrg    return TRUE;
40635c4bbdfSmrg}
40735c4bbdfSmrg
40835c4bbdfSmrgstatic void
40935c4bbdfSmrgms_dri2_del_frame_event(ms_dri2_frame_event_rec *info)
41035c4bbdfSmrg{
41135c4bbdfSmrg    xorg_list_del(&info->client_resource);
41235c4bbdfSmrg    xorg_list_del(&info->drawable_resource);
41335c4bbdfSmrg
41435c4bbdfSmrg    if (info->front)
41535c4bbdfSmrg        ms_dri2_destroy_buffer(NULL, info->front);
41635c4bbdfSmrg    if (info->back)
41735c4bbdfSmrg        ms_dri2_destroy_buffer(NULL, info->back);
41835c4bbdfSmrg
41935c4bbdfSmrg    free(info);
42035c4bbdfSmrg}
42135c4bbdfSmrg
42235c4bbdfSmrgstatic void
42335c4bbdfSmrgms_dri2_blit_swap(DrawablePtr drawable,
42435c4bbdfSmrg                  DRI2BufferPtr dst,
42535c4bbdfSmrg                  DRI2BufferPtr src)
42635c4bbdfSmrg{
42735c4bbdfSmrg    BoxRec box;
42835c4bbdfSmrg    RegionRec region;
42935c4bbdfSmrg
43035c4bbdfSmrg    box.x1 = 0;
43135c4bbdfSmrg    box.y1 = 0;
43235c4bbdfSmrg    box.x2 = drawable->width;
43335c4bbdfSmrg    box.y2 = drawable->height;
43435c4bbdfSmrg    REGION_INIT(pScreen, &region, &box, 0);
43535c4bbdfSmrg
43635c4bbdfSmrg    ms_dri2_copy_region(drawable, &region, dst, src);
43735c4bbdfSmrg}
43835c4bbdfSmrg
4391b5d61b8Smrgstruct ms_dri2_vblank_event {
4401b5d61b8Smrg    XID drawable_id;
4411b5d61b8Smrg    ClientPtr client;
4421b5d61b8Smrg    DRI2SwapEventPtr event_complete;
4431b5d61b8Smrg    void *event_data;
4441b5d61b8Smrg};
4451b5d61b8Smrg
4461b5d61b8Smrgstatic void
4471b5d61b8Smrgms_dri2_flip_abort(modesettingPtr ms, void *data)
4481b5d61b8Smrg{
4491b5d61b8Smrg    struct ms_present_vblank_event *event = data;
4501b5d61b8Smrg
4511b5d61b8Smrg    ms->drmmode.dri2_flipping = FALSE;
4521b5d61b8Smrg    free(event);
4531b5d61b8Smrg}
4541b5d61b8Smrg
4551b5d61b8Smrgstatic void
4561b5d61b8Smrgms_dri2_flip_handler(modesettingPtr ms, uint64_t msc,
4571b5d61b8Smrg                     uint64_t ust, void *data)
4581b5d61b8Smrg{
4591b5d61b8Smrg    struct ms_dri2_vblank_event *event = data;
4601b5d61b8Smrg    uint32_t frame = msc;
4611b5d61b8Smrg    uint32_t tv_sec = ust / 1000000;
4621b5d61b8Smrg    uint32_t tv_usec = ust % 1000000;
4631b5d61b8Smrg    DrawablePtr drawable;
4641b5d61b8Smrg    int status;
4651b5d61b8Smrg
4661b5d61b8Smrg    status = dixLookupDrawable(&drawable, event->drawable_id, serverClient,
4671b5d61b8Smrg                               M_ANY, DixWriteAccess);
4681b5d61b8Smrg    if (status == Success)
4691b5d61b8Smrg        DRI2SwapComplete(event->client, drawable, frame, tv_sec, tv_usec,
4701b5d61b8Smrg                         DRI2_FLIP_COMPLETE, event->event_complete,
4711b5d61b8Smrg                         event->event_data);
4721b5d61b8Smrg
4731b5d61b8Smrg    ms->drmmode.dri2_flipping = FALSE;
4741b5d61b8Smrg    free(event);
4751b5d61b8Smrg}
4761b5d61b8Smrg
4771b5d61b8Smrgstatic Bool
4781b5d61b8Smrgms_dri2_schedule_flip(ms_dri2_frame_event_ptr info)
4791b5d61b8Smrg{
4801b5d61b8Smrg    DrawablePtr draw = info->drawable;
4811b5d61b8Smrg    ScreenPtr screen = draw->pScreen;
4821b5d61b8Smrg    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
4831b5d61b8Smrg    modesettingPtr ms = modesettingPTR(scrn);
4841b5d61b8Smrg    ms_dri2_buffer_private_ptr back_priv = info->back->driverPrivate;
4851b5d61b8Smrg    struct ms_dri2_vblank_event *event;
4861b5d61b8Smrg    drmmode_crtc_private_ptr drmmode_crtc = info->crtc->driver_private;
4871b5d61b8Smrg
4881b5d61b8Smrg    event = calloc(1, sizeof(struct ms_dri2_vblank_event));
4891b5d61b8Smrg    if (!event)
4901b5d61b8Smrg        return FALSE;
4911b5d61b8Smrg
4921b5d61b8Smrg    event->drawable_id = draw->id;
4931b5d61b8Smrg    event->client = info->client;
4941b5d61b8Smrg    event->event_complete = info->event_complete;
4951b5d61b8Smrg    event->event_data = info->event_data;
4961b5d61b8Smrg
4971b5d61b8Smrg    if (ms_do_pageflip(screen, back_priv->pixmap, event,
4981b5d61b8Smrg                       drmmode_crtc->vblank_pipe, FALSE,
4991b5d61b8Smrg                       ms_dri2_flip_handler,
500ed6184dfSmrg                       ms_dri2_flip_abort,
501ed6184dfSmrg                       "DRI2-flip")) {
5021b5d61b8Smrg        ms->drmmode.dri2_flipping = TRUE;
5031b5d61b8Smrg        return TRUE;
5041b5d61b8Smrg    }
5051b5d61b8Smrg    return FALSE;
5061b5d61b8Smrg}
5071b5d61b8Smrg
5081b5d61b8Smrgstatic Bool
5091b5d61b8Smrgupdate_front(DrawablePtr draw, DRI2BufferPtr front)
5101b5d61b8Smrg{
5111b5d61b8Smrg    ScreenPtr screen = draw->pScreen;
5121b5d61b8Smrg    PixmapPtr pixmap = get_drawable_pixmap(draw);
5131b5d61b8Smrg    ms_dri2_buffer_private_ptr priv = front->driverPrivate;
514ed6184dfSmrg    modesettingPtr ms = modesettingPTR(xf86ScreenToScrn(screen));
5151b5d61b8Smrg    CARD32 size;
5161b5d61b8Smrg    CARD16 pitch;
5171b5d61b8Smrg    int name;
5181b5d61b8Smrg
519ed6184dfSmrg    name = ms->glamor.name_from_pixmap(pixmap, &pitch, &size);
5201b5d61b8Smrg    if (name < 0)
5211b5d61b8Smrg        return FALSE;
5221b5d61b8Smrg
5231b5d61b8Smrg    front->name = name;
5241b5d61b8Smrg
5251b5d61b8Smrg    (*screen->DestroyPixmap) (priv->pixmap);
5261b5d61b8Smrg    front->pitch = pixmap->devKind;
5271b5d61b8Smrg    front->cpp = pixmap->drawable.bitsPerPixel / 8;
5281b5d61b8Smrg    priv->pixmap = pixmap;
5291b5d61b8Smrg    pixmap->refcnt++;
5301b5d61b8Smrg
5311b5d61b8Smrg    return TRUE;
5321b5d61b8Smrg}
5331b5d61b8Smrg
5341b5d61b8Smrgstatic Bool
5351b5d61b8Smrgcan_exchange(ScrnInfoPtr scrn, DrawablePtr draw,
5361b5d61b8Smrg	     DRI2BufferPtr front, DRI2BufferPtr back)
5371b5d61b8Smrg{
5381b5d61b8Smrg    ms_dri2_buffer_private_ptr front_priv = front->driverPrivate;
5391b5d61b8Smrg    ms_dri2_buffer_private_ptr back_priv = back->driverPrivate;
5401b5d61b8Smrg    PixmapPtr front_pixmap;
5411b5d61b8Smrg    PixmapPtr back_pixmap = back_priv->pixmap;
5421b5d61b8Smrg    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
5431b5d61b8Smrg    int num_crtcs_on = 0;
5441b5d61b8Smrg    int i;
5451b5d61b8Smrg
5461b5d61b8Smrg    for (i = 0; i < config->num_crtc; i++) {
5471b5d61b8Smrg        drmmode_crtc_private_ptr drmmode_crtc = config->crtc[i]->driver_private;
5481b5d61b8Smrg
5491b5d61b8Smrg        /* Don't do pageflipping if CRTCs are rotated. */
5501b5d61b8Smrg#ifdef GLAMOR_HAS_GBM
5511b5d61b8Smrg        if (drmmode_crtc->rotate_bo.gbm)
5521b5d61b8Smrg            return FALSE;
5531b5d61b8Smrg#endif
5541b5d61b8Smrg
555a035e2b2Smrg        if (xf86_crtc_on(config->crtc[i]))
5561b5d61b8Smrg            num_crtcs_on++;
5571b5d61b8Smrg    }
5581b5d61b8Smrg
5591b5d61b8Smrg    /* We can't do pageflipping if all the CRTCs are off. */
5601b5d61b8Smrg    if (num_crtcs_on == 0)
5611b5d61b8Smrg        return FALSE;
5621b5d61b8Smrg
5631b5d61b8Smrg    if (!update_front(draw, front))
5641b5d61b8Smrg        return FALSE;
5651b5d61b8Smrg
5661b5d61b8Smrg    front_pixmap = front_priv->pixmap;
5671b5d61b8Smrg
5681b5d61b8Smrg    if (front_pixmap->drawable.width != back_pixmap->drawable.width)
5691b5d61b8Smrg        return FALSE;
5701b5d61b8Smrg
5711b5d61b8Smrg    if (front_pixmap->drawable.height != back_pixmap->drawable.height)
5721b5d61b8Smrg        return FALSE;
5731b5d61b8Smrg
5741b5d61b8Smrg    if (front_pixmap->drawable.bitsPerPixel !=
5751b5d61b8Smrg        back_pixmap->drawable.bitsPerPixel)
5761b5d61b8Smrg        return FALSE;
5771b5d61b8Smrg
5781b5d61b8Smrg    if (front_pixmap->devKind != back_pixmap->devKind)
5791b5d61b8Smrg        return FALSE;
5801b5d61b8Smrg
5811b5d61b8Smrg    return TRUE;
5821b5d61b8Smrg}
5831b5d61b8Smrg
5841b5d61b8Smrgstatic Bool
5851b5d61b8Smrgcan_flip(ScrnInfoPtr scrn, DrawablePtr draw,
5861b5d61b8Smrg	 DRI2BufferPtr front, DRI2BufferPtr back)
5871b5d61b8Smrg{
5881b5d61b8Smrg    modesettingPtr ms = modesettingPTR(scrn);
5891b5d61b8Smrg
5901b5d61b8Smrg    return draw->type == DRAWABLE_WINDOW &&
5911b5d61b8Smrg        ms->drmmode.pageflip &&
5925a7dfde8Smrg        !ms->drmmode.sprites_visible &&
5931b5d61b8Smrg        !ms->drmmode.present_flipping &&
5941b5d61b8Smrg        scrn->vtSema &&
5951b5d61b8Smrg        DRI2CanFlip(draw) && can_exchange(scrn, draw, front, back);
5961b5d61b8Smrg}
5971b5d61b8Smrg
5981b5d61b8Smrgstatic void
5991b5d61b8Smrgms_dri2_exchange_buffers(DrawablePtr draw, DRI2BufferPtr front,
6001b5d61b8Smrg                         DRI2BufferPtr back)
6011b5d61b8Smrg{
6021b5d61b8Smrg    ms_dri2_buffer_private_ptr front_priv = front->driverPrivate;
6031b5d61b8Smrg    ms_dri2_buffer_private_ptr back_priv = back->driverPrivate;
6041b5d61b8Smrg    ScreenPtr screen = draw->pScreen;
6051b5d61b8Smrg    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
6061b5d61b8Smrg    modesettingPtr ms = modesettingPTR(scrn);
6071b5d61b8Smrg    msPixmapPrivPtr front_pix = msGetPixmapPriv(&ms->drmmode, front_priv->pixmap);
6081b5d61b8Smrg    msPixmapPrivPtr back_pix = msGetPixmapPriv(&ms->drmmode, back_priv->pixmap);
6091b5d61b8Smrg    msPixmapPrivRec tmp_pix;
6101b5d61b8Smrg    RegionRec region;
6111b5d61b8Smrg    int tmp;
6121b5d61b8Smrg
6131b5d61b8Smrg    /* Swap BO names so DRI works */
6141b5d61b8Smrg    tmp = front->name;
6151b5d61b8Smrg    front->name = back->name;
6161b5d61b8Smrg    back->name = tmp;
6171b5d61b8Smrg
6181b5d61b8Smrg    /* Swap pixmap privates */
6191b5d61b8Smrg    tmp_pix = *front_pix;
6201b5d61b8Smrg    *front_pix = *back_pix;
6211b5d61b8Smrg    *back_pix = tmp_pix;
6221b5d61b8Smrg
623ed6184dfSmrg    ms->glamor.egl_exchange_buffers(front_priv->pixmap, back_priv->pixmap);
6241b5d61b8Smrg
6251b5d61b8Smrg    /* Post damage on the front buffer so that listeners, such
6261b5d61b8Smrg     * as DisplayLink know take a copy and shove it over the USB.
6271b5d61b8Smrg     */
6281b5d61b8Smrg    region.extents.x1 = region.extents.y1 = 0;
6291b5d61b8Smrg    region.extents.x2 = front_priv->pixmap->drawable.width;
6301b5d61b8Smrg    region.extents.y2 = front_priv->pixmap->drawable.height;
6311b5d61b8Smrg    region.data = NULL;
6321b5d61b8Smrg    DamageRegionAppend(&front_priv->pixmap->drawable, &region);
6331b5d61b8Smrg    DamageRegionProcessPending(&front_priv->pixmap->drawable);
6341b5d61b8Smrg}
6351b5d61b8Smrg
63635c4bbdfSmrgstatic void
63735c4bbdfSmrgms_dri2_frame_event_handler(uint64_t msc,
63835c4bbdfSmrg                            uint64_t usec,
63935c4bbdfSmrg                            void *data)
64035c4bbdfSmrg{
64135c4bbdfSmrg    ms_dri2_frame_event_ptr frame_info = data;
64235c4bbdfSmrg    DrawablePtr drawable = frame_info->drawable;
64335c4bbdfSmrg    ScreenPtr screen = frame_info->screen;
64435c4bbdfSmrg    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
64535c4bbdfSmrg    uint32_t tv_sec = usec / 1000000;
64635c4bbdfSmrg    uint32_t tv_usec = usec % 1000000;
64735c4bbdfSmrg
64835c4bbdfSmrg    if (!drawable) {
64935c4bbdfSmrg        ms_dri2_del_frame_event(frame_info);
65035c4bbdfSmrg        return;
65135c4bbdfSmrg    }
65235c4bbdfSmrg
65335c4bbdfSmrg    switch (frame_info->type) {
6541b5d61b8Smrg    case MS_DRI2_QUEUE_FLIP:
6551b5d61b8Smrg        if (can_flip(scrn, drawable, frame_info->front, frame_info->back) &&
6561b5d61b8Smrg            ms_dri2_schedule_flip(frame_info)) {
6571b5d61b8Smrg            ms_dri2_exchange_buffers(drawable, frame_info->front, frame_info->back);
6581b5d61b8Smrg            break;
6591b5d61b8Smrg        }
6601b5d61b8Smrg        /* else fall through to blit */
66135c4bbdfSmrg    case MS_DRI2_QUEUE_SWAP:
66235c4bbdfSmrg        ms_dri2_blit_swap(drawable, frame_info->front, frame_info->back);
66335c4bbdfSmrg        DRI2SwapComplete(frame_info->client, drawable, msc, tv_sec, tv_usec,
66435c4bbdfSmrg                         DRI2_BLIT_COMPLETE,
66535c4bbdfSmrg                         frame_info->client ? frame_info->event_complete : NULL,
66635c4bbdfSmrg                         frame_info->event_data);
66735c4bbdfSmrg        break;
66835c4bbdfSmrg
66935c4bbdfSmrg    case MS_DRI2_WAIT_MSC:
67035c4bbdfSmrg        if (frame_info->client)
67135c4bbdfSmrg            DRI2WaitMSCComplete(frame_info->client, drawable,
67235c4bbdfSmrg                                msc, tv_sec, tv_usec);
67335c4bbdfSmrg        break;
67435c4bbdfSmrg
67535c4bbdfSmrg    default:
67635c4bbdfSmrg        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
67735c4bbdfSmrg                   "%s: unknown vblank event (type %d) received\n", __func__,
67835c4bbdfSmrg                   frame_info->type);
67935c4bbdfSmrg        break;
68035c4bbdfSmrg    }
68135c4bbdfSmrg
68235c4bbdfSmrg    ms_dri2_del_frame_event(frame_info);
68335c4bbdfSmrg}
68435c4bbdfSmrg
68535c4bbdfSmrgstatic void
68635c4bbdfSmrgms_dri2_frame_event_abort(void *data)
68735c4bbdfSmrg{
68835c4bbdfSmrg    ms_dri2_frame_event_ptr frame_info = data;
68935c4bbdfSmrg
69035c4bbdfSmrg    ms_dri2_del_frame_event(frame_info);
69135c4bbdfSmrg}
69235c4bbdfSmrg
69335c4bbdfSmrg/**
69435c4bbdfSmrg * Request a DRM event when the requested conditions will be satisfied.
69535c4bbdfSmrg *
69635c4bbdfSmrg * We need to handle the event and ask the server to wake up the client when
69735c4bbdfSmrg * we receive it.
69835c4bbdfSmrg */
69935c4bbdfSmrgstatic int
70035c4bbdfSmrgms_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
70135c4bbdfSmrg                          CARD64 divisor, CARD64 remainder)
70235c4bbdfSmrg{
70335c4bbdfSmrg    ScreenPtr screen = draw->pScreen;
70435c4bbdfSmrg    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
70535c4bbdfSmrg    ms_dri2_frame_event_ptr wait_info;
70635c4bbdfSmrg    int ret;
70735c4bbdfSmrg    xf86CrtcPtr crtc = ms_dri2_crtc_covering_drawable(draw);
70835c4bbdfSmrg    CARD64 current_msc, current_ust, request_msc;
70935c4bbdfSmrg    uint32_t seq;
7101b5d61b8Smrg    uint64_t queued_msc;
71135c4bbdfSmrg
71235c4bbdfSmrg    /* Drawable not visible, return immediately */
71335c4bbdfSmrg    if (!crtc)
71435c4bbdfSmrg        goto out_complete;
71535c4bbdfSmrg
71635c4bbdfSmrg    wait_info = calloc(1, sizeof(*wait_info));
71735c4bbdfSmrg    if (!wait_info)
71835c4bbdfSmrg        goto out_complete;
71935c4bbdfSmrg
72035c4bbdfSmrg    wait_info->screen = screen;
72135c4bbdfSmrg    wait_info->drawable = draw;
72235c4bbdfSmrg    wait_info->client = client;
72335c4bbdfSmrg    wait_info->type = MS_DRI2_WAIT_MSC;
72435c4bbdfSmrg
72535c4bbdfSmrg    if (!ms_dri2_add_frame_event(wait_info)) {
72635c4bbdfSmrg        free(wait_info);
72735c4bbdfSmrg        wait_info = NULL;
72835c4bbdfSmrg        goto out_complete;
72935c4bbdfSmrg    }
73035c4bbdfSmrg
73135c4bbdfSmrg    /* Get current count */
73235c4bbdfSmrg    ret = ms_get_crtc_ust_msc(crtc, &current_ust, &current_msc);
73335c4bbdfSmrg
73435c4bbdfSmrg    /*
73535c4bbdfSmrg     * If divisor is zero, or current_msc is smaller than target_msc,
73635c4bbdfSmrg     * we just need to make sure target_msc passes  before waking up the
73735c4bbdfSmrg     * client.
73835c4bbdfSmrg     */
73935c4bbdfSmrg    if (divisor == 0 || current_msc < target_msc) {
74035c4bbdfSmrg        /* If target_msc already reached or passed, set it to
74135c4bbdfSmrg         * current_msc to ensure we return a reasonable value back
74235c4bbdfSmrg         * to the caller. This keeps the client from continually
74335c4bbdfSmrg         * sending us MSC targets from the past by forcibly updating
74435c4bbdfSmrg         * their count on this call.
74535c4bbdfSmrg         */
74635c4bbdfSmrg        seq = ms_drm_queue_alloc(crtc, wait_info,
74735c4bbdfSmrg                                 ms_dri2_frame_event_handler,
74835c4bbdfSmrg                                 ms_dri2_frame_event_abort);
74935c4bbdfSmrg        if (!seq)
75035c4bbdfSmrg            goto out_free;
75135c4bbdfSmrg
75235c4bbdfSmrg        if (current_msc >= target_msc)
75335c4bbdfSmrg            target_msc = current_msc;
7541b5d61b8Smrg
7551b5d61b8Smrg        ret = ms_queue_vblank(crtc, MS_QUEUE_ABSOLUTE, target_msc, &queued_msc, seq);
7561b5d61b8Smrg        if (!ret) {
75735c4bbdfSmrg            static int limit = 5;
75835c4bbdfSmrg            if (limit) {
75935c4bbdfSmrg                xf86DrvMsg(scrn->scrnIndex, X_WARNING,
76035c4bbdfSmrg                           "%s:%d get vblank counter failed: %s\n",
76135c4bbdfSmrg                           __FUNCTION__, __LINE__,
76235c4bbdfSmrg                           strerror(errno));
76335c4bbdfSmrg                limit--;
76435c4bbdfSmrg            }
76535c4bbdfSmrg            goto out_free;
76635c4bbdfSmrg        }
76735c4bbdfSmrg
7681b5d61b8Smrg        wait_info->frame = queued_msc;
76935c4bbdfSmrg        DRI2BlockClient(client, draw);
77035c4bbdfSmrg        return TRUE;
77135c4bbdfSmrg    }
77235c4bbdfSmrg
77335c4bbdfSmrg    /*
77435c4bbdfSmrg     * If we get here, target_msc has already passed or we don't have one,
77535c4bbdfSmrg     * so we queue an event that will satisfy the divisor/remainder equation.
77635c4bbdfSmrg     */
77735c4bbdfSmrg    request_msc = current_msc - (current_msc % divisor) +
77835c4bbdfSmrg        remainder;
77935c4bbdfSmrg    /*
78035c4bbdfSmrg     * If calculated remainder is larger than requested remainder,
78135c4bbdfSmrg     * it means we've passed the last point where
78235c4bbdfSmrg     * seq % divisor == remainder, so we need to wait for the next time
78335c4bbdfSmrg     * that will happen.
78435c4bbdfSmrg     */
78535c4bbdfSmrg    if ((current_msc % divisor) >= remainder)
78635c4bbdfSmrg        request_msc += divisor;
78735c4bbdfSmrg
78835c4bbdfSmrg    seq = ms_drm_queue_alloc(crtc, wait_info,
78935c4bbdfSmrg                             ms_dri2_frame_event_handler,
79035c4bbdfSmrg                             ms_dri2_frame_event_abort);
79135c4bbdfSmrg    if (!seq)
79235c4bbdfSmrg        goto out_free;
79335c4bbdfSmrg
7941b5d61b8Smrg    if (!ms_queue_vblank(crtc, MS_QUEUE_ABSOLUTE, request_msc, &queued_msc, seq)) {
79535c4bbdfSmrg        static int limit = 5;
79635c4bbdfSmrg        if (limit) {
79735c4bbdfSmrg            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
79835c4bbdfSmrg                       "%s:%d get vblank counter failed: %s\n",
79935c4bbdfSmrg                       __FUNCTION__, __LINE__,
80035c4bbdfSmrg                       strerror(errno));
80135c4bbdfSmrg            limit--;
80235c4bbdfSmrg        }
80335c4bbdfSmrg        goto out_free;
80435c4bbdfSmrg    }
80535c4bbdfSmrg
8061b5d61b8Smrg    wait_info->frame = queued_msc;
8071b5d61b8Smrg
80835c4bbdfSmrg    DRI2BlockClient(client, draw);
80935c4bbdfSmrg
81035c4bbdfSmrg    return TRUE;
81135c4bbdfSmrg
81235c4bbdfSmrg out_free:
81335c4bbdfSmrg    ms_dri2_del_frame_event(wait_info);
81435c4bbdfSmrg out_complete:
81535c4bbdfSmrg    DRI2WaitMSCComplete(client, draw, target_msc, 0, 0);
81635c4bbdfSmrg    return TRUE;
81735c4bbdfSmrg}
81835c4bbdfSmrg
81935c4bbdfSmrg/**
82035c4bbdfSmrg * ScheduleSwap is responsible for requesting a DRM vblank event for
82135c4bbdfSmrg * the appropriate frame, or executing the swap immediately if it
82235c4bbdfSmrg * doesn't need to wait.
82335c4bbdfSmrg *
82435c4bbdfSmrg * When the swap is complete, the driver should call into the server so it
82535c4bbdfSmrg * can send any swap complete events that have been requested.
82635c4bbdfSmrg */
82735c4bbdfSmrgstatic int
82835c4bbdfSmrgms_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
82935c4bbdfSmrg                      DRI2BufferPtr front, DRI2BufferPtr back,
83035c4bbdfSmrg                      CARD64 *target_msc, CARD64 divisor,
83135c4bbdfSmrg                      CARD64 remainder, DRI2SwapEventPtr func, void *data)
83235c4bbdfSmrg{
83335c4bbdfSmrg    ScreenPtr screen = draw->pScreen;
83435c4bbdfSmrg    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
8351b5d61b8Smrg    int ret, flip = 0;
83635c4bbdfSmrg    xf86CrtcPtr crtc = ms_dri2_crtc_covering_drawable(draw);
83735c4bbdfSmrg    ms_dri2_frame_event_ptr frame_info = NULL;
83835c4bbdfSmrg    uint64_t current_msc, current_ust;
83935c4bbdfSmrg    uint64_t request_msc;
84035c4bbdfSmrg    uint32_t seq;
8411b5d61b8Smrg    ms_queue_flag ms_flag = MS_QUEUE_ABSOLUTE;
8421b5d61b8Smrg    uint64_t queued_msc;
84335c4bbdfSmrg
84435c4bbdfSmrg    /* Drawable not displayed... just complete the swap */
84535c4bbdfSmrg    if (!crtc)
84635c4bbdfSmrg        goto blit_fallback;
84735c4bbdfSmrg
84835c4bbdfSmrg    frame_info = calloc(1, sizeof(*frame_info));
84935c4bbdfSmrg    if (!frame_info)
85035c4bbdfSmrg        goto blit_fallback;
85135c4bbdfSmrg
85235c4bbdfSmrg    frame_info->screen = screen;
85335c4bbdfSmrg    frame_info->drawable = draw;
85435c4bbdfSmrg    frame_info->client = client;
85535c4bbdfSmrg    frame_info->event_complete = func;
85635c4bbdfSmrg    frame_info->event_data = data;
85735c4bbdfSmrg    frame_info->front = front;
85835c4bbdfSmrg    frame_info->back = back;
85935c4bbdfSmrg    frame_info->crtc = crtc;
86035c4bbdfSmrg    frame_info->type = MS_DRI2_QUEUE_SWAP;
86135c4bbdfSmrg
86235c4bbdfSmrg    if (!ms_dri2_add_frame_event(frame_info)) {
86335c4bbdfSmrg        free(frame_info);
86435c4bbdfSmrg        frame_info = NULL;
86535c4bbdfSmrg        goto blit_fallback;
86635c4bbdfSmrg    }
86735c4bbdfSmrg
86835c4bbdfSmrg    ms_dri2_reference_buffer(front);
86935c4bbdfSmrg    ms_dri2_reference_buffer(back);
87035c4bbdfSmrg
87135c4bbdfSmrg    ret = ms_get_crtc_ust_msc(crtc, &current_ust, &current_msc);
8721b5d61b8Smrg    if (ret != Success)
8731b5d61b8Smrg        goto blit_fallback;
8741b5d61b8Smrg
8751b5d61b8Smrg    /* Flips need to be submitted one frame before */
8761b5d61b8Smrg    if (can_flip(scrn, draw, front, back)) {
8771b5d61b8Smrg        frame_info->type = MS_DRI2_QUEUE_FLIP;
8781b5d61b8Smrg        flip = 1;
8791b5d61b8Smrg    }
8801b5d61b8Smrg
8811b5d61b8Smrg    /* Correct target_msc by 'flip' if frame_info->type == MS_DRI2_QUEUE_FLIP.
8821b5d61b8Smrg     * Do it early, so handling of different timing constraints
8831b5d61b8Smrg     * for divisor, remainder and msc vs. target_msc works.
8841b5d61b8Smrg     */
8851b5d61b8Smrg    if (*target_msc > 0)
8861b5d61b8Smrg        *target_msc -= flip;
8871b5d61b8Smrg
8881b5d61b8Smrg    /* If non-pageflipping, but blitting/exchanging, we need to use
8891b5d61b8Smrg     * DRM_VBLANK_NEXTONMISS to avoid unreliable timestamping later
8901b5d61b8Smrg     * on.
8911b5d61b8Smrg     */
8921b5d61b8Smrg    if (flip == 0)
8931b5d61b8Smrg        ms_flag |= MS_QUEUE_NEXT_ON_MISS;
89435c4bbdfSmrg
89535c4bbdfSmrg    /*
89635c4bbdfSmrg     * If divisor is zero, or current_msc is smaller than target_msc
89735c4bbdfSmrg     * we just need to make sure target_msc passes before initiating
89835c4bbdfSmrg     * the swap.
89935c4bbdfSmrg     */
90035c4bbdfSmrg    if (divisor == 0 || current_msc < *target_msc) {
90135c4bbdfSmrg
90235c4bbdfSmrg        /* If target_msc already reached or passed, set it to
90335c4bbdfSmrg         * current_msc to ensure we return a reasonable value back
90435c4bbdfSmrg         * to the caller. This makes swap_interval logic more robust.
90535c4bbdfSmrg         */
90635c4bbdfSmrg        if (current_msc >= *target_msc)
90735c4bbdfSmrg            *target_msc = current_msc;
90835c4bbdfSmrg
90935c4bbdfSmrg        seq = ms_drm_queue_alloc(crtc, frame_info,
91035c4bbdfSmrg                                 ms_dri2_frame_event_handler,
91135c4bbdfSmrg                                 ms_dri2_frame_event_abort);
91235c4bbdfSmrg        if (!seq)
91335c4bbdfSmrg            goto blit_fallback;
91435c4bbdfSmrg
9151b5d61b8Smrg        if (!ms_queue_vblank(crtc, ms_flag, *target_msc, &queued_msc, seq)) {
91635c4bbdfSmrg            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
91735c4bbdfSmrg                       "divisor 0 get vblank counter failed: %s\n",
91835c4bbdfSmrg                       strerror(errno));
91935c4bbdfSmrg            goto blit_fallback;
92035c4bbdfSmrg        }
92135c4bbdfSmrg
9221b5d61b8Smrg        *target_msc = queued_msc + flip;
92335c4bbdfSmrg        frame_info->frame = *target_msc;
92435c4bbdfSmrg
92535c4bbdfSmrg        return TRUE;
92635c4bbdfSmrg    }
92735c4bbdfSmrg
92835c4bbdfSmrg    /*
92935c4bbdfSmrg     * If we get here, target_msc has already passed or we don't have one,
93035c4bbdfSmrg     * and we need to queue an event that will satisfy the divisor/remainder
93135c4bbdfSmrg     * equation.
93235c4bbdfSmrg     */
93335c4bbdfSmrg
93435c4bbdfSmrg    request_msc = current_msc - (current_msc % divisor) +
93535c4bbdfSmrg        remainder;
93635c4bbdfSmrg
93735c4bbdfSmrg    /*
93835c4bbdfSmrg     * If the calculated deadline vbl.request.sequence is smaller than
93935c4bbdfSmrg     * or equal to current_msc, it means we've passed the last point
94035c4bbdfSmrg     * when effective onset frame seq could satisfy
94135c4bbdfSmrg     * seq % divisor == remainder, so we need to wait for the next time
94235c4bbdfSmrg     * this will happen.
94335c4bbdfSmrg
94435c4bbdfSmrg     * This comparison takes the DRM_VBLANK_NEXTONMISS delay into account.
94535c4bbdfSmrg     */
94635c4bbdfSmrg    if (request_msc <= current_msc)
94735c4bbdfSmrg        request_msc += divisor;
94835c4bbdfSmrg
94935c4bbdfSmrg    seq = ms_drm_queue_alloc(crtc, frame_info,
95035c4bbdfSmrg                             ms_dri2_frame_event_handler,
95135c4bbdfSmrg                             ms_dri2_frame_event_abort);
95235c4bbdfSmrg    if (!seq)
95335c4bbdfSmrg        goto blit_fallback;
95435c4bbdfSmrg
9551b5d61b8Smrg    /* Account for 1 frame extra pageflip delay if flip > 0 */
9561b5d61b8Smrg    if (!ms_queue_vblank(crtc, ms_flag, request_msc - flip, &queued_msc, seq)) {
95735c4bbdfSmrg        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
95835c4bbdfSmrg                   "final get vblank counter failed: %s\n",
95935c4bbdfSmrg                   strerror(errno));
96035c4bbdfSmrg        goto blit_fallback;
96135c4bbdfSmrg    }
96235c4bbdfSmrg
9631b5d61b8Smrg    /* Adjust returned value for 1 fame pageflip offset of flip > 0 */
9641b5d61b8Smrg    *target_msc = queued_msc + flip;
96535c4bbdfSmrg    frame_info->frame = *target_msc;
96635c4bbdfSmrg
96735c4bbdfSmrg    return TRUE;
96835c4bbdfSmrg
96935c4bbdfSmrg blit_fallback:
97035c4bbdfSmrg    ms_dri2_blit_swap(draw, front, back);
97135c4bbdfSmrg    DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data);
97235c4bbdfSmrg    if (frame_info)
97335c4bbdfSmrg        ms_dri2_del_frame_event(frame_info);
97435c4bbdfSmrg    *target_msc = 0; /* offscreen, so zero out target vblank count */
97535c4bbdfSmrg    return TRUE;
97635c4bbdfSmrg}
97735c4bbdfSmrg
97835c4bbdfSmrgstatic int
97935c4bbdfSmrgms_dri2_frame_event_client_gone(void *data, XID id)
98035c4bbdfSmrg{
98135c4bbdfSmrg    struct ms_dri2_resource *resource = data;
98235c4bbdfSmrg
98335c4bbdfSmrg    while (!xorg_list_is_empty(&resource->list)) {
98435c4bbdfSmrg        ms_dri2_frame_event_ptr info =
98535c4bbdfSmrg            xorg_list_first_entry(&resource->list,
98635c4bbdfSmrg                                  ms_dri2_frame_event_rec,
98735c4bbdfSmrg                                  client_resource);
98835c4bbdfSmrg
98935c4bbdfSmrg        xorg_list_del(&info->client_resource);
99035c4bbdfSmrg        info->client = NULL;
99135c4bbdfSmrg    }
99235c4bbdfSmrg    free(resource);
99335c4bbdfSmrg
99435c4bbdfSmrg    return Success;
99535c4bbdfSmrg}
99635c4bbdfSmrg
99735c4bbdfSmrgstatic int
99835c4bbdfSmrgms_dri2_frame_event_drawable_gone(void *data, XID id)
99935c4bbdfSmrg{
100035c4bbdfSmrg    struct ms_dri2_resource *resource = data;
100135c4bbdfSmrg
100235c4bbdfSmrg    while (!xorg_list_is_empty(&resource->list)) {
100335c4bbdfSmrg        ms_dri2_frame_event_ptr info =
100435c4bbdfSmrg            xorg_list_first_entry(&resource->list,
100535c4bbdfSmrg                                  ms_dri2_frame_event_rec,
100635c4bbdfSmrg                                  drawable_resource);
100735c4bbdfSmrg
100835c4bbdfSmrg        xorg_list_del(&info->drawable_resource);
100935c4bbdfSmrg        info->drawable = NULL;
101035c4bbdfSmrg    }
101135c4bbdfSmrg    free(resource);
101235c4bbdfSmrg
101335c4bbdfSmrg    return Success;
101435c4bbdfSmrg}
101535c4bbdfSmrg
101635c4bbdfSmrgstatic Bool
101735c4bbdfSmrgms_dri2_register_frame_event_resource_types(void)
101835c4bbdfSmrg{
101935c4bbdfSmrg    frame_event_client_type =
102035c4bbdfSmrg        CreateNewResourceType(ms_dri2_frame_event_client_gone,
102135c4bbdfSmrg                              "Frame Event Client");
102235c4bbdfSmrg    if (!frame_event_client_type)
102335c4bbdfSmrg        return FALSE;
102435c4bbdfSmrg
102535c4bbdfSmrg    frame_event_drawable_type =
102635c4bbdfSmrg        CreateNewResourceType(ms_dri2_frame_event_drawable_gone,
102735c4bbdfSmrg                              "Frame Event Drawable");
102835c4bbdfSmrg    if (!frame_event_drawable_type)
102935c4bbdfSmrg        return FALSE;
103035c4bbdfSmrg
103135c4bbdfSmrg    return TRUE;
103235c4bbdfSmrg}
103335c4bbdfSmrg
103435c4bbdfSmrgBool
103535c4bbdfSmrgms_dri2_screen_init(ScreenPtr screen)
103635c4bbdfSmrg{
103735c4bbdfSmrg    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
103835c4bbdfSmrg    modesettingPtr ms = modesettingPTR(scrn);
103935c4bbdfSmrg    DRI2InfoRec info;
10405a7dfde8Smrg    const char *driver_names[2] = { NULL, NULL };
104135c4bbdfSmrg
1042ed6184dfSmrg    if (!ms->glamor.supports_pixmap_import_export(screen)) {
104335c4bbdfSmrg        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
104435c4bbdfSmrg                   "DRI2: glamor lacks support for pixmap import/export\n");
104535c4bbdfSmrg    }
104635c4bbdfSmrg
104735c4bbdfSmrg    if (!xf86LoaderCheckSymbol("DRI2Version"))
104835c4bbdfSmrg        return FALSE;
104935c4bbdfSmrg
105035c4bbdfSmrg    if (!dixRegisterPrivateKey(&ms_dri2_client_key,
105135c4bbdfSmrg                               PRIVATE_CLIENT, sizeof(XID)))
105235c4bbdfSmrg        return FALSE;
105335c4bbdfSmrg
105435c4bbdfSmrg    if (serverGeneration != ms_dri2_server_generation) {
105535c4bbdfSmrg        ms_dri2_server_generation = serverGeneration;
105635c4bbdfSmrg        if (!ms_dri2_register_frame_event_resource_types()) {
105735c4bbdfSmrg            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
105835c4bbdfSmrg                       "Cannot register DRI2 frame event resources\n");
105935c4bbdfSmrg            return FALSE;
106035c4bbdfSmrg        }
106135c4bbdfSmrg    }
106235c4bbdfSmrg
106335c4bbdfSmrg    memset(&info, '\0', sizeof(info));
106435c4bbdfSmrg    info.fd = ms->fd;
106535c4bbdfSmrg    info.driverName = NULL; /* Compat field, unused. */
106635c4bbdfSmrg    info.deviceName = drmGetDeviceNameFromFd(ms->fd);
106735c4bbdfSmrg
10681b5d61b8Smrg    info.version = 9;
106935c4bbdfSmrg    info.CreateBuffer = ms_dri2_create_buffer;
107035c4bbdfSmrg    info.DestroyBuffer = ms_dri2_destroy_buffer;
107135c4bbdfSmrg    info.CopyRegion = ms_dri2_copy_region;
107235c4bbdfSmrg    info.ScheduleSwap = ms_dri2_schedule_swap;
107335c4bbdfSmrg    info.GetMSC = ms_dri2_get_msc;
107435c4bbdfSmrg    info.ScheduleWaitMSC = ms_dri2_schedule_wait_msc;
10751b5d61b8Smrg    info.CreateBuffer2 = ms_dri2_create_buffer2;
10761b5d61b8Smrg    info.DestroyBuffer2 = ms_dri2_destroy_buffer2;
10771b5d61b8Smrg    info.CopyRegion2 = ms_dri2_copy_region2;
107835c4bbdfSmrg
1079ed6184dfSmrg    /* Ask Glamor to obtain the DRI driver name via EGL_MESA_query_driver, */
1080ed6184dfSmrg    if (ms->glamor.egl_get_driver_name)
1081ed6184dfSmrg        driver_names[0] = ms->glamor.egl_get_driver_name(screen);
10825a7dfde8Smrg
10835a7dfde8Smrg    if (driver_names[0]) {
10845a7dfde8Smrg        /* There is no VDPAU driver for Intel, fallback to the generic
10855a7dfde8Smrg         * OpenGL/VAAPI va_gl backend to emulate VDPAU.  Otherwise,
10865a7dfde8Smrg         * guess that the DRI and VDPAU drivers have the same name.
10875a7dfde8Smrg         */
10885a7dfde8Smrg        if (strcmp(driver_names[0], "i965") == 0 ||
1089ed6184dfSmrg            strcmp(driver_names[0], "iris") == 0 ||
1090ed6184dfSmrg            strcmp(driver_names[0], "crocus") == 0) {
10915a7dfde8Smrg            driver_names[1] = "va_gl";
10925a7dfde8Smrg        } else {
10935a7dfde8Smrg            driver_names[1] = driver_names[0];
10945a7dfde8Smrg        }
10955a7dfde8Smrg
10965a7dfde8Smrg        info.numDrivers = 2;
10975a7dfde8Smrg        info.driverNames = driver_names;
10985a7dfde8Smrg    } else {
10995a7dfde8Smrg        /* EGL_MESA_query_driver was unavailable; let dri2.c select the
11005a7dfde8Smrg         * driver and fill in these fields for us.
11015a7dfde8Smrg         */
11025a7dfde8Smrg        info.numDrivers = 0;
11035a7dfde8Smrg        info.driverNames = NULL;
11045a7dfde8Smrg    }
110535c4bbdfSmrg
110635c4bbdfSmrg    return DRI2ScreenInit(screen, &info);
110735c4bbdfSmrg}
110835c4bbdfSmrg
110935c4bbdfSmrgvoid
111035c4bbdfSmrgms_dri2_close_screen(ScreenPtr screen)
111135c4bbdfSmrg{
111235c4bbdfSmrg    DRI2CloseScreen(screen);
111335c4bbdfSmrg}
111435c4bbdfSmrg
11151b5d61b8Smrg#endif /* GLAMOR_HAS_GBM */
1116