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