vblank.c revision d44ca368
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 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 rr_crtc_box(RRCrtcPtr crtc, BoxPtr crtc_box)
68{
69    if (crtc->mode) {
70        crtc_box->x1 = crtc->x;
71        crtc_box->y1 = crtc->y;
72        switch (crtc->rotation) {
73            case RR_Rotate_0:
74            case RR_Rotate_180:
75            default:
76                crtc_box->x2 = crtc->x + crtc->mode->mode.width;
77                crtc_box->y2 = crtc->y + crtc->mode->mode.height;
78                break;
79            case RR_Rotate_90:
80            case RR_Rotate_270:
81                crtc_box->x2 = crtc->x + crtc->mode->mode.height;
82                crtc_box->y2 = crtc->y + crtc->mode->mode.width;
83                break;
84        }
85    } else
86        crtc_box->x1 = crtc_box->x2 = crtc_box->y1 = crtc_box->y2 = 0;
87}
88
89static int box_area(BoxPtr box)
90{
91    return (int)(box->x2 - box->x1) * (int)(box->y2 - box->y1);
92}
93
94static Bool rr_crtc_on(RRCrtcPtr crtc, Bool crtc_is_xf86_hint)
95{
96    if (!crtc) {
97        return FALSE;
98    }
99    if (crtc_is_xf86_hint && crtc->devPrivate) {
100         return xf86_crtc_on(crtc->devPrivate);
101    } else {
102        return !!crtc->mode;
103    }
104}
105
106Bool
107xf86_crtc_on(xf86CrtcPtr crtc)
108{
109    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
110
111    return crtc->enabled && drmmode_crtc->dpms_mode == DPMSModeOn;
112}
113
114
115/*
116 * Return the crtc covering 'box'. If two crtcs cover a portion of
117 * 'box', then prefer the crtc with greater coverage.
118 */
119static RRCrtcPtr
120rr_crtc_covering_box(ScreenPtr pScreen, BoxPtr box, Bool screen_is_xf86_hint)
121{
122    rrScrPrivPtr pScrPriv;
123    RRCrtcPtr crtc, best_crtc;
124    int coverage, best_coverage;
125    int c;
126    BoxRec crtc_box, cover_box;
127
128    best_crtc = NULL;
129    best_coverage = 0;
130
131    if (!dixPrivateKeyRegistered(rrPrivKey))
132        return NULL;
133
134    pScrPriv = rrGetScrPriv(pScreen);
135
136    if (!pScrPriv)
137        return NULL;
138
139    for (c = 0; c < pScrPriv->numCrtcs; c++) {
140        crtc = pScrPriv->crtcs[c];
141
142        /* If the CRTC is off, treat it as not covering */
143        if (!rr_crtc_on(crtc, screen_is_xf86_hint))
144            continue;
145
146        rr_crtc_box(crtc, &crtc_box);
147        box_intersect(&cover_box, &crtc_box, box);
148        coverage = box_area(&cover_box);
149        if (coverage > best_coverage) {
150            best_crtc = crtc;
151            best_coverage = coverage;
152        }
153    }
154
155    return best_crtc;
156}
157
158static RRCrtcPtr
159rr_crtc_covering_box_on_slave(ScreenPtr pScreen, BoxPtr box)
160{
161    if (!pScreen->isGPU) {
162        ScreenPtr slave;
163        RRCrtcPtr crtc = NULL;
164
165        xorg_list_for_each_entry(slave, &pScreen->slave_list, slave_head) {
166            if (!slave->is_output_slave)
167                continue;
168
169            crtc = rr_crtc_covering_box(slave, box, FALSE);
170            if (crtc)
171                return crtc;
172        }
173    }
174
175    return NULL;
176}
177
178xf86CrtcPtr
179ms_dri2_crtc_covering_drawable(DrawablePtr pDraw)
180{
181    ScreenPtr pScreen = pDraw->pScreen;
182    RRCrtcPtr crtc = NULL;
183    BoxRec box;
184
185    box.x1 = pDraw->x;
186    box.y1 = pDraw->y;
187    box.x2 = box.x1 + pDraw->width;
188    box.y2 = box.y1 + pDraw->height;
189
190    crtc = rr_crtc_covering_box(pScreen, &box, TRUE);
191    if (crtc) {
192        return crtc->devPrivate;
193    }
194    return NULL;
195}
196
197RRCrtcPtr
198ms_randr_crtc_covering_drawable(DrawablePtr pDraw)
199{
200    ScreenPtr pScreen = pDraw->pScreen;
201    RRCrtcPtr crtc = NULL;
202    BoxRec box;
203
204    box.x1 = pDraw->x;
205    box.y1 = pDraw->y;
206    box.x2 = box.x1 + pDraw->width;
207    box.y2 = box.y1 + pDraw->height;
208
209    crtc = rr_crtc_covering_box(pScreen, &box, TRUE);
210    if (!crtc) {
211        crtc = rr_crtc_covering_box_on_slave(pScreen, &box);
212    }
213    return crtc;
214}
215
216static Bool
217ms_get_kernel_ust_msc(xf86CrtcPtr crtc,
218                      uint64_t *msc, uint64_t *ust)
219{
220    ScreenPtr screen = crtc->randr_crtc->pScreen;
221    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
222    modesettingPtr ms = modesettingPTR(scrn);
223    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
224    drmVBlank vbl;
225    int ret;
226
227    if (ms->has_queue_sequence || !ms->tried_queue_sequence) {
228        uint64_t ns;
229        ms->tried_queue_sequence = TRUE;
230
231        ret = drmCrtcGetSequence(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
232                                 msc, &ns);
233        if (ret != -1 || (errno != ENOTTY && errno != EINVAL)) {
234            ms->has_queue_sequence = TRUE;
235            if (ret == 0)
236                *ust = ns / 1000;
237            return ret == 0;
238        }
239    }
240    /* Get current count */
241    vbl.request.type = DRM_VBLANK_RELATIVE | drmmode_crtc->vblank_pipe;
242    vbl.request.sequence = 0;
243    vbl.request.signal = 0;
244    ret = drmWaitVBlank(ms->fd, &vbl);
245    if (ret) {
246        *msc = 0;
247        *ust = 0;
248        return FALSE;
249    } else {
250        *msc = vbl.reply.sequence;
251        *ust = (CARD64) vbl.reply.tval_sec * 1000000 + vbl.reply.tval_usec;
252        return TRUE;
253    }
254}
255
256Bool
257ms_queue_vblank(xf86CrtcPtr crtc, ms_queue_flag flags,
258                uint64_t msc, uint64_t *msc_queued, uint32_t seq)
259{
260    ScreenPtr screen = crtc->randr_crtc->pScreen;
261    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
262    modesettingPtr ms = modesettingPTR(scrn);
263    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
264    drmVBlank vbl;
265    int ret;
266
267    for (;;) {
268        /* Queue an event at the specified sequence */
269        if (ms->has_queue_sequence || !ms->tried_queue_sequence) {
270            uint32_t drm_flags = 0;
271            uint64_t kernel_queued;
272
273            ms->tried_queue_sequence = TRUE;
274
275            if (flags & MS_QUEUE_RELATIVE)
276                drm_flags |= DRM_CRTC_SEQUENCE_RELATIVE;
277            if (flags & MS_QUEUE_NEXT_ON_MISS)
278                drm_flags |= DRM_CRTC_SEQUENCE_NEXT_ON_MISS;
279
280            ret = drmCrtcQueueSequence(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
281                                       drm_flags, msc, &kernel_queued, seq);
282            if (ret == 0) {
283                if (msc_queued)
284                    *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, kernel_queued, TRUE);
285                ms->has_queue_sequence = TRUE;
286                return TRUE;
287            }
288
289            if (ret != -1 || (errno != ENOTTY && errno != EINVAL)) {
290                ms->has_queue_sequence = TRUE;
291                goto check;
292            }
293        }
294        vbl.request.type = DRM_VBLANK_EVENT | drmmode_crtc->vblank_pipe;
295        if (flags & MS_QUEUE_RELATIVE)
296            vbl.request.type |= DRM_VBLANK_RELATIVE;
297        else
298            vbl.request.type |= DRM_VBLANK_ABSOLUTE;
299        if (flags & MS_QUEUE_NEXT_ON_MISS)
300            vbl.request.type |= DRM_VBLANK_NEXTONMISS;
301
302        vbl.request.sequence = msc;
303        vbl.request.signal = seq;
304        ret = drmWaitVBlank(ms->fd, &vbl);
305        if (ret == 0) {
306            if (msc_queued)
307                *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence, FALSE);
308            return TRUE;
309        }
310    check:
311        if (errno != EBUSY) {
312            ms_drm_abort_seq(scrn, seq);
313            return FALSE;
314        }
315        ms_flush_drm_events(screen);
316    }
317}
318
319/**
320 * Convert a 32-bit or 64-bit kernel MSC sequence number to a 64-bit local
321 * sequence number, adding in the high 32 bits, and dealing with 32-bit
322 * wrapping if needed.
323 */
324uint64_t
325ms_kernel_msc_to_crtc_msc(xf86CrtcPtr crtc, uint64_t sequence, Bool is64bit)
326{
327    drmmode_crtc_private_rec *drmmode_crtc = crtc->driver_private;
328
329    if (!is64bit) {
330        /* sequence is provided as a 32 bit value from one of the 32 bit apis,
331         * e.g., drmWaitVBlank(), classic vblank events, or pageflip events.
332         *
333         * Track and handle 32-Bit wrapping, somewhat robust against occasional
334         * out-of-order not always monotonically increasing sequence values.
335         */
336        if ((int64_t) sequence < ((int64_t) drmmode_crtc->msc_prev - 0x40000000))
337            drmmode_crtc->msc_high += 0x100000000L;
338
339        if ((int64_t) sequence > ((int64_t) drmmode_crtc->msc_prev + 0x40000000))
340            drmmode_crtc->msc_high -= 0x100000000L;
341
342        drmmode_crtc->msc_prev = sequence;
343
344        return drmmode_crtc->msc_high + sequence;
345    }
346
347    /* True 64-Bit sequence from Linux 4.15+ 64-Bit drmCrtcGetSequence /
348     * drmCrtcQueueSequence apis and events. Pass through sequence unmodified,
349     * but update the 32-bit tracking variables with reliable ground truth.
350     *
351     * With 64-Bit api in use, the only !is64bit input is from pageflip events,
352     * and any pageflip event is usually preceeded by some is64bit input from
353     * swap scheduling, so this should provide reliable mapping for pageflip
354     * events based on true 64-bit input as baseline as well.
355     */
356    drmmode_crtc->msc_prev = sequence;
357    drmmode_crtc->msc_high = sequence & 0xffffffff00000000;
358
359    return sequence;
360}
361
362int
363ms_get_crtc_ust_msc(xf86CrtcPtr crtc, uint64_t *ust, uint64_t *msc)
364{
365    ScreenPtr screen = crtc->randr_crtc->pScreen;
366    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
367    modesettingPtr ms = modesettingPTR(scrn);
368    uint64_t kernel_msc;
369
370    if (!ms_get_kernel_ust_msc(crtc, &kernel_msc, ust))
371        return BadMatch;
372    *msc = ms_kernel_msc_to_crtc_msc(crtc, kernel_msc, ms->has_queue_sequence);
373
374    return Success;
375}
376
377/**
378 * Check for pending DRM events and process them.
379 */
380static void
381ms_drm_socket_handler(int fd, int ready, void *data)
382{
383    ScreenPtr screen = data;
384    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
385    modesettingPtr ms = modesettingPTR(scrn);
386
387    if (data == NULL)
388        return;
389
390    drmHandleEvent(fd, &ms->event_context);
391}
392
393/*
394 * Enqueue a potential drm response; when the associated response
395 * appears, we've got data to pass to the handler from here
396 */
397uint32_t
398ms_drm_queue_alloc(xf86CrtcPtr crtc,
399                   void *data,
400                   ms_drm_handler_proc handler,
401                   ms_drm_abort_proc abort)
402{
403    ScreenPtr screen = crtc->randr_crtc->pScreen;
404    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
405    struct ms_drm_queue *q;
406
407    q = calloc(1, sizeof(struct ms_drm_queue));
408
409    if (!q)
410        return 0;
411    if (!ms_drm_seq)
412        ++ms_drm_seq;
413    q->seq = ms_drm_seq++;
414    q->scrn = scrn;
415    q->crtc = crtc;
416    q->data = data;
417    q->handler = handler;
418    q->abort = abort;
419
420    xorg_list_add(&q->list, &ms_drm_queue);
421
422    return q->seq;
423}
424
425/**
426 * Abort one queued DRM entry, removing it
427 * from the list, calling the abort function and
428 * freeing the memory
429 */
430static void
431ms_drm_abort_one(struct ms_drm_queue *q)
432{
433        xorg_list_del(&q->list);
434        q->abort(q->data);
435        free(q);
436}
437
438/**
439 * Abort all queued entries on a specific scrn, used
440 * when resetting the X server
441 */
442static void
443ms_drm_abort_scrn(ScrnInfoPtr scrn)
444{
445    struct ms_drm_queue *q, *tmp;
446
447    xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
448        if (q->scrn == scrn)
449            ms_drm_abort_one(q);
450    }
451}
452
453/**
454 * Abort by drm queue sequence number.
455 */
456void
457ms_drm_abort_seq(ScrnInfoPtr scrn, uint32_t seq)
458{
459    struct ms_drm_queue *q, *tmp;
460
461    xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
462        if (q->seq == seq) {
463            ms_drm_abort_one(q);
464            break;
465        }
466    }
467}
468
469/*
470 * Externally usable abort function that uses a callback to match a single
471 * queued entry to abort
472 */
473void
474ms_drm_abort(ScrnInfoPtr scrn, Bool (*match)(void *data, void *match_data),
475             void *match_data)
476{
477    struct ms_drm_queue *q;
478
479    xorg_list_for_each_entry(q, &ms_drm_queue, list) {
480        if (match(q->data, match_data)) {
481            ms_drm_abort_one(q);
482            break;
483        }
484    }
485}
486
487/*
488 * General DRM kernel handler. Looks for the matching sequence number in the
489 * drm event queue and calls the handler for it.
490 */
491static void
492ms_drm_sequence_handler(int fd, uint64_t frame, uint64_t ns, Bool is64bit, uint64_t user_data)
493{
494    struct ms_drm_queue *q, *tmp;
495    uint32_t seq = (uint32_t) user_data;
496
497    xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
498        if (q->seq == seq) {
499            uint64_t msc;
500
501            msc = ms_kernel_msc_to_crtc_msc(q->crtc, frame, is64bit);
502            xorg_list_del(&q->list);
503            q->handler(msc, ns / 1000, q->data);
504            free(q);
505            break;
506        }
507    }
508}
509
510static void
511ms_drm_sequence_handler_64bit(int fd, uint64_t frame, uint64_t ns, uint64_t user_data)
512{
513    /* frame is true 64 bit wrapped into 64 bit */
514    ms_drm_sequence_handler(fd, frame, ns, TRUE, user_data);
515}
516
517static void
518ms_drm_handler(int fd, uint32_t frame, uint32_t sec, uint32_t usec,
519               void *user_ptr)
520{
521    /* frame is 32 bit wrapped into 64 bit */
522    ms_drm_sequence_handler(fd, frame, ((uint64_t) sec * 1000000 + usec) * 1000,
523                            FALSE, (uint32_t) (uintptr_t) user_ptr);
524}
525
526Bool
527ms_vblank_screen_init(ScreenPtr screen)
528{
529    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
530    modesettingPtr ms = modesettingPTR(scrn);
531    modesettingEntPtr ms_ent = ms_ent_priv(scrn);
532    xorg_list_init(&ms_drm_queue);
533
534    ms->event_context.version = 4;
535    ms->event_context.vblank_handler = ms_drm_handler;
536    ms->event_context.page_flip_handler = ms_drm_handler;
537    ms->event_context.sequence_handler = ms_drm_sequence_handler_64bit;
538
539    /* We need to re-register the DRM fd for the synchronisation
540     * feedback on every server generation, so perform the
541     * registration within ScreenInit and not PreInit.
542     */
543    if (ms_ent->fd_wakeup_registered != serverGeneration) {
544        SetNotifyFd(ms->fd, ms_drm_socket_handler, X_NOTIFY_READ, screen);
545        ms_ent->fd_wakeup_registered = serverGeneration;
546        ms_ent->fd_wakeup_ref = 1;
547    } else
548        ms_ent->fd_wakeup_ref++;
549
550    return TRUE;
551}
552
553void
554ms_vblank_close_screen(ScreenPtr screen)
555{
556    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
557    modesettingPtr ms = modesettingPTR(scrn);
558    modesettingEntPtr ms_ent = ms_ent_priv(scrn);
559
560    ms_drm_abort_scrn(scrn);
561
562    if (ms_ent->fd_wakeup_registered == serverGeneration &&
563        !--ms_ent->fd_wakeup_ref) {
564        RemoveNotifyFd(ms->fd);
565    }
566}
567