vblank.c revision 9076ab76
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 <poll.h> 36#include "driver.h" 37#include "drmmode_display.h" 38 39/** 40 * Tracking for outstanding events queued to the kernel. 41 * 42 * Each list entry is a struct ms_drm_queue, which has a uint32_t 43 * value generated from drm_seq that identifies the event and a 44 * reference back to the crtc/screen associated with the event. It's 45 * done this way rather than in the screen because we want to be able 46 * to drain the list of event handlers that should be called at server 47 * regen time, even though we don't close the drm fd and have no way 48 * to actually drain the kernel events. 49 */ 50static struct xorg_list ms_drm_queue; 51static uint32_t ms_drm_seq; 52 53static void ms_box_intersect(BoxPtr dest, BoxPtr a, BoxPtr b) 54{ 55 dest->x1 = a->x1 > b->x1 ? a->x1 : b->x1; 56 dest->x2 = a->x2 < b->x2 ? a->x2 : b->x2; 57 if (dest->x1 >= dest->x2) { 58 dest->x1 = dest->x2 = dest->y1 = dest->y2 = 0; 59 return; 60 } 61 62 dest->y1 = a->y1 > b->y1 ? a->y1 : b->y1; 63 dest->y2 = a->y2 < b->y2 ? a->y2 : b->y2; 64 if (dest->y1 >= dest->y2) 65 dest->x1 = dest->x2 = dest->y1 = dest->y2 = 0; 66} 67 68static void ms_crtc_box(xf86CrtcPtr crtc, BoxPtr crtc_box) 69{ 70 if (crtc->enabled) { 71 crtc_box->x1 = crtc->x; 72 crtc_box->x2 = 73 crtc->x + xf86ModeWidth(&crtc->mode, crtc->rotation); 74 crtc_box->y1 = crtc->y; 75 crtc_box->y2 = 76 crtc->y + xf86ModeHeight(&crtc->mode, crtc->rotation); 77 } else 78 crtc_box->x1 = crtc_box->x2 = crtc_box->y1 = crtc_box->y2 = 0; 79} 80 81static int ms_box_area(BoxPtr box) 82{ 83 return (int)(box->x2 - box->x1) * (int)(box->y2 - box->y1); 84} 85 86Bool 87ms_crtc_on(xf86CrtcPtr crtc) 88{ 89 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; 90 91 return crtc->enabled && drmmode_crtc->dpms_mode == DPMSModeOn; 92} 93 94/* 95 * Return the crtc covering 'box'. If two crtcs cover a portion of 96 * 'box', then prefer 'desired'. If 'desired' is NULL, then prefer the crtc 97 * with greater coverage 98 */ 99 100xf86CrtcPtr 101ms_covering_crtc(ScrnInfoPtr scrn, 102 BoxPtr box, xf86CrtcPtr desired, BoxPtr crtc_box_ret) 103{ 104 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); 105 xf86CrtcPtr crtc, best_crtc; 106 int coverage, best_coverage; 107 int c; 108 BoxRec crtc_box, cover_box; 109 110 best_crtc = NULL; 111 best_coverage = 0; 112 crtc_box_ret->x1 = 0; 113 crtc_box_ret->x2 = 0; 114 crtc_box_ret->y1 = 0; 115 crtc_box_ret->y2 = 0; 116 for (c = 0; c < xf86_config->num_crtc; c++) { 117 crtc = xf86_config->crtc[c]; 118 119 /* If the CRTC is off, treat it as not covering */ 120 if (!ms_crtc_on(crtc)) 121 continue; 122 123 ms_crtc_box(crtc, &crtc_box); 124 ms_box_intersect(&cover_box, &crtc_box, box); 125 coverage = ms_box_area(&cover_box); 126 if (coverage && crtc == desired) { 127 *crtc_box_ret = crtc_box; 128 return crtc; 129 } 130 if (coverage > best_coverage) { 131 *crtc_box_ret = crtc_box; 132 best_crtc = crtc; 133 best_coverage = coverage; 134 } 135 } 136 return best_crtc; 137} 138 139xf86CrtcPtr 140ms_dri2_crtc_covering_drawable(DrawablePtr pDraw) 141{ 142 ScreenPtr pScreen = pDraw->pScreen; 143 ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); 144 BoxRec box, crtcbox; 145 146 box.x1 = pDraw->x; 147 box.y1 = pDraw->y; 148 box.x2 = box.x1 + pDraw->width; 149 box.y2 = box.y1 + pDraw->height; 150 151 return ms_covering_crtc(pScrn, &box, NULL, &crtcbox); 152} 153 154static Bool 155ms_get_kernel_ust_msc(xf86CrtcPtr crtc, 156 uint32_t *msc, uint64_t *ust) 157{ 158 ScreenPtr screen = crtc->randr_crtc->pScreen; 159 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 160 modesettingPtr ms = modesettingPTR(scrn); 161 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; 162 drmVBlank vbl; 163 int ret; 164 165 /* Get current count */ 166 vbl.request.type = DRM_VBLANK_RELATIVE | drmmode_crtc->vblank_pipe; 167 vbl.request.sequence = 0; 168 vbl.request.signal = 0; 169 ret = drmWaitVBlank(ms->fd, &vbl); 170 if (ret) { 171 *msc = 0; 172 *ust = 0; 173 return FALSE; 174 } else { 175 *msc = vbl.reply.sequence; 176 *ust = (CARD64) vbl.reply.tval_sec * 1000000 + vbl.reply.tval_usec; 177 return TRUE; 178 } 179} 180 181/** 182 * Convert a 32-bit kernel MSC sequence number to a 64-bit local sequence 183 * number, adding in the vblank_offset and high 32 bits, and dealing 184 * with 64-bit wrapping 185 */ 186uint64_t 187ms_kernel_msc_to_crtc_msc(xf86CrtcPtr crtc, uint32_t sequence) 188{ 189 drmmode_crtc_private_rec *drmmode_crtc = crtc->driver_private; 190 sequence += drmmode_crtc->vblank_offset; 191 192 if ((int32_t) (sequence - drmmode_crtc->msc_prev) < -0x40000000) 193 drmmode_crtc->msc_high += 0x100000000L; 194 drmmode_crtc->msc_prev = sequence; 195 return drmmode_crtc->msc_high + sequence; 196} 197 198int 199ms_get_crtc_ust_msc(xf86CrtcPtr crtc, CARD64 *ust, CARD64 *msc) 200{ 201 uint32_t kernel_msc; 202 203 if (!ms_get_kernel_ust_msc(crtc, &kernel_msc, ust)) 204 return BadMatch; 205 *msc = ms_kernel_msc_to_crtc_msc(crtc, kernel_msc); 206 207 return Success; 208} 209 210#define MAX_VBLANK_OFFSET 1000 211 212/** 213 * Convert a 64-bit adjusted MSC value into a 32-bit kernel sequence number, 214 * removing the high 32 bits and subtracting out the vblank_offset term. 215 * 216 * This also updates the vblank_offset when it notices that the value should 217 * change. 218 */ 219uint32_t 220ms_crtc_msc_to_kernel_msc(xf86CrtcPtr crtc, uint64_t expect) 221{ 222 drmmode_crtc_private_rec *drmmode_crtc = crtc->driver_private; 223 uint64_t msc; 224 uint64_t ust; 225 int64_t diff; 226 227 if (ms_get_crtc_ust_msc(crtc, &ust, &msc) == Success) { 228 diff = expect - msc; 229 230 /* We're way off here, assume that the kernel has lost its mind 231 * and smack the vblank back to something sensible 232 */ 233 if (diff < -MAX_VBLANK_OFFSET || MAX_VBLANK_OFFSET < diff) { 234 drmmode_crtc->vblank_offset += (int32_t) diff; 235 if (drmmode_crtc->vblank_offset > -MAX_VBLANK_OFFSET && 236 drmmode_crtc->vblank_offset < MAX_VBLANK_OFFSET) 237 drmmode_crtc->vblank_offset = 0; 238 } 239 } 240 return (uint32_t) (expect - drmmode_crtc->vblank_offset); 241} 242 243/** 244 * Check for pending DRM events and process them. 245 */ 246static void 247ms_drm_wakeup_handler(void *data, int err, void *mask) 248{ 249 ScreenPtr screen; 250 ScrnInfoPtr scrn; 251 modesettingPtr ms; 252 fd_set *read_mask; 253 254 if (data == NULL || err < 0) 255 return; 256 257 screen = data; 258 scrn = xf86ScreenToScrn(screen); 259 ms = modesettingPTR(scrn); 260 read_mask = mask; 261 262 if (FD_ISSET(ms->fd, read_mask)) 263 drmHandleEvent(ms->fd, &ms->event_context); 264} 265 266/* 267 * Enqueue a potential drm response; when the associated response 268 * appears, we've got data to pass to the handler from here 269 */ 270uint32_t 271ms_drm_queue_alloc(xf86CrtcPtr crtc, 272 void *data, 273 ms_drm_handler_proc handler, 274 ms_drm_abort_proc abort) 275{ 276 ScreenPtr screen = crtc->randr_crtc->pScreen; 277 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 278 struct ms_drm_queue *q; 279 280 q = calloc(1, sizeof(struct ms_drm_queue)); 281 282 if (!q) 283 return 0; 284 if (!ms_drm_seq) 285 ++ms_drm_seq; 286 q->seq = ms_drm_seq++; 287 q->scrn = scrn; 288 q->crtc = crtc; 289 q->data = data; 290 q->handler = handler; 291 q->abort = abort; 292 293 xorg_list_add(&q->list, &ms_drm_queue); 294 295 return q->seq; 296} 297 298/** 299 * Abort one queued DRM entry, removing it 300 * from the list, calling the abort function and 301 * freeing the memory 302 */ 303static void 304ms_drm_abort_one(struct ms_drm_queue *q) 305{ 306 xorg_list_del(&q->list); 307 q->abort(q->data); 308 free(q); 309} 310 311/** 312 * Abort all queued entries on a specific scrn, used 313 * when resetting the X server 314 */ 315static void 316ms_drm_abort_scrn(ScrnInfoPtr scrn) 317{ 318 struct ms_drm_queue *q, *tmp; 319 320 xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) { 321 if (q->scrn == scrn) 322 ms_drm_abort_one(q); 323 } 324} 325 326/** 327 * Abort by drm queue sequence number. 328 */ 329void 330ms_drm_abort_seq(ScrnInfoPtr scrn, uint32_t seq) 331{ 332 struct ms_drm_queue *q, *tmp; 333 334 xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) { 335 if (q->seq == seq) { 336 ms_drm_abort_one(q); 337 break; 338 } 339 } 340} 341 342/* 343 * Externally usable abort function that uses a callback to match a single 344 * queued entry to abort 345 */ 346void 347ms_drm_abort(ScrnInfoPtr scrn, Bool (*match)(void *data, void *match_data), 348 void *match_data) 349{ 350 struct ms_drm_queue *q; 351 352 xorg_list_for_each_entry(q, &ms_drm_queue, list) { 353 if (match(q->data, match_data)) { 354 ms_drm_abort_one(q); 355 break; 356 } 357 } 358} 359 360/* 361 * General DRM kernel handler. Looks for the matching sequence number in the 362 * drm event queue and calls the handler for it. 363 */ 364static void 365ms_drm_handler(int fd, uint32_t frame, uint32_t sec, uint32_t usec, 366 void *user_ptr) 367{ 368 struct ms_drm_queue *q, *tmp; 369 uint32_t user_data = (uint32_t) (intptr_t) user_ptr; 370 371 xorg_list_for_each_entry_safe(q, tmp, &ms_drm_queue, list) { 372 if (q->seq == user_data) { 373 uint64_t msc; 374 375 msc = ms_kernel_msc_to_crtc_msc(q->crtc, frame); 376 xorg_list_del(&q->list); 377 q->handler(msc, (uint64_t) sec * 1000000 + usec, q->data); 378 free(q); 379 break; 380 } 381 } 382} 383 384Bool 385ms_vblank_screen_init(ScreenPtr screen) 386{ 387 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 388 modesettingPtr ms = modesettingPTR(scrn); 389 modesettingEntPtr ms_ent = ms_ent_priv(scrn); 390 xorg_list_init(&ms_drm_queue); 391 392 ms->event_context.version = DRM_EVENT_CONTEXT_VERSION; 393 ms->event_context.vblank_handler = ms_drm_handler; 394 ms->event_context.page_flip_handler = ms_drm_handler; 395 396 /* We need to re-register the DRM fd for the synchronisation 397 * feedback on every server generation, so perform the 398 * registration within ScreenInit and not PreInit. 399 */ 400 if (ms_ent->fd_wakeup_registered != serverGeneration) { 401 AddGeneralSocket(ms->fd); 402 RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA, 403 ms_drm_wakeup_handler, screen); 404 ms_ent->fd_wakeup_registered = serverGeneration; 405 ms_ent->fd_wakeup_ref = 1; 406 } else 407 ms_ent->fd_wakeup_ref++; 408 409 return TRUE; 410} 411 412void 413ms_vblank_close_screen(ScreenPtr screen) 414{ 415 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 416 modesettingPtr ms = modesettingPTR(scrn); 417 modesettingEntPtr ms_ent = ms_ent_priv(scrn); 418 419 ms_drm_abort_scrn(scrn); 420 421 if (ms_ent->fd_wakeup_registered == serverGeneration && 422 !--ms_ent->fd_wakeup_ref) { 423 RemoveBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA, 424 ms_drm_wakeup_handler, screen); 425 RemoveGeneralSocket(ms->fd); 426 } 427} 428