135c4bbdfSmrg/*
235c4bbdfSmrg * Copyright © 2013 Keith Packard
335c4bbdfSmrg *
435c4bbdfSmrg * Permission to use, copy, modify, distribute, and sell this software and its
535c4bbdfSmrg * documentation for any purpose is hereby granted without fee, provided that
635c4bbdfSmrg * the above copyright notice appear in all copies and that both that copyright
735c4bbdfSmrg * notice and this permission notice appear in supporting documentation, and
835c4bbdfSmrg * that the name of the copyright holders not be used in advertising or
935c4bbdfSmrg * publicity pertaining to distribution of the software without specific,
1035c4bbdfSmrg * written prior permission.  The copyright holders make no representations
1135c4bbdfSmrg * about the suitability of this software for any purpose.  It is provided "as
1235c4bbdfSmrg * is" without express or implied warranty.
1335c4bbdfSmrg *
1435c4bbdfSmrg * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
1535c4bbdfSmrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
1635c4bbdfSmrg * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
1735c4bbdfSmrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
1835c4bbdfSmrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
1935c4bbdfSmrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
2035c4bbdfSmrg * OF THIS SOFTWARE.
2135c4bbdfSmrg */
2235c4bbdfSmrg
2335c4bbdfSmrg/** @file vblank.c
2435c4bbdfSmrg *
2535c4bbdfSmrg * Support for tracking the DRM's vblank events.
2635c4bbdfSmrg */
2735c4bbdfSmrg
2835c4bbdfSmrg#ifdef HAVE_DIX_CONFIG_H
2935c4bbdfSmrg#include "dix-config.h"
3035c4bbdfSmrg#endif
3135c4bbdfSmrg
3235c4bbdfSmrg#include <unistd.h>
3335c4bbdfSmrg#include <xf86.h>
3435c4bbdfSmrg#include <xf86Crtc.h>
3535c4bbdfSmrg#include "driver.h"
3635c4bbdfSmrg#include "drmmode_display.h"
3735c4bbdfSmrg
3835c4bbdfSmrg/**
3935c4bbdfSmrg * Tracking for outstanding events queued to the kernel.
4035c4bbdfSmrg *
4135c4bbdfSmrg * Each list entry is a struct ms_drm_queue, which has a uint32_t
4235c4bbdfSmrg * value generated from drm_seq that identifies the event and a
4335c4bbdfSmrg * reference back to the crtc/screen associated with the event.  It's
4435c4bbdfSmrg * done this way rather than in the screen because we want to be able
4535c4bbdfSmrg * to drain the list of event handlers that should be called at server
4635c4bbdfSmrg * regen time, even though we don't close the drm fd and have no way
4735c4bbdfSmrg * to actually drain the kernel events.
4835c4bbdfSmrg */
4935c4bbdfSmrgstatic struct xorg_list ms_drm_queue;
5035c4bbdfSmrgstatic uint32_t ms_drm_seq;
5135c4bbdfSmrg
52d44ca368Smrgstatic void box_intersect(BoxPtr dest, BoxPtr a, BoxPtr b)
5335c4bbdfSmrg{
5435c4bbdfSmrg    dest->x1 = a->x1 > b->x1 ? a->x1 : b->x1;
5535c4bbdfSmrg    dest->x2 = a->x2 < b->x2 ? a->x2 : b->x2;
5635c4bbdfSmrg    if (dest->x1 >= dest->x2) {
5735c4bbdfSmrg        dest->x1 = dest->x2 = dest->y1 = dest->y2 = 0;
5835c4bbdfSmrg        return;
5935c4bbdfSmrg    }
6035c4bbdfSmrg
6135c4bbdfSmrg    dest->y1 = a->y1 > b->y1 ? a->y1 : b->y1;
6235c4bbdfSmrg    dest->y2 = a->y2 < b->y2 ? a->y2 : b->y2;
6335c4bbdfSmrg    if (dest->y1 >= dest->y2)
6435c4bbdfSmrg        dest->x1 = dest->x2 = dest->y1 = dest->y2 = 0;
6535c4bbdfSmrg}
6635c4bbdfSmrg
67d44ca368Smrgstatic void rr_crtc_box(RRCrtcPtr crtc, BoxPtr crtc_box)
684e185dc0Smrg{
694e185dc0Smrg    if (crtc->mode) {
704e185dc0Smrg        crtc_box->x1 = crtc->x;
714e185dc0Smrg        crtc_box->y1 = crtc->y;
724e185dc0Smrg        switch (crtc->rotation) {
734e185dc0Smrg            case RR_Rotate_0:
744e185dc0Smrg            case RR_Rotate_180:
754e185dc0Smrg            default:
764e185dc0Smrg                crtc_box->x2 = crtc->x + crtc->mode->mode.width;
774e185dc0Smrg                crtc_box->y2 = crtc->y + crtc->mode->mode.height;
784e185dc0Smrg                break;
794e185dc0Smrg            case RR_Rotate_90:
804e185dc0Smrg            case RR_Rotate_270:
814e185dc0Smrg                crtc_box->x2 = crtc->x + crtc->mode->mode.height;
824e185dc0Smrg                crtc_box->y2 = crtc->y + crtc->mode->mode.width;
834e185dc0Smrg                break;
844e185dc0Smrg        }
854e185dc0Smrg    } else
864e185dc0Smrg        crtc_box->x1 = crtc_box->x2 = crtc_box->y1 = crtc_box->y2 = 0;
874e185dc0Smrg}
884e185dc0Smrg
89d44ca368Smrgstatic int box_area(BoxPtr box)
9035c4bbdfSmrg{
9135c4bbdfSmrg    return (int)(box->x2 - box->x1) * (int)(box->y2 - box->y1);
9235c4bbdfSmrg}
9335c4bbdfSmrg
94d44ca368Smrgstatic Bool rr_crtc_on(RRCrtcPtr crtc, Bool crtc_is_xf86_hint)
95d44ca368Smrg{
96d44ca368Smrg    if (!crtc) {
97d44ca368Smrg        return FALSE;
98d44ca368Smrg    }
99d44ca368Smrg    if (crtc_is_xf86_hint && crtc->devPrivate) {
100d44ca368Smrg         return xf86_crtc_on(crtc->devPrivate);
101d44ca368Smrg    } else {
102d44ca368Smrg        return !!crtc->mode;
103d44ca368Smrg    }
104d44ca368Smrg}
105d44ca368Smrg
10635c4bbdfSmrgBool
107d44ca368Smrgxf86_crtc_on(xf86CrtcPtr crtc)
10835c4bbdfSmrg{
10935c4bbdfSmrg    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
11035c4bbdfSmrg
11135c4bbdfSmrg    return crtc->enabled && drmmode_crtc->dpms_mode == DPMSModeOn;
11235c4bbdfSmrg}
11335c4bbdfSmrg
1144e185dc0Smrg
11535c4bbdfSmrg/*
11635c4bbdfSmrg * Return the crtc covering 'box'. If two crtcs cover a portion of
1177e31ba66Smrg * 'box', then prefer the crtc with greater coverage.
11835c4bbdfSmrg */
1194e185dc0Smrgstatic RRCrtcPtr
120d44ca368Smrgrr_crtc_covering_box(ScreenPtr pScreen, BoxPtr box, Bool screen_is_xf86_hint)
1214e185dc0Smrg{
122806e81e9Smrg    rrScrPrivPtr pScrPriv;
1235a112b11Smrg    RROutputPtr primary_output;
1245a112b11Smrg    RRCrtcPtr crtc, best_crtc, primary_crtc;
1254e185dc0Smrg    int coverage, best_coverage;
1264e185dc0Smrg    int c;
1274e185dc0Smrg    BoxRec crtc_box, cover_box;
1284e185dc0Smrg
1294e185dc0Smrg    best_crtc = NULL;
1304e185dc0Smrg    best_coverage = 0;
1314e185dc0Smrg
132806e81e9Smrg    if (!dixPrivateKeyRegistered(rrPrivKey))
133806e81e9Smrg        return NULL;
134806e81e9Smrg
135806e81e9Smrg    pScrPriv = rrGetScrPriv(pScreen);
136806e81e9Smrg
1374e185dc0Smrg    if (!pScrPriv)
1384e185dc0Smrg        return NULL;
1394e185dc0Smrg
1405a112b11Smrg    primary_crtc = NULL;
1415a112b11Smrg    primary_output = RRFirstOutput(pScreen);
1425a112b11Smrg    if (primary_output)
1435a112b11Smrg        primary_crtc = primary_output->crtc;
1445a112b11Smrg
1454e185dc0Smrg    for (c = 0; c < pScrPriv->numCrtcs; c++) {
1464e185dc0Smrg        crtc = pScrPriv->crtcs[c];
1474e185dc0Smrg
1484e185dc0Smrg        /* If the CRTC is off, treat it as not covering */
149d44ca368Smrg        if (!rr_crtc_on(crtc, screen_is_xf86_hint))
1504e185dc0Smrg            continue;
1514e185dc0Smrg
152d44ca368Smrg        rr_crtc_box(crtc, &crtc_box);
153d44ca368Smrg        box_intersect(&cover_box, &crtc_box, box);
154d44ca368Smrg        coverage = box_area(&cover_box);
1555a112b11Smrg        if ((coverage > best_coverage) ||
1565a112b11Smrg            (coverage == best_coverage && crtc == primary_crtc)) {
1574e185dc0Smrg            best_crtc = crtc;
1584e185dc0Smrg            best_coverage = coverage;
1594e185dc0Smrg        }
1604e185dc0Smrg    }
1614e185dc0Smrg
162d44ca368Smrg    return best_crtc;
163d44ca368Smrg}
1644e185dc0Smrg
165d44ca368Smrgstatic RRCrtcPtr
1665a112b11Smrgrr_crtc_covering_box_on_secondary(ScreenPtr pScreen, BoxPtr box)
167d44ca368Smrg{
168d44ca368Smrg    if (!pScreen->isGPU) {
1695a112b11Smrg        ScreenPtr secondary;
170d44ca368Smrg        RRCrtcPtr crtc = NULL;
1714e185dc0Smrg
1725a112b11Smrg        xorg_list_for_each_entry(secondary, &pScreen->secondary_list, secondary_head) {
1735a112b11Smrg            if (!secondary->is_output_secondary)
1744e185dc0Smrg                continue;
1754e185dc0Smrg
1765a112b11Smrg            crtc = rr_crtc_covering_box(secondary, box, FALSE);
177d44ca368Smrg            if (crtc)
1787e31ba66Smrg                return crtc;
1797e31ba66Smrg        }
1807e31ba66Smrg    }
1817e31ba66Smrg
182d44ca368Smrg    return NULL;
18335c4bbdfSmrg}
18435c4bbdfSmrg
18535c4bbdfSmrgxf86CrtcPtr
18635c4bbdfSmrgms_dri2_crtc_covering_drawable(DrawablePtr pDraw)
18735c4bbdfSmrg{
18835c4bbdfSmrg    ScreenPtr pScreen = pDraw->pScreen;
189d44ca368Smrg    RRCrtcPtr crtc = NULL;
1907e31ba66Smrg    BoxRec box;
19135c4bbdfSmrg
19235c4bbdfSmrg    box.x1 = pDraw->x;
19335c4bbdfSmrg    box.y1 = pDraw->y;
19435c4bbdfSmrg    box.x2 = box.x1 + pDraw->width;
19535c4bbdfSmrg    box.y2 = box.y1 + pDraw->height;
19635c4bbdfSmrg
197d44ca368Smrg    crtc = rr_crtc_covering_box(pScreen, &box, TRUE);
198d44ca368Smrg    if (crtc) {
199d44ca368Smrg        return crtc->devPrivate;
200d44ca368Smrg    }
201d44ca368Smrg    return NULL;
2024e185dc0Smrg}
2034e185dc0Smrg
2044e185dc0SmrgRRCrtcPtr
2054e185dc0Smrgms_randr_crtc_covering_drawable(DrawablePtr pDraw)
2064e185dc0Smrg{
2074e185dc0Smrg    ScreenPtr pScreen = pDraw->pScreen;
208d44ca368Smrg    RRCrtcPtr crtc = NULL;
2094e185dc0Smrg    BoxRec box;
2104e185dc0Smrg
2114e185dc0Smrg    box.x1 = pDraw->x;
2124e185dc0Smrg    box.y1 = pDraw->y;
2134e185dc0Smrg    box.x2 = box.x1 + pDraw->width;
2144e185dc0Smrg    box.y2 = box.y1 + pDraw->height;
2154e185dc0Smrg
216d44ca368Smrg    crtc = rr_crtc_covering_box(pScreen, &box, TRUE);
217d44ca368Smrg    if (!crtc) {
2185a112b11Smrg        crtc = rr_crtc_covering_box_on_secondary(pScreen, &box);
219d44ca368Smrg    }
220d44ca368Smrg    return crtc;
22135c4bbdfSmrg}
22235c4bbdfSmrg
22335c4bbdfSmrgstatic Bool
22435c4bbdfSmrgms_get_kernel_ust_msc(xf86CrtcPtr crtc,
2257e31ba66Smrg                      uint64_t *msc, uint64_t *ust)
22635c4bbdfSmrg{
22735c4bbdfSmrg    ScreenPtr screen = crtc->randr_crtc->pScreen;
22835c4bbdfSmrg    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
22935c4bbdfSmrg    modesettingPtr ms = modesettingPTR(scrn);
23035c4bbdfSmrg    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
23135c4bbdfSmrg    drmVBlank vbl;
23235c4bbdfSmrg    int ret;
23335c4bbdfSmrg
2347e31ba66Smrg    if (ms->has_queue_sequence || !ms->tried_queue_sequence) {
2357e31ba66Smrg        uint64_t ns;
2367e31ba66Smrg        ms->tried_queue_sequence = TRUE;
2377e31ba66Smrg
2387e31ba66Smrg        ret = drmCrtcGetSequence(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
2397e31ba66Smrg                                 msc, &ns);
2407e31ba66Smrg        if (ret != -1 || (errno != ENOTTY && errno != EINVAL)) {
2417e31ba66Smrg            ms->has_queue_sequence = TRUE;
2427e31ba66Smrg            if (ret == 0)
2437e31ba66Smrg                *ust = ns / 1000;
2447e31ba66Smrg            return ret == 0;
2457e31ba66Smrg        }
2467e31ba66Smrg    }
24735c4bbdfSmrg    /* Get current count */
24835c4bbdfSmrg    vbl.request.type = DRM_VBLANK_RELATIVE | drmmode_crtc->vblank_pipe;
24935c4bbdfSmrg    vbl.request.sequence = 0;
25035c4bbdfSmrg    vbl.request.signal = 0;
25135c4bbdfSmrg    ret = drmWaitVBlank(ms->fd, &vbl);
25235c4bbdfSmrg    if (ret) {
25335c4bbdfSmrg        *msc = 0;
25435c4bbdfSmrg        *ust = 0;
25535c4bbdfSmrg        return FALSE;
25635c4bbdfSmrg    } else {
25735c4bbdfSmrg        *msc = vbl.reply.sequence;
25835c4bbdfSmrg        *ust = (CARD64) vbl.reply.tval_sec * 1000000 + vbl.reply.tval_usec;
25935c4bbdfSmrg        return TRUE;
26035c4bbdfSmrg    }
26135c4bbdfSmrg}
26235c4bbdfSmrg
2637e31ba66SmrgBool
2647e31ba66Smrgms_queue_vblank(xf86CrtcPtr crtc, ms_queue_flag flags,
2657e31ba66Smrg                uint64_t msc, uint64_t *msc_queued, uint32_t seq)
2667e31ba66Smrg{
2677e31ba66Smrg    ScreenPtr screen = crtc->randr_crtc->pScreen;
2687e31ba66Smrg    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
2697e31ba66Smrg    modesettingPtr ms = modesettingPTR(scrn);
2707e31ba66Smrg    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
2717e31ba66Smrg    drmVBlank vbl;
2727e31ba66Smrg    int ret;
2737e31ba66Smrg
2747e31ba66Smrg    for (;;) {
2757e31ba66Smrg        /* Queue an event at the specified sequence */
2767e31ba66Smrg        if (ms->has_queue_sequence || !ms->tried_queue_sequence) {
2777e31ba66Smrg            uint32_t drm_flags = 0;
2787e31ba66Smrg            uint64_t kernel_queued;
2797e31ba66Smrg
2807e31ba66Smrg            ms->tried_queue_sequence = TRUE;
2817e31ba66Smrg
2827e31ba66Smrg            if (flags & MS_QUEUE_RELATIVE)
2837e31ba66Smrg                drm_flags |= DRM_CRTC_SEQUENCE_RELATIVE;
2847e31ba66Smrg            if (flags & MS_QUEUE_NEXT_ON_MISS)
2857e31ba66Smrg                drm_flags |= DRM_CRTC_SEQUENCE_NEXT_ON_MISS;
2867e31ba66Smrg
2877e31ba66Smrg            ret = drmCrtcQueueSequence(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
2887e31ba66Smrg                                       drm_flags, msc, &kernel_queued, seq);
2897e31ba66Smrg            if (ret == 0) {
2907e31ba66Smrg                if (msc_queued)
2917e31ba66Smrg                    *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, kernel_queued, TRUE);
2927e31ba66Smrg                ms->has_queue_sequence = TRUE;
2937e31ba66Smrg                return TRUE;
2947e31ba66Smrg            }
2957e31ba66Smrg
2967e31ba66Smrg            if (ret != -1 || (errno != ENOTTY && errno != EINVAL)) {
2977e31ba66Smrg                ms->has_queue_sequence = TRUE;
2987e31ba66Smrg                goto check;
2997e31ba66Smrg            }
3007e31ba66Smrg        }
3017e31ba66Smrg        vbl.request.type = DRM_VBLANK_EVENT | drmmode_crtc->vblank_pipe;
3027e31ba66Smrg        if (flags & MS_QUEUE_RELATIVE)
3037e31ba66Smrg            vbl.request.type |= DRM_VBLANK_RELATIVE;
3047e31ba66Smrg        else
3057e31ba66Smrg            vbl.request.type |= DRM_VBLANK_ABSOLUTE;
3067e31ba66Smrg        if (flags & MS_QUEUE_NEXT_ON_MISS)
3077e31ba66Smrg            vbl.request.type |= DRM_VBLANK_NEXTONMISS;
3087e31ba66Smrg
3097e31ba66Smrg        vbl.request.sequence = msc;
3107e31ba66Smrg        vbl.request.signal = seq;
3117e31ba66Smrg        ret = drmWaitVBlank(ms->fd, &vbl);
3127e31ba66Smrg        if (ret == 0) {
3137e31ba66Smrg            if (msc_queued)
3147e31ba66Smrg                *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence, FALSE);
3157e31ba66Smrg            return TRUE;
3167e31ba66Smrg        }
3177e31ba66Smrg    check:
3187e31ba66Smrg        if (errno != EBUSY) {
3197e31ba66Smrg            ms_drm_abort_seq(scrn, seq);
3207e31ba66Smrg            return FALSE;
3217e31ba66Smrg        }
3227e31ba66Smrg        ms_flush_drm_events(screen);
3237e31ba66Smrg    }
3247e31ba66Smrg}
3257e31ba66Smrg
32635c4bbdfSmrg/**
3277e31ba66Smrg * Convert a 32-bit or 64-bit kernel MSC sequence number to a 64-bit local
3287e31ba66Smrg * sequence number, adding in the high 32 bits, and dealing with 32-bit
3297e31ba66Smrg * wrapping if needed.
33035c4bbdfSmrg */
33135c4bbdfSmrguint64_t
3327e31ba66Smrgms_kernel_msc_to_crtc_msc(xf86CrtcPtr crtc, uint64_t sequence, Bool is64bit)
33335c4bbdfSmrg{
33435c4bbdfSmrg    drmmode_crtc_private_rec *drmmode_crtc = crtc->driver_private;
33535c4bbdfSmrg
3367e31ba66Smrg    if (!is64bit) {
3377e31ba66Smrg        /* sequence is provided as a 32 bit value from one of the 32 bit apis,
3387e31ba66Smrg         * e.g., drmWaitVBlank(), classic vblank events, or pageflip events.
3397e31ba66Smrg         *
3407e31ba66Smrg         * Track and handle 32-Bit wrapping, somewhat robust against occasional
3417e31ba66Smrg         * out-of-order not always monotonically increasing sequence values.
3427e31ba66Smrg         */
3437e31ba66Smrg        if ((int64_t) sequence < ((int64_t) drmmode_crtc->msc_prev - 0x40000000))
3447e31ba66Smrg            drmmode_crtc->msc_high += 0x100000000L;
3457e31ba66Smrg
3467e31ba66Smrg        if ((int64_t) sequence > ((int64_t) drmmode_crtc->msc_prev + 0x40000000))
3477e31ba66Smrg            drmmode_crtc->msc_high -= 0x100000000L;
3487e31ba66Smrg
3497e31ba66Smrg        drmmode_crtc->msc_prev = sequence;
3507e31ba66Smrg
3517e31ba66Smrg        return drmmode_crtc->msc_high + sequence;
3527e31ba66Smrg    }
3537e31ba66Smrg
3547e31ba66Smrg    /* True 64-Bit sequence from Linux 4.15+ 64-Bit drmCrtcGetSequence /
3557e31ba66Smrg     * drmCrtcQueueSequence apis and events. Pass through sequence unmodified,
3567e31ba66Smrg     * but update the 32-bit tracking variables with reliable ground truth.
3577e31ba66Smrg     *
3587e31ba66Smrg     * With 64-Bit api in use, the only !is64bit input is from pageflip events,
3595a112b11Smrg     * and any pageflip event is usually preceded by some is64bit input from
3607e31ba66Smrg     * swap scheduling, so this should provide reliable mapping for pageflip
3617e31ba66Smrg     * events based on true 64-bit input as baseline as well.
3627e31ba66Smrg     */
36335c4bbdfSmrg    drmmode_crtc->msc_prev = sequence;
3647e31ba66Smrg    drmmode_crtc->msc_high = sequence & 0xffffffff00000000;
3657e31ba66Smrg
3667e31ba66Smrg    return sequence;
36735c4bbdfSmrg}
36835c4bbdfSmrg
36935c4bbdfSmrgint
3702c83f951Sryoms_get_crtc_ust_msc(xf86CrtcPtr crtc, uint64_t *ust, uint64_t *msc)
37135c4bbdfSmrg{
3727e31ba66Smrg    ScreenPtr screen = crtc->randr_crtc->pScreen;
3737e31ba66Smrg    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
3747e31ba66Smrg    modesettingPtr ms = modesettingPTR(scrn);
3757e31ba66Smrg    uint64_t kernel_msc;
37635c4bbdfSmrg
37735c4bbdfSmrg    if (!ms_get_kernel_ust_msc(crtc, &kernel_msc, ust))
37835c4bbdfSmrg        return BadMatch;
3797e31ba66Smrg    *msc = ms_kernel_msc_to_crtc_msc(crtc, kernel_msc, ms->has_queue_sequence);
38035c4bbdfSmrg
38135c4bbdfSmrg    return Success;
38235c4bbdfSmrg}
38335c4bbdfSmrg
38435c4bbdfSmrg/**
38535c4bbdfSmrg * Check for pending DRM events and process them.
38635c4bbdfSmrg */
38735c4bbdfSmrgstatic void
3887e31ba66Smrgms_drm_socket_handler(int fd, int ready, void *data)
38935c4bbdfSmrg{
3907e31ba66Smrg    ScreenPtr screen = data;
3917e31ba66Smrg    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
3927e31ba66Smrg    modesettingPtr ms = modesettingPTR(scrn);
39335c4bbdfSmrg
3947e31ba66Smrg    if (data == NULL)
39535c4bbdfSmrg        return;
39635c4bbdfSmrg
3977e31ba66Smrg    drmHandleEvent(fd, &ms->event_context);
39835c4bbdfSmrg}
39935c4bbdfSmrg
40035c4bbdfSmrg/*
40135c4bbdfSmrg * Enqueue a potential drm response; when the associated response
40235c4bbdfSmrg * appears, we've got data to pass to the handler from here
40335c4bbdfSmrg */
40435c4bbdfSmrguint32_t
40535c4bbdfSmrgms_drm_queue_alloc(xf86CrtcPtr crtc,
40635c4bbdfSmrg                   void *data,
40735c4bbdfSmrg                   ms_drm_handler_proc handler,
40835c4bbdfSmrg                   ms_drm_abort_proc abort)
40935c4bbdfSmrg{
41035c4bbdfSmrg    ScreenPtr screen = crtc->randr_crtc->pScreen;
41135c4bbdfSmrg    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
41235c4bbdfSmrg    struct ms_drm_queue *q;
41335c4bbdfSmrg
41435c4bbdfSmrg    q = calloc(1, sizeof(struct ms_drm_queue));
41535c4bbdfSmrg
41635c4bbdfSmrg    if (!q)
41735c4bbdfSmrg        return 0;
41835c4bbdfSmrg    if (!ms_drm_seq)
41935c4bbdfSmrg        ++ms_drm_seq;
42035c4bbdfSmrg    q->seq = ms_drm_seq++;
42135c4bbdfSmrg    q->scrn = scrn;
42235c4bbdfSmrg    q->crtc = crtc;
42335c4bbdfSmrg    q->data = data;
42435c4bbdfSmrg    q->handler = handler;
42535c4bbdfSmrg    q->abort = abort;
42635c4bbdfSmrg
42735c4bbdfSmrg    xorg_list_add(&q->list, &ms_drm_queue);
42835c4bbdfSmrg
42935c4bbdfSmrg    return q->seq;
43035c4bbdfSmrg}
43135c4bbdfSmrg
43235c4bbdfSmrg/**
43335c4bbdfSmrg * Abort one queued DRM entry, removing it
43435c4bbdfSmrg * from the list, calling the abort function and
43535c4bbdfSmrg * freeing the memory
43635c4bbdfSmrg */
43735c4bbdfSmrgstatic void
43835c4bbdfSmrgms_drm_abort_one(struct ms_drm_queue *q)
43935c4bbdfSmrg{
44035c4bbdfSmrg        xorg_list_del(&q->list);
44135c4bbdfSmrg        q->abort(q->data);
44235c4bbdfSmrg        free(q);
44335c4bbdfSmrg}
44435c4bbdfSmrg
44535c4bbdfSmrg/**
44635c4bbdfSmrg * Abort all queued entries on a specific scrn, used
44735c4bbdfSmrg * when resetting the X server
44835c4bbdfSmrg */
44935c4bbdfSmrgstatic void
45035c4bbdfSmrgms_drm_abort_scrn(ScrnInfoPtr scrn)
45135c4bbdfSmrg{
45235c4bbdfSmrg    struct ms_drm_queue *q, *tmp;
45335c4bbdfSmrg
45435c4bbdfSmrg    xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
45535c4bbdfSmrg        if (q->scrn == scrn)
45635c4bbdfSmrg            ms_drm_abort_one(q);
45735c4bbdfSmrg    }
45835c4bbdfSmrg}
45935c4bbdfSmrg
46035c4bbdfSmrg/**
46135c4bbdfSmrg * Abort by drm queue sequence number.
46235c4bbdfSmrg */
46335c4bbdfSmrgvoid
46435c4bbdfSmrgms_drm_abort_seq(ScrnInfoPtr scrn, uint32_t seq)
46535c4bbdfSmrg{
46635c4bbdfSmrg    struct ms_drm_queue *q, *tmp;
46735c4bbdfSmrg
46835c4bbdfSmrg    xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
46935c4bbdfSmrg        if (q->seq == seq) {
47035c4bbdfSmrg            ms_drm_abort_one(q);
47135c4bbdfSmrg            break;
47235c4bbdfSmrg        }
47335c4bbdfSmrg    }
47435c4bbdfSmrg}
47535c4bbdfSmrg
47635c4bbdfSmrg/*
47735c4bbdfSmrg * Externally usable abort function that uses a callback to match a single
47835c4bbdfSmrg * queued entry to abort
47935c4bbdfSmrg */
48035c4bbdfSmrgvoid
48135c4bbdfSmrgms_drm_abort(ScrnInfoPtr scrn, Bool (*match)(void *data, void *match_data),
48235c4bbdfSmrg             void *match_data)
48335c4bbdfSmrg{
48435c4bbdfSmrg    struct ms_drm_queue *q;
48535c4bbdfSmrg
48635c4bbdfSmrg    xorg_list_for_each_entry(q, &ms_drm_queue, list) {
48735c4bbdfSmrg        if (match(q->data, match_data)) {
48835c4bbdfSmrg            ms_drm_abort_one(q);
48935c4bbdfSmrg            break;
49035c4bbdfSmrg        }
49135c4bbdfSmrg    }
49235c4bbdfSmrg}
49335c4bbdfSmrg
49435c4bbdfSmrg/*
49535c4bbdfSmrg * General DRM kernel handler. Looks for the matching sequence number in the
49635c4bbdfSmrg * drm event queue and calls the handler for it.
49735c4bbdfSmrg */
49835c4bbdfSmrgstatic void
4997e31ba66Smrgms_drm_sequence_handler(int fd, uint64_t frame, uint64_t ns, Bool is64bit, uint64_t user_data)
50035c4bbdfSmrg{
50135c4bbdfSmrg    struct ms_drm_queue *q, *tmp;
5027e31ba66Smrg    uint32_t seq = (uint32_t) user_data;
50335c4bbdfSmrg
50435c4bbdfSmrg    xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
5057e31ba66Smrg        if (q->seq == seq) {
50635c4bbdfSmrg            uint64_t msc;
50735c4bbdfSmrg
5087e31ba66Smrg            msc = ms_kernel_msc_to_crtc_msc(q->crtc, frame, is64bit);
50935c4bbdfSmrg            xorg_list_del(&q->list);
5107e31ba66Smrg            q->handler(msc, ns / 1000, q->data);
51135c4bbdfSmrg            free(q);
51235c4bbdfSmrg            break;
51335c4bbdfSmrg        }
51435c4bbdfSmrg    }
51535c4bbdfSmrg}
51635c4bbdfSmrg
5177e31ba66Smrgstatic void
5187e31ba66Smrgms_drm_sequence_handler_64bit(int fd, uint64_t frame, uint64_t ns, uint64_t user_data)
5197e31ba66Smrg{
5207e31ba66Smrg    /* frame is true 64 bit wrapped into 64 bit */
5217e31ba66Smrg    ms_drm_sequence_handler(fd, frame, ns, TRUE, user_data);
5227e31ba66Smrg}
5237e31ba66Smrg
5247e31ba66Smrgstatic void
5257e31ba66Smrgms_drm_handler(int fd, uint32_t frame, uint32_t sec, uint32_t usec,
5267e31ba66Smrg               void *user_ptr)
5277e31ba66Smrg{
5287e31ba66Smrg    /* frame is 32 bit wrapped into 64 bit */
5297e31ba66Smrg    ms_drm_sequence_handler(fd, frame, ((uint64_t) sec * 1000000 + usec) * 1000,
5307e31ba66Smrg                            FALSE, (uint32_t) (uintptr_t) user_ptr);
5317e31ba66Smrg}
5327e31ba66Smrg
53335c4bbdfSmrgBool
53435c4bbdfSmrgms_vblank_screen_init(ScreenPtr screen)
53535c4bbdfSmrg{
53635c4bbdfSmrg    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
53735c4bbdfSmrg    modesettingPtr ms = modesettingPTR(scrn);
53835c4bbdfSmrg    modesettingEntPtr ms_ent = ms_ent_priv(scrn);
53935c4bbdfSmrg    xorg_list_init(&ms_drm_queue);
54035c4bbdfSmrg
5417e31ba66Smrg    ms->event_context.version = 4;
54235c4bbdfSmrg    ms->event_context.vblank_handler = ms_drm_handler;
54335c4bbdfSmrg    ms->event_context.page_flip_handler = ms_drm_handler;
5447e31ba66Smrg    ms->event_context.sequence_handler = ms_drm_sequence_handler_64bit;
54535c4bbdfSmrg
54635c4bbdfSmrg    /* We need to re-register the DRM fd for the synchronisation
54735c4bbdfSmrg     * feedback on every server generation, so perform the
54835c4bbdfSmrg     * registration within ScreenInit and not PreInit.
54935c4bbdfSmrg     */
55035c4bbdfSmrg    if (ms_ent->fd_wakeup_registered != serverGeneration) {
5517e31ba66Smrg        SetNotifyFd(ms->fd, ms_drm_socket_handler, X_NOTIFY_READ, screen);
55235c4bbdfSmrg        ms_ent->fd_wakeup_registered = serverGeneration;
55335c4bbdfSmrg        ms_ent->fd_wakeup_ref = 1;
55435c4bbdfSmrg    } else
55535c4bbdfSmrg        ms_ent->fd_wakeup_ref++;
55635c4bbdfSmrg
55735c4bbdfSmrg    return TRUE;
55835c4bbdfSmrg}
55935c4bbdfSmrg
56035c4bbdfSmrgvoid
56135c4bbdfSmrgms_vblank_close_screen(ScreenPtr screen)
56235c4bbdfSmrg{
56335c4bbdfSmrg    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
56435c4bbdfSmrg    modesettingPtr ms = modesettingPTR(scrn);
56535c4bbdfSmrg    modesettingEntPtr ms_ent = ms_ent_priv(scrn);
56635c4bbdfSmrg
56735c4bbdfSmrg    ms_drm_abort_scrn(scrn);
56835c4bbdfSmrg
56935c4bbdfSmrg    if (ms_ent->fd_wakeup_registered == serverGeneration &&
57035c4bbdfSmrg        !--ms_ent->fd_wakeup_ref) {
5717e31ba66Smrg        RemoveNotifyFd(ms->fd);
57235c4bbdfSmrg    }
57335c4bbdfSmrg}
574