vblank.c revision 4e185dc0
1/*
2 * Copyright © 2013 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  The copyright holders make no representations
11 * about the suitability of this software for any purpose.  It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23/** @file vblank.c
24 *
25 * Support for tracking the DRM's vblank events.
26 */
27
28#ifdef HAVE_DIX_CONFIG_H
29#include "dix-config.h"
30#endif
31
32#include <unistd.h>
33#include <xf86.h>
34#include <xf86Crtc.h>
35#include "driver.h"
36#include "drmmode_display.h"
37
38/**
39 * Tracking for outstanding events queued to the kernel.
40 *
41 * Each list entry is a struct ms_drm_queue, which has a uint32_t
42 * value generated from drm_seq that identifies the event and a
43 * reference back to the crtc/screen associated with the event.  It's
44 * done this way rather than in the screen because we want to be able
45 * to drain the list of event handlers that should be called at server
46 * regen time, even though we don't close the drm fd and have no way
47 * to actually drain the kernel events.
48 */
49static struct xorg_list ms_drm_queue;
50static uint32_t ms_drm_seq;
51
52static void ms_box_intersect(BoxPtr dest, BoxPtr a, BoxPtr b)
53{
54    dest->x1 = a->x1 > b->x1 ? a->x1 : b->x1;
55    dest->x2 = a->x2 < b->x2 ? a->x2 : b->x2;
56    if (dest->x1 >= dest->x2) {
57        dest->x1 = dest->x2 = dest->y1 = dest->y2 = 0;
58        return;
59    }
60
61    dest->y1 = a->y1 > b->y1 ? a->y1 : b->y1;
62    dest->y2 = a->y2 < b->y2 ? a->y2 : b->y2;
63    if (dest->y1 >= dest->y2)
64        dest->x1 = dest->x2 = dest->y1 = dest->y2 = 0;
65}
66
67static void ms_crtc_box(xf86CrtcPtr crtc, BoxPtr crtc_box)
68{
69    if (crtc->enabled) {
70        crtc_box->x1 = crtc->x;
71        crtc_box->x2 =
72            crtc->x + xf86ModeWidth(&crtc->mode, crtc->rotation);
73        crtc_box->y1 = crtc->y;
74        crtc_box->y2 =
75            crtc->y + xf86ModeHeight(&crtc->mode, crtc->rotation);
76    } else
77        crtc_box->x1 = crtc_box->x2 = crtc_box->y1 = crtc_box->y2 = 0;
78}
79
80static void ms_randr_crtc_box(RRCrtcPtr crtc, BoxPtr crtc_box)
81{
82    if (crtc->mode) {
83        crtc_box->x1 = crtc->x;
84        crtc_box->y1 = crtc->y;
85        switch (crtc->rotation) {
86            case RR_Rotate_0:
87            case RR_Rotate_180:
88            default:
89                crtc_box->x2 = crtc->x + crtc->mode->mode.width;
90                crtc_box->y2 = crtc->y + crtc->mode->mode.height;
91                break;
92            case RR_Rotate_90:
93            case RR_Rotate_270:
94                crtc_box->x2 = crtc->x + crtc->mode->mode.height;
95                crtc_box->y2 = crtc->y + crtc->mode->mode.width;
96                break;
97        }
98    } else
99        crtc_box->x1 = crtc_box->x2 = crtc_box->y1 = crtc_box->y2 = 0;
100}
101
102static int ms_box_area(BoxPtr box)
103{
104    return (int)(box->x2 - box->x1) * (int)(box->y2 - box->y1);
105}
106
107Bool
108ms_crtc_on(xf86CrtcPtr crtc)
109{
110    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
111
112    return crtc->enabled && drmmode_crtc->dpms_mode == DPMSModeOn;
113}
114
115/*
116 * Return the first output which is connected to an active CRTC on this screen.
117 *
118 * RRFirstOutput() will return an output from a slave screen if it is primary,
119 * which is not the behavior that ms_covering_crtc() wants.
120 */
121
122static RROutputPtr ms_first_output(ScreenPtr pScreen)
123{
124    rrScrPriv(pScreen);
125    RROutputPtr output;
126    int i, j;
127
128    if (!pScrPriv)
129        return NULL;
130
131    if (pScrPriv->primaryOutput && pScrPriv->primaryOutput->crtc &&
132        (pScrPriv->primaryOutput->pScreen == pScreen)) {
133        return pScrPriv->primaryOutput;
134    }
135
136    for (i = 0; i < pScrPriv->numCrtcs; i++) {
137        RRCrtcPtr crtc = pScrPriv->crtcs[i];
138
139        for (j = 0; j < pScrPriv->numOutputs; j++) {
140            output = pScrPriv->outputs[j];
141            if (output->crtc == crtc)
142                return output;
143        }
144    }
145    return NULL;
146}
147
148/*
149 * Return the crtc covering 'box'. If two crtcs cover a portion of
150 * 'box', then prefer the crtc with greater coverage.
151 */
152
153static xf86CrtcPtr
154ms_covering_xf86_crtc(ScreenPtr pScreen, BoxPtr box, Bool screen_is_ms)
155{
156    ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
157    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
158    xf86CrtcPtr crtc, best_crtc;
159    int coverage, best_coverage;
160    int c;
161    BoxRec crtc_box, cover_box;
162    Bool crtc_on;
163
164    best_crtc = NULL;
165    best_coverage = 0;
166
167    if (!xf86_config)
168        return NULL;
169
170    for (c = 0; c < xf86_config->num_crtc; c++) {
171        crtc = xf86_config->crtc[c];
172
173        if (screen_is_ms)
174            crtc_on = ms_crtc_on(crtc);
175        else
176            crtc_on = crtc->enabled;
177
178        /* If the CRTC is off, treat it as not covering */
179        if (!crtc_on)
180            continue;
181
182        ms_crtc_box(crtc, &crtc_box);
183        ms_box_intersect(&cover_box, &crtc_box, box);
184        coverage = ms_box_area(&cover_box);
185        if (coverage > best_coverage) {
186            best_crtc = crtc;
187            best_coverage = coverage;
188        }
189    }
190
191    /* Fallback to primary crtc for drawable's on slave outputs */
192    if (best_crtc == NULL && !pScreen->isGPU) {
193        RROutputPtr primary_output = NULL;
194        ScreenPtr slave;
195
196        if (dixPrivateKeyRegistered(rrPrivKey))
197            primary_output = ms_first_output(scrn->pScreen);
198        if (!primary_output || !primary_output->crtc)
199            return NULL;
200
201        crtc = primary_output->crtc->devPrivate;
202        if (!ms_crtc_on(crtc))
203            return NULL;
204
205        xorg_list_for_each_entry(slave, &pScreen->slave_list, slave_head) {
206            if (!slave->is_output_slave)
207                continue;
208
209            if (ms_covering_xf86_crtc(slave, box, FALSE)) {
210                /* The drawable is on a slave output, return primary crtc */
211                return crtc;
212            }
213        }
214    }
215
216    return best_crtc;
217}
218
219static RRCrtcPtr
220ms_covering_randr_crtc(ScreenPtr pScreen, BoxPtr box, Bool screen_is_ms)
221{
222    ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
223    rrScrPrivPtr pScrPriv = rrGetScrPriv(pScreen);
224    RRCrtcPtr crtc, best_crtc;
225    int coverage, best_coverage;
226    int c;
227    BoxRec crtc_box, cover_box;
228    Bool crtc_on;
229
230    best_crtc = NULL;
231    best_coverage = 0;
232
233    if (!pScrPriv)
234        return NULL;
235
236    for (c = 0; c < pScrPriv->numCrtcs; c++) {
237        crtc = pScrPriv->crtcs[c];
238
239        if (screen_is_ms) {
240            crtc_on = ms_crtc_on((xf86CrtcPtr) crtc->devPrivate);
241        } else {
242            crtc_on = !!crtc->mode;
243        }
244
245        /* If the CRTC is off, treat it as not covering */
246        if (!crtc_on)
247            continue;
248
249        ms_randr_crtc_box(crtc, &crtc_box);
250        ms_box_intersect(&cover_box, &crtc_box, box);
251        coverage = ms_box_area(&cover_box);
252        if (coverage > best_coverage) {
253            best_crtc = crtc;
254            best_coverage = coverage;
255        }
256    }
257
258    /* Fallback to primary crtc for drawable's on slave outputs */
259    if (best_crtc == NULL && !pScreen->isGPU) {
260        RROutputPtr primary_output = NULL;
261        ScreenPtr slave;
262
263        if (dixPrivateKeyRegistered(rrPrivKey))
264            primary_output = ms_first_output(scrn->pScreen);
265        if (!primary_output || !primary_output->crtc)
266            return NULL;
267
268        crtc = primary_output->crtc;
269        if (!ms_crtc_on((xf86CrtcPtr) crtc->devPrivate))
270            return NULL;
271
272        xorg_list_for_each_entry(slave, &pScreen->slave_list, slave_head) {
273            if (!slave->is_output_slave)
274                continue;
275
276            if (ms_covering_randr_crtc(slave, box, FALSE)) {
277                /* The drawable is on a slave output, return primary crtc */
278                return crtc;
279            }
280        }
281    }
282
283    return best_crtc;
284}
285
286xf86CrtcPtr
287ms_dri2_crtc_covering_drawable(DrawablePtr pDraw)
288{
289    ScreenPtr pScreen = pDraw->pScreen;
290    BoxRec box;
291
292    box.x1 = pDraw->x;
293    box.y1 = pDraw->y;
294    box.x2 = box.x1 + pDraw->width;
295    box.y2 = box.y1 + pDraw->height;
296
297    return ms_covering_xf86_crtc(pScreen, &box, TRUE);
298}
299
300RRCrtcPtr
301ms_randr_crtc_covering_drawable(DrawablePtr pDraw)
302{
303    ScreenPtr pScreen = pDraw->pScreen;
304    BoxRec box;
305
306    box.x1 = pDraw->x;
307    box.y1 = pDraw->y;
308    box.x2 = box.x1 + pDraw->width;
309    box.y2 = box.y1 + pDraw->height;
310
311    return ms_covering_randr_crtc(pScreen, &box, TRUE);
312}
313
314static Bool
315ms_get_kernel_ust_msc(xf86CrtcPtr crtc,
316                      uint64_t *msc, uint64_t *ust)
317{
318    ScreenPtr screen = crtc->randr_crtc->pScreen;
319    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
320    modesettingPtr ms = modesettingPTR(scrn);
321    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
322    drmVBlank vbl;
323    int ret;
324
325    if (ms->has_queue_sequence || !ms->tried_queue_sequence) {
326        uint64_t ns;
327        ms->tried_queue_sequence = TRUE;
328
329        ret = drmCrtcGetSequence(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
330                                 msc, &ns);
331        if (ret != -1 || (errno != ENOTTY && errno != EINVAL)) {
332            ms->has_queue_sequence = TRUE;
333            if (ret == 0)
334                *ust = ns / 1000;
335            return ret == 0;
336        }
337    }
338    /* Get current count */
339    vbl.request.type = DRM_VBLANK_RELATIVE | drmmode_crtc->vblank_pipe;
340    vbl.request.sequence = 0;
341    vbl.request.signal = 0;
342    ret = drmWaitVBlank(ms->fd, &vbl);
343    if (ret) {
344        *msc = 0;
345        *ust = 0;
346        return FALSE;
347    } else {
348        *msc = vbl.reply.sequence;
349        *ust = (CARD64) vbl.reply.tval_sec * 1000000 + vbl.reply.tval_usec;
350        return TRUE;
351    }
352}
353
354Bool
355ms_queue_vblank(xf86CrtcPtr crtc, ms_queue_flag flags,
356                uint64_t msc, uint64_t *msc_queued, uint32_t seq)
357{
358    ScreenPtr screen = crtc->randr_crtc->pScreen;
359    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
360    modesettingPtr ms = modesettingPTR(scrn);
361    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
362    drmVBlank vbl;
363    int ret;
364
365    for (;;) {
366        /* Queue an event at the specified sequence */
367        if (ms->has_queue_sequence || !ms->tried_queue_sequence) {
368            uint32_t drm_flags = 0;
369            uint64_t kernel_queued;
370
371            ms->tried_queue_sequence = TRUE;
372
373            if (flags & MS_QUEUE_RELATIVE)
374                drm_flags |= DRM_CRTC_SEQUENCE_RELATIVE;
375            if (flags & MS_QUEUE_NEXT_ON_MISS)
376                drm_flags |= DRM_CRTC_SEQUENCE_NEXT_ON_MISS;
377
378            ret = drmCrtcQueueSequence(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
379                                       drm_flags, msc, &kernel_queued, seq);
380            if (ret == 0) {
381                if (msc_queued)
382                    *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, kernel_queued, TRUE);
383                ms->has_queue_sequence = TRUE;
384                return TRUE;
385            }
386
387            if (ret != -1 || (errno != ENOTTY && errno != EINVAL)) {
388                ms->has_queue_sequence = TRUE;
389                goto check;
390            }
391        }
392        vbl.request.type = DRM_VBLANK_EVENT | drmmode_crtc->vblank_pipe;
393        if (flags & MS_QUEUE_RELATIVE)
394            vbl.request.type |= DRM_VBLANK_RELATIVE;
395        else
396            vbl.request.type |= DRM_VBLANK_ABSOLUTE;
397        if (flags & MS_QUEUE_NEXT_ON_MISS)
398            vbl.request.type |= DRM_VBLANK_NEXTONMISS;
399
400        vbl.request.sequence = msc;
401        vbl.request.signal = seq;
402        ret = drmWaitVBlank(ms->fd, &vbl);
403        if (ret == 0) {
404            if (msc_queued)
405                *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence, FALSE);
406            return TRUE;
407        }
408    check:
409        if (errno != EBUSY) {
410            ms_drm_abort_seq(scrn, seq);
411            return FALSE;
412        }
413        ms_flush_drm_events(screen);
414    }
415}
416
417/**
418 * Convert a 32-bit or 64-bit kernel MSC sequence number to a 64-bit local
419 * sequence number, adding in the high 32 bits, and dealing with 32-bit
420 * wrapping if needed.
421 */
422uint64_t
423ms_kernel_msc_to_crtc_msc(xf86CrtcPtr crtc, uint64_t sequence, Bool is64bit)
424{
425    drmmode_crtc_private_rec *drmmode_crtc = crtc->driver_private;
426
427    if (!is64bit) {
428        /* sequence is provided as a 32 bit value from one of the 32 bit apis,
429         * e.g., drmWaitVBlank(), classic vblank events, or pageflip events.
430         *
431         * Track and handle 32-Bit wrapping, somewhat robust against occasional
432         * out-of-order not always monotonically increasing sequence values.
433         */
434        if ((int64_t) sequence < ((int64_t) drmmode_crtc->msc_prev - 0x40000000))
435            drmmode_crtc->msc_high += 0x100000000L;
436
437        if ((int64_t) sequence > ((int64_t) drmmode_crtc->msc_prev + 0x40000000))
438            drmmode_crtc->msc_high -= 0x100000000L;
439
440        drmmode_crtc->msc_prev = sequence;
441
442        return drmmode_crtc->msc_high + sequence;
443    }
444
445    /* True 64-Bit sequence from Linux 4.15+ 64-Bit drmCrtcGetSequence /
446     * drmCrtcQueueSequence apis and events. Pass through sequence unmodified,
447     * but update the 32-bit tracking variables with reliable ground truth.
448     *
449     * With 64-Bit api in use, the only !is64bit input is from pageflip events,
450     * and any pageflip event is usually preceeded by some is64bit input from
451     * swap scheduling, so this should provide reliable mapping for pageflip
452     * events based on true 64-bit input as baseline as well.
453     */
454    drmmode_crtc->msc_prev = sequence;
455    drmmode_crtc->msc_high = sequence & 0xffffffff00000000;
456
457    return sequence;
458}
459
460int
461ms_get_crtc_ust_msc(xf86CrtcPtr crtc, uint64_t *ust, uint64_t *msc)
462{
463    ScreenPtr screen = crtc->randr_crtc->pScreen;
464    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
465    modesettingPtr ms = modesettingPTR(scrn);
466    uint64_t kernel_msc;
467
468    if (!ms_get_kernel_ust_msc(crtc, &kernel_msc, ust))
469        return BadMatch;
470    *msc = ms_kernel_msc_to_crtc_msc(crtc, kernel_msc, ms->has_queue_sequence);
471
472    return Success;
473}
474
475/**
476 * Check for pending DRM events and process them.
477 */
478static void
479ms_drm_socket_handler(int fd, int ready, void *data)
480{
481    ScreenPtr screen = data;
482    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
483    modesettingPtr ms = modesettingPTR(scrn);
484
485    if (data == NULL)
486        return;
487
488    drmHandleEvent(fd, &ms->event_context);
489}
490
491/*
492 * Enqueue a potential drm response; when the associated response
493 * appears, we've got data to pass to the handler from here
494 */
495uint32_t
496ms_drm_queue_alloc(xf86CrtcPtr crtc,
497                   void *data,
498                   ms_drm_handler_proc handler,
499                   ms_drm_abort_proc abort)
500{
501    ScreenPtr screen = crtc->randr_crtc->pScreen;
502    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
503    struct ms_drm_queue *q;
504
505    q = calloc(1, sizeof(struct ms_drm_queue));
506
507    if (!q)
508        return 0;
509    if (!ms_drm_seq)
510        ++ms_drm_seq;
511    q->seq = ms_drm_seq++;
512    q->scrn = scrn;
513    q->crtc = crtc;
514    q->data = data;
515    q->handler = handler;
516    q->abort = abort;
517
518    xorg_list_add(&q->list, &ms_drm_queue);
519
520    return q->seq;
521}
522
523/**
524 * Abort one queued DRM entry, removing it
525 * from the list, calling the abort function and
526 * freeing the memory
527 */
528static void
529ms_drm_abort_one(struct ms_drm_queue *q)
530{
531        xorg_list_del(&q->list);
532        q->abort(q->data);
533        free(q);
534}
535
536/**
537 * Abort all queued entries on a specific scrn, used
538 * when resetting the X server
539 */
540static void
541ms_drm_abort_scrn(ScrnInfoPtr scrn)
542{
543    struct ms_drm_queue *q, *tmp;
544
545    xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
546        if (q->scrn == scrn)
547            ms_drm_abort_one(q);
548    }
549}
550
551/**
552 * Abort by drm queue sequence number.
553 */
554void
555ms_drm_abort_seq(ScrnInfoPtr scrn, uint32_t seq)
556{
557    struct ms_drm_queue *q, *tmp;
558
559    xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
560        if (q->seq == seq) {
561            ms_drm_abort_one(q);
562            break;
563        }
564    }
565}
566
567/*
568 * Externally usable abort function that uses a callback to match a single
569 * queued entry to abort
570 */
571void
572ms_drm_abort(ScrnInfoPtr scrn, Bool (*match)(void *data, void *match_data),
573             void *match_data)
574{
575    struct ms_drm_queue *q;
576
577    xorg_list_for_each_entry(q, &ms_drm_queue, list) {
578        if (match(q->data, match_data)) {
579            ms_drm_abort_one(q);
580            break;
581        }
582    }
583}
584
585/*
586 * General DRM kernel handler. Looks for the matching sequence number in the
587 * drm event queue and calls the handler for it.
588 */
589static void
590ms_drm_sequence_handler(int fd, uint64_t frame, uint64_t ns, Bool is64bit, uint64_t user_data)
591{
592    struct ms_drm_queue *q, *tmp;
593    uint32_t seq = (uint32_t) user_data;
594
595    xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
596        if (q->seq == seq) {
597            uint64_t msc;
598
599            msc = ms_kernel_msc_to_crtc_msc(q->crtc, frame, is64bit);
600            xorg_list_del(&q->list);
601            q->handler(msc, ns / 1000, q->data);
602            free(q);
603            break;
604        }
605    }
606}
607
608static void
609ms_drm_sequence_handler_64bit(int fd, uint64_t frame, uint64_t ns, uint64_t user_data)
610{
611    /* frame is true 64 bit wrapped into 64 bit */
612    ms_drm_sequence_handler(fd, frame, ns, TRUE, user_data);
613}
614
615static void
616ms_drm_handler(int fd, uint32_t frame, uint32_t sec, uint32_t usec,
617               void *user_ptr)
618{
619    /* frame is 32 bit wrapped into 64 bit */
620    ms_drm_sequence_handler(fd, frame, ((uint64_t) sec * 1000000 + usec) * 1000,
621                            FALSE, (uint32_t) (uintptr_t) user_ptr);
622}
623
624Bool
625ms_vblank_screen_init(ScreenPtr screen)
626{
627    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
628    modesettingPtr ms = modesettingPTR(scrn);
629    modesettingEntPtr ms_ent = ms_ent_priv(scrn);
630    xorg_list_init(&ms_drm_queue);
631
632    ms->event_context.version = 4;
633    ms->event_context.vblank_handler = ms_drm_handler;
634    ms->event_context.page_flip_handler = ms_drm_handler;
635    ms->event_context.sequence_handler = ms_drm_sequence_handler_64bit;
636
637    /* We need to re-register the DRM fd for the synchronisation
638     * feedback on every server generation, so perform the
639     * registration within ScreenInit and not PreInit.
640     */
641    if (ms_ent->fd_wakeup_registered != serverGeneration) {
642        SetNotifyFd(ms->fd, ms_drm_socket_handler, X_NOTIFY_READ, screen);
643        ms_ent->fd_wakeup_registered = serverGeneration;
644        ms_ent->fd_wakeup_ref = 1;
645    } else
646        ms_ent->fd_wakeup_ref++;
647
648    return TRUE;
649}
650
651void
652ms_vblank_close_screen(ScreenPtr screen)
653{
654    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
655    modesettingPtr ms = modesettingPTR(scrn);
656    modesettingEntPtr ms_ent = ms_ent_priv(scrn);
657
658    ms_drm_abort_scrn(scrn);
659
660    if (ms_ent->fd_wakeup_registered == serverGeneration &&
661        !--ms_ent->fd_wakeup_ref) {
662        RemoveNotifyFd(ms->fd);
663    }
664}
665