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 */
49 static struct xorg_list ms_drm_queue;
50 static uint32_t ms_drm_seq;
51
ms_box_intersect(BoxPtr dest,BoxPtr a,BoxPtr b)52 static 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
ms_crtc_box(xf86CrtcPtr crtc,BoxPtr crtc_box)67 static 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
ms_randr_crtc_box(RRCrtcPtr crtc,BoxPtr crtc_box)80 static 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
ms_box_area(BoxPtr box)102 static int ms_box_area(BoxPtr box)
103 {
104 return (int)(box->x2 - box->x1) * (int)(box->y2 - box->y1);
105 }
106
107 Bool
ms_crtc_on(xf86CrtcPtr crtc)108 ms_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
ms_first_output(ScreenPtr pScreen)122 static 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
153 static xf86CrtcPtr
ms_covering_xf86_crtc(ScreenPtr pScreen,BoxPtr box,Bool screen_is_ms)154 ms_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
219 static RRCrtcPtr
ms_covering_randr_crtc(ScreenPtr pScreen,BoxPtr box,Bool screen_is_ms)220 ms_covering_randr_crtc(ScreenPtr pScreen, BoxPtr box, Bool screen_is_ms)
221 {
222 ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
223 rrScrPrivPtr pScrPriv;
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 (!dixPrivateKeyRegistered(rrPrivKey))
234 return NULL;
235
236 pScrPriv = rrGetScrPriv(pScreen);
237
238 if (!pScrPriv)
239 return NULL;
240
241 for (c = 0; c < pScrPriv->numCrtcs; c++) {
242 crtc = pScrPriv->crtcs[c];
243
244 if (screen_is_ms) {
245 crtc_on = ms_crtc_on((xf86CrtcPtr) crtc->devPrivate);
246 } else {
247 crtc_on = !!crtc->mode;
248 }
249
250 /* If the CRTC is off, treat it as not covering */
251 if (!crtc_on)
252 continue;
253
254 ms_randr_crtc_box(crtc, &crtc_box);
255 ms_box_intersect(&cover_box, &crtc_box, box);
256 coverage = ms_box_area(&cover_box);
257 if (coverage > best_coverage) {
258 best_crtc = crtc;
259 best_coverage = coverage;
260 }
261 }
262
263 /* Fallback to primary crtc for drawable's on slave outputs */
264 if (best_crtc == NULL && !pScreen->isGPU) {
265 RROutputPtr primary_output = NULL;
266 ScreenPtr slave;
267
268 if (dixPrivateKeyRegistered(rrPrivKey))
269 primary_output = ms_first_output(scrn->pScreen);
270 if (!primary_output || !primary_output->crtc)
271 return NULL;
272
273 crtc = primary_output->crtc;
274 if (!ms_crtc_on((xf86CrtcPtr) crtc->devPrivate))
275 return NULL;
276
277 xorg_list_for_each_entry(slave, &pScreen->slave_list, slave_head) {
278 if (!slave->is_output_slave)
279 continue;
280
281 if (ms_covering_randr_crtc(slave, box, FALSE)) {
282 /* The drawable is on a slave output, return primary crtc */
283 return crtc;
284 }
285 }
286 }
287
288 return best_crtc;
289 }
290
291 xf86CrtcPtr
ms_dri2_crtc_covering_drawable(DrawablePtr pDraw)292 ms_dri2_crtc_covering_drawable(DrawablePtr pDraw)
293 {
294 ScreenPtr pScreen = pDraw->pScreen;
295 BoxRec box;
296
297 box.x1 = pDraw->x;
298 box.y1 = pDraw->y;
299 box.x2 = box.x1 + pDraw->width;
300 box.y2 = box.y1 + pDraw->height;
301
302 return ms_covering_xf86_crtc(pScreen, &box, TRUE);
303 }
304
305 RRCrtcPtr
ms_randr_crtc_covering_drawable(DrawablePtr pDraw)306 ms_randr_crtc_covering_drawable(DrawablePtr pDraw)
307 {
308 ScreenPtr pScreen = pDraw->pScreen;
309 BoxRec box;
310
311 box.x1 = pDraw->x;
312 box.y1 = pDraw->y;
313 box.x2 = box.x1 + pDraw->width;
314 box.y2 = box.y1 + pDraw->height;
315
316 return ms_covering_randr_crtc(pScreen, &box, TRUE);
317 }
318
319 static Bool
ms_get_kernel_ust_msc(xf86CrtcPtr crtc,uint64_t * msc,uint64_t * ust)320 ms_get_kernel_ust_msc(xf86CrtcPtr crtc,
321 uint64_t *msc, uint64_t *ust)
322 {
323 ScreenPtr screen = crtc->randr_crtc->pScreen;
324 ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
325 modesettingPtr ms = modesettingPTR(scrn);
326 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
327 drmVBlank vbl;
328 int ret;
329
330 if (ms->has_queue_sequence || !ms->tried_queue_sequence) {
331 uint64_t ns;
332 ms->tried_queue_sequence = TRUE;
333
334 ret = drmCrtcGetSequence(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
335 msc, &ns);
336 if (ret != -1 || (errno != ENOTTY && errno != EINVAL)) {
337 ms->has_queue_sequence = TRUE;
338 if (ret == 0)
339 *ust = ns / 1000;
340 return ret == 0;
341 }
342 }
343 /* Get current count */
344 vbl.request.type = DRM_VBLANK_RELATIVE | drmmode_crtc->vblank_pipe;
345 vbl.request.sequence = 0;
346 vbl.request.signal = 0;
347 ret = drmWaitVBlank(ms->fd, &vbl);
348 if (ret) {
349 *msc = 0;
350 *ust = 0;
351 return FALSE;
352 } else {
353 *msc = vbl.reply.sequence;
354 *ust = (CARD64) vbl.reply.tval_sec * 1000000 + vbl.reply.tval_usec;
355 return TRUE;
356 }
357 }
358
359 Bool
ms_queue_vblank(xf86CrtcPtr crtc,ms_queue_flag flags,uint64_t msc,uint64_t * msc_queued,uint32_t seq)360 ms_queue_vblank(xf86CrtcPtr crtc, ms_queue_flag flags,
361 uint64_t msc, uint64_t *msc_queued, uint32_t seq)
362 {
363 ScreenPtr screen = crtc->randr_crtc->pScreen;
364 ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
365 modesettingPtr ms = modesettingPTR(scrn);
366 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
367 drmVBlank vbl;
368 int ret;
369
370 for (;;) {
371 /* Queue an event at the specified sequence */
372 if (ms->has_queue_sequence || !ms->tried_queue_sequence) {
373 uint32_t drm_flags = 0;
374 uint64_t kernel_queued;
375
376 ms->tried_queue_sequence = TRUE;
377
378 if (flags & MS_QUEUE_RELATIVE)
379 drm_flags |= DRM_CRTC_SEQUENCE_RELATIVE;
380 if (flags & MS_QUEUE_NEXT_ON_MISS)
381 drm_flags |= DRM_CRTC_SEQUENCE_NEXT_ON_MISS;
382
383 ret = drmCrtcQueueSequence(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
384 drm_flags, msc, &kernel_queued, seq);
385 if (ret == 0) {
386 if (msc_queued)
387 *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, kernel_queued, TRUE);
388 ms->has_queue_sequence = TRUE;
389 return TRUE;
390 }
391
392 if (ret != -1 || (errno != ENOTTY && errno != EINVAL)) {
393 ms->has_queue_sequence = TRUE;
394 goto check;
395 }
396 }
397 vbl.request.type = DRM_VBLANK_EVENT | drmmode_crtc->vblank_pipe;
398 if (flags & MS_QUEUE_RELATIVE)
399 vbl.request.type |= DRM_VBLANK_RELATIVE;
400 else
401 vbl.request.type |= DRM_VBLANK_ABSOLUTE;
402 if (flags & MS_QUEUE_NEXT_ON_MISS)
403 vbl.request.type |= DRM_VBLANK_NEXTONMISS;
404
405 vbl.request.sequence = msc;
406 vbl.request.signal = seq;
407 ret = drmWaitVBlank(ms->fd, &vbl);
408 if (ret == 0) {
409 if (msc_queued)
410 *msc_queued = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence, FALSE);
411 return TRUE;
412 }
413 check:
414 if (errno != EBUSY) {
415 ms_drm_abort_seq(scrn, seq);
416 return FALSE;
417 }
418 ms_flush_drm_events(screen);
419 }
420 }
421
422 /**
423 * Convert a 32-bit or 64-bit kernel MSC sequence number to a 64-bit local
424 * sequence number, adding in the high 32 bits, and dealing with 32-bit
425 * wrapping if needed.
426 */
427 uint64_t
ms_kernel_msc_to_crtc_msc(xf86CrtcPtr crtc,uint64_t sequence,Bool is64bit)428 ms_kernel_msc_to_crtc_msc(xf86CrtcPtr crtc, uint64_t sequence, Bool is64bit)
429 {
430 drmmode_crtc_private_rec *drmmode_crtc = crtc->driver_private;
431
432 if (!is64bit) {
433 /* sequence is provided as a 32 bit value from one of the 32 bit apis,
434 * e.g., drmWaitVBlank(), classic vblank events, or pageflip events.
435 *
436 * Track and handle 32-Bit wrapping, somewhat robust against occasional
437 * out-of-order not always monotonically increasing sequence values.
438 */
439 if ((int64_t) sequence < ((int64_t) drmmode_crtc->msc_prev - 0x40000000))
440 drmmode_crtc->msc_high += 0x100000000L;
441
442 if ((int64_t) sequence > ((int64_t) drmmode_crtc->msc_prev + 0x40000000))
443 drmmode_crtc->msc_high -= 0x100000000L;
444
445 drmmode_crtc->msc_prev = sequence;
446
447 return drmmode_crtc->msc_high + sequence;
448 }
449
450 /* True 64-Bit sequence from Linux 4.15+ 64-Bit drmCrtcGetSequence /
451 * drmCrtcQueueSequence apis and events. Pass through sequence unmodified,
452 * but update the 32-bit tracking variables with reliable ground truth.
453 *
454 * With 64-Bit api in use, the only !is64bit input is from pageflip events,
455 * and any pageflip event is usually preceeded by some is64bit input from
456 * swap scheduling, so this should provide reliable mapping for pageflip
457 * events based on true 64-bit input as baseline as well.
458 */
459 drmmode_crtc->msc_prev = sequence;
460 drmmode_crtc->msc_high = sequence & 0xffffffff00000000;
461
462 return sequence;
463 }
464
465 int
ms_get_crtc_ust_msc(xf86CrtcPtr crtc,CARD64 * ust,CARD64 * msc)466 ms_get_crtc_ust_msc(xf86CrtcPtr crtc, CARD64 *ust, CARD64 *msc)
467 {
468 ScreenPtr screen = crtc->randr_crtc->pScreen;
469 ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
470 modesettingPtr ms = modesettingPTR(scrn);
471 uint64_t kernel_msc;
472
473 if (!ms_get_kernel_ust_msc(crtc, &kernel_msc, ust))
474 return BadMatch;
475 *msc = ms_kernel_msc_to_crtc_msc(crtc, kernel_msc, ms->has_queue_sequence);
476
477 return Success;
478 }
479
480 /**
481 * Check for pending DRM events and process them.
482 */
483 static void
ms_drm_socket_handler(int fd,int ready,void * data)484 ms_drm_socket_handler(int fd, int ready, void *data)
485 {
486 ScreenPtr screen = data;
487 ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
488 modesettingPtr ms = modesettingPTR(scrn);
489
490 if (data == NULL)
491 return;
492
493 drmHandleEvent(fd, &ms->event_context);
494 }
495
496 /*
497 * Enqueue a potential drm response; when the associated response
498 * appears, we've got data to pass to the handler from here
499 */
500 uint32_t
ms_drm_queue_alloc(xf86CrtcPtr crtc,void * data,ms_drm_handler_proc handler,ms_drm_abort_proc abort)501 ms_drm_queue_alloc(xf86CrtcPtr crtc,
502 void *data,
503 ms_drm_handler_proc handler,
504 ms_drm_abort_proc abort)
505 {
506 ScreenPtr screen = crtc->randr_crtc->pScreen;
507 ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
508 struct ms_drm_queue *q;
509
510 q = calloc(1, sizeof(struct ms_drm_queue));
511
512 if (!q)
513 return 0;
514 if (!ms_drm_seq)
515 ++ms_drm_seq;
516 q->seq = ms_drm_seq++;
517 q->scrn = scrn;
518 q->crtc = crtc;
519 q->data = data;
520 q->handler = handler;
521 q->abort = abort;
522
523 xorg_list_add(&q->list, &ms_drm_queue);
524
525 return q->seq;
526 }
527
528 /**
529 * Abort one queued DRM entry, removing it
530 * from the list, calling the abort function and
531 * freeing the memory
532 */
533 static void
ms_drm_abort_one(struct ms_drm_queue * q)534 ms_drm_abort_one(struct ms_drm_queue *q)
535 {
536 xorg_list_del(&q->list);
537 q->abort(q->data);
538 free(q);
539 }
540
541 /**
542 * Abort all queued entries on a specific scrn, used
543 * when resetting the X server
544 */
545 static void
ms_drm_abort_scrn(ScrnInfoPtr scrn)546 ms_drm_abort_scrn(ScrnInfoPtr scrn)
547 {
548 struct ms_drm_queue *q, *tmp;
549
550 xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
551 if (q->scrn == scrn)
552 ms_drm_abort_one(q);
553 }
554 }
555
556 /**
557 * Abort by drm queue sequence number.
558 */
559 void
ms_drm_abort_seq(ScrnInfoPtr scrn,uint32_t seq)560 ms_drm_abort_seq(ScrnInfoPtr scrn, uint32_t seq)
561 {
562 struct ms_drm_queue *q, *tmp;
563
564 xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
565 if (q->seq == seq) {
566 ms_drm_abort_one(q);
567 break;
568 }
569 }
570 }
571
572 /*
573 * Externally usable abort function that uses a callback to match a single
574 * queued entry to abort
575 */
576 void
ms_drm_abort(ScrnInfoPtr scrn,Bool (* match)(void * data,void * match_data),void * match_data)577 ms_drm_abort(ScrnInfoPtr scrn, Bool (*match)(void *data, void *match_data),
578 void *match_data)
579 {
580 struct ms_drm_queue *q;
581
582 xorg_list_for_each_entry(q, &ms_drm_queue, list) {
583 if (match(q->data, match_data)) {
584 ms_drm_abort_one(q);
585 break;
586 }
587 }
588 }
589
590 /*
591 * General DRM kernel handler. Looks for the matching sequence number in the
592 * drm event queue and calls the handler for it.
593 */
594 static void
ms_drm_sequence_handler(int fd,uint64_t frame,uint64_t ns,Bool is64bit,uint64_t user_data)595 ms_drm_sequence_handler(int fd, uint64_t frame, uint64_t ns, Bool is64bit, uint64_t user_data)
596 {
597 struct ms_drm_queue *q, *tmp;
598 uint32_t seq = (uint32_t) user_data;
599
600 xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) {
601 if (q->seq == seq) {
602 uint64_t msc;
603
604 msc = ms_kernel_msc_to_crtc_msc(q->crtc, frame, is64bit);
605 xorg_list_del(&q->list);
606 q->handler(msc, ns / 1000, q->data);
607 free(q);
608 break;
609 }
610 }
611 }
612
613 static void
ms_drm_sequence_handler_64bit(int fd,uint64_t frame,uint64_t ns,uint64_t user_data)614 ms_drm_sequence_handler_64bit(int fd, uint64_t frame, uint64_t ns, uint64_t user_data)
615 {
616 /* frame is true 64 bit wrapped into 64 bit */
617 ms_drm_sequence_handler(fd, frame, ns, TRUE, user_data);
618 }
619
620 static void
ms_drm_handler(int fd,uint32_t frame,uint32_t sec,uint32_t usec,void * user_ptr)621 ms_drm_handler(int fd, uint32_t frame, uint32_t sec, uint32_t usec,
622 void *user_ptr)
623 {
624 /* frame is 32 bit wrapped into 64 bit */
625 ms_drm_sequence_handler(fd, frame, ((uint64_t) sec * 1000000 + usec) * 1000,
626 FALSE, (uint32_t) (uintptr_t) user_ptr);
627 }
628
629 Bool
ms_vblank_screen_init(ScreenPtr screen)630 ms_vblank_screen_init(ScreenPtr screen)
631 {
632 ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
633 modesettingPtr ms = modesettingPTR(scrn);
634 modesettingEntPtr ms_ent = ms_ent_priv(scrn);
635 xorg_list_init(&ms_drm_queue);
636
637 ms->event_context.version = 4;
638 ms->event_context.vblank_handler = ms_drm_handler;
639 ms->event_context.page_flip_handler = ms_drm_handler;
640 ms->event_context.sequence_handler = ms_drm_sequence_handler_64bit;
641
642 /* We need to re-register the DRM fd for the synchronisation
643 * feedback on every server generation, so perform the
644 * registration within ScreenInit and not PreInit.
645 */
646 if (ms_ent->fd_wakeup_registered != serverGeneration) {
647 SetNotifyFd(ms->fd, ms_drm_socket_handler, X_NOTIFY_READ, screen);
648 ms_ent->fd_wakeup_registered = serverGeneration;
649 ms_ent->fd_wakeup_ref = 1;
650 } else
651 ms_ent->fd_wakeup_ref++;
652
653 return TRUE;
654 }
655
656 void
ms_vblank_close_screen(ScreenPtr screen)657 ms_vblank_close_screen(ScreenPtr screen)
658 {
659 ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
660 modesettingPtr ms = modesettingPTR(scrn);
661 modesettingEntPtr ms_ent = ms_ent_priv(scrn);
662
663 ms_drm_abort_scrn(scrn);
664
665 if (ms_ent->fd_wakeup_registered == serverGeneration &&
666 !--ms_ent->fd_wakeup_ref) {
667 RemoveNotifyFd(ms->fd);
668 }
669 }
670