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