amdgpu_dri2.c revision 35d5b7c7
1d6c0b56eSmrg/* 2d6c0b56eSmrg * Copyright 2008 Kristian Høgsberg 3d6c0b56eSmrg * Copyright 2008 Jérôme Glisse 4d6c0b56eSmrg * 5d6c0b56eSmrg * All Rights Reserved. 6d6c0b56eSmrg * 7d6c0b56eSmrg * Permission is hereby granted, free of charge, to any person obtaining 8d6c0b56eSmrg * a copy of this software and associated documentation files (the 9d6c0b56eSmrg * "Software"), to deal in the Software without restriction, including 10d6c0b56eSmrg * without limitation on the rights to use, copy, modify, merge, 11d6c0b56eSmrg * publish, distribute, sublicense, and/or sell copies of the Software, 12d6c0b56eSmrg * and to permit persons to whom the Software is furnished to do so, 13d6c0b56eSmrg * subject to the following conditions: 14d6c0b56eSmrg * 15d6c0b56eSmrg * The above copyright notice and this permission notice (including the 16d6c0b56eSmrg * next paragraph) shall be included in all copies or substantial 17d6c0b56eSmrg * portions of the Software. 18d6c0b56eSmrg * 19d6c0b56eSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20d6c0b56eSmrg * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21d6c0b56eSmrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22d6c0b56eSmrg * NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR 23d6c0b56eSmrg * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24d6c0b56eSmrg * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25d6c0b56eSmrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26d6c0b56eSmrg * DEALINGS IN THE SOFTWARE. 27d6c0b56eSmrg */ 28d6c0b56eSmrg#ifdef HAVE_CONFIG_H 29d6c0b56eSmrg#include "config.h" 30d6c0b56eSmrg#endif 31d6c0b56eSmrg 32d6c0b56eSmrg#include "amdgpu_drv.h" 33d6c0b56eSmrg#include "amdgpu_dri2.h" 34d6c0b56eSmrg#include "amdgpu_glamor.h" 35d6c0b56eSmrg#include "amdgpu_video.h" 36d6c0b56eSmrg#include "amdgpu_pixmap.h" 37d6c0b56eSmrg 38d6c0b56eSmrg#ifdef DRI2 39d6c0b56eSmrg 40d6c0b56eSmrg#include <sys/ioctl.h> 41d6c0b56eSmrg#include <sys/types.h> 42d6c0b56eSmrg#include <sys/stat.h> 43d6c0b56eSmrg#include <fcntl.h> 44d6c0b56eSmrg#include <errno.h> 45d6c0b56eSmrg 46d6c0b56eSmrg#include <gbm.h> 47d6c0b56eSmrg 48d6c0b56eSmrg#include "amdgpu_bo_helper.h" 49d6c0b56eSmrg#include "amdgpu_version.h" 50d6c0b56eSmrg 5124b90cf4Smrg#include <list.h> 52d6c0b56eSmrg#include <xf86Priv.h> 5311bf0794Smrg#include <X11/extensions/dpmsconst.h> 54d6c0b56eSmrg 55d6c0b56eSmrg#define FALLBACK_SWAP_DELAY 16 56d6c0b56eSmrg 57d6c0b56eSmrgtypedef DRI2BufferPtr BufferPtr; 58d6c0b56eSmrg 59d6c0b56eSmrgstruct dri2_buffer_priv { 60d6c0b56eSmrg PixmapPtr pixmap; 61d6c0b56eSmrg unsigned int attachment; 62d6c0b56eSmrg unsigned int refcnt; 63d6c0b56eSmrg}; 64d6c0b56eSmrg 65d6c0b56eSmrgstruct dri2_window_priv { 66d6c0b56eSmrg xf86CrtcPtr crtc; 67d6c0b56eSmrg int vblank_delta; 68d6c0b56eSmrg}; 69d6c0b56eSmrg 70d6c0b56eSmrgstatic DevPrivateKeyRec dri2_window_private_key_rec; 71d6c0b56eSmrg#define dri2_window_private_key (&dri2_window_private_key_rec) 72d6c0b56eSmrg 73d6c0b56eSmrg#define get_dri2_window_priv(window) \ 74d6c0b56eSmrg ((struct dri2_window_priv*) \ 75d6c0b56eSmrg dixLookupPrivate(&(window)->devPrivates, dri2_window_private_key)) 76d6c0b56eSmrg 77d6c0b56eSmrg/* Get GEM flink name for a pixmap */ 78d6c0b56eSmrgstatic Bool 79d6c0b56eSmrgamdgpu_get_flink_name(AMDGPUEntPtr pAMDGPUEnt, PixmapPtr pixmap, uint32_t *name) 80d6c0b56eSmrg{ 81d6c0b56eSmrg struct amdgpu_buffer *bo = amdgpu_get_pixmap_bo(pixmap); 82d6c0b56eSmrg struct drm_gem_flink flink; 83d6c0b56eSmrg 84d6c0b56eSmrg if (bo && !(bo->flags & AMDGPU_BO_FLAGS_GBM) && 85d6c0b56eSmrg amdgpu_bo_export(bo->bo.amdgpu, 86d6c0b56eSmrg amdgpu_bo_handle_type_gem_flink_name, 87d6c0b56eSmrg name) == 0) 88d6c0b56eSmrg return TRUE; 89d6c0b56eSmrg 90d6c0b56eSmrg if (!amdgpu_pixmap_get_handle(pixmap, &flink.handle) || 91d6c0b56eSmrg ioctl(pAMDGPUEnt->fd, DRM_IOCTL_GEM_FLINK, &flink) < 0) 92d6c0b56eSmrg return FALSE; 93d6c0b56eSmrg *name = flink.name; 94d6c0b56eSmrg return TRUE; 95d6c0b56eSmrg} 96d6c0b56eSmrg 97d6c0b56eSmrgstatic BufferPtr 98d6c0b56eSmrgamdgpu_dri2_create_buffer2(ScreenPtr pScreen, 99d6c0b56eSmrg DrawablePtr drawable, 100d6c0b56eSmrg unsigned int attachment, unsigned int format) 101d6c0b56eSmrg{ 102d6c0b56eSmrg ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); 103d6c0b56eSmrg AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn); 104d6c0b56eSmrg AMDGPUInfoPtr info = AMDGPUPTR(pScrn); 105d6c0b56eSmrg BufferPtr buffers; 106d6c0b56eSmrg struct dri2_buffer_priv *privates; 107d6c0b56eSmrg PixmapPtr pixmap; 108d6c0b56eSmrg unsigned front_width; 109d6c0b56eSmrg unsigned aligned_width = drawable->width; 110d6c0b56eSmrg unsigned height = drawable->height; 111d6c0b56eSmrg Bool is_glamor_pixmap = FALSE; 112d6c0b56eSmrg int depth; 113d6c0b56eSmrg int cpp; 114d6c0b56eSmrg 115d6c0b56eSmrg if (format) { 116d6c0b56eSmrg depth = format; 117d6c0b56eSmrg 118d6c0b56eSmrg switch (depth) { 119d6c0b56eSmrg case 15: 120d6c0b56eSmrg cpp = 2; 121d6c0b56eSmrg break; 122d6c0b56eSmrg case 24: 12324b90cf4Smrg case 30: 124d6c0b56eSmrg cpp = 4; 125d6c0b56eSmrg break; 126d6c0b56eSmrg default: 127d6c0b56eSmrg cpp = depth / 8; 128d6c0b56eSmrg } 129d6c0b56eSmrg } else { 130d6c0b56eSmrg depth = drawable->depth; 131d6c0b56eSmrg cpp = drawable->bitsPerPixel / 8; 132d6c0b56eSmrg } 133d6c0b56eSmrg 134d6c0b56eSmrg front_width = pScreen->GetScreenPixmap(pScreen)->drawable.width; 135d6c0b56eSmrg 136d6c0b56eSmrg pixmap = NULL; 137d6c0b56eSmrg 138d6c0b56eSmrg if (attachment == DRI2BufferFrontLeft) { 139d6c0b56eSmrg uint32_t handle; 140d6c0b56eSmrg 141d6c0b56eSmrg pixmap = get_drawable_pixmap(drawable); 142d6c0b56eSmrg if (pScreen != pixmap->drawable.pScreen) 143d6c0b56eSmrg pixmap = NULL; 144d6c0b56eSmrg else if (info->use_glamor && !amdgpu_pixmap_get_handle(pixmap, &handle)) { 145d6c0b56eSmrg is_glamor_pixmap = TRUE; 146d6c0b56eSmrg aligned_width = pixmap->drawable.width; 147d6c0b56eSmrg height = pixmap->drawable.height; 148d6c0b56eSmrg pixmap = NULL; 149d6c0b56eSmrg } else 150d6c0b56eSmrg pixmap->refcnt++; 151d6c0b56eSmrg } 152d6c0b56eSmrg 153d6c0b56eSmrg if (!pixmap && (is_glamor_pixmap || attachment != DRI2BufferFrontLeft)) { 154d6c0b56eSmrg if (aligned_width == front_width) 155d6c0b56eSmrg aligned_width = pScrn->virtualX; 156d6c0b56eSmrg 157d6c0b56eSmrg pixmap = (*pScreen->CreatePixmap) (pScreen, 158d6c0b56eSmrg aligned_width, 159d6c0b56eSmrg height, 160d6c0b56eSmrg depth, 161d6c0b56eSmrg AMDGPU_CREATE_PIXMAP_DRI2); 162d6c0b56eSmrg } 163d6c0b56eSmrg 16435d5b7c7Smrg if (!pixmap) 16535d5b7c7Smrg return NULL; 16635d5b7c7Smrg 167d6c0b56eSmrg buffers = calloc(1, sizeof *buffers); 16835d5b7c7Smrg if (!buffers) 169d6c0b56eSmrg goto error; 170d6c0b56eSmrg 17135d5b7c7Smrg if (is_glamor_pixmap) { 17235d5b7c7Smrg pixmap = amdgpu_glamor_set_pixmap_bo(drawable, pixmap); 17335d5b7c7Smrg pixmap->refcnt++; 174d6c0b56eSmrg } 175d6c0b56eSmrg 17635d5b7c7Smrg if (!amdgpu_get_flink_name(pAMDGPUEnt, pixmap, &buffers->name)) 17735d5b7c7Smrg goto error; 17835d5b7c7Smrg 179d6c0b56eSmrg privates = calloc(1, sizeof(struct dri2_buffer_priv)); 18035d5b7c7Smrg if (!privates) 181d6c0b56eSmrg goto error; 182d6c0b56eSmrg 183d6c0b56eSmrg buffers->attachment = attachment; 18435d5b7c7Smrg buffers->pitch = pixmap->devKind; 18535d5b7c7Smrg buffers->cpp = cpp; 186d6c0b56eSmrg buffers->driverPrivate = privates; 187d6c0b56eSmrg buffers->format = format; 188d6c0b56eSmrg buffers->flags = 0; /* not tiled */ 189d6c0b56eSmrg privates->pixmap = pixmap; 190d6c0b56eSmrg privates->attachment = attachment; 191d6c0b56eSmrg privates->refcnt = 1; 192d6c0b56eSmrg 193d6c0b56eSmrg return buffers; 194d6c0b56eSmrg 195d6c0b56eSmrgerror: 196d6c0b56eSmrg free(buffers); 19735d5b7c7Smrg (*pScreen->DestroyPixmap) (pixmap); 198d6c0b56eSmrg return NULL; 199d6c0b56eSmrg} 200d6c0b56eSmrg 201d6c0b56eSmrgstatic void 202d6c0b56eSmrgamdgpu_dri2_destroy_buffer2(ScreenPtr pScreen, 203d6c0b56eSmrg DrawablePtr drawable, BufferPtr buffers) 204d6c0b56eSmrg{ 205d6c0b56eSmrg if (buffers) { 206d6c0b56eSmrg struct dri2_buffer_priv *private = buffers->driverPrivate; 207d6c0b56eSmrg 208d6c0b56eSmrg /* Trying to free an already freed buffer is unlikely to end well */ 209d6c0b56eSmrg if (private->refcnt == 0) { 210d6c0b56eSmrg ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen); 211d6c0b56eSmrg 212d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 213d6c0b56eSmrg "Attempted to destroy previously destroyed buffer.\ 214d6c0b56eSmrg This is a programming error\n"); 215d6c0b56eSmrg return; 216d6c0b56eSmrg } 217d6c0b56eSmrg 218d6c0b56eSmrg private->refcnt--; 219d6c0b56eSmrg if (private->refcnt == 0) { 220d6c0b56eSmrg if (private->pixmap) 221d6c0b56eSmrg (*pScreen->DestroyPixmap) (private->pixmap); 222d6c0b56eSmrg 223d6c0b56eSmrg free(buffers->driverPrivate); 224d6c0b56eSmrg free(buffers); 225d6c0b56eSmrg } 226d6c0b56eSmrg } 227d6c0b56eSmrg} 228d6c0b56eSmrg 229d6c0b56eSmrgstatic inline PixmapPtr GetDrawablePixmap(DrawablePtr drawable) 230d6c0b56eSmrg{ 231d6c0b56eSmrg if (drawable->type == DRAWABLE_PIXMAP) 232d6c0b56eSmrg return (PixmapPtr) drawable; 233d6c0b56eSmrg else { 234d6c0b56eSmrg struct _Window *pWin = (struct _Window *)drawable; 235d6c0b56eSmrg return drawable->pScreen->GetWindowPixmap(pWin); 236d6c0b56eSmrg } 237d6c0b56eSmrg} 238d6c0b56eSmrg 239d6c0b56eSmrgstatic void 240d6c0b56eSmrgamdgpu_dri2_copy_region2(ScreenPtr pScreen, 241d6c0b56eSmrg DrawablePtr drawable, 242d6c0b56eSmrg RegionPtr region, 243d6c0b56eSmrg BufferPtr dest_buffer, BufferPtr src_buffer) 244d6c0b56eSmrg{ 245d6c0b56eSmrg struct dri2_buffer_priv *src_private = src_buffer->driverPrivate; 246d6c0b56eSmrg struct dri2_buffer_priv *dst_private = dest_buffer->driverPrivate; 247d6c0b56eSmrg DrawablePtr src_drawable; 248d6c0b56eSmrg DrawablePtr dst_drawable; 249d6c0b56eSmrg RegionPtr copy_clip; 250d6c0b56eSmrg GCPtr gc; 251d6c0b56eSmrg Bool translate = FALSE; 252d6c0b56eSmrg int off_x = 0, off_y = 0; 253d6c0b56eSmrg 254d6c0b56eSmrg src_drawable = &src_private->pixmap->drawable; 255d6c0b56eSmrg dst_drawable = &dst_private->pixmap->drawable; 256d6c0b56eSmrg 257d6c0b56eSmrg if (src_private->attachment == DRI2BufferFrontLeft) { 258d6c0b56eSmrg if (drawable->pScreen != pScreen) { 259d6c0b56eSmrg src_drawable = DRI2UpdatePrime(drawable, src_buffer); 260d6c0b56eSmrg if (!src_drawable) 261d6c0b56eSmrg return; 262d6c0b56eSmrg } else 263d6c0b56eSmrg src_drawable = drawable; 264d6c0b56eSmrg } 265d6c0b56eSmrg if (dst_private->attachment == DRI2BufferFrontLeft) { 266d6c0b56eSmrg if (drawable->pScreen != pScreen) { 267d6c0b56eSmrg dst_drawable = DRI2UpdatePrime(drawable, dest_buffer); 268d6c0b56eSmrg if (!dst_drawable) 269d6c0b56eSmrg return; 270d6c0b56eSmrg if (dst_drawable != drawable) 271d6c0b56eSmrg translate = TRUE; 272d6c0b56eSmrg } else 273d6c0b56eSmrg dst_drawable = drawable; 274d6c0b56eSmrg } 275d6c0b56eSmrg 276d6c0b56eSmrg if (translate && drawable->type == DRAWABLE_WINDOW) { 277d6c0b56eSmrg PixmapPtr pPix = GetDrawablePixmap(drawable); 278d6c0b56eSmrg 279d6c0b56eSmrg off_x = drawable->x - pPix->screen_x; 280d6c0b56eSmrg off_y = drawable->y - pPix->screen_y; 281d6c0b56eSmrg } 282d6c0b56eSmrg gc = GetScratchGC(dst_drawable->depth, pScreen); 283d6c0b56eSmrg copy_clip = REGION_CREATE(pScreen, NULL, 0); 284d6c0b56eSmrg REGION_COPY(pScreen, copy_clip, region); 285d6c0b56eSmrg 286d6c0b56eSmrg if (translate) { 287d6c0b56eSmrg REGION_TRANSLATE(pScreen, copy_clip, off_x, off_y); 288d6c0b56eSmrg } 289d6c0b56eSmrg 290d6c0b56eSmrg (*gc->funcs->ChangeClip) (gc, CT_REGION, copy_clip, 0); 291d6c0b56eSmrg ValidateGC(dst_drawable, gc); 292d6c0b56eSmrg 293d6c0b56eSmrg (*gc->ops->CopyArea) (src_drawable, dst_drawable, gc, 294d6c0b56eSmrg 0, 0, drawable->width, drawable->height, off_x, 295d6c0b56eSmrg off_y); 296d6c0b56eSmrg 297d6c0b56eSmrg FreeScratchGC(gc); 298d6c0b56eSmrg} 299d6c0b56eSmrg 300d6c0b56eSmrgenum DRI2FrameEventType { 301d6c0b56eSmrg DRI2_SWAP, 302d6c0b56eSmrg DRI2_FLIP, 303d6c0b56eSmrg DRI2_WAITMSC, 304d6c0b56eSmrg}; 305d6c0b56eSmrg 306d6c0b56eSmrgtypedef struct _DRI2FrameEvent { 307d6c0b56eSmrg XID drawable_id; 308d6c0b56eSmrg ClientPtr client; 309d6c0b56eSmrg enum DRI2FrameEventType type; 310d6c0b56eSmrg unsigned frame; 311d6c0b56eSmrg xf86CrtcPtr crtc; 312d6c0b56eSmrg OsTimerPtr timer; 313d6c0b56eSmrg uintptr_t drm_queue_seq; 314d6c0b56eSmrg 315d6c0b56eSmrg /* for swaps & flips only */ 316d6c0b56eSmrg DRI2SwapEventPtr event_complete; 317d6c0b56eSmrg void *event_data; 318d6c0b56eSmrg DRI2BufferPtr front; 319d6c0b56eSmrg DRI2BufferPtr back; 320d6c0b56eSmrg} DRI2FrameEventRec, *DRI2FrameEventPtr; 321d6c0b56eSmrg 322d6c0b56eSmrgstatic int DRI2InfoCnt; 323d6c0b56eSmrg 324d6c0b56eSmrgstatic void amdgpu_dri2_ref_buffer(BufferPtr buffer) 325d6c0b56eSmrg{ 326d6c0b56eSmrg struct dri2_buffer_priv *private = buffer->driverPrivate; 327d6c0b56eSmrg private->refcnt++; 328d6c0b56eSmrg} 329d6c0b56eSmrg 330d6c0b56eSmrgstatic void amdgpu_dri2_unref_buffer(BufferPtr buffer) 331d6c0b56eSmrg{ 332d6c0b56eSmrg if (buffer) { 333d6c0b56eSmrg struct dri2_buffer_priv *private = buffer->driverPrivate; 33424b90cf4Smrg DrawablePtr draw = &private->pixmap->drawable; 33524b90cf4Smrg 33624b90cf4Smrg amdgpu_dri2_destroy_buffer2(draw->pScreen, draw, buffer); 337d6c0b56eSmrg } 338d6c0b56eSmrg} 339d6c0b56eSmrg 340d6c0b56eSmrgstatic void 341d6c0b56eSmrgamdgpu_dri2_client_state_changed(CallbackListPtr * ClientStateCallback, 342d6c0b56eSmrg pointer data, pointer calldata) 343d6c0b56eSmrg{ 344d6c0b56eSmrg NewClientInfoRec *clientinfo = calldata; 345d6c0b56eSmrg ClientPtr pClient = clientinfo->client; 346d6c0b56eSmrg 347d6c0b56eSmrg switch (pClient->clientState) { 348d6c0b56eSmrg case ClientStateRetained: 349d6c0b56eSmrg case ClientStateGone: 350d6c0b56eSmrg amdgpu_drm_abort_client(pClient); 351d6c0b56eSmrg break; 352d6c0b56eSmrg default: 353d6c0b56eSmrg break; 354d6c0b56eSmrg } 355d6c0b56eSmrg} 356d6c0b56eSmrg 357d6c0b56eSmrg/* 358d6c0b56eSmrg * Get current frame count delta for the specified drawable and CRTC 359d6c0b56eSmrg */ 360d6c0b56eSmrgstatic uint32_t amdgpu_get_msc_delta(DrawablePtr pDraw, xf86CrtcPtr crtc) 361d6c0b56eSmrg{ 362d6c0b56eSmrg drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; 363d6c0b56eSmrg 364d6c0b56eSmrg if (pDraw && pDraw->type == DRAWABLE_WINDOW) 365d6c0b56eSmrg return drmmode_crtc->interpolated_vblanks + 366d6c0b56eSmrg get_dri2_window_priv((WindowPtr)pDraw)->vblank_delta; 367d6c0b56eSmrg 368d6c0b56eSmrg return drmmode_crtc->interpolated_vblanks; 369d6c0b56eSmrg} 370d6c0b56eSmrg 371d6c0b56eSmrg/* 372d6c0b56eSmrg * Get current frame count and timestamp of the specified CRTC 373d6c0b56eSmrg */ 374d6c0b56eSmrgstatic Bool amdgpu_dri2_get_crtc_msc(xf86CrtcPtr crtc, CARD64 *ust, CARD64 *msc) 375d6c0b56eSmrg{ 376504d986fSmrg drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; 377504d986fSmrg 378d6c0b56eSmrg if (!amdgpu_crtc_is_enabled(crtc) || 379d6c0b56eSmrg drmmode_crtc_get_ust_msc(crtc, ust, msc) != Success) { 380d6c0b56eSmrg /* CRTC is not running, extrapolate MSC and timestamp */ 381d6c0b56eSmrg ScrnInfoPtr scrn = crtc->scrn; 382d6c0b56eSmrg AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn); 383d6c0b56eSmrg CARD64 now, delta_t, delta_seq; 384d6c0b56eSmrg 385d6c0b56eSmrg if (!drmmode_crtc->dpms_last_ust) 386d6c0b56eSmrg return FALSE; 387d6c0b56eSmrg 388d6c0b56eSmrg if (drmmode_get_current_ust(pAMDGPUEnt->fd, &now) != 0) { 389d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_ERROR, 390d6c0b56eSmrg "%s cannot get current time\n", __func__); 391d6c0b56eSmrg return FALSE; 392d6c0b56eSmrg } 393d6c0b56eSmrg 394d6c0b56eSmrg delta_t = now - drmmode_crtc->dpms_last_ust; 395d6c0b56eSmrg delta_seq = delta_t * drmmode_crtc->dpms_last_fps; 396d6c0b56eSmrg delta_seq /= 1000000; 397d6c0b56eSmrg *ust = drmmode_crtc->dpms_last_ust; 398d6c0b56eSmrg delta_t = delta_seq * 1000000; 399d6c0b56eSmrg delta_t /= drmmode_crtc->dpms_last_fps; 400d6c0b56eSmrg *ust += delta_t; 401d6c0b56eSmrg *msc = drmmode_crtc->dpms_last_seq; 402d6c0b56eSmrg *msc += delta_seq; 403d6c0b56eSmrg } 404d6c0b56eSmrg 405504d986fSmrg *msc += drmmode_crtc->interpolated_vblanks; 406504d986fSmrg 407d6c0b56eSmrg return TRUE; 408d6c0b56eSmrg} 409d6c0b56eSmrg 410d6c0b56eSmrgstatic 411d6c0b56eSmrgxf86CrtcPtr amdgpu_dri2_drawable_crtc(DrawablePtr pDraw, Bool consider_disabled) 412d6c0b56eSmrg{ 413d6c0b56eSmrg ScreenPtr pScreen = pDraw->pScreen; 414d6c0b56eSmrg ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); 415d6c0b56eSmrg xf86CrtcPtr crtc = amdgpu_pick_best_crtc(pScrn, consider_disabled, 416d6c0b56eSmrg pDraw->x, pDraw->x + pDraw->width, 417d6c0b56eSmrg pDraw->y, pDraw->y + pDraw->height); 418d6c0b56eSmrg 419d6c0b56eSmrg if (crtc && pDraw->type == DRAWABLE_WINDOW) { 420d6c0b56eSmrg struct dri2_window_priv *priv = get_dri2_window_priv((WindowPtr)pDraw); 421d6c0b56eSmrg 422d6c0b56eSmrg if (priv->crtc && priv->crtc != crtc) { 423d6c0b56eSmrg CARD64 ust, mscold, mscnew; 424d6c0b56eSmrg 425d6c0b56eSmrg if (amdgpu_dri2_get_crtc_msc(priv->crtc, &ust, &mscold) && 426d6c0b56eSmrg amdgpu_dri2_get_crtc_msc(crtc, &ust, &mscnew)) 427d6c0b56eSmrg priv->vblank_delta += mscold - mscnew; 428d6c0b56eSmrg } 429d6c0b56eSmrg 430d6c0b56eSmrg priv->crtc = crtc; 431d6c0b56eSmrg } 432d6c0b56eSmrg 433d6c0b56eSmrg return crtc; 434d6c0b56eSmrg} 435d6c0b56eSmrg 436d6c0b56eSmrgstatic void 437d6c0b56eSmrgamdgpu_dri2_flip_event_abort(xf86CrtcPtr crtc, void *event_data) 438d6c0b56eSmrg{ 43911bf0794Smrg if (crtc) 44011bf0794Smrg AMDGPUPTR(crtc->scrn)->drmmode.dri2_flipping = FALSE; 441d6c0b56eSmrg 442d6c0b56eSmrg free(event_data); 443d6c0b56eSmrg} 444d6c0b56eSmrg 445d6c0b56eSmrgstatic void 446d6c0b56eSmrgamdgpu_dri2_flip_event_handler(xf86CrtcPtr crtc, uint32_t frame, uint64_t usec, 447d6c0b56eSmrg void *event_data) 448d6c0b56eSmrg{ 449d6c0b56eSmrg DRI2FrameEventPtr flip = event_data; 450d6c0b56eSmrg ScrnInfoPtr scrn = crtc->scrn; 451d6c0b56eSmrg unsigned tv_sec, tv_usec; 452d6c0b56eSmrg DrawablePtr drawable; 453d6c0b56eSmrg ScreenPtr screen; 454d6c0b56eSmrg int status; 455d6c0b56eSmrg PixmapPtr pixmap; 456d6c0b56eSmrg 457d6c0b56eSmrg status = dixLookupDrawable(&drawable, flip->drawable_id, serverClient, 458d6c0b56eSmrg M_ANY, DixWriteAccess); 459d6c0b56eSmrg if (status != Success) 460d6c0b56eSmrg goto abort; 461d6c0b56eSmrg 462d6c0b56eSmrg frame += amdgpu_get_msc_delta(drawable, crtc); 463d6c0b56eSmrg 464d6c0b56eSmrg screen = scrn->pScreen; 465d6c0b56eSmrg pixmap = screen->GetScreenPixmap(screen); 466d6c0b56eSmrg xf86DrvMsgVerb(scrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG, 467d6c0b56eSmrg "%s:%d fevent[%p] width %d pitch %d (/4 %d)\n", 468d6c0b56eSmrg __func__, __LINE__, flip, pixmap->drawable.width, 469d6c0b56eSmrg pixmap->devKind, pixmap->devKind / 4); 470d6c0b56eSmrg 471d6c0b56eSmrg tv_sec = usec / 1000000; 472d6c0b56eSmrg tv_usec = usec % 1000000; 473d6c0b56eSmrg 474d6c0b56eSmrg /* We assume our flips arrive in order, so we don't check the frame */ 475d6c0b56eSmrg switch (flip->type) { 476d6c0b56eSmrg case DRI2_SWAP: 477d6c0b56eSmrg /* Check for too small vblank count of pageflip completion, taking wraparound 478d6c0b56eSmrg * into account. This usually means some defective kms pageflip completion, 479d6c0b56eSmrg * causing wrong (msc, ust) return values and possible visual corruption. 480d6c0b56eSmrg */ 481d6c0b56eSmrg if ((frame < flip->frame) && (flip->frame - frame < 5)) { 482d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 483d6c0b56eSmrg "%s: Pageflip completion event has impossible msc %u < target_msc %u\n", 484d6c0b56eSmrg __func__, frame, flip->frame); 485d6c0b56eSmrg /* All-Zero values signal failure of (msc, ust) timestamping to client. */ 486d6c0b56eSmrg frame = tv_sec = tv_usec = 0; 487d6c0b56eSmrg } 488d6c0b56eSmrg 489d6c0b56eSmrg DRI2SwapComplete(flip->client, drawable, frame, tv_sec, tv_usec, 490d6c0b56eSmrg DRI2_FLIP_COMPLETE, flip->event_complete, 491d6c0b56eSmrg flip->event_data); 492d6c0b56eSmrg break; 493d6c0b56eSmrg default: 494d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 495d6c0b56eSmrg "%s: unknown vblank event received\n", __func__); 496d6c0b56eSmrg /* Unknown type */ 497d6c0b56eSmrg break; 498d6c0b56eSmrg } 499d6c0b56eSmrg 500d6c0b56eSmrgabort: 501d6c0b56eSmrg amdgpu_dri2_flip_event_abort(crtc, event_data); 502d6c0b56eSmrg} 503d6c0b56eSmrg 504d6c0b56eSmrgstatic Bool 505d6c0b56eSmrgamdgpu_dri2_schedule_flip(xf86CrtcPtr crtc, ClientPtr client, 506d6c0b56eSmrg DrawablePtr draw, DRI2BufferPtr front, 507d6c0b56eSmrg DRI2BufferPtr back, DRI2SwapEventPtr func, 508d6c0b56eSmrg void *data, unsigned int target_msc) 509d6c0b56eSmrg{ 510d6c0b56eSmrg ScrnInfoPtr scrn = crtc->scrn; 511d6c0b56eSmrg AMDGPUInfoPtr info = AMDGPUPTR(scrn); 512d6c0b56eSmrg struct dri2_buffer_priv *back_priv; 513d6c0b56eSmrg DRI2FrameEventPtr flip_info; 514d6c0b56eSmrg 515d6c0b56eSmrg flip_info = calloc(1, sizeof(DRI2FrameEventRec)); 516d6c0b56eSmrg if (!flip_info) 517d6c0b56eSmrg return FALSE; 518d6c0b56eSmrg 519d6c0b56eSmrg flip_info->drawable_id = draw->id; 520d6c0b56eSmrg flip_info->client = client; 521d6c0b56eSmrg flip_info->type = DRI2_SWAP; 522d6c0b56eSmrg flip_info->event_complete = func; 523d6c0b56eSmrg flip_info->event_data = data; 524d6c0b56eSmrg flip_info->frame = target_msc; 525d6c0b56eSmrg flip_info->crtc = crtc; 526d6c0b56eSmrg 527d6c0b56eSmrg xf86DrvMsgVerb(scrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG, 528d6c0b56eSmrg "%s:%d fevent[%p]\n", __func__, __LINE__, flip_info); 529d6c0b56eSmrg 530d6c0b56eSmrg /* Page flip the full screen buffer */ 531d6c0b56eSmrg back_priv = back->driverPrivate; 532d6c0b56eSmrg if (amdgpu_do_pageflip(scrn, client, back_priv->pixmap, 53324b90cf4Smrg AMDGPU_DRM_QUEUE_ID_DEFAULT, flip_info, crtc, 534d6c0b56eSmrg amdgpu_dri2_flip_event_handler, 53511bf0794Smrg amdgpu_dri2_flip_event_abort, FLIP_VSYNC, 53611bf0794Smrg target_msc - amdgpu_get_msc_delta(draw, crtc))) { 537d6c0b56eSmrg info->drmmode.dri2_flipping = TRUE; 538d6c0b56eSmrg return TRUE; 539d6c0b56eSmrg } 540d6c0b56eSmrg return FALSE; 541d6c0b56eSmrg} 542d6c0b56eSmrg 543d6c0b56eSmrgstatic Bool update_front(DrawablePtr draw, DRI2BufferPtr front) 544d6c0b56eSmrg{ 545d6c0b56eSmrg ScreenPtr screen = draw->pScreen; 546d6c0b56eSmrg ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 547d6c0b56eSmrg AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn); 548d6c0b56eSmrg PixmapPtr pixmap = get_drawable_pixmap(draw); 549d6c0b56eSmrg struct dri2_buffer_priv *priv = front->driverPrivate; 550d6c0b56eSmrg 551d6c0b56eSmrg if (!amdgpu_get_flink_name(pAMDGPUEnt, pixmap, &front->name)) 552d6c0b56eSmrg return FALSE; 553d6c0b56eSmrg 554d6c0b56eSmrg (*draw->pScreen->DestroyPixmap) (priv->pixmap); 555d6c0b56eSmrg front->pitch = pixmap->devKind; 556d6c0b56eSmrg front->cpp = pixmap->drawable.bitsPerPixel / 8; 557d6c0b56eSmrg priv->pixmap = pixmap; 558d6c0b56eSmrg pixmap->refcnt++; 559d6c0b56eSmrg 560d6c0b56eSmrg return TRUE; 561d6c0b56eSmrg} 562d6c0b56eSmrg 563d6c0b56eSmrgstatic Bool 564d6c0b56eSmrgcan_exchange(ScrnInfoPtr pScrn, DrawablePtr draw, 565d6c0b56eSmrg DRI2BufferPtr front, DRI2BufferPtr back) 566d6c0b56eSmrg{ 567d6c0b56eSmrg struct dri2_buffer_priv *front_priv = front->driverPrivate; 568d6c0b56eSmrg struct dri2_buffer_priv *back_priv = back->driverPrivate; 569d6c0b56eSmrg PixmapPtr front_pixmap; 570d6c0b56eSmrg PixmapPtr back_pixmap = back_priv->pixmap; 571d6c0b56eSmrg 572d6c0b56eSmrg if (!update_front(draw, front)) 573d6c0b56eSmrg return FALSE; 574d6c0b56eSmrg 575d6c0b56eSmrg front_pixmap = front_priv->pixmap; 576d6c0b56eSmrg 577d6c0b56eSmrg if (front_pixmap->drawable.width != back_pixmap->drawable.width) 578d6c0b56eSmrg return FALSE; 579d6c0b56eSmrg 580d6c0b56eSmrg if (front_pixmap->drawable.height != back_pixmap->drawable.height) 581d6c0b56eSmrg return FALSE; 582d6c0b56eSmrg 583d6c0b56eSmrg if (front_pixmap->drawable.bitsPerPixel != 584d6c0b56eSmrg back_pixmap->drawable.bitsPerPixel) 585d6c0b56eSmrg return FALSE; 586d6c0b56eSmrg 587d6c0b56eSmrg if (front_pixmap->devKind != back_pixmap->devKind) 588d6c0b56eSmrg return FALSE; 589d6c0b56eSmrg 590d6c0b56eSmrg return TRUE; 591d6c0b56eSmrg} 592d6c0b56eSmrg 593d6c0b56eSmrgstatic Bool 59424b90cf4Smrgcan_flip(xf86CrtcPtr crtc, DrawablePtr draw, 595d6c0b56eSmrg DRI2BufferPtr front, DRI2BufferPtr back) 596d6c0b56eSmrg{ 59724b90cf4Smrg ScrnInfoPtr pScrn = crtc->scrn; 598d6c0b56eSmrg AMDGPUInfoPtr info = AMDGPUPTR(pScrn); 59911bf0794Smrg xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn); 60011bf0794Smrg int num_crtcs_on; 60111bf0794Smrg int i; 60211bf0794Smrg 60311bf0794Smrg if (draw->type != DRAWABLE_WINDOW || 60411bf0794Smrg !info->allowPageFlip || 60524b90cf4Smrg info->sprites_visible > 0 || 60611bf0794Smrg info->drmmode.present_flipping || 60711bf0794Smrg !pScrn->vtSema || 60811bf0794Smrg !DRI2CanFlip(draw)) 60911bf0794Smrg return FALSE; 61011bf0794Smrg 61111bf0794Smrg for (i = 0, num_crtcs_on = 0; i < config->num_crtc; i++) { 61224b90cf4Smrg if (drmmode_crtc_can_flip(config->crtc[i])) 61311bf0794Smrg num_crtcs_on++; 61411bf0794Smrg } 61511bf0794Smrg 61611bf0794Smrg return num_crtcs_on > 0 && can_exchange(pScrn, draw, front, back); 617d6c0b56eSmrg} 618d6c0b56eSmrg 619d6c0b56eSmrgstatic void 620d6c0b56eSmrgamdgpu_dri2_exchange_buffers(DrawablePtr draw, DRI2BufferPtr front, 621d6c0b56eSmrg DRI2BufferPtr back) 622d6c0b56eSmrg{ 623d6c0b56eSmrg struct dri2_buffer_priv *front_priv = front->driverPrivate; 624d6c0b56eSmrg struct dri2_buffer_priv *back_priv = back->driverPrivate; 625d6c0b56eSmrg struct amdgpu_pixmap *front_pix; 626d6c0b56eSmrg struct amdgpu_pixmap *back_pix; 627d6c0b56eSmrg ScreenPtr screen; 628d6c0b56eSmrg AMDGPUInfoPtr info; 629d6c0b56eSmrg RegionRec region; 630d6c0b56eSmrg int tmp; 631d6c0b56eSmrg 632d6c0b56eSmrg region.extents.x1 = region.extents.y1 = 0; 633d6c0b56eSmrg region.extents.x2 = front_priv->pixmap->drawable.width; 634504d986fSmrg region.extents.y2 = front_priv->pixmap->drawable.height; 635d6c0b56eSmrg region.data = NULL; 636d6c0b56eSmrg DamageRegionAppend(&front_priv->pixmap->drawable, ®ion); 637d6c0b56eSmrg 638d6c0b56eSmrg /* Swap BO names so DRI works */ 639d6c0b56eSmrg tmp = front->name; 640d6c0b56eSmrg front->name = back->name; 641d6c0b56eSmrg back->name = tmp; 642d6c0b56eSmrg 643d6c0b56eSmrg /* Swap pixmap privates */ 644d6c0b56eSmrg front_pix = amdgpu_get_pixmap_private(front_priv->pixmap); 645d6c0b56eSmrg back_pix = amdgpu_get_pixmap_private(back_priv->pixmap); 646d6c0b56eSmrg amdgpu_set_pixmap_private(front_priv->pixmap, back_pix); 647d6c0b56eSmrg amdgpu_set_pixmap_private(back_priv->pixmap, front_pix); 648d6c0b56eSmrg 649d6c0b56eSmrg /* Do we need to update the Screen? */ 650d6c0b56eSmrg screen = draw->pScreen; 651d6c0b56eSmrg info = AMDGPUPTR(xf86ScreenToScrn(screen)); 652d6c0b56eSmrg if (front_pix->bo == info->front_buffer) { 653d6c0b56eSmrg struct amdgpu_pixmap *screen_priv = 654d6c0b56eSmrg amdgpu_get_pixmap_private(screen->GetScreenPixmap(screen)); 655d6c0b56eSmrg 656d6c0b56eSmrg amdgpu_bo_ref(back_pix->bo); 657d6c0b56eSmrg amdgpu_bo_unref(&info->front_buffer); 658d6c0b56eSmrg info->front_buffer = back_pix->bo; 659d6c0b56eSmrg *screen_priv = *back_pix; 660d6c0b56eSmrg } 661d6c0b56eSmrg 662d6c0b56eSmrg amdgpu_glamor_exchange_buffers(front_priv->pixmap, back_priv->pixmap); 663d6c0b56eSmrg 664d6c0b56eSmrg DamageRegionProcessPending(&front_priv->pixmap->drawable); 665d6c0b56eSmrg} 666d6c0b56eSmrg 667d6c0b56eSmrgstatic void amdgpu_dri2_frame_event_abort(xf86CrtcPtr crtc, void *event_data) 668d6c0b56eSmrg{ 669d6c0b56eSmrg DRI2FrameEventPtr event = event_data; 670d6c0b56eSmrg 671d6c0b56eSmrg TimerCancel(event->timer); 672d6c0b56eSmrg TimerFree(event->timer); 673d6c0b56eSmrg amdgpu_dri2_unref_buffer(event->front); 674d6c0b56eSmrg amdgpu_dri2_unref_buffer(event->back); 675d6c0b56eSmrg free(event); 676d6c0b56eSmrg} 677d6c0b56eSmrg 678d6c0b56eSmrgstatic void amdgpu_dri2_frame_event_handler(xf86CrtcPtr crtc, uint32_t seq, 679d6c0b56eSmrg uint64_t usec, void *event_data) 680d6c0b56eSmrg{ 681d6c0b56eSmrg DRI2FrameEventPtr event = event_data; 682d6c0b56eSmrg ScrnInfoPtr scrn = crtc->scrn; 683d6c0b56eSmrg DrawablePtr drawable; 684d6c0b56eSmrg int status; 685d6c0b56eSmrg int swap_type; 686d6c0b56eSmrg BoxRec box; 687d6c0b56eSmrg RegionRec region; 688d6c0b56eSmrg 689d6c0b56eSmrg status = dixLookupDrawable(&drawable, event->drawable_id, serverClient, 690d6c0b56eSmrg M_ANY, DixWriteAccess); 691d6c0b56eSmrg if (status != Success) 692d6c0b56eSmrg goto cleanup; 693d6c0b56eSmrg 694d6c0b56eSmrg seq += amdgpu_get_msc_delta(drawable, crtc); 695d6c0b56eSmrg 696d6c0b56eSmrg switch (event->type) { 697d6c0b56eSmrg case DRI2_FLIP: 69824b90cf4Smrg if (can_flip(crtc, drawable, event->front, event->back) && 699d6c0b56eSmrg amdgpu_dri2_schedule_flip(crtc, 700d6c0b56eSmrg event->client, 701d6c0b56eSmrg drawable, 702d6c0b56eSmrg event->front, 703d6c0b56eSmrg event->back, 704d6c0b56eSmrg event->event_complete, 705d6c0b56eSmrg event->event_data, 706d6c0b56eSmrg event->frame)) { 707d6c0b56eSmrg amdgpu_dri2_exchange_buffers(drawable, event->front, 708d6c0b56eSmrg event->back); 709d6c0b56eSmrg break; 710d6c0b56eSmrg } 711d6c0b56eSmrg /* else fall through to exchange/blit */ 712d6c0b56eSmrg case DRI2_SWAP: 713d6c0b56eSmrg if (DRI2CanExchange(drawable) && 714d6c0b56eSmrg can_exchange(scrn, drawable, event->front, event->back)) { 715d6c0b56eSmrg amdgpu_dri2_exchange_buffers(drawable, event->front, 716d6c0b56eSmrg event->back); 717d6c0b56eSmrg swap_type = DRI2_EXCHANGE_COMPLETE; 718d6c0b56eSmrg } else { 719d6c0b56eSmrg box.x1 = 0; 720d6c0b56eSmrg box.y1 = 0; 721d6c0b56eSmrg box.x2 = drawable->width; 722d6c0b56eSmrg box.y2 = drawable->height; 723d6c0b56eSmrg REGION_INIT(pScreen, ®ion, &box, 0); 72424b90cf4Smrg amdgpu_dri2_copy_region2(drawable->pScreen, drawable, ®ion, 72524b90cf4Smrg event->front, event->back); 726d6c0b56eSmrg swap_type = DRI2_BLIT_COMPLETE; 727d6c0b56eSmrg } 728d6c0b56eSmrg 729d6c0b56eSmrg DRI2SwapComplete(event->client, drawable, seq, usec / 1000000, 730d6c0b56eSmrg usec % 1000000, swap_type, event->event_complete, 731d6c0b56eSmrg event->event_data); 732d6c0b56eSmrg 733d6c0b56eSmrg break; 734d6c0b56eSmrg case DRI2_WAITMSC: 735d6c0b56eSmrg DRI2WaitMSCComplete(event->client, drawable, seq, usec / 1000000, 736d6c0b56eSmrg usec % 1000000); 737d6c0b56eSmrg break; 738d6c0b56eSmrg default: 739d6c0b56eSmrg /* Unknown type */ 740d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 741d6c0b56eSmrg "%s: unknown vblank event received\n", __func__); 742d6c0b56eSmrg break; 743d6c0b56eSmrg } 744d6c0b56eSmrg 745d6c0b56eSmrgcleanup: 746d6c0b56eSmrg amdgpu_dri2_frame_event_abort(crtc, event_data); 747d6c0b56eSmrg} 748d6c0b56eSmrg 749d6c0b56eSmrg/* 750d6c0b56eSmrg * This function should be called on a disabled CRTC only (i.e., CRTC 751d6c0b56eSmrg * in DPMS-off state). It will calculate the delay necessary to reach 752d6c0b56eSmrg * target_msc from present time if the CRTC were running. 753d6c0b56eSmrg */ 754d6c0b56eSmrgstatic 755d6c0b56eSmrgCARD32 amdgpu_dri2_extrapolate_msc_delay(xf86CrtcPtr crtc, CARD64 * target_msc, 756d6c0b56eSmrg CARD64 divisor, CARD64 remainder) 757d6c0b56eSmrg{ 758d6c0b56eSmrg drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; 759d6c0b56eSmrg ScrnInfoPtr pScrn = crtc->scrn; 760d6c0b56eSmrg AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn); 761d6c0b56eSmrg int nominal_frame_rate = drmmode_crtc->dpms_last_fps; 762d6c0b56eSmrg CARD64 last_vblank_ust = drmmode_crtc->dpms_last_ust; 763d6c0b56eSmrg uint32_t last_vblank_seq = drmmode_crtc->dpms_last_seq; 764d6c0b56eSmrg CARD64 now, target_time, delta_t; 765d6c0b56eSmrg int64_t d, delta_seq; 766d6c0b56eSmrg int ret; 767d6c0b56eSmrg CARD32 d_ms; 768d6c0b56eSmrg 769d6c0b56eSmrg if (!last_vblank_ust) { 770d6c0b56eSmrg *target_msc = 0; 771d6c0b56eSmrg return FALLBACK_SWAP_DELAY; 772d6c0b56eSmrg } 773d6c0b56eSmrg ret = drmmode_get_current_ust(pAMDGPUEnt->fd, &now); 774d6c0b56eSmrg if (ret) { 775d6c0b56eSmrg xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 776d6c0b56eSmrg "%s cannot get current time\n", __func__); 777d6c0b56eSmrg *target_msc = 0; 778d6c0b56eSmrg return FALLBACK_SWAP_DELAY; 779d6c0b56eSmrg } 780d6c0b56eSmrg delta_seq = *target_msc - last_vblank_seq; 781d6c0b56eSmrg delta_seq *= 1000000; 782d6c0b56eSmrg target_time = last_vblank_ust; 783d6c0b56eSmrg target_time += delta_seq / nominal_frame_rate; 784d6c0b56eSmrg d = target_time - now; 785d6c0b56eSmrg if (d < 0) { 786d6c0b56eSmrg /* we missed the event, adjust target_msc, do the divisor magic */ 787d6c0b56eSmrg CARD64 current_msc = last_vblank_seq; 788d6c0b56eSmrg 789d6c0b56eSmrg delta_t = now - last_vblank_ust; 790d6c0b56eSmrg delta_seq = delta_t * nominal_frame_rate; 791d6c0b56eSmrg current_msc += delta_seq / 1000000; 792d6c0b56eSmrg current_msc &= 0xffffffff; 793d6c0b56eSmrg if (divisor == 0) { 794d6c0b56eSmrg *target_msc = current_msc; 795d6c0b56eSmrg d = 0; 796d6c0b56eSmrg } else { 797d6c0b56eSmrg *target_msc = 798d6c0b56eSmrg current_msc - (current_msc % divisor) + remainder; 799d6c0b56eSmrg if ((current_msc % divisor) >= remainder) 800d6c0b56eSmrg *target_msc += divisor; 801d6c0b56eSmrg *target_msc &= 0xffffffff; 802d6c0b56eSmrg delta_seq = *target_msc - last_vblank_seq; 803d6c0b56eSmrg delta_seq *= 1000000; 804d6c0b56eSmrg target_time = last_vblank_ust; 805d6c0b56eSmrg target_time += delta_seq / nominal_frame_rate; 806d6c0b56eSmrg d = target_time - now; 807d6c0b56eSmrg } 808d6c0b56eSmrg } 809d6c0b56eSmrg /* 810d6c0b56eSmrg * convert delay to milliseconds and add margin to prevent the client 811d6c0b56eSmrg * from coming back early (due to timer granularity and rounding 812d6c0b56eSmrg * errors) and getting the same MSC it just got 813d6c0b56eSmrg */ 814d6c0b56eSmrg d_ms = (CARD32) d / 1000; 815d6c0b56eSmrg if ((CARD32) d - d_ms * 1000 > 0) 816d6c0b56eSmrg d_ms += 2; 817d6c0b56eSmrg else 818d6c0b56eSmrg d_ms++; 819d6c0b56eSmrg return d_ms; 820d6c0b56eSmrg} 821d6c0b56eSmrg 822d6c0b56eSmrg/* 823d6c0b56eSmrg * Get current interpolated frame count and frame count timestamp, based on 824d6c0b56eSmrg * drawable's crtc. 825d6c0b56eSmrg */ 826d6c0b56eSmrgstatic int amdgpu_dri2_get_msc(DrawablePtr draw, CARD64 * ust, CARD64 * msc) 827d6c0b56eSmrg{ 828d6c0b56eSmrg xf86CrtcPtr crtc = amdgpu_dri2_drawable_crtc(draw, TRUE); 829d6c0b56eSmrg 830d6c0b56eSmrg /* Drawable not displayed, make up a value */ 83135d5b7c7Smrg if (!crtc) { 832d6c0b56eSmrg *ust = 0; 833d6c0b56eSmrg *msc = 0; 834d6c0b56eSmrg return TRUE; 835d6c0b56eSmrg } 836d6c0b56eSmrg 837d6c0b56eSmrg if (!amdgpu_dri2_get_crtc_msc(crtc, ust, msc)) 838d6c0b56eSmrg return FALSE; 839d6c0b56eSmrg 840504d986fSmrg if (draw && draw->type == DRAWABLE_WINDOW) 841504d986fSmrg *msc += get_dri2_window_priv((WindowPtr)draw)->vblank_delta; 842d6c0b56eSmrg *msc &= 0xffffffff; 843d6c0b56eSmrg return TRUE; 844d6c0b56eSmrg} 845d6c0b56eSmrg 846d6c0b56eSmrgstatic 847d6c0b56eSmrgCARD32 amdgpu_dri2_deferred_event(OsTimerPtr timer, CARD32 now, pointer data) 848d6c0b56eSmrg{ 849d6c0b56eSmrg DRI2FrameEventPtr event_info = (DRI2FrameEventPtr) data; 850d6c0b56eSmrg xf86CrtcPtr crtc = event_info->crtc; 851d6c0b56eSmrg ScrnInfoPtr scrn; 852d6c0b56eSmrg AMDGPUEntPtr pAMDGPUEnt; 853d6c0b56eSmrg CARD64 drm_now; 854d6c0b56eSmrg int ret; 855d6c0b56eSmrg CARD64 delta_t, delta_seq, frame; 856d6c0b56eSmrg drmmode_crtc_private_ptr drmmode_crtc; 857d6c0b56eSmrg 858d6c0b56eSmrg /* 859d6c0b56eSmrg * This is emulated event, so its time is current time, which we 860d6c0b56eSmrg * have to get in DRM-compatible form (which is a bit messy given 861d6c0b56eSmrg * the information that we have at this point). Can't use now argument 862d6c0b56eSmrg * because DRM event time may come from monotonic clock, while 863d6c0b56eSmrg * DIX timer facility uses real-time clock. 864d6c0b56eSmrg */ 865d6c0b56eSmrg if (!event_info->crtc) { 866d6c0b56eSmrg ErrorF("%s no crtc\n", __func__); 867d6c0b56eSmrg if (event_info->drm_queue_seq) 868d6c0b56eSmrg amdgpu_drm_abort_entry(event_info->drm_queue_seq); 869d6c0b56eSmrg else 870d6c0b56eSmrg amdgpu_dri2_frame_event_abort(NULL, data); 871d6c0b56eSmrg return 0; 872d6c0b56eSmrg } 873d6c0b56eSmrg 874d6c0b56eSmrg scrn = crtc->scrn; 875d6c0b56eSmrg pAMDGPUEnt = AMDGPUEntPriv(scrn); 87635d5b7c7Smrg drmmode_crtc = event_info->crtc->driver_private; 877d6c0b56eSmrg ret = drmmode_get_current_ust(pAMDGPUEnt->fd, &drm_now); 878d6c0b56eSmrg if (ret) { 879d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_ERROR, 880d6c0b56eSmrg "%s cannot get current time\n", __func__); 881d6c0b56eSmrg if (event_info->drm_queue_seq) 88235d5b7c7Smrg drmmode_crtc->drmmode->event_context. 88335d5b7c7Smrg vblank_handler(pAMDGPUEnt->fd, 0, 0, 0, 88435d5b7c7Smrg (void*)event_info->drm_queue_seq); 885d6c0b56eSmrg else 886d6c0b56eSmrg amdgpu_dri2_frame_event_handler(crtc, 0, 0, data); 887d6c0b56eSmrg return 0; 888d6c0b56eSmrg } 889d6c0b56eSmrg /* 890d6c0b56eSmrg * calculate the frame number from current time 891d6c0b56eSmrg * that would come from CRTC if it were running 892d6c0b56eSmrg */ 893d6c0b56eSmrg delta_t = drm_now - (CARD64) drmmode_crtc->dpms_last_ust; 894d6c0b56eSmrg delta_seq = delta_t * drmmode_crtc->dpms_last_fps; 895d6c0b56eSmrg delta_seq /= 1000000; 896d6c0b56eSmrg frame = (CARD64) drmmode_crtc->dpms_last_seq + delta_seq; 897d6c0b56eSmrg if (event_info->drm_queue_seq) 89835d5b7c7Smrg drmmode_crtc->drmmode->event_context. 89935d5b7c7Smrg vblank_handler(pAMDGPUEnt->fd, frame, drm_now / 1000000, 90035d5b7c7Smrg drm_now % 1000000, 90135d5b7c7Smrg (void*)event_info->drm_queue_seq); 902d6c0b56eSmrg else 903d6c0b56eSmrg amdgpu_dri2_frame_event_handler(crtc, frame, drm_now, data); 904d6c0b56eSmrg return 0; 905d6c0b56eSmrg} 906d6c0b56eSmrg 907d6c0b56eSmrgstatic 908d6c0b56eSmrgvoid amdgpu_dri2_schedule_event(CARD32 delay, DRI2FrameEventPtr event_info) 909d6c0b56eSmrg{ 910d6c0b56eSmrg event_info->timer = TimerSet(NULL, 0, delay, amdgpu_dri2_deferred_event, 911d6c0b56eSmrg event_info); 912d6c0b56eSmrg if (delay == 0) { 913d6c0b56eSmrg CARD32 now = GetTimeInMillis(); 914d6c0b56eSmrg amdgpu_dri2_deferred_event(event_info->timer, now, event_info); 915d6c0b56eSmrg } 916d6c0b56eSmrg} 917d6c0b56eSmrg 918d6c0b56eSmrg/* 919d6c0b56eSmrg * Request a DRM event when the requested conditions will be satisfied. 920d6c0b56eSmrg * 921d6c0b56eSmrg * We need to handle the event and ask the server to wake up the client when 922d6c0b56eSmrg * we receive it. 923d6c0b56eSmrg */ 924d6c0b56eSmrgstatic int amdgpu_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, 925d6c0b56eSmrg CARD64 target_msc, CARD64 divisor, 926d6c0b56eSmrg CARD64 remainder) 927d6c0b56eSmrg{ 928d6c0b56eSmrg ScreenPtr screen = draw->pScreen; 929d6c0b56eSmrg ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 930d6c0b56eSmrg DRI2FrameEventPtr wait_info = NULL; 931d6c0b56eSmrg uintptr_t drm_queue_seq = 0; 932d6c0b56eSmrg xf86CrtcPtr crtc = amdgpu_dri2_drawable_crtc(draw, TRUE); 933d6c0b56eSmrg uint32_t msc_delta; 93424b90cf4Smrg uint32_t seq; 935d6c0b56eSmrg CARD64 current_msc; 936d6c0b56eSmrg 937d6c0b56eSmrg /* Truncate to match kernel interfaces; means occasional overflow 938d6c0b56eSmrg * misses, but that's generally not a big deal */ 939d6c0b56eSmrg target_msc &= 0xffffffff; 940d6c0b56eSmrg divisor &= 0xffffffff; 941d6c0b56eSmrg remainder &= 0xffffffff; 942d6c0b56eSmrg 943d6c0b56eSmrg /* Drawable not visible, return immediately */ 94435d5b7c7Smrg if (!crtc) 945d6c0b56eSmrg goto out_complete; 946d6c0b56eSmrg 947d6c0b56eSmrg msc_delta = amdgpu_get_msc_delta(draw, crtc); 948d6c0b56eSmrg 949d6c0b56eSmrg wait_info = calloc(1, sizeof(DRI2FrameEventRec)); 950d6c0b56eSmrg if (!wait_info) 951d6c0b56eSmrg goto out_complete; 952d6c0b56eSmrg 953d6c0b56eSmrg wait_info->drawable_id = draw->id; 954d6c0b56eSmrg wait_info->client = client; 955d6c0b56eSmrg wait_info->type = DRI2_WAITMSC; 956d6c0b56eSmrg wait_info->crtc = crtc; 957d6c0b56eSmrg 958d6c0b56eSmrg /* 959d6c0b56eSmrg * CRTC is in DPMS off state, calculate wait time from current time, 960d6c0b56eSmrg * target_msc and last vblank time/sequence when CRTC was turned off 961d6c0b56eSmrg */ 962d6c0b56eSmrg if (!amdgpu_crtc_is_enabled(crtc)) { 963d6c0b56eSmrg CARD32 delay; 964d6c0b56eSmrg target_msc -= msc_delta; 965d6c0b56eSmrg delay = amdgpu_dri2_extrapolate_msc_delay(crtc, &target_msc, 966d6c0b56eSmrg divisor, remainder); 967d6c0b56eSmrg amdgpu_dri2_schedule_event(delay, wait_info); 968d6c0b56eSmrg DRI2BlockClient(client, draw); 969d6c0b56eSmrg return TRUE; 970d6c0b56eSmrg } 971d6c0b56eSmrg 972d6c0b56eSmrg /* Get current count */ 97324b90cf4Smrg if (!drmmode_wait_vblank(crtc, DRM_VBLANK_RELATIVE, 0, 0, NULL, &seq)) { 974d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 975d6c0b56eSmrg "get vblank counter failed: %s\n", strerror(errno)); 976d6c0b56eSmrg goto out_complete; 977d6c0b56eSmrg } 978d6c0b56eSmrg 97924b90cf4Smrg current_msc = seq + msc_delta; 980d6c0b56eSmrg current_msc &= 0xffffffff; 981d6c0b56eSmrg 982d6c0b56eSmrg drm_queue_seq = amdgpu_drm_queue_alloc(crtc, client, AMDGPU_DRM_QUEUE_ID_DEFAULT, 983d6c0b56eSmrg wait_info, amdgpu_dri2_frame_event_handler, 984d6c0b56eSmrg amdgpu_dri2_frame_event_abort); 985504d986fSmrg if (drm_queue_seq == AMDGPU_DRM_QUEUE_ERROR) { 986d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 987d6c0b56eSmrg "Allocating DRM queue event entry failed.\n"); 988d6c0b56eSmrg goto out_complete; 989d6c0b56eSmrg } 990d6c0b56eSmrg wait_info->drm_queue_seq = drm_queue_seq; 991d6c0b56eSmrg 992d6c0b56eSmrg /* 993d6c0b56eSmrg * If divisor is zero, or current_msc is smaller than target_msc, 994d6c0b56eSmrg * we just need to make sure target_msc passes before waking up the 995d6c0b56eSmrg * client. 996d6c0b56eSmrg */ 997d6c0b56eSmrg if (divisor == 0 || current_msc < target_msc) { 998d6c0b56eSmrg /* If target_msc already reached or passed, set it to 999d6c0b56eSmrg * current_msc to ensure we return a reasonable value back 1000d6c0b56eSmrg * to the caller. This keeps the client from continually 1001d6c0b56eSmrg * sending us MSC targets from the past by forcibly updating 1002d6c0b56eSmrg * their count on this call. 1003d6c0b56eSmrg */ 1004d6c0b56eSmrg if (current_msc >= target_msc) 1005d6c0b56eSmrg target_msc = current_msc; 100624b90cf4Smrg if (!drmmode_wait_vblank(crtc, DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT, 100724b90cf4Smrg target_msc - msc_delta, drm_queue_seq, NULL, 100824b90cf4Smrg NULL)) { 1009d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 1010d6c0b56eSmrg "get vblank counter failed: %s\n", 1011d6c0b56eSmrg strerror(errno)); 1012d6c0b56eSmrg goto out_complete; 1013d6c0b56eSmrg } 1014d6c0b56eSmrg 1015d6c0b56eSmrg DRI2BlockClient(client, draw); 1016d6c0b56eSmrg return TRUE; 1017d6c0b56eSmrg } 1018d6c0b56eSmrg 1019d6c0b56eSmrg /* 1020d6c0b56eSmrg * If we get here, target_msc has already passed or we don't have one, 1021d6c0b56eSmrg * so we queue an event that will satisfy the divisor/remainder equation. 1022d6c0b56eSmrg */ 102324b90cf4Smrg target_msc = current_msc - (current_msc % divisor) + remainder - msc_delta; 1024d6c0b56eSmrg 1025d6c0b56eSmrg /* 1026d6c0b56eSmrg * If calculated remainder is larger than requested remainder, 1027d6c0b56eSmrg * it means we've passed the last point where 1028d6c0b56eSmrg * seq % divisor == remainder, so we need to wait for the next time 1029d6c0b56eSmrg * that will happen. 1030d6c0b56eSmrg */ 1031d6c0b56eSmrg if ((current_msc % divisor) >= remainder) 103224b90cf4Smrg target_msc += divisor; 1033d6c0b56eSmrg 103424b90cf4Smrg if (!drmmode_wait_vblank(crtc, DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT, 103524b90cf4Smrg target_msc, drm_queue_seq, NULL, NULL)) { 1036d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 1037d6c0b56eSmrg "get vblank counter failed: %s\n", strerror(errno)); 1038d6c0b56eSmrg goto out_complete; 1039d6c0b56eSmrg } 1040d6c0b56eSmrg 1041d6c0b56eSmrg DRI2BlockClient(client, draw); 1042d6c0b56eSmrg 1043d6c0b56eSmrg return TRUE; 1044d6c0b56eSmrg 1045d6c0b56eSmrgout_complete: 1046d6c0b56eSmrg if (wait_info) 1047d6c0b56eSmrg amdgpu_dri2_deferred_event(NULL, 0, wait_info); 1048d6c0b56eSmrg return TRUE; 1049d6c0b56eSmrg} 1050d6c0b56eSmrg 1051d6c0b56eSmrg/* 1052d6c0b56eSmrg * ScheduleSwap is responsible for requesting a DRM vblank event for the 1053d6c0b56eSmrg * appropriate frame. 1054d6c0b56eSmrg * 1055d6c0b56eSmrg * In the case of a blit (e.g. for a windowed swap) or buffer exchange, 1056d6c0b56eSmrg * the vblank requested can simply be the last queued swap frame + the swap 1057d6c0b56eSmrg * interval for the drawable. 1058d6c0b56eSmrg * 1059d6c0b56eSmrg * In the case of a page flip, we request an event for the last queued swap 1060d6c0b56eSmrg * frame + swap interval - 1, since we'll need to queue the flip for the frame 1061d6c0b56eSmrg * immediately following the received event. 1062d6c0b56eSmrg * 1063d6c0b56eSmrg * The client will be blocked if it tries to perform further GL commands 1064d6c0b56eSmrg * after queueing a swap, though in the Intel case after queueing a flip, the 1065d6c0b56eSmrg * client is free to queue more commands; they'll block in the kernel if 1066d6c0b56eSmrg * they access buffers busy with the flip. 1067d6c0b56eSmrg * 1068d6c0b56eSmrg * When the swap is complete, the driver should call into the server so it 1069d6c0b56eSmrg * can send any swap complete events that have been requested. 1070d6c0b56eSmrg */ 1071d6c0b56eSmrgstatic int amdgpu_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, 1072d6c0b56eSmrg DRI2BufferPtr front, DRI2BufferPtr back, 1073d6c0b56eSmrg CARD64 * target_msc, CARD64 divisor, 1074d6c0b56eSmrg CARD64 remainder, DRI2SwapEventPtr func, 1075d6c0b56eSmrg void *data) 1076d6c0b56eSmrg{ 1077d6c0b56eSmrg ScreenPtr screen = draw->pScreen; 1078d6c0b56eSmrg ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 1079d6c0b56eSmrg xf86CrtcPtr crtc = amdgpu_dri2_drawable_crtc(draw, TRUE); 1080d6c0b56eSmrg uint32_t msc_delta; 108124b90cf4Smrg drmVBlankSeqType type; 108224b90cf4Smrg uint32_t seq; 108324b90cf4Smrg int flip = 0; 1084d6c0b56eSmrg DRI2FrameEventPtr swap_info = NULL; 1085d6c0b56eSmrg uintptr_t drm_queue_seq; 108624b90cf4Smrg CARD64 current_msc, event_msc; 1087d6c0b56eSmrg BoxRec box; 1088d6c0b56eSmrg RegionRec region; 1089d6c0b56eSmrg 1090d6c0b56eSmrg /* Truncate to match kernel interfaces; means occasional overflow 1091d6c0b56eSmrg * misses, but that's generally not a big deal */ 1092d6c0b56eSmrg *target_msc &= 0xffffffff; 1093d6c0b56eSmrg divisor &= 0xffffffff; 1094d6c0b56eSmrg remainder &= 0xffffffff; 1095d6c0b56eSmrg 1096d6c0b56eSmrg /* amdgpu_dri2_frame_event_handler will get called some unknown time in the 1097d6c0b56eSmrg * future with these buffers. Take a reference to ensure that they won't 1098d6c0b56eSmrg * get destroyed before then. 1099d6c0b56eSmrg */ 1100d6c0b56eSmrg amdgpu_dri2_ref_buffer(front); 1101d6c0b56eSmrg amdgpu_dri2_ref_buffer(back); 1102d6c0b56eSmrg 1103d6c0b56eSmrg /* either off-screen or CRTC not usable... just complete the swap */ 110435d5b7c7Smrg if (!crtc) 1105d6c0b56eSmrg goto blit_fallback; 1106d6c0b56eSmrg 1107d6c0b56eSmrg msc_delta = amdgpu_get_msc_delta(draw, crtc); 1108d6c0b56eSmrg 1109d6c0b56eSmrg swap_info = calloc(1, sizeof(DRI2FrameEventRec)); 1110d6c0b56eSmrg if (!swap_info) 1111d6c0b56eSmrg goto blit_fallback; 1112d6c0b56eSmrg 1113d6c0b56eSmrg swap_info->type = DRI2_SWAP; 1114d6c0b56eSmrg swap_info->drawable_id = draw->id; 1115d6c0b56eSmrg swap_info->client = client; 1116d6c0b56eSmrg swap_info->event_complete = func; 1117d6c0b56eSmrg swap_info->event_data = data; 1118d6c0b56eSmrg swap_info->front = front; 1119d6c0b56eSmrg swap_info->back = back; 1120d6c0b56eSmrg swap_info->crtc = crtc; 1121d6c0b56eSmrg 1122d6c0b56eSmrg drm_queue_seq = amdgpu_drm_queue_alloc(crtc, client, AMDGPU_DRM_QUEUE_ID_DEFAULT, 1123d6c0b56eSmrg swap_info, amdgpu_dri2_frame_event_handler, 1124d6c0b56eSmrg amdgpu_dri2_frame_event_abort); 1125504d986fSmrg if (drm_queue_seq == AMDGPU_DRM_QUEUE_ERROR) { 1126d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 1127d6c0b56eSmrg "Allocating DRM queue entry failed.\n"); 1128d6c0b56eSmrg goto blit_fallback; 1129d6c0b56eSmrg } 1130d6c0b56eSmrg swap_info->drm_queue_seq = drm_queue_seq; 1131d6c0b56eSmrg 1132d6c0b56eSmrg /* 1133d6c0b56eSmrg * CRTC is in DPMS off state, fallback to blit, but calculate 1134d6c0b56eSmrg * wait time from current time, target_msc and last vblank 1135d6c0b56eSmrg * time/sequence when CRTC was turned off 1136d6c0b56eSmrg */ 1137d6c0b56eSmrg if (!amdgpu_crtc_is_enabled(crtc)) { 1138d6c0b56eSmrg CARD32 delay; 1139d6c0b56eSmrg *target_msc -= msc_delta; 1140d6c0b56eSmrg delay = amdgpu_dri2_extrapolate_msc_delay(crtc, target_msc, 1141d6c0b56eSmrg divisor, remainder); 1142d6c0b56eSmrg *target_msc += msc_delta; 1143d6c0b56eSmrg *target_msc &= 0xffffffff; 1144d6c0b56eSmrg amdgpu_dri2_schedule_event(delay, swap_info); 1145d6c0b56eSmrg return TRUE; 1146d6c0b56eSmrg } 1147d6c0b56eSmrg 1148d6c0b56eSmrg /* Get current count */ 114924b90cf4Smrg if (!drmmode_wait_vblank(crtc, DRM_VBLANK_RELATIVE, 0, 0, NULL, &seq)) { 1150d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 1151d6c0b56eSmrg "first get vblank counter failed: %s\n", 1152d6c0b56eSmrg strerror(errno)); 1153d6c0b56eSmrg goto blit_fallback; 1154d6c0b56eSmrg } 1155d6c0b56eSmrg 115624b90cf4Smrg current_msc = seq + msc_delta; 1157d6c0b56eSmrg current_msc &= 0xffffffff; 1158d6c0b56eSmrg 1159d6c0b56eSmrg /* Flips need to be submitted one frame before */ 116024b90cf4Smrg if (can_flip(crtc, draw, front, back)) { 1161d6c0b56eSmrg swap_info->type = DRI2_FLIP; 1162d6c0b56eSmrg flip = 1; 1163d6c0b56eSmrg } 1164d6c0b56eSmrg 1165d6c0b56eSmrg /* Correct target_msc by 'flip' if swap_info->type == DRI2_FLIP. 1166d6c0b56eSmrg * Do it early, so handling of different timing constraints 1167d6c0b56eSmrg * for divisor, remainder and msc vs. target_msc works. 1168d6c0b56eSmrg */ 1169d6c0b56eSmrg if (*target_msc > 0) 1170d6c0b56eSmrg *target_msc -= flip; 1171d6c0b56eSmrg 1172d6c0b56eSmrg /* 1173d6c0b56eSmrg * If divisor is zero, or current_msc is smaller than target_msc 1174d6c0b56eSmrg * we just need to make sure target_msc passes before initiating 1175d6c0b56eSmrg * the swap. 1176d6c0b56eSmrg */ 1177d6c0b56eSmrg if (divisor == 0 || current_msc < *target_msc) { 117824b90cf4Smrg type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; 1179d6c0b56eSmrg /* If non-pageflipping, but blitting/exchanging, we need to use 1180d6c0b56eSmrg * DRM_VBLANK_NEXTONMISS to avoid unreliable timestamping later 1181d6c0b56eSmrg * on. 1182d6c0b56eSmrg */ 1183d6c0b56eSmrg if (flip == 0) 118424b90cf4Smrg type |= DRM_VBLANK_NEXTONMISS; 1185d6c0b56eSmrg 1186d6c0b56eSmrg /* If target_msc already reached or passed, set it to 1187d6c0b56eSmrg * current_msc to ensure we return a reasonable value back 1188d6c0b56eSmrg * to the caller. This makes swap_interval logic more robust. 1189d6c0b56eSmrg */ 1190d6c0b56eSmrg if (current_msc >= *target_msc) 1191d6c0b56eSmrg *target_msc = current_msc; 1192d6c0b56eSmrg 119324b90cf4Smrg if (!drmmode_wait_vblank(crtc, type, *target_msc - msc_delta, 119424b90cf4Smrg drm_queue_seq, NULL, &seq)) { 1195d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 1196d6c0b56eSmrg "divisor 0 get vblank counter failed: %s\n", 1197d6c0b56eSmrg strerror(errno)); 1198d6c0b56eSmrg goto blit_fallback; 1199d6c0b56eSmrg } 1200d6c0b56eSmrg 120124b90cf4Smrg *target_msc = seq + flip + msc_delta; 1202d6c0b56eSmrg *target_msc &= 0xffffffff; 1203d6c0b56eSmrg swap_info->frame = *target_msc; 1204d6c0b56eSmrg 1205d6c0b56eSmrg return TRUE; 1206d6c0b56eSmrg } 1207d6c0b56eSmrg 1208d6c0b56eSmrg /* 1209d6c0b56eSmrg * If we get here, target_msc has already passed or we don't have one, 1210d6c0b56eSmrg * and we need to queue an event that will satisfy the divisor/remainder 1211d6c0b56eSmrg * equation. 1212d6c0b56eSmrg */ 121324b90cf4Smrg type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; 1214d6c0b56eSmrg if (flip == 0) 121524b90cf4Smrg type |= DRM_VBLANK_NEXTONMISS; 1216d6c0b56eSmrg 121724b90cf4Smrg event_msc = current_msc - (current_msc % divisor) + remainder - msc_delta; 1218d6c0b56eSmrg 1219d6c0b56eSmrg /* 1220d6c0b56eSmrg * If the calculated deadline vbl.request.sequence is smaller than 1221d6c0b56eSmrg * or equal to current_msc, it means we've passed the last point 1222d6c0b56eSmrg * when effective onset frame seq could satisfy 1223d6c0b56eSmrg * seq % divisor == remainder, so we need to wait for the next time 1224d6c0b56eSmrg * this will happen. 1225d6c0b56eSmrg 1226d6c0b56eSmrg * This comparison takes the 1 frame swap delay in pageflipping mode 1227d6c0b56eSmrg * into account, as well as a potential DRM_VBLANK_NEXTONMISS delay 1228d6c0b56eSmrg * if we are blitting/exchanging instead of flipping. 1229d6c0b56eSmrg */ 123024b90cf4Smrg if (event_msc <= current_msc) 123124b90cf4Smrg event_msc += divisor; 1232d6c0b56eSmrg 1233d6c0b56eSmrg /* Account for 1 frame extra pageflip delay if flip > 0 */ 123424b90cf4Smrg event_msc -= flip; 1235d6c0b56eSmrg 123624b90cf4Smrg if (!drmmode_wait_vblank(crtc, type, event_msc, drm_queue_seq, NULL, &seq)) { 1237d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 1238d6c0b56eSmrg "final get vblank counter failed: %s\n", 1239d6c0b56eSmrg strerror(errno)); 1240d6c0b56eSmrg goto blit_fallback; 1241d6c0b56eSmrg } 1242d6c0b56eSmrg 1243d6c0b56eSmrg /* Adjust returned value for 1 fame pageflip offset of flip > 0 */ 124424b90cf4Smrg *target_msc = seq + flip + msc_delta; 1245d6c0b56eSmrg *target_msc &= 0xffffffff; 1246d6c0b56eSmrg swap_info->frame = *target_msc; 1247d6c0b56eSmrg 1248d6c0b56eSmrg return TRUE; 1249d6c0b56eSmrg 1250d6c0b56eSmrgblit_fallback: 1251d6c0b56eSmrg if (swap_info) { 1252d6c0b56eSmrg swap_info->type = DRI2_SWAP; 1253d6c0b56eSmrg amdgpu_dri2_schedule_event(FALLBACK_SWAP_DELAY, swap_info); 1254d6c0b56eSmrg } else { 1255d6c0b56eSmrg box.x1 = 0; 1256d6c0b56eSmrg box.y1 = 0; 1257d6c0b56eSmrg box.x2 = draw->width; 1258d6c0b56eSmrg box.y2 = draw->height; 1259d6c0b56eSmrg REGION_INIT(pScreen, ®ion, &box, 0); 1260d6c0b56eSmrg 126124b90cf4Smrg amdgpu_dri2_copy_region2(draw->pScreen, draw, ®ion, front, back); 1262d6c0b56eSmrg 1263d6c0b56eSmrg DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data); 1264d6c0b56eSmrg 1265d6c0b56eSmrg amdgpu_dri2_unref_buffer(front); 1266d6c0b56eSmrg amdgpu_dri2_unref_buffer(back); 1267d6c0b56eSmrg } 1268d6c0b56eSmrg 1269d6c0b56eSmrg *target_msc = 0; /* offscreen, so zero out target vblank count */ 1270d6c0b56eSmrg return TRUE; 1271d6c0b56eSmrg} 1272d6c0b56eSmrg 1273d6c0b56eSmrgBool amdgpu_dri2_screen_init(ScreenPtr pScreen) 1274d6c0b56eSmrg{ 1275d6c0b56eSmrg ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); 1276d6c0b56eSmrg AMDGPUInfoPtr info = AMDGPUPTR(pScrn); 1277d6c0b56eSmrg AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn); 1278d6c0b56eSmrg DRI2InfoRec dri2_info = { 0 }; 1279d6c0b56eSmrg const char *driverNames[2]; 1280d6c0b56eSmrg Bool scheduling_works = TRUE; 1281d6c0b56eSmrg 1282d6c0b56eSmrg if (!info->dri2.available) 1283d6c0b56eSmrg return FALSE; 1284d6c0b56eSmrg 1285d6c0b56eSmrg info->dri2.device_name = drmGetDeviceNameFromFd(pAMDGPUEnt->fd); 1286d6c0b56eSmrg 1287d6c0b56eSmrg dri2_info.driverName = SI_DRIVER_NAME; 1288d6c0b56eSmrg dri2_info.fd = pAMDGPUEnt->fd; 1289d6c0b56eSmrg dri2_info.deviceName = info->dri2.device_name; 1290d6c0b56eSmrg 1291d6c0b56eSmrg if (info->drmmode.count_crtcs > 2) { 1292d6c0b56eSmrg uint64_t cap_value; 1293d6c0b56eSmrg 1294d6c0b56eSmrg if (drmGetCap 1295d6c0b56eSmrg (pAMDGPUEnt->fd, DRM_CAP_VBLANK_HIGH_CRTC, &cap_value)) { 1296d6c0b56eSmrg xf86DrvMsg(pScrn->scrnIndex, X_WARNING, 1297d6c0b56eSmrg "You need a newer kernel " 1298d6c0b56eSmrg "for VBLANKs on CRTC > 1\n"); 1299d6c0b56eSmrg scheduling_works = FALSE; 1300d6c0b56eSmrg } else if (!cap_value) { 1301d6c0b56eSmrg xf86DrvMsg(pScrn->scrnIndex, X_WARNING, 1302d6c0b56eSmrg "Your kernel does not " 1303d6c0b56eSmrg "handle VBLANKs on CRTC > 1\n"); 1304d6c0b56eSmrg scheduling_works = FALSE; 1305d6c0b56eSmrg } 1306d6c0b56eSmrg } 1307d6c0b56eSmrg 1308d6c0b56eSmrg if (scheduling_works) { 1309d6c0b56eSmrg dri2_info.ScheduleSwap = amdgpu_dri2_schedule_swap; 1310d6c0b56eSmrg dri2_info.GetMSC = amdgpu_dri2_get_msc; 1311d6c0b56eSmrg dri2_info.ScheduleWaitMSC = amdgpu_dri2_schedule_wait_msc; 131224b90cf4Smrg dri2_info.numDrivers = ARRAY_SIZE(driverNames); 1313d6c0b56eSmrg dri2_info.driverNames = driverNames; 1314d6c0b56eSmrg driverNames[0] = driverNames[1] = dri2_info.driverName; 1315d6c0b56eSmrg 1316d6c0b56eSmrg if (DRI2InfoCnt == 0) { 1317d6c0b56eSmrg if (!dixRegisterPrivateKey(dri2_window_private_key, 1318d6c0b56eSmrg PRIVATE_WINDOW, 1319d6c0b56eSmrg sizeof(struct dri2_window_priv))) { 1320d6c0b56eSmrg xf86DrvMsg(pScrn->scrnIndex, X_WARNING, 1321d6c0b56eSmrg "Failed to get DRI2 window private\n"); 1322d6c0b56eSmrg return FALSE; 1323d6c0b56eSmrg } 1324d6c0b56eSmrg 1325d6c0b56eSmrg AddCallback(&ClientStateCallback, 1326d6c0b56eSmrg amdgpu_dri2_client_state_changed, 0); 1327d6c0b56eSmrg } 1328d6c0b56eSmrg 1329d6c0b56eSmrg DRI2InfoCnt++; 1330d6c0b56eSmrg } 1331d6c0b56eSmrg 1332d6c0b56eSmrg dri2_info.version = 9; 1333d6c0b56eSmrg dri2_info.CreateBuffer2 = amdgpu_dri2_create_buffer2; 1334d6c0b56eSmrg dri2_info.DestroyBuffer2 = amdgpu_dri2_destroy_buffer2; 1335d6c0b56eSmrg dri2_info.CopyRegion2 = amdgpu_dri2_copy_region2; 1336d6c0b56eSmrg 1337d6c0b56eSmrg info->dri2.enabled = DRI2ScreenInit(pScreen, &dri2_info); 1338d6c0b56eSmrg return info->dri2.enabled; 1339d6c0b56eSmrg} 1340d6c0b56eSmrg 1341d6c0b56eSmrgvoid amdgpu_dri2_close_screen(ScreenPtr pScreen) 1342d6c0b56eSmrg{ 1343d6c0b56eSmrg ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); 1344d6c0b56eSmrg AMDGPUInfoPtr info = AMDGPUPTR(pScrn); 1345d6c0b56eSmrg 1346d6c0b56eSmrg if (--DRI2InfoCnt == 0) 1347d6c0b56eSmrg DeleteCallback(&ClientStateCallback, 1348d6c0b56eSmrg amdgpu_dri2_client_state_changed, 0); 1349d6c0b56eSmrg 1350d6c0b56eSmrg DRI2CloseScreen(pScreen); 1351d6c0b56eSmrg drmFree(info->dri2.device_name); 1352d6c0b56eSmrg} 1353d6c0b56eSmrg 1354d6c0b56eSmrg#endif /* DRI2 */ 1355