1/* 2 * Copyright © 2014 Intel Corporation 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#ifdef HAVE_DIX_CONFIG_H 24#include "dix-config.h" 25#endif 26 27#include <assert.h> 28#include <errno.h> 29#include <fcntl.h> 30#include <unistd.h> 31#include <stdio.h> 32#include <stdint.h> 33#include <string.h> 34#include <sys/ioctl.h> 35#include <sys/time.h> 36#include <sys/types.h> 37#include <time.h> 38 39#include <xf86.h> 40#include <xf86Crtc.h> 41#include <xf86drm.h> 42#include <xf86str.h> 43#include <present.h> 44 45#include "driver.h" 46#include "drmmode_display.h" 47 48#if 0 49#define DebugPresent(x) ErrorF x 50#else 51#define DebugPresent(x) 52#endif 53 54struct ms_present_vblank_event { 55 uint64_t event_id; 56 Bool unflip; 57}; 58 59static RRCrtcPtr 60ms_present_get_crtc(WindowPtr window) 61{ 62 return ms_randr_crtc_covering_drawable(&window->drawable); 63} 64 65static int 66ms_present_get_ust_msc(RRCrtcPtr crtc, uint64_t *ust, uint64_t *msc) 67{ 68 xf86CrtcPtr xf86_crtc = crtc->devPrivate; 69 70 return ms_get_crtc_ust_msc(xf86_crtc, ust, msc); 71} 72 73/* 74 * Changes the variable refresh state for every CRTC on the screen. 75 */ 76void 77ms_present_set_screen_vrr(ScrnInfoPtr scrn, Bool vrr_enabled) 78{ 79 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); 80 xf86CrtcPtr crtc; 81 int i; 82 83 for (i = 0; i < config->num_crtc; i++) { 84 crtc = config->crtc[i]; 85 drmmode_crtc_set_vrr(crtc, vrr_enabled); 86 } 87} 88 89/* 90 * Called when the queued vblank event has occurred 91 */ 92static void 93ms_present_vblank_handler(uint64_t msc, uint64_t usec, void *data) 94{ 95 struct ms_present_vblank_event *event = data; 96 97 DebugPresent(("\t\tmh %lld msc %llu\n", 98 (long long) event->event_id, (long long) msc)); 99 100 present_event_notify(event->event_id, usec, msc); 101 free(event); 102} 103 104/* 105 * Called when the queued vblank is aborted 106 */ 107static void 108ms_present_vblank_abort(void *data) 109{ 110 struct ms_present_vblank_event *event = data; 111 112 DebugPresent(("\t\tma %lld\n", (long long) event->event_id)); 113 114 free(event); 115} 116 117/* 118 * Queue an event to report back to the Present extension when the specified 119 * MSC has past 120 */ 121static int 122ms_present_queue_vblank(RRCrtcPtr crtc, 123 uint64_t event_id, 124 uint64_t msc) 125{ 126 xf86CrtcPtr xf86_crtc = crtc->devPrivate; 127 struct ms_present_vblank_event *event; 128 uint32_t seq; 129 130 event = calloc(sizeof(struct ms_present_vblank_event), 1); 131 if (!event) 132 return BadAlloc; 133 event->event_id = event_id; 134 seq = ms_drm_queue_alloc(xf86_crtc, event, 135 ms_present_vblank_handler, 136 ms_present_vblank_abort); 137 if (!seq) { 138 free(event); 139 return BadAlloc; 140 } 141 142 if (!ms_queue_vblank(xf86_crtc, MS_QUEUE_ABSOLUTE, msc, NULL, seq)) 143 return BadAlloc; 144 145 DebugPresent(("\t\tmq %lld seq %u msc %llu\n", 146 (long long) event_id, seq, (long long) msc)); 147 return Success; 148} 149 150static Bool 151ms_present_event_match(void *data, void *match_data) 152{ 153 struct ms_present_vblank_event *event = data; 154 uint64_t *match = match_data; 155 156 return *match == event->event_id; 157} 158 159/* 160 * Remove a pending vblank event from the DRM queue so that it is not reported 161 * to the extension 162 */ 163static void 164ms_present_abort_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc) 165{ 166 ScreenPtr screen = crtc->pScreen; 167 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 168 169 ms_drm_abort(scrn, ms_present_event_match, &event_id); 170} 171 172/* 173 * Flush our batch buffer when requested by the Present extension. 174 */ 175static void 176ms_present_flush(WindowPtr window) 177{ 178#ifdef GLAMOR_HAS_GBM 179 ScreenPtr screen = window->drawable.pScreen; 180 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 181 modesettingPtr ms = modesettingPTR(scrn); 182 183 if (ms->drmmode.glamor) 184 ms->glamor.block_handler(screen); 185#endif 186} 187 188#ifdef GLAMOR_HAS_GBM 189 190/** 191 * Callback for the DRM event queue when a flip has completed on all pipes 192 * 193 * Notify the extension code 194 */ 195static void 196ms_present_flip_handler(modesettingPtr ms, uint64_t msc, 197 uint64_t ust, void *data) 198{ 199 struct ms_present_vblank_event *event = data; 200 201 DebugPresent(("\t\tms:fc %lld msc %llu ust %llu\n", 202 (long long) event->event_id, 203 (long long) msc, (long long) ust)); 204 205 if (event->unflip) 206 ms->drmmode.present_flipping = FALSE; 207 208 ms_present_vblank_handler(msc, ust, event); 209} 210 211/* 212 * Callback for the DRM queue abort code. A flip has been aborted. 213 */ 214static void 215ms_present_flip_abort(modesettingPtr ms, void *data) 216{ 217 struct ms_present_vblank_event *event = data; 218 219 DebugPresent(("\t\tms:fa %lld\n", (long long) event->event_id)); 220 221 free(event); 222} 223 224/* 225 * Test to see if page flipping is possible on the target crtc 226 * 227 * We ignore sw-cursors when *disabling* flipping, we may very well be 228 * returning to scanning out the normal framebuffer *because* we just 229 * switched to sw-cursor mode and check_flip just failed because of that. 230 */ 231static Bool 232ms_present_check_unflip(RRCrtcPtr crtc, 233 WindowPtr window, 234 PixmapPtr pixmap, 235 Bool sync_flip, 236 PresentFlipReason *reason) 237{ 238 ScreenPtr screen = window->drawable.pScreen; 239 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 240 modesettingPtr ms = modesettingPTR(scrn); 241 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); 242 int num_crtcs_on = 0; 243 int i; 244 struct gbm_bo *gbm; 245 246 if (!ms->drmmode.pageflip) 247 return FALSE; 248 249 if (ms->drmmode.dri2_flipping) 250 return FALSE; 251 252 if (!scrn->vtSema) 253 return FALSE; 254 255 for (i = 0; i < config->num_crtc; i++) { 256 drmmode_crtc_private_ptr drmmode_crtc = config->crtc[i]->driver_private; 257 258 /* Don't do pageflipping if CRTCs are rotated. */ 259 if (drmmode_crtc->rotate_bo.gbm) 260 return FALSE; 261 262 if (xf86_crtc_on(config->crtc[i])) 263 num_crtcs_on++; 264 } 265 266 /* We can't do pageflipping if all the CRTCs are off. */ 267 if (num_crtcs_on == 0) 268 return FALSE; 269 270 /* 271 * Check stride, can't change that reliably on flip on some drivers, unless 272 * the kms driver is atomic_modeset_capable. 273 */ 274 if (!ms->atomic_modeset_capable && 275 pixmap->devKind != drmmode_bo_get_pitch(&ms->drmmode.front_bo)) 276 return FALSE; 277 278 if (!ms->drmmode.glamor) 279 return FALSE; 280 281#ifdef GBM_BO_WITH_MODIFIERS 282 /* Check if buffer format/modifier is supported by all active CRTCs */ 283 gbm = ms->glamor.gbm_bo_from_pixmap(screen, pixmap); 284 if (gbm) { 285 uint32_t format; 286 uint64_t modifier; 287 288 format = gbm_bo_get_format(gbm); 289 modifier = gbm_bo_get_modifier(gbm); 290 gbm_bo_destroy(gbm); 291 292 if (!drmmode_is_format_supported(scrn, format, modifier)) { 293 if (reason) 294 *reason = PRESENT_FLIP_REASON_BUFFER_FORMAT; 295 return FALSE; 296 } 297 } 298#endif 299 300 /* Make sure there's a bo we can get to */ 301 /* XXX: actually do this. also...is it sufficient? 302 * if (!glamor_get_pixmap_private(pixmap)) 303 * return FALSE; 304 */ 305 306 return TRUE; 307} 308 309static Bool 310ms_present_check_flip(RRCrtcPtr crtc, 311 WindowPtr window, 312 PixmapPtr pixmap, 313 Bool sync_flip, 314 PresentFlipReason *reason) 315{ 316 ScreenPtr screen = window->drawable.pScreen; 317 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 318 modesettingPtr ms = modesettingPTR(scrn); 319 320 if (ms->drmmode.sprites_visible > 0) 321 return FALSE; 322 323 if(!ms_present_check_unflip(crtc, window, pixmap, sync_flip, reason)) 324 return FALSE; 325 326 ms->flip_window = window; 327 328 return TRUE; 329} 330 331/* 332 * Queue a flip on 'crtc' to 'pixmap' at 'target_msc'. If 'sync_flip' is true, 333 * then wait for vblank. Otherwise, flip immediately 334 */ 335static Bool 336ms_present_flip(RRCrtcPtr crtc, 337 uint64_t event_id, 338 uint64_t target_msc, 339 PixmapPtr pixmap, 340 Bool sync_flip) 341{ 342 ScreenPtr screen = crtc->pScreen; 343 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 344 modesettingPtr ms = modesettingPTR(scrn); 345 xf86CrtcPtr xf86_crtc = crtc->devPrivate; 346 drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private; 347 Bool ret; 348 struct ms_present_vblank_event *event; 349 350 if (!ms_present_check_flip(crtc, ms->flip_window, pixmap, sync_flip, NULL)) 351 return FALSE; 352 353 event = calloc(1, sizeof(struct ms_present_vblank_event)); 354 if (!event) 355 return FALSE; 356 357 DebugPresent(("\t\tms:pf %lld msc %llu\n", 358 (long long) event_id, (long long) target_msc)); 359 360 event->event_id = event_id; 361 event->unflip = FALSE; 362 363 /* A window can only flip if it covers the entire X screen. 364 * Only one window can flip at a time. 365 * 366 * If the window also has the variable refresh property then 367 * variable refresh supported can be enabled on every CRTC. 368 */ 369 if (ms->vrr_support && ms->is_connector_vrr_capable && 370 ms_window_has_variable_refresh(ms, ms->flip_window)) { 371 ms_present_set_screen_vrr(scrn, TRUE); 372 } 373 374 ret = ms_do_pageflip(screen, pixmap, event, drmmode_crtc->vblank_pipe, !sync_flip, 375 ms_present_flip_handler, ms_present_flip_abort, 376 "Present-flip"); 377 if (ret) 378 ms->drmmode.present_flipping = TRUE; 379 380 return ret; 381} 382 383/* 384 * Queue a flip back to the normal frame buffer 385 */ 386static void 387ms_present_unflip(ScreenPtr screen, uint64_t event_id) 388{ 389 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 390 modesettingPtr ms = modesettingPTR(scrn); 391 PixmapPtr pixmap = screen->GetScreenPixmap(screen); 392 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); 393 int i; 394 395 ms_present_set_screen_vrr(scrn, FALSE); 396 397 if (ms_present_check_unflip(NULL, screen->root, pixmap, TRUE, NULL)) { 398 struct ms_present_vblank_event *event; 399 400 event = calloc(1, sizeof(struct ms_present_vblank_event)); 401 if (!event) 402 return; 403 404 event->event_id = event_id; 405 event->unflip = TRUE; 406 407 if (ms_do_pageflip(screen, pixmap, event, -1, FALSE, 408 ms_present_flip_handler, ms_present_flip_abort, 409 "Present-unflip")) { 410 return; 411 } 412 } 413 414 for (i = 0; i < config->num_crtc; i++) { 415 xf86CrtcPtr crtc = config->crtc[i]; 416 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; 417 418 if (!crtc->enabled) 419 continue; 420 421 /* info->drmmode.fb_id still points to the FB for the last flipped BO. 422 * Clear it, drmmode_set_mode_major will re-create it 423 */ 424 if (drmmode_crtc->drmmode->fb_id) { 425 drmModeRmFB(drmmode_crtc->drmmode->fd, 426 drmmode_crtc->drmmode->fb_id); 427 drmmode_crtc->drmmode->fb_id = 0; 428 } 429 430 if (drmmode_crtc->dpms_mode == DPMSModeOn) 431 crtc->funcs->set_mode_major(crtc, &crtc->mode, crtc->rotation, 432 crtc->x, crtc->y); 433 else 434 drmmode_crtc->need_modeset = TRUE; 435 } 436 437 present_event_notify(event_id, 0, 0); 438 ms->drmmode.present_flipping = FALSE; 439} 440#endif 441 442static present_screen_info_rec ms_present_screen_info = { 443 .version = PRESENT_SCREEN_INFO_VERSION, 444 445 .get_crtc = ms_present_get_crtc, 446 .get_ust_msc = ms_present_get_ust_msc, 447 .queue_vblank = ms_present_queue_vblank, 448 .abort_vblank = ms_present_abort_vblank, 449 .flush = ms_present_flush, 450 451 .capabilities = PresentCapabilityNone, 452#ifdef GLAMOR_HAS_GBM 453 .check_flip = NULL, 454 .check_flip2 = ms_present_check_flip, 455 .flip = ms_present_flip, 456 .unflip = ms_present_unflip, 457#endif 458}; 459 460Bool 461ms_present_screen_init(ScreenPtr screen) 462{ 463 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 464 modesettingPtr ms = modesettingPTR(scrn); 465 uint64_t value; 466 int ret; 467 468 ret = drmGetCap(ms->fd, DRM_CAP_ASYNC_PAGE_FLIP, &value); 469 if (ret == 0 && value == 1) { 470 ms_present_screen_info.capabilities |= PresentCapabilityAsync; 471 ms->drmmode.can_async_flip = TRUE; 472 } 473 474 return present_screen_init(screen, &ms_present_screen_info); 475} 476