nouveau_present.c revision 92405695
1/* 2 * Copyright 2013 Red Hat Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: Ben Skeggs <bskeggs@redhat.com> 23 */ 24 25#include "nouveau_present.h" 26#if defined(DRI3) 27#include "nv_include.h" 28#include "xf86drmMode.h" 29 30struct nouveau_present { 31 struct present_screen_info info; 32}; 33 34static RRCrtcPtr 35nouveau_present_crtc(WindowPtr window) 36{ 37 ScrnInfoPtr scrn = xf86ScreenToScrn(window->drawable.pScreen); 38 xf86CrtcPtr crtc; 39 40 crtc = nouveau_pick_best_crtc(scrn, FALSE, 41 window->drawable.x, 42 window->drawable.y, 43 window->drawable.width, 44 window->drawable.height); 45 46 if (!crtc) 47 return NULL; 48 49 return crtc->randr_crtc; 50} 51 52static int 53nouveau_present_ust_msc(RRCrtcPtr rrcrtc, uint64_t *ust, uint64_t *msc) 54{ 55 xf86CrtcPtr crtc = rrcrtc->devPrivate; 56 NVPtr pNv = NVPTR(crtc->scrn); 57 drmVBlank args; 58 int ret; 59 60 args.request.type = DRM_VBLANK_RELATIVE; 61 args.request.type |= drmmode_head(crtc) << DRM_VBLANK_HIGH_CRTC_SHIFT; 62 args.request.sequence = 0, 63 args.request.signal = 0, 64 65 ret = drmWaitVBlank(pNv->dev->fd, &args); 66 if (ret) { 67 *ust = *msc = 0; 68 return BadMatch; 69 } 70 71 *ust = (CARD64)args.reply.tval_sec * 1000000 + args.reply.tval_usec; 72 *msc = args.reply.sequence; 73 return Success; 74} 75 76struct nouveau_present_vblank { 77 uint64_t msc; 78}; 79 80static void 81nouveau_present_vblank(void *priv, uint64_t name, uint64_t ust, uint32_t msc_lo) 82{ 83 struct nouveau_present_vblank *event = priv; 84 uint64_t msc; 85 86 msc = (event->msc & 0xffffffff00000000ULL) | msc_lo; 87 if (msc < event->msc) 88 event->msc += 1ULL << 32; 89 90 present_event_notify(name, ust, msc); 91} 92 93static int 94nouveau_present_vblank_queue(RRCrtcPtr rrcrtc, uint64_t event_id, uint64_t msc) 95{ 96 xf86CrtcPtr crtc = rrcrtc->devPrivate; 97 NVPtr pNv = NVPTR(crtc->scrn); 98 drmVBlank args; 99 struct nouveau_present_vblank *event; 100 void *token; 101 int ret; 102 103 event = drmmode_event_queue(crtc->scrn, event_id, sizeof(*event), 104 nouveau_present_vblank, &token); 105 if (!event) 106 return BadAlloc; 107 108 event->msc = msc; 109 110 args.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; 111 args.request.type |= drmmode_head(crtc) << DRM_VBLANK_HIGH_CRTC_SHIFT; 112 args.request.sequence = msc; 113 args.request.signal = (unsigned long)token; 114 115 while ((ret = drmWaitVBlank(pNv->dev->fd, &args)) != 0) { 116 if (errno != EBUSY) { 117 xf86DrvMsgVerb(crtc->scrn->scrnIndex, X_WARNING, 4, 118 "PRESENT: Wait for VBlank failed: %s\n", strerror(errno)); 119 drmmode_event_abort(crtc->scrn, event_id, false); 120 return BadAlloc; 121 } 122 ret = drmmode_event_flush(crtc->scrn); 123 if (ret < 0) { 124 xf86DrvMsgVerb(crtc->scrn->scrnIndex, X_WARNING, 4, 125 "PRESENT: Event flush failed\n"); 126 drmmode_event_abort(crtc->scrn, event_id, false); 127 return BadAlloc; 128 } 129 } 130 131 return Success; 132} 133 134static void 135nouveau_present_vblank_abort(RRCrtcPtr rrcrtc, uint64_t event_id, uint64_t msc) 136{ 137 xf86CrtcPtr crtc = rrcrtc->devPrivate; 138 drmmode_event_abort(crtc->scrn, event_id, true); 139} 140 141static void 142nouveau_present_flush(WindowPtr window) 143{ 144 ScrnInfoPtr scrn = xf86ScreenToScrn(window->drawable.pScreen); 145 NVPtr pNv = NVPTR(scrn); 146 if (pNv->Flush) 147 pNv->Flush(scrn); 148} 149 150struct nouveau_present_flip { 151 uint64_t msc; 152 uint32_t old; 153 int fd; 154}; 155 156static Bool 157nouveau_present_flip_check(RRCrtcPtr rrcrtc, WindowPtr window, 158 PixmapPtr pixmap, Bool sync_flip) 159{ 160 ScrnInfoPtr scrn = xf86ScreenToScrn(window->drawable.pScreen); 161 NVPtr pNv = NVPTR(scrn); 162 xf86CrtcPtr crtc = rrcrtc->devPrivate; 163 struct nouveau_pixmap *priv = nouveau_pixmap(pixmap); 164 165 if (!scrn->vtSema || !drmmode_crtc_on(crtc) || crtc->rotatedData) 166 return FALSE; 167 168 if (!priv) { 169 /* The pixmap may not have had backing for low-memory GPUs, or 170 * if we ran out of VRAM. Make sure it's properly backed for 171 * flipping. 172 */ 173 pNv->exa_force_cp = TRUE; 174 exaMoveInPixmap(pixmap); 175 pNv->exa_force_cp = FALSE; 176 priv = nouveau_pixmap(pixmap); 177 } 178 179 return priv ? TRUE : FALSE; 180} 181 182static void 183nouveau_present_flip(void *priv, uint64_t name, uint64_t ust, uint32_t msc_lo) 184{ 185 struct nouveau_present_flip *flip = priv; 186 uint64_t msc; 187 188 msc = (flip->msc & ~0xffffffffULL) | msc_lo; 189 if (msc < flip->msc) 190 msc += 1ULL << 32; 191 192 present_event_notify(name, ust, msc); 193 drmModeRmFB(flip->fd, flip->old); 194} 195 196static Bool 197nouveau_present_flip_exec(ScrnInfoPtr scrn, uint64_t event_id, int sync, 198 uint64_t target_msc, PixmapPtr pixmap, Bool vsync) 199{ 200 struct nouveau_pixmap *priv = nouveau_pixmap(pixmap); 201 NVPtr pNv = NVPTR(scrn); 202 uint32_t next_fb; 203 void *token; 204 int ret; 205 206 ret = drmModeAddFB(pNv->dev->fd, pixmap->drawable.width, 207 pixmap->drawable.height, pixmap->drawable.depth, 208 pixmap->drawable.bitsPerPixel, pixmap->devKind, 209 priv->bo->handle, &next_fb); 210 if (ret == 0) { 211 struct nouveau_present_flip *flip = 212 drmmode_event_queue(scrn, event_id, sizeof(*flip), 213 nouveau_present_flip, &token); 214 if (flip) { 215 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); 216 int last = 0, i; 217 218 drmmode_swap(scrn, next_fb, &flip->old); 219 flip->fd = pNv->dev->fd; 220 flip->msc = target_msc; 221 222 for (i = 0; i < config->num_crtc; i++) { 223 if (drmmode_crtc_on(config->crtc[i])) 224 last = i; 225 } 226 227 for (i = 0; i < config->num_crtc; i++) { 228 int type = vsync ? 0 : DRM_MODE_PAGE_FLIP_ASYNC; 229 int crtc = drmmode_crtc(config->crtc[i]); 230 void *user = NULL; 231 232 if (!drmmode_crtc_on(config->crtc[i])) 233 continue; 234 235 if (token && ((crtc == sync) || (i == last))) { 236 type |= DRM_MODE_PAGE_FLIP_EVENT; 237 user = token; 238 } 239 240 ret = drmModePageFlip(pNv->dev->fd, crtc, 241 next_fb, type, user); 242 if (ret == 0 && user) { 243 token = NULL; 244 } 245 } 246 247 if (token == NULL) { 248 return TRUE; 249 } 250 251 drmmode_swap(scrn, flip->old, &next_fb); 252 drmmode_event_abort(scrn, event_id, false); 253 } 254 255 drmModeRmFB(pNv->dev->fd, next_fb); 256 } 257 258 return FALSE; 259} 260 261static Bool 262nouveau_present_flip_next(RRCrtcPtr rrcrtc, uint64_t event_id, 263 uint64_t target_msc, PixmapPtr pixmap, Bool vsync) 264{ 265 xf86CrtcPtr crtc = rrcrtc->devPrivate; 266 ScrnInfoPtr scrn = crtc->scrn; 267 return nouveau_present_flip_exec(scrn, event_id, drmmode_crtc(crtc), 268 target_msc, pixmap, vsync); 269} 270 271static void 272nouveau_present_flip_stop(ScreenPtr screen, uint64_t event_id) 273{ 274 PixmapPtr pixmap = screen->GetScreenPixmap(screen); 275 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 276 nouveau_present_flip_exec(scrn, event_id, 0, 0, pixmap, TRUE); 277} 278 279void 280nouveau_present_fini(ScreenPtr screen) 281{ 282 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 283 NVPtr pNv = NVPTR(scrn); 284 if (pNv->present) { 285 free(pNv->present); 286 pNv->present = NULL; 287 } 288} 289 290Bool 291nouveau_present_init(ScreenPtr screen) 292{ 293 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 294 NVPtr pNv = NVPTR(scrn); 295 struct nouveau_present *present; 296 uint64_t value; 297 int ret; 298 299 present = pNv->present = calloc(1, sizeof(*present)); 300 if (!present) 301 return FALSE; 302 303 present->info.version = PRESENT_SCREEN_INFO_VERSION; 304 present->info.get_crtc = nouveau_present_crtc; 305 present->info.get_ust_msc = nouveau_present_ust_msc; 306 present->info.queue_vblank = nouveau_present_vblank_queue; 307 present->info.abort_vblank = nouveau_present_vblank_abort; 308 present->info.flush = nouveau_present_flush; 309 310 if (pNv->has_pageflip) { 311#ifdef DRM_CAP_ASYNC_PAGE_FLIP 312 ret = drmGetCap(pNv->dev->fd, DRM_CAP_ASYNC_PAGE_FLIP, &value); 313 if (ret == 0 && value == 1) 314 present->info.capabilities |= PresentCapabilityAsync; 315#endif 316 present->info.check_flip = nouveau_present_flip_check; 317 present->info.flip = nouveau_present_flip_next; 318 present->info.unflip = nouveau_present_flip_stop; 319 } 320 321 return present_screen_init(screen, &present->info); 322} 323#endif 324