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