pageflip.c revision a035e2b2
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 <xserver_poll.h> 28#include <xf86drm.h> 29 30#include "driver.h" 31 32/* 33 * Flush the DRM event queue when full; makes space for new events. 34 * 35 * Returns a negative value on error, 0 if there was nothing to process, 36 * or 1 if we handled any events. 37 */ 38int 39ms_flush_drm_events(ScreenPtr screen) 40{ 41 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 42 modesettingPtr ms = modesettingPTR(scrn); 43 44 struct pollfd p = { .fd = ms->fd, .events = POLLIN }; 45 int r; 46 47 do { 48 r = xserver_poll(&p, 1, 0); 49 } while (r == -1 && (errno == EINTR || errno == EAGAIN)); 50 51 /* If there was an error, r will be < 0. Return that. If there was 52 * nothing to process, r == 0. Return that. 53 */ 54 if (r <= 0) 55 return r; 56 57 /* Try to handle the event. If there was an error, return it. */ 58 r = drmHandleEvent(ms->fd, &ms->event_context); 59 if (r < 0) 60 return r; 61 62 /* Otherwise return 1 to indicate that we handled an event. */ 63 return 1; 64} 65 66#ifdef GLAMOR_HAS_GBM 67 68/* 69 * Event data for an in progress flip. 70 * This contains a pointer to the vblank event, 71 * and information about the flip in progress. 72 * a reference to this is stored in the per-crtc 73 * flips. 74 */ 75struct ms_flipdata { 76 ScreenPtr screen; 77 void *event; 78 ms_pageflip_handler_proc event_handler; 79 ms_pageflip_abort_proc abort_handler; 80 /* number of CRTC events referencing this */ 81 int flip_count; 82 uint64_t fe_msc; 83 uint64_t fe_usec; 84 uint32_t old_fb_id; 85}; 86 87/* 88 * Per crtc pageflipping infomation, 89 * These are submitted to the queuing code 90 * one of them per crtc per flip. 91 */ 92struct ms_crtc_pageflip { 93 Bool on_reference_crtc; 94 /* reference to the ms_flipdata */ 95 struct ms_flipdata *flipdata; 96}; 97 98/** 99 * Free an ms_crtc_pageflip. 100 * 101 * Drops the reference count on the flipdata. 102 */ 103static void 104ms_pageflip_free(struct ms_crtc_pageflip *flip) 105{ 106 struct ms_flipdata *flipdata = flip->flipdata; 107 108 free(flip); 109 if (--flipdata->flip_count > 0) 110 return; 111 free(flipdata); 112} 113 114/** 115 * Callback for the DRM event queue when a single flip has completed 116 * 117 * Once the flip has been completed on all pipes, notify the 118 * extension code telling it when that happened 119 */ 120static void 121ms_pageflip_handler(uint64_t msc, uint64_t ust, void *data) 122{ 123 struct ms_crtc_pageflip *flip = data; 124 struct ms_flipdata *flipdata = flip->flipdata; 125 ScreenPtr screen = flipdata->screen; 126 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 127 modesettingPtr ms = modesettingPTR(scrn); 128 129 if (flip->on_reference_crtc) { 130 flipdata->fe_msc = msc; 131 flipdata->fe_usec = ust; 132 } 133 134 if (flipdata->flip_count == 1) { 135 flipdata->event_handler(ms, flipdata->fe_msc, 136 flipdata->fe_usec, 137 flipdata->event); 138 139 drmModeRmFB(ms->fd, flipdata->old_fb_id); 140 } 141 ms_pageflip_free(flip); 142} 143 144/* 145 * Callback for the DRM queue abort code. A flip has been aborted. 146 */ 147static void 148ms_pageflip_abort(void *data) 149{ 150 struct ms_crtc_pageflip *flip = data; 151 struct ms_flipdata *flipdata = flip->flipdata; 152 ScreenPtr screen = flipdata->screen; 153 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 154 modesettingPtr ms = modesettingPTR(scrn); 155 156 if (flipdata->flip_count == 1) 157 flipdata->abort_handler(ms, flipdata->event); 158 159 ms_pageflip_free(flip); 160} 161 162static Bool 163do_queue_flip_on_crtc(modesettingPtr ms, xf86CrtcPtr crtc, 164 uint32_t flags, uint32_t seq) 165{ 166 return drmmode_crtc_flip(crtc, ms->drmmode.fb_id, flags, 167 (void *) (uintptr_t) seq); 168} 169 170static Bool 171queue_flip_on_crtc(ScreenPtr screen, xf86CrtcPtr crtc, 172 struct ms_flipdata *flipdata, 173 int ref_crtc_vblank_pipe, uint32_t flags) 174{ 175 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 176 modesettingPtr ms = modesettingPTR(scrn); 177 drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; 178 struct ms_crtc_pageflip *flip; 179 uint32_t seq; 180 int err; 181 182 flip = calloc(1, sizeof(struct ms_crtc_pageflip)); 183 if (flip == NULL) { 184 xf86DrvMsg(scrn->scrnIndex, X_WARNING, 185 "flip queue: carrier alloc failed.\n"); 186 return FALSE; 187 } 188 189 /* Only the reference crtc will finally deliver its page flip 190 * completion event. All other crtc's events will be discarded. 191 */ 192 flip->on_reference_crtc = (drmmode_crtc->vblank_pipe == ref_crtc_vblank_pipe); 193 flip->flipdata = flipdata; 194 195 seq = ms_drm_queue_alloc(crtc, flip, ms_pageflip_handler, ms_pageflip_abort); 196 if (!seq) { 197 free(flip); 198 return FALSE; 199 } 200 201 /* take a reference on flipdata for use in flip */ 202 flipdata->flip_count++; 203 204 while (do_queue_flip_on_crtc(ms, crtc, flags, seq)) { 205 err = errno; 206 /* We may have failed because the event queue was full. Flush it 207 * and retry. If there was nothing to flush, then we failed for 208 * some other reason and should just return an error. 209 */ 210 if (ms_flush_drm_events(screen) <= 0) { 211 xf86DrvMsg(scrn->scrnIndex, X_WARNING, 212 "flip queue failed: %s\n", strerror(err)); 213 /* Aborting will also decrement flip_count and free(flip). */ 214 ms_drm_abort_seq(scrn, seq); 215 return FALSE; 216 } 217 218 /* We flushed some events, so try again. */ 219 xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue retry\n"); 220 } 221 222 /* The page flip succeded. */ 223 return TRUE; 224} 225 226 227Bool 228ms_do_pageflip(ScreenPtr screen, 229 PixmapPtr new_front, 230 void *event, 231 int ref_crtc_vblank_pipe, 232 Bool async, 233 ms_pageflip_handler_proc pageflip_handler, 234 ms_pageflip_abort_proc pageflip_abort) 235{ 236#ifndef GLAMOR_HAS_GBM 237 return FALSE; 238#else 239 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 240 modesettingPtr ms = modesettingPTR(scrn); 241 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); 242 drmmode_bo new_front_bo; 243 uint32_t flags; 244 int i; 245 struct ms_flipdata *flipdata; 246 glamor_block_handler(screen); 247 248 new_front_bo.gbm = glamor_gbm_bo_from_pixmap(screen, new_front); 249 new_front_bo.dumb = NULL; 250 251 if (!new_front_bo.gbm) { 252 xf86DrvMsg(scrn->scrnIndex, X_ERROR, 253 "Failed to get GBM bo for flip to new front.\n"); 254 return FALSE; 255 } 256 257 flipdata = calloc(1, sizeof(struct ms_flipdata)); 258 if (!flipdata) { 259 drmmode_bo_destroy(&ms->drmmode, &new_front_bo); 260 xf86DrvMsg(scrn->scrnIndex, X_ERROR, 261 "Failed to allocate flipdata.\n"); 262 return FALSE; 263 } 264 265 flipdata->event = event; 266 flipdata->screen = screen; 267 flipdata->event_handler = pageflip_handler; 268 flipdata->abort_handler = pageflip_abort; 269 270 /* 271 * Take a local reference on flipdata. 272 * if the first flip fails, the sequence abort 273 * code will free the crtc flip data, and drop 274 * it's reference which would cause this to be 275 * freed when we still required it. 276 */ 277 flipdata->flip_count++; 278 279 /* Create a new handle for the back buffer */ 280 flipdata->old_fb_id = ms->drmmode.fb_id; 281 282 new_front_bo.width = new_front->drawable.width; 283 new_front_bo.height = new_front->drawable.height; 284 if (drmmode_bo_import(&ms->drmmode, &new_front_bo, 285 &ms->drmmode.fb_id)) 286 goto error_out; 287 288 flags = DRM_MODE_PAGE_FLIP_EVENT; 289 if (async) 290 flags |= DRM_MODE_PAGE_FLIP_ASYNC; 291 292 /* Queue flips on all enabled CRTCs. 293 * 294 * Note that if/when we get per-CRTC buffers, we'll have to update this. 295 * Right now it assumes a single shared fb across all CRTCs, with the 296 * kernel fixing up the offset of each CRTC as necessary. 297 * 298 * Also, flips queued on disabled or incorrectly configured displays 299 * may never complete; this is a configuration error. 300 */ 301 for (i = 0; i < config->num_crtc; i++) { 302 xf86CrtcPtr crtc = config->crtc[i]; 303 304 if (!xf86_crtc_on(crtc)) 305 continue; 306 307 if (!queue_flip_on_crtc(screen, crtc, flipdata, 308 ref_crtc_vblank_pipe, 309 flags)) { 310 goto error_undo; 311 } 312 } 313 314 drmmode_bo_destroy(&ms->drmmode, &new_front_bo); 315 316 /* 317 * Do we have more than our local reference, 318 * if so and no errors, then drop our local 319 * reference and return now. 320 */ 321 if (flipdata->flip_count > 1) { 322 flipdata->flip_count--; 323 return TRUE; 324 } 325 326error_undo: 327 328 /* 329 * Have we just got the local reference? 330 * free the framebuffer if so since nobody successfully 331 * submitted anything 332 */ 333 if (flipdata->flip_count == 1) { 334 drmModeRmFB(ms->fd, ms->drmmode.fb_id); 335 ms->drmmode.fb_id = flipdata->old_fb_id; 336 } 337 338error_out: 339 xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Page flip failed: %s\n", 340 strerror(errno)); 341 drmmode_bo_destroy(&ms->drmmode, &new_front_bo); 342 /* if only the local reference - free the structure, 343 * else drop the local reference and return */ 344 if (flipdata->flip_count == 1) 345 free(flipdata); 346 else 347 flipdata->flip_count--; 348 349 return FALSE; 350#endif /* GLAMOR_HAS_GBM */ 351} 352 353#endif 354