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