1/*
2 * Copyright © 2013 Intel Corporation
3 * Copyright © 2014 Broadcom
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25/**
26 * @file dri2.c
27 *
28 * Implements generic support for DRI2 on KMS, using glamor pixmaps
29 * for color buffer management (no support for other aux buffers), and
30 * the DRM vblank ioctls.
31 *
32 * This doesn't implement pageflipping yet.
33 */
34
35#ifdef HAVE_DIX_CONFIG_H
36#include "dix-config.h"
37#endif
38
39#include <time.h>
40#include "list.h"
41#include "xf86.h"
42#include "driver.h"
43#include "dri2.h"
44
45#ifdef GLAMOR_HAS_GBM
46
47enum ms_dri2_frame_event_type {
48    MS_DRI2_QUEUE_SWAP,
49    MS_DRI2_QUEUE_FLIP,
50    MS_DRI2_WAIT_MSC,
51};
52
53typedef struct ms_dri2_frame_event {
54    ScreenPtr screen;
55
56    DrawablePtr drawable;
57    ClientPtr client;
58    enum ms_dri2_frame_event_type type;
59    int frame;
60    xf86CrtcPtr crtc;
61
62    struct xorg_list drawable_resource, client_resource;
63
64    /* for swaps & flips only */
65    DRI2SwapEventPtr event_complete;
66    void *event_data;
67    DRI2BufferPtr front;
68    DRI2BufferPtr back;
69} ms_dri2_frame_event_rec, *ms_dri2_frame_event_ptr;
70
71typedef struct {
72    int refcnt;
73    PixmapPtr pixmap;
74} ms_dri2_buffer_private_rec, *ms_dri2_buffer_private_ptr;
75
76static DevPrivateKeyRec ms_dri2_client_key;
77static RESTYPE frame_event_client_type, frame_event_drawable_type;
78static int ms_dri2_server_generation;
79
80struct ms_dri2_resource {
81    XID id;
82    RESTYPE type;
83    struct xorg_list list;
84};
85
86static struct ms_dri2_resource *
87ms_get_resource(XID id, RESTYPE type)
88{
89    struct ms_dri2_resource *resource;
90    void *ptr;
91
92    ptr = NULL;
93    dixLookupResourceByType(&ptr, id, type, NULL, DixWriteAccess);
94    if (ptr)
95        return ptr;
96
97    resource = malloc(sizeof(*resource));
98    if (resource == NULL)
99        return NULL;
100
101    if (!AddResource(id, type, resource))
102        return NULL;
103
104    resource->id = id;
105    resource->type = type;
106    xorg_list_init(&resource->list);
107    return resource;
108}
109
110static inline PixmapPtr
111get_drawable_pixmap(DrawablePtr drawable)
112{
113    ScreenPtr screen = drawable->pScreen;
114
115    if (drawable->type == DRAWABLE_PIXMAP)
116        return (PixmapPtr) drawable;
117    else
118        return screen->GetWindowPixmap((WindowPtr) drawable);
119}
120
121static DRI2Buffer2Ptr
122ms_dri2_create_buffer2(ScreenPtr screen, DrawablePtr drawable,
123                       unsigned int attachment, unsigned int format)
124{
125    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
126    modesettingPtr ms = modesettingPTR(scrn);
127    DRI2Buffer2Ptr buffer;
128    PixmapPtr pixmap;
129    CARD32 size;
130    CARD16 pitch;
131    ms_dri2_buffer_private_ptr private;
132
133    buffer = calloc(1, sizeof *buffer);
134    if (buffer == NULL)
135        return NULL;
136
137    private = calloc(1, sizeof(*private));
138    if (private == NULL) {
139        free(buffer);
140        return NULL;
141    }
142
143    pixmap = NULL;
144    if (attachment == DRI2BufferFrontLeft) {
145        pixmap = get_drawable_pixmap(drawable);
146        if (pixmap && pixmap->drawable.pScreen != screen)
147            pixmap = NULL;
148        if (pixmap)
149            pixmap->refcnt++;
150    }
151
152    if (pixmap == NULL) {
153        int pixmap_width = drawable->width;
154        int pixmap_height = drawable->height;
155        int pixmap_cpp = (format != 0) ? format : drawable->depth;
156
157        /* Assume that non-color-buffers require special
158         * device-specific handling.  Mesa currently makes no requests
159         * for non-color aux buffers.
160         */
161        switch (attachment) {
162        case DRI2BufferAccum:
163        case DRI2BufferBackLeft:
164        case DRI2BufferBackRight:
165        case DRI2BufferFakeFrontLeft:
166        case DRI2BufferFakeFrontRight:
167        case DRI2BufferFrontLeft:
168        case DRI2BufferFrontRight:
169            break;
170
171        case DRI2BufferStencil:
172        case DRI2BufferDepth:
173        case DRI2BufferDepthStencil:
174        case DRI2BufferHiz:
175        default:
176            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
177                       "Request for DRI2 buffer attachment %d unsupported\n",
178                       attachment);
179            free(private);
180            free(buffer);
181            return NULL;
182        }
183
184        pixmap = screen->CreatePixmap(screen,
185                                      pixmap_width,
186                                      pixmap_height,
187                                      pixmap_cpp,
188                                      0);
189        if (pixmap == NULL) {
190            free(private);
191            free(buffer);
192            return NULL;
193        }
194    }
195
196    buffer->attachment = attachment;
197    buffer->cpp = pixmap->drawable.bitsPerPixel / 8;
198    buffer->format = format;
199    /* The buffer's flags field is unused by the client drivers in
200     * Mesa currently.
201     */
202    buffer->flags = 0;
203
204    buffer->name = ms->glamor.name_from_pixmap(pixmap, &pitch, &size);
205    buffer->pitch = pitch;
206    if (buffer->name == -1) {
207        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
208                   "Failed to get DRI2 name for pixmap\n");
209        screen->DestroyPixmap(pixmap);
210        free(private);
211        free(buffer);
212        return NULL;
213    }
214
215    buffer->driverPrivate = private;
216    private->refcnt = 1;
217    private->pixmap = pixmap;
218
219    return buffer;
220}
221
222static DRI2Buffer2Ptr
223ms_dri2_create_buffer(DrawablePtr drawable, unsigned int attachment,
224                      unsigned int format)
225{
226    return ms_dri2_create_buffer2(drawable->pScreen, drawable, attachment,
227                                  format);
228}
229
230static void
231ms_dri2_reference_buffer(DRI2Buffer2Ptr buffer)
232{
233    if (buffer) {
234        ms_dri2_buffer_private_ptr private = buffer->driverPrivate;
235        private->refcnt++;
236    }
237}
238
239static void ms_dri2_destroy_buffer2(ScreenPtr unused, DrawablePtr unused2,
240                                    DRI2Buffer2Ptr buffer)
241{
242    if (!buffer)
243        return;
244
245    if (buffer->driverPrivate) {
246        ms_dri2_buffer_private_ptr private = buffer->driverPrivate;
247        if (--private->refcnt == 0) {
248            ScreenPtr screen = private->pixmap->drawable.pScreen;
249            screen->DestroyPixmap(private->pixmap);
250            free(private);
251            free(buffer);
252        }
253    } else {
254        free(buffer);
255    }
256}
257
258static void ms_dri2_destroy_buffer(DrawablePtr drawable, DRI2Buffer2Ptr buffer)
259{
260    ms_dri2_destroy_buffer2(NULL, drawable, buffer);
261}
262
263static void
264ms_dri2_copy_region2(ScreenPtr screen, DrawablePtr drawable, RegionPtr pRegion,
265                     DRI2BufferPtr destBuffer, DRI2BufferPtr sourceBuffer)
266{
267    ms_dri2_buffer_private_ptr src_priv = sourceBuffer->driverPrivate;
268    ms_dri2_buffer_private_ptr dst_priv = destBuffer->driverPrivate;
269    PixmapPtr src_pixmap = src_priv->pixmap;
270    PixmapPtr dst_pixmap = dst_priv->pixmap;
271    DrawablePtr src = (sourceBuffer->attachment == DRI2BufferFrontLeft)
272        ? drawable : &src_pixmap->drawable;
273    DrawablePtr dst = (destBuffer->attachment == DRI2BufferFrontLeft)
274        ? drawable : &dst_pixmap->drawable;
275    int off_x = 0, off_y = 0;
276    Bool translate = FALSE;
277    RegionPtr pCopyClip;
278    GCPtr gc;
279
280    if (destBuffer->attachment == DRI2BufferFrontLeft &&
281             drawable->pScreen != screen) {
282        dst = DRI2UpdatePrime(drawable, destBuffer);
283        if (!dst)
284            return;
285        if (dst != drawable)
286            translate = TRUE;
287    }
288
289    if (translate && drawable->type == DRAWABLE_WINDOW) {
290#ifdef COMPOSITE
291        PixmapPtr pixmap = get_drawable_pixmap(drawable);
292        off_x = -pixmap->screen_x;
293        off_y = -pixmap->screen_y;
294#endif
295        off_x += drawable->x;
296        off_y += drawable->y;
297    }
298
299    gc = GetScratchGC(dst->depth, screen);
300    if (!gc)
301        return;
302
303    pCopyClip = REGION_CREATE(screen, NULL, 0);
304    REGION_COPY(screen, pCopyClip, pRegion);
305    if (translate)
306        REGION_TRANSLATE(screen, pCopyClip, off_x, off_y);
307    (*gc->funcs->ChangeClip) (gc, CT_REGION, pCopyClip, 0);
308    ValidateGC(dst, gc);
309
310    /* It's important that this copy gets submitted before the direct
311     * rendering client submits rendering for the next frame, but we
312     * don't actually need to submit right now.  The client will wait
313     * for the DRI2CopyRegion reply or the swap buffer event before
314     * rendering, and we'll hit the flush callback chain before those
315     * messages are sent.  We submit our batch buffers from the flush
316     * callback chain so we know that will happen before the client
317     * tries to render again.
318     */
319    gc->ops->CopyArea(src, dst, gc,
320                      0, 0,
321                      drawable->width, drawable->height,
322                      off_x, off_y);
323
324    FreeScratchGC(gc);
325}
326
327static void
328ms_dri2_copy_region(DrawablePtr drawable, RegionPtr pRegion,
329                    DRI2BufferPtr destBuffer, DRI2BufferPtr sourceBuffer)
330{
331    ms_dri2_copy_region2(drawable->pScreen, drawable, pRegion, destBuffer,
332                         sourceBuffer);
333}
334
335static uint64_t
336gettime_us(void)
337{
338    struct timespec tv;
339
340    if (clock_gettime(CLOCK_MONOTONIC, &tv))
341        return 0;
342
343    return (uint64_t)tv.tv_sec * 1000000 + tv.tv_nsec / 1000;
344}
345
346/**
347 * Get current frame count and frame count timestamp, based on drawable's
348 * crtc.
349 */
350static int
351ms_dri2_get_msc(DrawablePtr draw, CARD64 *ust, CARD64 *msc)
352{
353    int ret;
354    xf86CrtcPtr crtc = ms_dri2_crtc_covering_drawable(draw);
355
356    /* Drawable not displayed, make up a *monotonic* value */
357    if (crtc == NULL) {
358        *ust = gettime_us();
359        *msc = 0;
360        return TRUE;
361    }
362
363    ret = ms_get_crtc_ust_msc(crtc, ust, msc);
364
365    if (ret)
366        return FALSE;
367
368    return TRUE;
369}
370
371static XID
372get_client_id(ClientPtr client)
373{
374    XID *ptr = dixGetPrivateAddr(&client->devPrivates, &ms_dri2_client_key);
375    if (*ptr == 0)
376        *ptr = FakeClientID(client->index);
377    return *ptr;
378}
379
380/*
381 * Hook this frame event into the server resource
382 * database so we can clean it up if the drawable or
383 * client exits while the swap is pending
384 */
385static Bool
386ms_dri2_add_frame_event(ms_dri2_frame_event_ptr info)
387{
388    struct ms_dri2_resource *resource;
389
390    resource = ms_get_resource(get_client_id(info->client),
391                               frame_event_client_type);
392    if (resource == NULL)
393        return FALSE;
394
395    xorg_list_add(&info->client_resource, &resource->list);
396
397    resource = ms_get_resource(info->drawable->id, frame_event_drawable_type);
398    if (resource == NULL) {
399        xorg_list_del(&info->client_resource);
400        return FALSE;
401    }
402
403    xorg_list_add(&info->drawable_resource, &resource->list);
404
405    return TRUE;
406}
407
408static void
409ms_dri2_del_frame_event(ms_dri2_frame_event_rec *info)
410{
411    xorg_list_del(&info->client_resource);
412    xorg_list_del(&info->drawable_resource);
413
414    if (info->front)
415        ms_dri2_destroy_buffer(NULL, info->front);
416    if (info->back)
417        ms_dri2_destroy_buffer(NULL, info->back);
418
419    free(info);
420}
421
422static void
423ms_dri2_blit_swap(DrawablePtr drawable,
424                  DRI2BufferPtr dst,
425                  DRI2BufferPtr src)
426{
427    BoxRec box;
428    RegionRec region;
429
430    box.x1 = 0;
431    box.y1 = 0;
432    box.x2 = drawable->width;
433    box.y2 = drawable->height;
434    REGION_INIT(pScreen, &region, &box, 0);
435
436    ms_dri2_copy_region(drawable, &region, dst, src);
437}
438
439struct ms_dri2_vblank_event {
440    XID drawable_id;
441    ClientPtr client;
442    DRI2SwapEventPtr event_complete;
443    void *event_data;
444};
445
446static void
447ms_dri2_flip_abort(modesettingPtr ms, void *data)
448{
449    struct ms_present_vblank_event *event = data;
450
451    ms->drmmode.dri2_flipping = FALSE;
452    free(event);
453}
454
455static void
456ms_dri2_flip_handler(modesettingPtr ms, uint64_t msc,
457                     uint64_t ust, void *data)
458{
459    struct ms_dri2_vblank_event *event = data;
460    uint32_t frame = msc;
461    uint32_t tv_sec = ust / 1000000;
462    uint32_t tv_usec = ust % 1000000;
463    DrawablePtr drawable;
464    int status;
465
466    status = dixLookupDrawable(&drawable, event->drawable_id, serverClient,
467                               M_ANY, DixWriteAccess);
468    if (status == Success)
469        DRI2SwapComplete(event->client, drawable, frame, tv_sec, tv_usec,
470                         DRI2_FLIP_COMPLETE, event->event_complete,
471                         event->event_data);
472
473    ms->drmmode.dri2_flipping = FALSE;
474    free(event);
475}
476
477static Bool
478ms_dri2_schedule_flip(ms_dri2_frame_event_ptr info)
479{
480    DrawablePtr draw = info->drawable;
481    ScreenPtr screen = draw->pScreen;
482    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
483    modesettingPtr ms = modesettingPTR(scrn);
484    ms_dri2_buffer_private_ptr back_priv = info->back->driverPrivate;
485    struct ms_dri2_vblank_event *event;
486    drmmode_crtc_private_ptr drmmode_crtc = info->crtc->driver_private;
487
488    event = calloc(1, sizeof(struct ms_dri2_vblank_event));
489    if (!event)
490        return FALSE;
491
492    event->drawable_id = draw->id;
493    event->client = info->client;
494    event->event_complete = info->event_complete;
495    event->event_data = info->event_data;
496
497    if (ms_do_pageflip(screen, back_priv->pixmap, event,
498                       drmmode_crtc->vblank_pipe, FALSE,
499                       ms_dri2_flip_handler,
500                       ms_dri2_flip_abort,
501                       "DRI2-flip")) {
502        ms->drmmode.dri2_flipping = TRUE;
503        return TRUE;
504    }
505    return FALSE;
506}
507
508static Bool
509update_front(DrawablePtr draw, DRI2BufferPtr front)
510{
511    ScreenPtr screen = draw->pScreen;
512    PixmapPtr pixmap = get_drawable_pixmap(draw);
513    ms_dri2_buffer_private_ptr priv = front->driverPrivate;
514    modesettingPtr ms = modesettingPTR(xf86ScreenToScrn(screen));
515    CARD32 size;
516    CARD16 pitch;
517    int name;
518
519    name = ms->glamor.name_from_pixmap(pixmap, &pitch, &size);
520    if (name < 0)
521        return FALSE;
522
523    front->name = name;
524
525    (*screen->DestroyPixmap) (priv->pixmap);
526    front->pitch = pixmap->devKind;
527    front->cpp = pixmap->drawable.bitsPerPixel / 8;
528    priv->pixmap = pixmap;
529    pixmap->refcnt++;
530
531    return TRUE;
532}
533
534static Bool
535can_exchange(ScrnInfoPtr scrn, DrawablePtr draw,
536	     DRI2BufferPtr front, DRI2BufferPtr back)
537{
538    ms_dri2_buffer_private_ptr front_priv = front->driverPrivate;
539    ms_dri2_buffer_private_ptr back_priv = back->driverPrivate;
540    PixmapPtr front_pixmap;
541    PixmapPtr back_pixmap = back_priv->pixmap;
542    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
543    int num_crtcs_on = 0;
544    int i;
545
546    for (i = 0; i < config->num_crtc; i++) {
547        drmmode_crtc_private_ptr drmmode_crtc = config->crtc[i]->driver_private;
548
549        /* Don't do pageflipping if CRTCs are rotated. */
550#ifdef GLAMOR_HAS_GBM
551        if (drmmode_crtc->rotate_bo.gbm)
552            return FALSE;
553#endif
554
555        if (xf86_crtc_on(config->crtc[i]))
556            num_crtcs_on++;
557    }
558
559    /* We can't do pageflipping if all the CRTCs are off. */
560    if (num_crtcs_on == 0)
561        return FALSE;
562
563    if (!update_front(draw, front))
564        return FALSE;
565
566    front_pixmap = front_priv->pixmap;
567
568    if (front_pixmap->drawable.width != back_pixmap->drawable.width)
569        return FALSE;
570
571    if (front_pixmap->drawable.height != back_pixmap->drawable.height)
572        return FALSE;
573
574    if (front_pixmap->drawable.bitsPerPixel !=
575        back_pixmap->drawable.bitsPerPixel)
576        return FALSE;
577
578    if (front_pixmap->devKind != back_pixmap->devKind)
579        return FALSE;
580
581    return TRUE;
582}
583
584static Bool
585can_flip(ScrnInfoPtr scrn, DrawablePtr draw,
586	 DRI2BufferPtr front, DRI2BufferPtr back)
587{
588    modesettingPtr ms = modesettingPTR(scrn);
589
590    return draw->type == DRAWABLE_WINDOW &&
591        ms->drmmode.pageflip &&
592        !ms->drmmode.sprites_visible &&
593        !ms->drmmode.present_flipping &&
594        scrn->vtSema &&
595        DRI2CanFlip(draw) && can_exchange(scrn, draw, front, back);
596}
597
598static void
599ms_dri2_exchange_buffers(DrawablePtr draw, DRI2BufferPtr front,
600                         DRI2BufferPtr back)
601{
602    ms_dri2_buffer_private_ptr front_priv = front->driverPrivate;
603    ms_dri2_buffer_private_ptr back_priv = back->driverPrivate;
604    ScreenPtr screen = draw->pScreen;
605    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
606    modesettingPtr ms = modesettingPTR(scrn);
607    msPixmapPrivPtr front_pix = msGetPixmapPriv(&ms->drmmode, front_priv->pixmap);
608    msPixmapPrivPtr back_pix = msGetPixmapPriv(&ms->drmmode, back_priv->pixmap);
609    msPixmapPrivRec tmp_pix;
610    RegionRec region;
611    int tmp;
612
613    /* Swap BO names so DRI works */
614    tmp = front->name;
615    front->name = back->name;
616    back->name = tmp;
617
618    /* Swap pixmap privates */
619    tmp_pix = *front_pix;
620    *front_pix = *back_pix;
621    *back_pix = tmp_pix;
622
623    ms->glamor.egl_exchange_buffers(front_priv->pixmap, back_priv->pixmap);
624
625    /* Post damage on the front buffer so that listeners, such
626     * as DisplayLink know take a copy and shove it over the USB.
627     */
628    region.extents.x1 = region.extents.y1 = 0;
629    region.extents.x2 = front_priv->pixmap->drawable.width;
630    region.extents.y2 = front_priv->pixmap->drawable.height;
631    region.data = NULL;
632    DamageRegionAppend(&front_priv->pixmap->drawable, &region);
633    DamageRegionProcessPending(&front_priv->pixmap->drawable);
634}
635
636static void
637ms_dri2_frame_event_handler(uint64_t msc,
638                            uint64_t usec,
639                            void *data)
640{
641    ms_dri2_frame_event_ptr frame_info = data;
642    DrawablePtr drawable = frame_info->drawable;
643    ScreenPtr screen = frame_info->screen;
644    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
645    uint32_t tv_sec = usec / 1000000;
646    uint32_t tv_usec = usec % 1000000;
647
648    if (!drawable) {
649        ms_dri2_del_frame_event(frame_info);
650        return;
651    }
652
653    switch (frame_info->type) {
654    case MS_DRI2_QUEUE_FLIP:
655        if (can_flip(scrn, drawable, frame_info->front, frame_info->back) &&
656            ms_dri2_schedule_flip(frame_info)) {
657            ms_dri2_exchange_buffers(drawable, frame_info->front, frame_info->back);
658            break;
659        }
660        /* else fall through to blit */
661    case MS_DRI2_QUEUE_SWAP:
662        ms_dri2_blit_swap(drawable, frame_info->front, frame_info->back);
663        DRI2SwapComplete(frame_info->client, drawable, msc, tv_sec, tv_usec,
664                         DRI2_BLIT_COMPLETE,
665                         frame_info->client ? frame_info->event_complete : NULL,
666                         frame_info->event_data);
667        break;
668
669    case MS_DRI2_WAIT_MSC:
670        if (frame_info->client)
671            DRI2WaitMSCComplete(frame_info->client, drawable,
672                                msc, tv_sec, tv_usec);
673        break;
674
675    default:
676        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
677                   "%s: unknown vblank event (type %d) received\n", __func__,
678                   frame_info->type);
679        break;
680    }
681
682    ms_dri2_del_frame_event(frame_info);
683}
684
685static void
686ms_dri2_frame_event_abort(void *data)
687{
688    ms_dri2_frame_event_ptr frame_info = data;
689
690    ms_dri2_del_frame_event(frame_info);
691}
692
693/**
694 * Request a DRM event when the requested conditions will be satisfied.
695 *
696 * We need to handle the event and ask the server to wake up the client when
697 * we receive it.
698 */
699static int
700ms_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
701                          CARD64 divisor, CARD64 remainder)
702{
703    ScreenPtr screen = draw->pScreen;
704    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
705    ms_dri2_frame_event_ptr wait_info;
706    int ret;
707    xf86CrtcPtr crtc = ms_dri2_crtc_covering_drawable(draw);
708    CARD64 current_msc, current_ust, request_msc;
709    uint32_t seq;
710    uint64_t queued_msc;
711
712    /* Drawable not visible, return immediately */
713    if (!crtc)
714        goto out_complete;
715
716    wait_info = calloc(1, sizeof(*wait_info));
717    if (!wait_info)
718        goto out_complete;
719
720    wait_info->screen = screen;
721    wait_info->drawable = draw;
722    wait_info->client = client;
723    wait_info->type = MS_DRI2_WAIT_MSC;
724
725    if (!ms_dri2_add_frame_event(wait_info)) {
726        free(wait_info);
727        wait_info = NULL;
728        goto out_complete;
729    }
730
731    /* Get current count */
732    ret = ms_get_crtc_ust_msc(crtc, &current_ust, &current_msc);
733
734    /*
735     * If divisor is zero, or current_msc is smaller than target_msc,
736     * we just need to make sure target_msc passes  before waking up the
737     * client.
738     */
739    if (divisor == 0 || current_msc < target_msc) {
740        /* If target_msc already reached or passed, set it to
741         * current_msc to ensure we return a reasonable value back
742         * to the caller. This keeps the client from continually
743         * sending us MSC targets from the past by forcibly updating
744         * their count on this call.
745         */
746        seq = ms_drm_queue_alloc(crtc, wait_info,
747                                 ms_dri2_frame_event_handler,
748                                 ms_dri2_frame_event_abort);
749        if (!seq)
750            goto out_free;
751
752        if (current_msc >= target_msc)
753            target_msc = current_msc;
754
755        ret = ms_queue_vblank(crtc, MS_QUEUE_ABSOLUTE, target_msc, &queued_msc, seq);
756        if (!ret) {
757            static int limit = 5;
758            if (limit) {
759                xf86DrvMsg(scrn->scrnIndex, X_WARNING,
760                           "%s:%d get vblank counter failed: %s\n",
761                           __FUNCTION__, __LINE__,
762                           strerror(errno));
763                limit--;
764            }
765            goto out_free;
766        }
767
768        wait_info->frame = queued_msc;
769        DRI2BlockClient(client, draw);
770        return TRUE;
771    }
772
773    /*
774     * If we get here, target_msc has already passed or we don't have one,
775     * so we queue an event that will satisfy the divisor/remainder equation.
776     */
777    request_msc = current_msc - (current_msc % divisor) +
778        remainder;
779    /*
780     * If calculated remainder is larger than requested remainder,
781     * it means we've passed the last point where
782     * seq % divisor == remainder, so we need to wait for the next time
783     * that will happen.
784     */
785    if ((current_msc % divisor) >= remainder)
786        request_msc += divisor;
787
788    seq = ms_drm_queue_alloc(crtc, wait_info,
789                             ms_dri2_frame_event_handler,
790                             ms_dri2_frame_event_abort);
791    if (!seq)
792        goto out_free;
793
794    if (!ms_queue_vblank(crtc, MS_QUEUE_ABSOLUTE, request_msc, &queued_msc, seq)) {
795        static int limit = 5;
796        if (limit) {
797            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
798                       "%s:%d get vblank counter failed: %s\n",
799                       __FUNCTION__, __LINE__,
800                       strerror(errno));
801            limit--;
802        }
803        goto out_free;
804    }
805
806    wait_info->frame = queued_msc;
807
808    DRI2BlockClient(client, draw);
809
810    return TRUE;
811
812 out_free:
813    ms_dri2_del_frame_event(wait_info);
814 out_complete:
815    DRI2WaitMSCComplete(client, draw, target_msc, 0, 0);
816    return TRUE;
817}
818
819/**
820 * ScheduleSwap is responsible for requesting a DRM vblank event for
821 * the appropriate frame, or executing the swap immediately if it
822 * doesn't need to wait.
823 *
824 * When the swap is complete, the driver should call into the server so it
825 * can send any swap complete events that have been requested.
826 */
827static int
828ms_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
829                      DRI2BufferPtr front, DRI2BufferPtr back,
830                      CARD64 *target_msc, CARD64 divisor,
831                      CARD64 remainder, DRI2SwapEventPtr func, void *data)
832{
833    ScreenPtr screen = draw->pScreen;
834    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
835    int ret, flip = 0;
836    xf86CrtcPtr crtc = ms_dri2_crtc_covering_drawable(draw);
837    ms_dri2_frame_event_ptr frame_info = NULL;
838    uint64_t current_msc, current_ust;
839    uint64_t request_msc;
840    uint32_t seq;
841    ms_queue_flag ms_flag = MS_QUEUE_ABSOLUTE;
842    uint64_t queued_msc;
843
844    /* Drawable not displayed... just complete the swap */
845    if (!crtc)
846        goto blit_fallback;
847
848    frame_info = calloc(1, sizeof(*frame_info));
849    if (!frame_info)
850        goto blit_fallback;
851
852    frame_info->screen = screen;
853    frame_info->drawable = draw;
854    frame_info->client = client;
855    frame_info->event_complete = func;
856    frame_info->event_data = data;
857    frame_info->front = front;
858    frame_info->back = back;
859    frame_info->crtc = crtc;
860    frame_info->type = MS_DRI2_QUEUE_SWAP;
861
862    if (!ms_dri2_add_frame_event(frame_info)) {
863        free(frame_info);
864        frame_info = NULL;
865        goto blit_fallback;
866    }
867
868    ms_dri2_reference_buffer(front);
869    ms_dri2_reference_buffer(back);
870
871    ret = ms_get_crtc_ust_msc(crtc, &current_ust, &current_msc);
872    if (ret != Success)
873        goto blit_fallback;
874
875    /* Flips need to be submitted one frame before */
876    if (can_flip(scrn, draw, front, back)) {
877        frame_info->type = MS_DRI2_QUEUE_FLIP;
878        flip = 1;
879    }
880
881    /* Correct target_msc by 'flip' if frame_info->type == MS_DRI2_QUEUE_FLIP.
882     * Do it early, so handling of different timing constraints
883     * for divisor, remainder and msc vs. target_msc works.
884     */
885    if (*target_msc > 0)
886        *target_msc -= flip;
887
888    /* If non-pageflipping, but blitting/exchanging, we need to use
889     * DRM_VBLANK_NEXTONMISS to avoid unreliable timestamping later
890     * on.
891     */
892    if (flip == 0)
893        ms_flag |= MS_QUEUE_NEXT_ON_MISS;
894
895    /*
896     * If divisor is zero, or current_msc is smaller than target_msc
897     * we just need to make sure target_msc passes before initiating
898     * the swap.
899     */
900    if (divisor == 0 || current_msc < *target_msc) {
901
902        /* If target_msc already reached or passed, set it to
903         * current_msc to ensure we return a reasonable value back
904         * to the caller. This makes swap_interval logic more robust.
905         */
906        if (current_msc >= *target_msc)
907            *target_msc = current_msc;
908
909        seq = ms_drm_queue_alloc(crtc, frame_info,
910                                 ms_dri2_frame_event_handler,
911                                 ms_dri2_frame_event_abort);
912        if (!seq)
913            goto blit_fallback;
914
915        if (!ms_queue_vblank(crtc, ms_flag, *target_msc, &queued_msc, seq)) {
916            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
917                       "divisor 0 get vblank counter failed: %s\n",
918                       strerror(errno));
919            goto blit_fallback;
920        }
921
922        *target_msc = queued_msc + flip;
923        frame_info->frame = *target_msc;
924
925        return TRUE;
926    }
927
928    /*
929     * If we get here, target_msc has already passed or we don't have one,
930     * and we need to queue an event that will satisfy the divisor/remainder
931     * equation.
932     */
933
934    request_msc = current_msc - (current_msc % divisor) +
935        remainder;
936
937    /*
938     * If the calculated deadline vbl.request.sequence is smaller than
939     * or equal to current_msc, it means we've passed the last point
940     * when effective onset frame seq could satisfy
941     * seq % divisor == remainder, so we need to wait for the next time
942     * this will happen.
943
944     * This comparison takes the DRM_VBLANK_NEXTONMISS delay into account.
945     */
946    if (request_msc <= current_msc)
947        request_msc += divisor;
948
949    seq = ms_drm_queue_alloc(crtc, frame_info,
950                             ms_dri2_frame_event_handler,
951                             ms_dri2_frame_event_abort);
952    if (!seq)
953        goto blit_fallback;
954
955    /* Account for 1 frame extra pageflip delay if flip > 0 */
956    if (!ms_queue_vblank(crtc, ms_flag, request_msc - flip, &queued_msc, seq)) {
957        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
958                   "final get vblank counter failed: %s\n",
959                   strerror(errno));
960        goto blit_fallback;
961    }
962
963    /* Adjust returned value for 1 fame pageflip offset of flip > 0 */
964    *target_msc = queued_msc + flip;
965    frame_info->frame = *target_msc;
966
967    return TRUE;
968
969 blit_fallback:
970    ms_dri2_blit_swap(draw, front, back);
971    DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data);
972    if (frame_info)
973        ms_dri2_del_frame_event(frame_info);
974    *target_msc = 0; /* offscreen, so zero out target vblank count */
975    return TRUE;
976}
977
978static int
979ms_dri2_frame_event_client_gone(void *data, XID id)
980{
981    struct ms_dri2_resource *resource = data;
982
983    while (!xorg_list_is_empty(&resource->list)) {
984        ms_dri2_frame_event_ptr info =
985            xorg_list_first_entry(&resource->list,
986                                  ms_dri2_frame_event_rec,
987                                  client_resource);
988
989        xorg_list_del(&info->client_resource);
990        info->client = NULL;
991    }
992    free(resource);
993
994    return Success;
995}
996
997static int
998ms_dri2_frame_event_drawable_gone(void *data, XID id)
999{
1000    struct ms_dri2_resource *resource = data;
1001
1002    while (!xorg_list_is_empty(&resource->list)) {
1003        ms_dri2_frame_event_ptr info =
1004            xorg_list_first_entry(&resource->list,
1005                                  ms_dri2_frame_event_rec,
1006                                  drawable_resource);
1007
1008        xorg_list_del(&info->drawable_resource);
1009        info->drawable = NULL;
1010    }
1011    free(resource);
1012
1013    return Success;
1014}
1015
1016static Bool
1017ms_dri2_register_frame_event_resource_types(void)
1018{
1019    frame_event_client_type =
1020        CreateNewResourceType(ms_dri2_frame_event_client_gone,
1021                              "Frame Event Client");
1022    if (!frame_event_client_type)
1023        return FALSE;
1024
1025    frame_event_drawable_type =
1026        CreateNewResourceType(ms_dri2_frame_event_drawable_gone,
1027                              "Frame Event Drawable");
1028    if (!frame_event_drawable_type)
1029        return FALSE;
1030
1031    return TRUE;
1032}
1033
1034Bool
1035ms_dri2_screen_init(ScreenPtr screen)
1036{
1037    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
1038    modesettingPtr ms = modesettingPTR(scrn);
1039    DRI2InfoRec info;
1040    const char *driver_names[2] = { NULL, NULL };
1041
1042    if (!ms->glamor.supports_pixmap_import_export(screen)) {
1043        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
1044                   "DRI2: glamor lacks support for pixmap import/export\n");
1045    }
1046
1047    if (!xf86LoaderCheckSymbol("DRI2Version"))
1048        return FALSE;
1049
1050    if (!dixRegisterPrivateKey(&ms_dri2_client_key,
1051                               PRIVATE_CLIENT, sizeof(XID)))
1052        return FALSE;
1053
1054    if (serverGeneration != ms_dri2_server_generation) {
1055        ms_dri2_server_generation = serverGeneration;
1056        if (!ms_dri2_register_frame_event_resource_types()) {
1057            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
1058                       "Cannot register DRI2 frame event resources\n");
1059            return FALSE;
1060        }
1061    }
1062
1063    memset(&info, '\0', sizeof(info));
1064    info.fd = ms->fd;
1065    info.driverName = NULL; /* Compat field, unused. */
1066    info.deviceName = drmGetDeviceNameFromFd(ms->fd);
1067
1068    info.version = 9;
1069    info.CreateBuffer = ms_dri2_create_buffer;
1070    info.DestroyBuffer = ms_dri2_destroy_buffer;
1071    info.CopyRegion = ms_dri2_copy_region;
1072    info.ScheduleSwap = ms_dri2_schedule_swap;
1073    info.GetMSC = ms_dri2_get_msc;
1074    info.ScheduleWaitMSC = ms_dri2_schedule_wait_msc;
1075    info.CreateBuffer2 = ms_dri2_create_buffer2;
1076    info.DestroyBuffer2 = ms_dri2_destroy_buffer2;
1077    info.CopyRegion2 = ms_dri2_copy_region2;
1078
1079    /* Ask Glamor to obtain the DRI driver name via EGL_MESA_query_driver, */
1080    if (ms->glamor.egl_get_driver_name)
1081        driver_names[0] = ms->glamor.egl_get_driver_name(screen);
1082
1083    if (driver_names[0]) {
1084        /* There is no VDPAU driver for Intel, fallback to the generic
1085         * OpenGL/VAAPI va_gl backend to emulate VDPAU.  Otherwise,
1086         * guess that the DRI and VDPAU drivers have the same name.
1087         */
1088        if (strcmp(driver_names[0], "i965") == 0 ||
1089            strcmp(driver_names[0], "iris") == 0 ||
1090            strcmp(driver_names[0], "crocus") == 0) {
1091            driver_names[1] = "va_gl";
1092        } else {
1093            driver_names[1] = driver_names[0];
1094        }
1095
1096        info.numDrivers = 2;
1097        info.driverNames = driver_names;
1098    } else {
1099        /* EGL_MESA_query_driver was unavailable; let dri2.c select the
1100         * driver and fill in these fields for us.
1101         */
1102        info.numDrivers = 0;
1103        info.driverNames = NULL;
1104    }
1105
1106    return DRI2ScreenInit(screen, &info);
1107}
1108
1109void
1110ms_dri2_close_screen(ScreenPtr screen)
1111{
1112    DRI2CloseScreen(screen);
1113}
1114
1115#endif /* GLAMOR_HAS_GBM */
1116