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 return randr_crtc_covering_drawable(&window->drawable); 38} 39 40static int 41nouveau_present_ust_msc(RRCrtcPtr rrcrtc, uint64_t *ust, uint64_t *msc) 42{ 43 xf86CrtcPtr crtc = rrcrtc->devPrivate; 44 NVPtr pNv = NVPTR(crtc->scrn); 45 drmVBlank args; 46 int ret; 47 48 args.request.type = DRM_VBLANK_RELATIVE; 49 args.request.type |= drmmode_head(crtc) << DRM_VBLANK_HIGH_CRTC_SHIFT; 50 args.request.sequence = 0, 51 args.request.signal = 0, 52 53 ret = drmWaitVBlank(pNv->dev->fd, &args); 54 if (ret) { 55 *ust = *msc = 0; 56 return BadMatch; 57 } 58 59 *ust = (CARD64)args.reply.tval_sec * 1000000 + args.reply.tval_usec; 60 *msc = args.reply.sequence; 61 return Success; 62} 63 64struct nouveau_present_vblank { 65 uint64_t msc; 66}; 67 68static void 69nouveau_present_vblank(void *priv, uint64_t name, uint64_t ust, uint32_t msc_lo) 70{ 71 struct nouveau_present_vblank *event = priv; 72 uint64_t msc; 73 74 msc = (event->msc & 0xffffffff00000000ULL) | msc_lo; 75 if (msc < event->msc) 76 event->msc += 1ULL << 32; 77 78 present_event_notify(name, ust, msc); 79} 80 81static int 82nouveau_present_vblank_queue(RRCrtcPtr rrcrtc, uint64_t event_id, uint64_t msc) 83{ 84 xf86CrtcPtr crtc = rrcrtc->devPrivate; 85 NVPtr pNv = NVPTR(crtc->scrn); 86 drmVBlank args; 87 struct nouveau_present_vblank *event; 88 void *token; 89 int ret; 90 91 event = drmmode_event_queue(crtc->scrn, event_id, sizeof(*event), 92 nouveau_present_vblank, &token); 93 if (!event) 94 return BadAlloc; 95 96 event->msc = msc; 97 98 args.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; 99 args.request.type |= drmmode_head(crtc) << DRM_VBLANK_HIGH_CRTC_SHIFT; 100 args.request.sequence = msc; 101 args.request.signal = (unsigned long)token; 102 103 while ((ret = drmWaitVBlank(pNv->dev->fd, &args)) != 0) { 104 if (errno != EBUSY) { 105 xf86DrvMsgVerb(crtc->scrn->scrnIndex, X_WARNING, 4, 106 "PRESENT: Wait for VBlank failed: %s\n", strerror(errno)); 107 drmmode_event_abort(crtc->scrn, event_id, false); 108 return BadAlloc; 109 } 110 ret = drmmode_event_flush(crtc->scrn); 111 if (ret < 0) { 112 xf86DrvMsgVerb(crtc->scrn->scrnIndex, X_WARNING, 4, 113 "PRESENT: Event flush failed\n"); 114 drmmode_event_abort(crtc->scrn, event_id, false); 115 return BadAlloc; 116 } 117 } 118 119 return Success; 120} 121 122static void 123nouveau_present_vblank_abort(RRCrtcPtr rrcrtc, uint64_t event_id, uint64_t msc) 124{ 125 xf86CrtcPtr crtc = rrcrtc->devPrivate; 126 drmmode_event_abort(crtc->scrn, event_id, true); 127} 128 129static void 130nouveau_present_flush(WindowPtr window) 131{ 132 ScrnInfoPtr scrn = xf86ScreenToScrn(window->drawable.pScreen); 133 NVPtr pNv = NVPTR(scrn); 134 if (pNv->Flush) 135 pNv->Flush(scrn); 136} 137 138struct nouveau_present_flip { 139 uint64_t msc; 140 uint32_t old; 141 int fd; 142}; 143 144static Bool 145nouveau_present_flip_check(RRCrtcPtr rrcrtc, WindowPtr window, 146 PixmapPtr pixmap, Bool sync_flip) 147{ 148 ScrnInfoPtr scrn = xf86ScreenToScrn(window->drawable.pScreen); 149 NVPtr pNv = NVPTR(scrn); 150 xf86CrtcPtr crtc = rrcrtc->devPrivate; 151 struct nouveau_pixmap *priv = nouveau_pixmap(pixmap); 152 153 if (!scrn->vtSema || !xf86_crtc_on(crtc) || crtc->rotatedData) 154 return FALSE; 155 156 if (!priv) { 157 /* The pixmap may not have had backing for low-memory GPUs, or 158 * if we ran out of VRAM. Make sure it's properly backed for 159 * flipping. 160 */ 161 pNv->exa_force_cp = TRUE; 162 exaMoveInPixmap(pixmap); 163 pNv->exa_force_cp = FALSE; 164 priv = nouveau_pixmap(pixmap); 165 } 166 167 return priv ? TRUE : FALSE; 168} 169 170static void 171nouveau_present_flip(void *priv, uint64_t name, uint64_t ust, uint32_t msc_lo) 172{ 173 struct nouveau_present_flip *flip = priv; 174 uint64_t msc; 175 176 msc = (flip->msc & ~0xffffffffULL) | msc_lo; 177 if (msc < flip->msc) 178 msc += 1ULL << 32; 179 180 present_event_notify(name, ust, msc); 181 drmModeRmFB(flip->fd, flip->old); 182} 183 184static Bool 185nouveau_present_flip_exec(ScrnInfoPtr scrn, uint64_t event_id, int sync, 186 uint64_t target_msc, PixmapPtr pixmap, Bool vsync) 187{ 188 struct nouveau_pixmap *priv = nouveau_pixmap(pixmap); 189 NVPtr pNv = NVPTR(scrn); 190 uint32_t next_fb; 191 void *token; 192 int ret; 193 194 ret = drmModeAddFB(pNv->dev->fd, pixmap->drawable.width, 195 pixmap->drawable.height, pixmap->drawable.depth, 196 pixmap->drawable.bitsPerPixel, pixmap->devKind, 197 priv->bo->handle, &next_fb); 198 if (ret == 0) { 199 struct nouveau_present_flip *flip = 200 drmmode_event_queue(scrn, event_id, sizeof(*flip), 201 nouveau_present_flip, &token); 202 if (flip) { 203 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); 204 int last = 0, i; 205 206 drmmode_swap(scrn, next_fb, &flip->old); 207 flip->fd = pNv->dev->fd; 208 flip->msc = target_msc; 209 210 for (i = 0; i < config->num_crtc; i++) { 211 if (xf86_crtc_on(config->crtc[i])) 212 last = i; 213 } 214 215 for (i = 0; i < config->num_crtc; i++) { 216 int type = vsync ? 0 : DRM_MODE_PAGE_FLIP_ASYNC; 217 int crtc = drmmode_crtc(config->crtc[i]); 218 void *user = NULL; 219 220 if (!xf86_crtc_on(config->crtc[i])) 221 continue; 222 223 if (token && ((crtc == sync) || (i == last))) { 224 type |= DRM_MODE_PAGE_FLIP_EVENT; 225 user = token; 226 } 227 228 ret = drmModePageFlip(pNv->dev->fd, crtc, 229 next_fb, type, user); 230 if (ret == 0 && user) { 231 token = NULL; 232 } 233 } 234 235 if (token == NULL) { 236 return TRUE; 237 } 238 239 drmmode_swap(scrn, flip->old, &next_fb); 240 drmmode_event_abort(scrn, event_id, false); 241 } 242 243 drmModeRmFB(pNv->dev->fd, next_fb); 244 } 245 246 return FALSE; 247} 248 249static Bool 250nouveau_present_flip_next(RRCrtcPtr rrcrtc, uint64_t event_id, 251 uint64_t target_msc, PixmapPtr pixmap, Bool vsync) 252{ 253 xf86CrtcPtr crtc = rrcrtc->devPrivate; 254 ScrnInfoPtr scrn = crtc->scrn; 255 return nouveau_present_flip_exec(scrn, event_id, drmmode_crtc(crtc), 256 target_msc, pixmap, vsync); 257} 258 259static void 260nouveau_present_flip_stop(ScreenPtr screen, uint64_t event_id) 261{ 262 PixmapPtr pixmap = screen->GetScreenPixmap(screen); 263 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 264 nouveau_present_flip_exec(scrn, event_id, 0, 0, pixmap, TRUE); 265} 266 267void 268nouveau_present_fini(ScreenPtr screen) 269{ 270 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 271 NVPtr pNv = NVPTR(scrn); 272 if (pNv->present) { 273 free(pNv->present); 274 pNv->present = NULL; 275 } 276} 277 278Bool 279nouveau_present_init(ScreenPtr screen) 280{ 281 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 282 NVPtr pNv = NVPTR(scrn); 283 struct nouveau_present *present; 284 uint64_t value; 285 int ret; 286 287 present = pNv->present = calloc(1, sizeof(*present)); 288 if (!present) 289 return FALSE; 290 291 present->info.version = PRESENT_SCREEN_INFO_VERSION; 292 present->info.get_crtc = nouveau_present_crtc; 293 present->info.get_ust_msc = nouveau_present_ust_msc; 294 present->info.queue_vblank = nouveau_present_vblank_queue; 295 present->info.abort_vblank = nouveau_present_vblank_abort; 296 present->info.flush = nouveau_present_flush; 297 298 if (pNv->has_pageflip) { 299#ifdef DRM_CAP_ASYNC_PAGE_FLIP 300 ret = drmGetCap(pNv->dev->fd, DRM_CAP_ASYNC_PAGE_FLIP, &value); 301 if (ret == 0 && value == 1) 302 present->info.capabilities |= PresentCapabilityAsync; 303#endif 304 present->info.check_flip = nouveau_present_flip_check; 305 present->info.flip = nouveau_present_flip_next; 306 present->info.unflip = nouveau_present_flip_stop; 307 } 308 309 return present_screen_init(screen, &present->info); 310} 311#endif 312