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++; 17490f2b693Smrg 17590f2b693Smrg /* The copy operation from amdgpu_glamor_set_pixmap_bo needs to 17690f2b693Smrg * be flushed to the kernel driver before the client starts 17790f2b693Smrg * using the pixmap storage for direct rendering. 17890f2b693Smrg */ 17990f2b693Smrg amdgpu_glamor_flush(pScrn); 180d6c0b56eSmrg } 181d6c0b56eSmrg 18235d5b7c7Smrg if (!amdgpu_get_flink_name(pAMDGPUEnt, pixmap, &buffers->name)) 18335d5b7c7Smrg goto error; 18435d5b7c7Smrg 185d6c0b56eSmrg privates = calloc(1, sizeof(struct dri2_buffer_priv)); 18635d5b7c7Smrg if (!privates) 187d6c0b56eSmrg goto error; 188d6c0b56eSmrg 189d6c0b56eSmrg buffers->attachment = attachment; 19035d5b7c7Smrg buffers->pitch = pixmap->devKind; 19135d5b7c7Smrg buffers->cpp = cpp; 192d6c0b56eSmrg buffers->driverPrivate = privates; 193d6c0b56eSmrg buffers->format = format; 194d6c0b56eSmrg buffers->flags = 0; /* not tiled */ 195d6c0b56eSmrg privates->pixmap = pixmap; 196d6c0b56eSmrg privates->attachment = attachment; 197d6c0b56eSmrg privates->refcnt = 1; 198d6c0b56eSmrg 199d6c0b56eSmrg return buffers; 200d6c0b56eSmrg 201d6c0b56eSmrgerror: 202d6c0b56eSmrg free(buffers); 20335d5b7c7Smrg (*pScreen->DestroyPixmap) (pixmap); 204d6c0b56eSmrg return NULL; 205d6c0b56eSmrg} 206d6c0b56eSmrg 207d6c0b56eSmrgstatic void 208d6c0b56eSmrgamdgpu_dri2_destroy_buffer2(ScreenPtr pScreen, 209d6c0b56eSmrg DrawablePtr drawable, BufferPtr buffers) 210d6c0b56eSmrg{ 211d6c0b56eSmrg if (buffers) { 212d6c0b56eSmrg struct dri2_buffer_priv *private = buffers->driverPrivate; 213d6c0b56eSmrg 214d6c0b56eSmrg /* Trying to free an already freed buffer is unlikely to end well */ 215d6c0b56eSmrg if (private->refcnt == 0) { 216d6c0b56eSmrg ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen); 217d6c0b56eSmrg 218d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 219d6c0b56eSmrg "Attempted to destroy previously destroyed buffer.\ 220d6c0b56eSmrg This is a programming error\n"); 221d6c0b56eSmrg return; 222d6c0b56eSmrg } 223d6c0b56eSmrg 224d6c0b56eSmrg private->refcnt--; 225d6c0b56eSmrg if (private->refcnt == 0) { 226d6c0b56eSmrg if (private->pixmap) 227d6c0b56eSmrg (*pScreen->DestroyPixmap) (private->pixmap); 228d6c0b56eSmrg 229d6c0b56eSmrg free(buffers->driverPrivate); 230d6c0b56eSmrg free(buffers); 231d6c0b56eSmrg } 232d6c0b56eSmrg } 233d6c0b56eSmrg} 234d6c0b56eSmrg 235d6c0b56eSmrgstatic inline PixmapPtr GetDrawablePixmap(DrawablePtr drawable) 236d6c0b56eSmrg{ 237d6c0b56eSmrg if (drawable->type == DRAWABLE_PIXMAP) 238d6c0b56eSmrg return (PixmapPtr) drawable; 239d6c0b56eSmrg else { 240d6c0b56eSmrg struct _Window *pWin = (struct _Window *)drawable; 241d6c0b56eSmrg return drawable->pScreen->GetWindowPixmap(pWin); 242d6c0b56eSmrg } 243d6c0b56eSmrg} 244d6c0b56eSmrg 245d6c0b56eSmrgstatic void 246d6c0b56eSmrgamdgpu_dri2_copy_region2(ScreenPtr pScreen, 247d6c0b56eSmrg DrawablePtr drawable, 248d6c0b56eSmrg RegionPtr region, 249d6c0b56eSmrg BufferPtr dest_buffer, BufferPtr src_buffer) 250d6c0b56eSmrg{ 251d6c0b56eSmrg struct dri2_buffer_priv *src_private = src_buffer->driverPrivate; 252d6c0b56eSmrg struct dri2_buffer_priv *dst_private = dest_buffer->driverPrivate; 253d6c0b56eSmrg DrawablePtr src_drawable; 254d6c0b56eSmrg DrawablePtr dst_drawable; 255d6c0b56eSmrg RegionPtr copy_clip; 256d6c0b56eSmrg GCPtr gc; 257d6c0b56eSmrg Bool translate = FALSE; 258d6c0b56eSmrg int off_x = 0, off_y = 0; 259d6c0b56eSmrg 260d6c0b56eSmrg src_drawable = &src_private->pixmap->drawable; 261d6c0b56eSmrg dst_drawable = &dst_private->pixmap->drawable; 262d6c0b56eSmrg 263d6c0b56eSmrg if (src_private->attachment == DRI2BufferFrontLeft) { 264d6c0b56eSmrg if (drawable->pScreen != pScreen) { 265d6c0b56eSmrg src_drawable = DRI2UpdatePrime(drawable, src_buffer); 266d6c0b56eSmrg if (!src_drawable) 267d6c0b56eSmrg return; 268d6c0b56eSmrg } else 269d6c0b56eSmrg src_drawable = drawable; 270d6c0b56eSmrg } 271d6c0b56eSmrg if (dst_private->attachment == DRI2BufferFrontLeft) { 272d6c0b56eSmrg if (drawable->pScreen != pScreen) { 273d6c0b56eSmrg dst_drawable = DRI2UpdatePrime(drawable, dest_buffer); 274d6c0b56eSmrg if (!dst_drawable) 275d6c0b56eSmrg return; 276d6c0b56eSmrg if (dst_drawable != drawable) 277d6c0b56eSmrg translate = TRUE; 278d6c0b56eSmrg } else 279d6c0b56eSmrg dst_drawable = drawable; 280d6c0b56eSmrg } 281d6c0b56eSmrg 282d6c0b56eSmrg if (translate && drawable->type == DRAWABLE_WINDOW) { 283d6c0b56eSmrg PixmapPtr pPix = GetDrawablePixmap(drawable); 284d6c0b56eSmrg 285d6c0b56eSmrg off_x = drawable->x - pPix->screen_x; 286d6c0b56eSmrg off_y = drawable->y - pPix->screen_y; 287d6c0b56eSmrg } 288d6c0b56eSmrg gc = GetScratchGC(dst_drawable->depth, pScreen); 289d6c0b56eSmrg copy_clip = REGION_CREATE(pScreen, NULL, 0); 290d6c0b56eSmrg REGION_COPY(pScreen, copy_clip, region); 291d6c0b56eSmrg 292d6c0b56eSmrg if (translate) { 293d6c0b56eSmrg REGION_TRANSLATE(pScreen, copy_clip, off_x, off_y); 294d6c0b56eSmrg } 295d6c0b56eSmrg 296d6c0b56eSmrg (*gc->funcs->ChangeClip) (gc, CT_REGION, copy_clip, 0); 297d6c0b56eSmrg ValidateGC(dst_drawable, gc); 298d6c0b56eSmrg 299d6c0b56eSmrg (*gc->ops->CopyArea) (src_drawable, dst_drawable, gc, 300d6c0b56eSmrg 0, 0, drawable->width, drawable->height, off_x, 301d6c0b56eSmrg off_y); 302d6c0b56eSmrg 303d6c0b56eSmrg FreeScratchGC(gc); 304d6c0b56eSmrg} 305d6c0b56eSmrg 306d6c0b56eSmrgenum DRI2FrameEventType { 307d6c0b56eSmrg DRI2_SWAP, 308d6c0b56eSmrg DRI2_FLIP, 309d6c0b56eSmrg DRI2_WAITMSC, 310d6c0b56eSmrg}; 311d6c0b56eSmrg 312d6c0b56eSmrgtypedef struct _DRI2FrameEvent { 313d6c0b56eSmrg XID drawable_id; 314d6c0b56eSmrg ClientPtr client; 315d6c0b56eSmrg enum DRI2FrameEventType type; 316d6c0b56eSmrg unsigned frame; 317d6c0b56eSmrg xf86CrtcPtr crtc; 318d6c0b56eSmrg OsTimerPtr timer; 319d6c0b56eSmrg uintptr_t drm_queue_seq; 320d6c0b56eSmrg 321d6c0b56eSmrg /* for swaps & flips only */ 322d6c0b56eSmrg DRI2SwapEventPtr event_complete; 323d6c0b56eSmrg void *event_data; 324d6c0b56eSmrg DRI2BufferPtr front; 325d6c0b56eSmrg DRI2BufferPtr back; 326d6c0b56eSmrg} DRI2FrameEventRec, *DRI2FrameEventPtr; 327d6c0b56eSmrg 328d6c0b56eSmrgstatic int DRI2InfoCnt; 329d6c0b56eSmrg 330d6c0b56eSmrgstatic void amdgpu_dri2_ref_buffer(BufferPtr buffer) 331d6c0b56eSmrg{ 332d6c0b56eSmrg struct dri2_buffer_priv *private = buffer->driverPrivate; 333d6c0b56eSmrg private->refcnt++; 334d6c0b56eSmrg} 335d6c0b56eSmrg 336d6c0b56eSmrgstatic void amdgpu_dri2_unref_buffer(BufferPtr buffer) 337d6c0b56eSmrg{ 338d6c0b56eSmrg if (buffer) { 339d6c0b56eSmrg struct dri2_buffer_priv *private = buffer->driverPrivate; 34024b90cf4Smrg DrawablePtr draw = &private->pixmap->drawable; 34124b90cf4Smrg 34224b90cf4Smrg amdgpu_dri2_destroy_buffer2(draw->pScreen, draw, buffer); 343d6c0b56eSmrg } 344d6c0b56eSmrg} 345d6c0b56eSmrg 346d6c0b56eSmrgstatic void 347d6c0b56eSmrgamdgpu_dri2_client_state_changed(CallbackListPtr * ClientStateCallback, 348d6c0b56eSmrg pointer data, pointer calldata) 349d6c0b56eSmrg{ 350d6c0b56eSmrg NewClientInfoRec *clientinfo = calldata; 351d6c0b56eSmrg ClientPtr pClient = clientinfo->client; 352d6c0b56eSmrg 353d6c0b56eSmrg switch (pClient->clientState) { 354d6c0b56eSmrg case ClientStateRetained: 355d6c0b56eSmrg case ClientStateGone: 356d6c0b56eSmrg amdgpu_drm_abort_client(pClient); 357d6c0b56eSmrg break; 358d6c0b56eSmrg default: 359d6c0b56eSmrg break; 360d6c0b56eSmrg } 361d6c0b56eSmrg} 362d6c0b56eSmrg 363d6c0b56eSmrg/* 364d6c0b56eSmrg * Get current frame count delta for the specified drawable and CRTC 365d6c0b56eSmrg */ 366d6c0b56eSmrgstatic uint32_t amdgpu_get_msc_delta(DrawablePtr pDraw, xf86CrtcPtr crtc) 367d6c0b56eSmrg{ 368d6c0b56eSmrg drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; 369d6c0b56eSmrg 370d6c0b56eSmrg if (pDraw && pDraw->type == DRAWABLE_WINDOW) 371d6c0b56eSmrg return drmmode_crtc->interpolated_vblanks + 372d6c0b56eSmrg get_dri2_window_priv((WindowPtr)pDraw)->vblank_delta; 373d6c0b56eSmrg 374d6c0b56eSmrg return drmmode_crtc->interpolated_vblanks; 375d6c0b56eSmrg} 376d6c0b56eSmrg 377d6c0b56eSmrg/* 378d6c0b56eSmrg * Get current frame count and timestamp of the specified CRTC 379d6c0b56eSmrg */ 380d6c0b56eSmrgstatic Bool amdgpu_dri2_get_crtc_msc(xf86CrtcPtr crtc, CARD64 *ust, CARD64 *msc) 381d6c0b56eSmrg{ 382504d986fSmrg drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; 383504d986fSmrg 384d6c0b56eSmrg if (!amdgpu_crtc_is_enabled(crtc) || 385d6c0b56eSmrg drmmode_crtc_get_ust_msc(crtc, ust, msc) != Success) { 386d6c0b56eSmrg /* CRTC is not running, extrapolate MSC and timestamp */ 387d6c0b56eSmrg ScrnInfoPtr scrn = crtc->scrn; 388d6c0b56eSmrg AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn); 389d6c0b56eSmrg CARD64 now, delta_t, delta_seq; 390d6c0b56eSmrg 391d6c0b56eSmrg if (!drmmode_crtc->dpms_last_ust) 392d6c0b56eSmrg return FALSE; 393d6c0b56eSmrg 394d6c0b56eSmrg if (drmmode_get_current_ust(pAMDGPUEnt->fd, &now) != 0) { 395d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_ERROR, 396d6c0b56eSmrg "%s cannot get current time\n", __func__); 397d6c0b56eSmrg return FALSE; 398d6c0b56eSmrg } 399d6c0b56eSmrg 400d6c0b56eSmrg delta_t = now - drmmode_crtc->dpms_last_ust; 401d6c0b56eSmrg delta_seq = delta_t * drmmode_crtc->dpms_last_fps; 402d6c0b56eSmrg delta_seq /= 1000000; 403d6c0b56eSmrg *ust = drmmode_crtc->dpms_last_ust; 404d6c0b56eSmrg delta_t = delta_seq * 1000000; 405d6c0b56eSmrg delta_t /= drmmode_crtc->dpms_last_fps; 406d6c0b56eSmrg *ust += delta_t; 407d6c0b56eSmrg *msc = drmmode_crtc->dpms_last_seq; 408d6c0b56eSmrg *msc += delta_seq; 409d6c0b56eSmrg } 410d6c0b56eSmrg 411504d986fSmrg *msc += drmmode_crtc->interpolated_vblanks; 412504d986fSmrg 413d6c0b56eSmrg return TRUE; 414d6c0b56eSmrg} 415d6c0b56eSmrg 416d6c0b56eSmrgstatic 41777d6d1ecSmrgxf86CrtcPtr amdgpu_dri2_drawable_crtc(DrawablePtr pDraw) 418d6c0b56eSmrg{ 419d6c0b56eSmrg ScreenPtr pScreen = pDraw->pScreen; 420e49c54bcSmrg xf86CrtcPtr crtc = amdgpu_pick_best_crtc(pScreen, 421d6c0b56eSmrg pDraw->x, pDraw->x + pDraw->width, 422d6c0b56eSmrg pDraw->y, pDraw->y + pDraw->height); 423d6c0b56eSmrg 42477d6d1ecSmrg if (pDraw->type == DRAWABLE_WINDOW) { 425d6c0b56eSmrg struct dri2_window_priv *priv = get_dri2_window_priv((WindowPtr)pDraw); 426d6c0b56eSmrg 42777d6d1ecSmrg if (!crtc) { 42877d6d1ecSmrg crtc = priv->crtc; 42977d6d1ecSmrg } else if (priv->crtc && priv->crtc != crtc) { 430d6c0b56eSmrg CARD64 ust, mscold, mscnew; 431d6c0b56eSmrg 432d6c0b56eSmrg if (amdgpu_dri2_get_crtc_msc(priv->crtc, &ust, &mscold) && 433d6c0b56eSmrg amdgpu_dri2_get_crtc_msc(crtc, &ust, &mscnew)) 434d6c0b56eSmrg priv->vblank_delta += mscold - mscnew; 435d6c0b56eSmrg } 436d6c0b56eSmrg 437d6c0b56eSmrg priv->crtc = crtc; 438d6c0b56eSmrg } 439d6c0b56eSmrg 440d6c0b56eSmrg return crtc; 441d6c0b56eSmrg} 442d6c0b56eSmrg 443d6c0b56eSmrgstatic void 444d6c0b56eSmrgamdgpu_dri2_flip_event_abort(xf86CrtcPtr crtc, void *event_data) 445d6c0b56eSmrg{ 44611bf0794Smrg if (crtc) 44711bf0794Smrg AMDGPUPTR(crtc->scrn)->drmmode.dri2_flipping = FALSE; 448d6c0b56eSmrg 449d6c0b56eSmrg free(event_data); 450d6c0b56eSmrg} 451d6c0b56eSmrg 452d6c0b56eSmrgstatic void 453d6c0b56eSmrgamdgpu_dri2_flip_event_handler(xf86CrtcPtr crtc, uint32_t frame, uint64_t usec, 454d6c0b56eSmrg void *event_data) 455d6c0b56eSmrg{ 456d6c0b56eSmrg DRI2FrameEventPtr flip = event_data; 457d6c0b56eSmrg ScrnInfoPtr scrn = crtc->scrn; 458d6c0b56eSmrg unsigned tv_sec, tv_usec; 459d6c0b56eSmrg DrawablePtr drawable; 460d6c0b56eSmrg ScreenPtr screen; 461d6c0b56eSmrg int status; 462d6c0b56eSmrg PixmapPtr pixmap; 463d6c0b56eSmrg 464d6c0b56eSmrg status = dixLookupDrawable(&drawable, flip->drawable_id, serverClient, 465d6c0b56eSmrg M_ANY, DixWriteAccess); 466d6c0b56eSmrg if (status != Success) 467d6c0b56eSmrg goto abort; 468d6c0b56eSmrg 469d6c0b56eSmrg frame += amdgpu_get_msc_delta(drawable, crtc); 470d6c0b56eSmrg 471d6c0b56eSmrg screen = scrn->pScreen; 472d6c0b56eSmrg pixmap = screen->GetScreenPixmap(screen); 473d6c0b56eSmrg xf86DrvMsgVerb(scrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG, 474d6c0b56eSmrg "%s:%d fevent[%p] width %d pitch %d (/4 %d)\n", 475d6c0b56eSmrg __func__, __LINE__, flip, pixmap->drawable.width, 476d6c0b56eSmrg pixmap->devKind, pixmap->devKind / 4); 477d6c0b56eSmrg 478d6c0b56eSmrg tv_sec = usec / 1000000; 479d6c0b56eSmrg tv_usec = usec % 1000000; 480d6c0b56eSmrg 481d6c0b56eSmrg /* We assume our flips arrive in order, so we don't check the frame */ 482d6c0b56eSmrg switch (flip->type) { 483d6c0b56eSmrg case DRI2_SWAP: 484d6c0b56eSmrg /* Check for too small vblank count of pageflip completion, taking wraparound 485d6c0b56eSmrg * into account. This usually means some defective kms pageflip completion, 486d6c0b56eSmrg * causing wrong (msc, ust) return values and possible visual corruption. 487d6c0b56eSmrg */ 488d6c0b56eSmrg if ((frame < flip->frame) && (flip->frame - frame < 5)) { 489d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 490d6c0b56eSmrg "%s: Pageflip completion event has impossible msc %u < target_msc %u\n", 491d6c0b56eSmrg __func__, frame, flip->frame); 492d6c0b56eSmrg /* All-Zero values signal failure of (msc, ust) timestamping to client. */ 493d6c0b56eSmrg frame = tv_sec = tv_usec = 0; 494d6c0b56eSmrg } 495d6c0b56eSmrg 496d6c0b56eSmrg DRI2SwapComplete(flip->client, drawable, frame, tv_sec, tv_usec, 497d6c0b56eSmrg DRI2_FLIP_COMPLETE, flip->event_complete, 498d6c0b56eSmrg flip->event_data); 499d6c0b56eSmrg break; 500d6c0b56eSmrg default: 501d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 502d6c0b56eSmrg "%s: unknown vblank event received\n", __func__); 503d6c0b56eSmrg /* Unknown type */ 504d6c0b56eSmrg break; 505d6c0b56eSmrg } 506d6c0b56eSmrg 507d6c0b56eSmrgabort: 508d6c0b56eSmrg amdgpu_dri2_flip_event_abort(crtc, event_data); 509d6c0b56eSmrg} 510d6c0b56eSmrg 511d6c0b56eSmrgstatic Bool 512d6c0b56eSmrgamdgpu_dri2_schedule_flip(xf86CrtcPtr crtc, ClientPtr client, 513d6c0b56eSmrg DrawablePtr draw, DRI2BufferPtr front, 514d6c0b56eSmrg DRI2BufferPtr back, DRI2SwapEventPtr func, 515d6c0b56eSmrg void *data, unsigned int target_msc) 516d6c0b56eSmrg{ 517d6c0b56eSmrg ScrnInfoPtr scrn = crtc->scrn; 518d6c0b56eSmrg AMDGPUInfoPtr info = AMDGPUPTR(scrn); 519d6c0b56eSmrg struct dri2_buffer_priv *back_priv; 520d6c0b56eSmrg DRI2FrameEventPtr flip_info; 521d6c0b56eSmrg 522d6c0b56eSmrg flip_info = calloc(1, sizeof(DRI2FrameEventRec)); 523d6c0b56eSmrg if (!flip_info) 524d6c0b56eSmrg return FALSE; 525d6c0b56eSmrg 526d6c0b56eSmrg flip_info->drawable_id = draw->id; 527d6c0b56eSmrg flip_info->client = client; 528d6c0b56eSmrg flip_info->type = DRI2_SWAP; 529d6c0b56eSmrg flip_info->event_complete = func; 530d6c0b56eSmrg flip_info->event_data = data; 531d6c0b56eSmrg flip_info->frame = target_msc; 532d6c0b56eSmrg flip_info->crtc = crtc; 533d6c0b56eSmrg 534d6c0b56eSmrg xf86DrvMsgVerb(scrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG, 535d6c0b56eSmrg "%s:%d fevent[%p]\n", __func__, __LINE__, flip_info); 536d6c0b56eSmrg 537d6c0b56eSmrg /* Page flip the full screen buffer */ 538d6c0b56eSmrg back_priv = back->driverPrivate; 539d6c0b56eSmrg if (amdgpu_do_pageflip(scrn, client, back_priv->pixmap, 54024b90cf4Smrg AMDGPU_DRM_QUEUE_ID_DEFAULT, flip_info, crtc, 541d6c0b56eSmrg amdgpu_dri2_flip_event_handler, 54211bf0794Smrg amdgpu_dri2_flip_event_abort, FLIP_VSYNC, 54311bf0794Smrg target_msc - amdgpu_get_msc_delta(draw, crtc))) { 544d6c0b56eSmrg info->drmmode.dri2_flipping = TRUE; 545d6c0b56eSmrg return TRUE; 546d6c0b56eSmrg } 547d6c0b56eSmrg return FALSE; 548d6c0b56eSmrg} 549d6c0b56eSmrg 550d6c0b56eSmrgstatic Bool update_front(DrawablePtr draw, DRI2BufferPtr front) 551d6c0b56eSmrg{ 552d6c0b56eSmrg ScreenPtr screen = draw->pScreen; 553d6c0b56eSmrg ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 554d6c0b56eSmrg AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn); 555d6c0b56eSmrg PixmapPtr pixmap = get_drawable_pixmap(draw); 556d6c0b56eSmrg struct dri2_buffer_priv *priv = front->driverPrivate; 557d6c0b56eSmrg 558d6c0b56eSmrg if (!amdgpu_get_flink_name(pAMDGPUEnt, pixmap, &front->name)) 559d6c0b56eSmrg return FALSE; 560d6c0b56eSmrg 561d6c0b56eSmrg (*draw->pScreen->DestroyPixmap) (priv->pixmap); 562d6c0b56eSmrg front->pitch = pixmap->devKind; 563d6c0b56eSmrg front->cpp = pixmap->drawable.bitsPerPixel / 8; 564d6c0b56eSmrg priv->pixmap = pixmap; 565d6c0b56eSmrg pixmap->refcnt++; 566d6c0b56eSmrg 567d6c0b56eSmrg return TRUE; 568d6c0b56eSmrg} 569d6c0b56eSmrg 570d6c0b56eSmrgstatic Bool 571d6c0b56eSmrgcan_exchange(ScrnInfoPtr pScrn, DrawablePtr draw, 572d6c0b56eSmrg DRI2BufferPtr front, DRI2BufferPtr back) 573d6c0b56eSmrg{ 574d6c0b56eSmrg struct dri2_buffer_priv *front_priv = front->driverPrivate; 575d6c0b56eSmrg struct dri2_buffer_priv *back_priv = back->driverPrivate; 576d6c0b56eSmrg PixmapPtr front_pixmap; 577d6c0b56eSmrg PixmapPtr back_pixmap = back_priv->pixmap; 578d6c0b56eSmrg 579d6c0b56eSmrg if (!update_front(draw, front)) 580d6c0b56eSmrg return FALSE; 581d6c0b56eSmrg 582d6c0b56eSmrg front_pixmap = front_priv->pixmap; 583d6c0b56eSmrg 584d6c0b56eSmrg if (front_pixmap->drawable.width != back_pixmap->drawable.width) 585d6c0b56eSmrg return FALSE; 586d6c0b56eSmrg 587d6c0b56eSmrg if (front_pixmap->drawable.height != back_pixmap->drawable.height) 588d6c0b56eSmrg return FALSE; 589d6c0b56eSmrg 590d6c0b56eSmrg if (front_pixmap->drawable.bitsPerPixel != 591d6c0b56eSmrg back_pixmap->drawable.bitsPerPixel) 592d6c0b56eSmrg return FALSE; 593d6c0b56eSmrg 594d6c0b56eSmrg if (front_pixmap->devKind != back_pixmap->devKind) 595d6c0b56eSmrg return FALSE; 596d6c0b56eSmrg 597d6c0b56eSmrg return TRUE; 598d6c0b56eSmrg} 599d6c0b56eSmrg 600d6c0b56eSmrgstatic Bool 60124b90cf4Smrgcan_flip(xf86CrtcPtr crtc, DrawablePtr draw, 602d6c0b56eSmrg DRI2BufferPtr front, DRI2BufferPtr back) 603d6c0b56eSmrg{ 60424b90cf4Smrg ScrnInfoPtr pScrn = crtc->scrn; 605d6c0b56eSmrg AMDGPUInfoPtr info = AMDGPUPTR(pScrn); 60611bf0794Smrg xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn); 60711bf0794Smrg int num_crtcs_on; 60811bf0794Smrg int i; 60911bf0794Smrg 61011bf0794Smrg if (draw->type != DRAWABLE_WINDOW || 61111bf0794Smrg !info->allowPageFlip || 61224b90cf4Smrg info->sprites_visible > 0 || 61311bf0794Smrg info->drmmode.present_flipping || 61411bf0794Smrg !pScrn->vtSema || 61511bf0794Smrg !DRI2CanFlip(draw)) 61611bf0794Smrg return FALSE; 61711bf0794Smrg 61811bf0794Smrg for (i = 0, num_crtcs_on = 0; i < config->num_crtc; i++) { 61924b90cf4Smrg if (drmmode_crtc_can_flip(config->crtc[i])) 62011bf0794Smrg num_crtcs_on++; 62111bf0794Smrg } 62211bf0794Smrg 62311bf0794Smrg return num_crtcs_on > 0 && can_exchange(pScrn, draw, front, back); 624d6c0b56eSmrg} 625d6c0b56eSmrg 626d6c0b56eSmrgstatic void 627d6c0b56eSmrgamdgpu_dri2_exchange_buffers(DrawablePtr draw, DRI2BufferPtr front, 628d6c0b56eSmrg DRI2BufferPtr back) 629d6c0b56eSmrg{ 630d6c0b56eSmrg struct dri2_buffer_priv *front_priv = front->driverPrivate; 631d6c0b56eSmrg struct dri2_buffer_priv *back_priv = back->driverPrivate; 632d6c0b56eSmrg struct amdgpu_pixmap *front_pix; 633d6c0b56eSmrg struct amdgpu_pixmap *back_pix; 634d6c0b56eSmrg ScreenPtr screen; 635d6c0b56eSmrg AMDGPUInfoPtr info; 636d6c0b56eSmrg RegionRec region; 637d6c0b56eSmrg int tmp; 638d6c0b56eSmrg 639d6c0b56eSmrg region.extents.x1 = region.extents.y1 = 0; 640d6c0b56eSmrg region.extents.x2 = front_priv->pixmap->drawable.width; 641504d986fSmrg region.extents.y2 = front_priv->pixmap->drawable.height; 642d6c0b56eSmrg region.data = NULL; 643d6c0b56eSmrg DamageRegionAppend(&front_priv->pixmap->drawable, ®ion); 644d6c0b56eSmrg 645d6c0b56eSmrg /* Swap BO names so DRI works */ 646d6c0b56eSmrg tmp = front->name; 647d6c0b56eSmrg front->name = back->name; 648d6c0b56eSmrg back->name = tmp; 649d6c0b56eSmrg 650d6c0b56eSmrg /* Swap pixmap privates */ 651d6c0b56eSmrg front_pix = amdgpu_get_pixmap_private(front_priv->pixmap); 652d6c0b56eSmrg back_pix = amdgpu_get_pixmap_private(back_priv->pixmap); 653d6c0b56eSmrg amdgpu_set_pixmap_private(front_priv->pixmap, back_pix); 654d6c0b56eSmrg amdgpu_set_pixmap_private(back_priv->pixmap, front_pix); 655d6c0b56eSmrg 656d6c0b56eSmrg /* Do we need to update the Screen? */ 657d6c0b56eSmrg screen = draw->pScreen; 658d6c0b56eSmrg info = AMDGPUPTR(xf86ScreenToScrn(screen)); 659d6c0b56eSmrg if (front_pix->bo == info->front_buffer) { 660d6c0b56eSmrg struct amdgpu_pixmap *screen_priv = 661d6c0b56eSmrg amdgpu_get_pixmap_private(screen->GetScreenPixmap(screen)); 662d6c0b56eSmrg 663d6c0b56eSmrg amdgpu_bo_ref(back_pix->bo); 664d6c0b56eSmrg amdgpu_bo_unref(&info->front_buffer); 665d6c0b56eSmrg info->front_buffer = back_pix->bo; 666d6c0b56eSmrg *screen_priv = *back_pix; 667d6c0b56eSmrg } 668d6c0b56eSmrg 669d6c0b56eSmrg amdgpu_glamor_exchange_buffers(front_priv->pixmap, back_priv->pixmap); 670d6c0b56eSmrg 671d6c0b56eSmrg DamageRegionProcessPending(&front_priv->pixmap->drawable); 672d6c0b56eSmrg} 673d6c0b56eSmrg 674d6c0b56eSmrgstatic void amdgpu_dri2_frame_event_abort(xf86CrtcPtr crtc, void *event_data) 675d6c0b56eSmrg{ 676d6c0b56eSmrg DRI2FrameEventPtr event = event_data; 677d6c0b56eSmrg 678d6c0b56eSmrg TimerCancel(event->timer); 679d6c0b56eSmrg TimerFree(event->timer); 680d6c0b56eSmrg amdgpu_dri2_unref_buffer(event->front); 681d6c0b56eSmrg amdgpu_dri2_unref_buffer(event->back); 682d6c0b56eSmrg free(event); 683d6c0b56eSmrg} 684d6c0b56eSmrg 685d6c0b56eSmrgstatic void amdgpu_dri2_frame_event_handler(xf86CrtcPtr crtc, uint32_t seq, 686d6c0b56eSmrg uint64_t usec, void *event_data) 687d6c0b56eSmrg{ 688d6c0b56eSmrg DRI2FrameEventPtr event = event_data; 689d6c0b56eSmrg ScrnInfoPtr scrn = crtc->scrn; 690d6c0b56eSmrg DrawablePtr drawable; 691d6c0b56eSmrg int status; 692d6c0b56eSmrg int swap_type; 693d6c0b56eSmrg BoxRec box; 694d6c0b56eSmrg RegionRec region; 695d6c0b56eSmrg 696d6c0b56eSmrg status = dixLookupDrawable(&drawable, event->drawable_id, serverClient, 697d6c0b56eSmrg M_ANY, DixWriteAccess); 698d6c0b56eSmrg if (status != Success) 699d6c0b56eSmrg goto cleanup; 700d6c0b56eSmrg 701d6c0b56eSmrg seq += amdgpu_get_msc_delta(drawable, crtc); 702d6c0b56eSmrg 703d6c0b56eSmrg switch (event->type) { 704d6c0b56eSmrg case DRI2_FLIP: 70524b90cf4Smrg if (can_flip(crtc, drawable, event->front, event->back) && 706d6c0b56eSmrg amdgpu_dri2_schedule_flip(crtc, 707d6c0b56eSmrg event->client, 708d6c0b56eSmrg drawable, 709d6c0b56eSmrg event->front, 710d6c0b56eSmrg event->back, 711d6c0b56eSmrg event->event_complete, 712d6c0b56eSmrg event->event_data, 713d6c0b56eSmrg event->frame)) { 714d6c0b56eSmrg amdgpu_dri2_exchange_buffers(drawable, event->front, 715d6c0b56eSmrg event->back); 716d6c0b56eSmrg break; 717d6c0b56eSmrg } 718d6c0b56eSmrg /* else fall through to exchange/blit */ 719d6c0b56eSmrg case DRI2_SWAP: 720d6c0b56eSmrg if (DRI2CanExchange(drawable) && 721d6c0b56eSmrg can_exchange(scrn, drawable, event->front, event->back)) { 722d6c0b56eSmrg amdgpu_dri2_exchange_buffers(drawable, event->front, 723d6c0b56eSmrg event->back); 724d6c0b56eSmrg swap_type = DRI2_EXCHANGE_COMPLETE; 725d6c0b56eSmrg } else { 726d6c0b56eSmrg box.x1 = 0; 727d6c0b56eSmrg box.y1 = 0; 728d6c0b56eSmrg box.x2 = drawable->width; 729d6c0b56eSmrg box.y2 = drawable->height; 730d6c0b56eSmrg REGION_INIT(pScreen, ®ion, &box, 0); 73124b90cf4Smrg amdgpu_dri2_copy_region2(drawable->pScreen, drawable, ®ion, 73224b90cf4Smrg event->front, event->back); 733d6c0b56eSmrg swap_type = DRI2_BLIT_COMPLETE; 734d6c0b56eSmrg } 735d6c0b56eSmrg 736d6c0b56eSmrg DRI2SwapComplete(event->client, drawable, seq, usec / 1000000, 737d6c0b56eSmrg usec % 1000000, swap_type, event->event_complete, 738d6c0b56eSmrg event->event_data); 739d6c0b56eSmrg 740d6c0b56eSmrg break; 741d6c0b56eSmrg case DRI2_WAITMSC: 742d6c0b56eSmrg DRI2WaitMSCComplete(event->client, drawable, seq, usec / 1000000, 743d6c0b56eSmrg usec % 1000000); 744d6c0b56eSmrg break; 745d6c0b56eSmrg default: 746d6c0b56eSmrg /* Unknown type */ 747d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 748d6c0b56eSmrg "%s: unknown vblank event received\n", __func__); 749d6c0b56eSmrg break; 750d6c0b56eSmrg } 751d6c0b56eSmrg 752d6c0b56eSmrgcleanup: 753d6c0b56eSmrg amdgpu_dri2_frame_event_abort(crtc, event_data); 754d6c0b56eSmrg} 755d6c0b56eSmrg 756d6c0b56eSmrg/* 757d6c0b56eSmrg * This function should be called on a disabled CRTC only (i.e., CRTC 758d6c0b56eSmrg * in DPMS-off state). It will calculate the delay necessary to reach 759d6c0b56eSmrg * target_msc from present time if the CRTC were running. 760d6c0b56eSmrg */ 761d6c0b56eSmrgstatic 762d6c0b56eSmrgCARD32 amdgpu_dri2_extrapolate_msc_delay(xf86CrtcPtr crtc, CARD64 * target_msc, 763d6c0b56eSmrg CARD64 divisor, CARD64 remainder) 764d6c0b56eSmrg{ 765d6c0b56eSmrg drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; 766d6c0b56eSmrg ScrnInfoPtr pScrn = crtc->scrn; 767d6c0b56eSmrg AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn); 768d6c0b56eSmrg int nominal_frame_rate = drmmode_crtc->dpms_last_fps; 769d6c0b56eSmrg CARD64 last_vblank_ust = drmmode_crtc->dpms_last_ust; 770d6c0b56eSmrg uint32_t last_vblank_seq = drmmode_crtc->dpms_last_seq; 771d6c0b56eSmrg CARD64 now, target_time, delta_t; 772d6c0b56eSmrg int64_t d, delta_seq; 773d6c0b56eSmrg int ret; 774d6c0b56eSmrg CARD32 d_ms; 775d6c0b56eSmrg 776d6c0b56eSmrg if (!last_vblank_ust) { 777d6c0b56eSmrg *target_msc = 0; 778d6c0b56eSmrg return FALLBACK_SWAP_DELAY; 779d6c0b56eSmrg } 780d6c0b56eSmrg ret = drmmode_get_current_ust(pAMDGPUEnt->fd, &now); 781d6c0b56eSmrg if (ret) { 782d6c0b56eSmrg xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 783d6c0b56eSmrg "%s cannot get current time\n", __func__); 784d6c0b56eSmrg *target_msc = 0; 785d6c0b56eSmrg return FALLBACK_SWAP_DELAY; 786d6c0b56eSmrg } 787d6c0b56eSmrg delta_seq = *target_msc - last_vblank_seq; 788d6c0b56eSmrg delta_seq *= 1000000; 789d6c0b56eSmrg target_time = last_vblank_ust; 790d6c0b56eSmrg target_time += delta_seq / nominal_frame_rate; 791d6c0b56eSmrg d = target_time - now; 792d6c0b56eSmrg if (d < 0) { 793d6c0b56eSmrg /* we missed the event, adjust target_msc, do the divisor magic */ 794d6c0b56eSmrg CARD64 current_msc = last_vblank_seq; 795d6c0b56eSmrg 796d6c0b56eSmrg delta_t = now - last_vblank_ust; 797d6c0b56eSmrg delta_seq = delta_t * nominal_frame_rate; 798d6c0b56eSmrg current_msc += delta_seq / 1000000; 799d6c0b56eSmrg current_msc &= 0xffffffff; 800d6c0b56eSmrg if (divisor == 0) { 801d6c0b56eSmrg *target_msc = current_msc; 802d6c0b56eSmrg d = 0; 803d6c0b56eSmrg } else { 804d6c0b56eSmrg *target_msc = 805d6c0b56eSmrg current_msc - (current_msc % divisor) + remainder; 806d6c0b56eSmrg if ((current_msc % divisor) >= remainder) 807d6c0b56eSmrg *target_msc += divisor; 808d6c0b56eSmrg *target_msc &= 0xffffffff; 809d6c0b56eSmrg delta_seq = *target_msc - last_vblank_seq; 810d6c0b56eSmrg delta_seq *= 1000000; 811d6c0b56eSmrg target_time = last_vblank_ust; 812d6c0b56eSmrg target_time += delta_seq / nominal_frame_rate; 813d6c0b56eSmrg d = target_time - now; 814d6c0b56eSmrg } 815d6c0b56eSmrg } 816d6c0b56eSmrg /* 817d6c0b56eSmrg * convert delay to milliseconds and add margin to prevent the client 818d6c0b56eSmrg * from coming back early (due to timer granularity and rounding 819d6c0b56eSmrg * errors) and getting the same MSC it just got 820d6c0b56eSmrg */ 821d6c0b56eSmrg d_ms = (CARD32) d / 1000; 822d6c0b56eSmrg if ((CARD32) d - d_ms * 1000 > 0) 823d6c0b56eSmrg d_ms += 2; 824d6c0b56eSmrg else 825d6c0b56eSmrg d_ms++; 826d6c0b56eSmrg return d_ms; 827d6c0b56eSmrg} 828d6c0b56eSmrg 829d6c0b56eSmrg/* 830d6c0b56eSmrg * Get current interpolated frame count and frame count timestamp, based on 831d6c0b56eSmrg * drawable's crtc. 832d6c0b56eSmrg */ 833d6c0b56eSmrgstatic int amdgpu_dri2_get_msc(DrawablePtr draw, CARD64 * ust, CARD64 * msc) 834d6c0b56eSmrg{ 83577d6d1ecSmrg xf86CrtcPtr crtc = amdgpu_dri2_drawable_crtc(draw); 836d6c0b56eSmrg 837d6c0b56eSmrg /* Drawable not displayed, make up a value */ 83835d5b7c7Smrg if (!crtc) { 839d6c0b56eSmrg *ust = 0; 840d6c0b56eSmrg *msc = 0; 841d6c0b56eSmrg return TRUE; 842d6c0b56eSmrg } 843d6c0b56eSmrg 844d6c0b56eSmrg if (!amdgpu_dri2_get_crtc_msc(crtc, ust, msc)) 845d6c0b56eSmrg return FALSE; 846d6c0b56eSmrg 847504d986fSmrg if (draw && draw->type == DRAWABLE_WINDOW) 848504d986fSmrg *msc += get_dri2_window_priv((WindowPtr)draw)->vblank_delta; 849d6c0b56eSmrg *msc &= 0xffffffff; 850d6c0b56eSmrg return TRUE; 851d6c0b56eSmrg} 852d6c0b56eSmrg 853d6c0b56eSmrgstatic 854d6c0b56eSmrgCARD32 amdgpu_dri2_deferred_event(OsTimerPtr timer, CARD32 now, pointer data) 855d6c0b56eSmrg{ 856d6c0b56eSmrg DRI2FrameEventPtr event_info = (DRI2FrameEventPtr) data; 857d6c0b56eSmrg xf86CrtcPtr crtc = event_info->crtc; 858d6c0b56eSmrg ScrnInfoPtr scrn; 859d6c0b56eSmrg AMDGPUEntPtr pAMDGPUEnt; 860d6c0b56eSmrg CARD64 drm_now; 861d6c0b56eSmrg int ret; 862d6c0b56eSmrg CARD64 delta_t, delta_seq, frame; 863d6c0b56eSmrg drmmode_crtc_private_ptr drmmode_crtc; 864d6c0b56eSmrg 865d6c0b56eSmrg /* 866d6c0b56eSmrg * This is emulated event, so its time is current time, which we 867d6c0b56eSmrg * have to get in DRM-compatible form (which is a bit messy given 868d6c0b56eSmrg * the information that we have at this point). Can't use now argument 869d6c0b56eSmrg * because DRM event time may come from monotonic clock, while 870d6c0b56eSmrg * DIX timer facility uses real-time clock. 871d6c0b56eSmrg */ 872d6c0b56eSmrg if (!event_info->crtc) { 873d6c0b56eSmrg ErrorF("%s no crtc\n", __func__); 874d6c0b56eSmrg if (event_info->drm_queue_seq) 875d6c0b56eSmrg amdgpu_drm_abort_entry(event_info->drm_queue_seq); 876d6c0b56eSmrg else 877d6c0b56eSmrg amdgpu_dri2_frame_event_abort(NULL, data); 878d6c0b56eSmrg return 0; 879d6c0b56eSmrg } 880d6c0b56eSmrg 881d6c0b56eSmrg scrn = crtc->scrn; 882d6c0b56eSmrg pAMDGPUEnt = AMDGPUEntPriv(scrn); 88335d5b7c7Smrg drmmode_crtc = event_info->crtc->driver_private; 884d6c0b56eSmrg ret = drmmode_get_current_ust(pAMDGPUEnt->fd, &drm_now); 885d6c0b56eSmrg if (ret) { 886d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_ERROR, 887d6c0b56eSmrg "%s cannot get current time\n", __func__); 88890f2b693Smrg 88990f2b693Smrg if (event_info->drm_queue_seq) { 89035d5b7c7Smrg drmmode_crtc->drmmode->event_context. 89135d5b7c7Smrg vblank_handler(pAMDGPUEnt->fd, 0, 0, 0, 89235d5b7c7Smrg (void*)event_info->drm_queue_seq); 89390f2b693Smrg drmmode_crtc->wait_flip_nesting_level++; 89490f2b693Smrg amdgpu_drm_queue_handle_deferred(crtc); 89590f2b693Smrg 89690f2b693Smrg } else { 897d6c0b56eSmrg amdgpu_dri2_frame_event_handler(crtc, 0, 0, data); 89890f2b693Smrg } 89990f2b693Smrg 900d6c0b56eSmrg return 0; 901d6c0b56eSmrg } 902d6c0b56eSmrg /* 903d6c0b56eSmrg * calculate the frame number from current time 904d6c0b56eSmrg * that would come from CRTC if it were running 905d6c0b56eSmrg */ 906d6c0b56eSmrg delta_t = drm_now - (CARD64) drmmode_crtc->dpms_last_ust; 907d6c0b56eSmrg delta_seq = delta_t * drmmode_crtc->dpms_last_fps; 908d6c0b56eSmrg delta_seq /= 1000000; 909d6c0b56eSmrg frame = (CARD64) drmmode_crtc->dpms_last_seq + delta_seq; 91090f2b693Smrg 91190f2b693Smrg if (event_info->drm_queue_seq) { 91235d5b7c7Smrg drmmode_crtc->drmmode->event_context. 91335d5b7c7Smrg vblank_handler(pAMDGPUEnt->fd, frame, drm_now / 1000000, 91435d5b7c7Smrg drm_now % 1000000, 91535d5b7c7Smrg (void*)event_info->drm_queue_seq); 91690f2b693Smrg drmmode_crtc->wait_flip_nesting_level++; 91790f2b693Smrg amdgpu_drm_queue_handle_deferred(crtc); 91890f2b693Smrg } else { 919d6c0b56eSmrg amdgpu_dri2_frame_event_handler(crtc, frame, drm_now, data); 92090f2b693Smrg } 92190f2b693Smrg 922d6c0b56eSmrg return 0; 923d6c0b56eSmrg} 924d6c0b56eSmrg 925d6c0b56eSmrgstatic 926d6c0b56eSmrgvoid amdgpu_dri2_schedule_event(CARD32 delay, DRI2FrameEventPtr event_info) 927d6c0b56eSmrg{ 928d6c0b56eSmrg event_info->timer = TimerSet(NULL, 0, delay, amdgpu_dri2_deferred_event, 929d6c0b56eSmrg event_info); 930d6c0b56eSmrg if (delay == 0) { 931d6c0b56eSmrg CARD32 now = GetTimeInMillis(); 932d6c0b56eSmrg amdgpu_dri2_deferred_event(event_info->timer, now, event_info); 933d6c0b56eSmrg } 934d6c0b56eSmrg} 935d6c0b56eSmrg 936d6c0b56eSmrg/* 937d6c0b56eSmrg * Request a DRM event when the requested conditions will be satisfied. 938d6c0b56eSmrg * 939d6c0b56eSmrg * We need to handle the event and ask the server to wake up the client when 940d6c0b56eSmrg * we receive it. 941d6c0b56eSmrg */ 942d6c0b56eSmrgstatic int amdgpu_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, 943d6c0b56eSmrg CARD64 target_msc, CARD64 divisor, 944d6c0b56eSmrg CARD64 remainder) 945d6c0b56eSmrg{ 946d6c0b56eSmrg ScreenPtr screen = draw->pScreen; 947d6c0b56eSmrg ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 948d6c0b56eSmrg DRI2FrameEventPtr wait_info = NULL; 949d6c0b56eSmrg uintptr_t drm_queue_seq = 0; 95077d6d1ecSmrg xf86CrtcPtr crtc = amdgpu_dri2_drawable_crtc(draw); 951d6c0b56eSmrg uint32_t msc_delta; 95224b90cf4Smrg uint32_t seq; 953d6c0b56eSmrg CARD64 current_msc; 954d6c0b56eSmrg 955d6c0b56eSmrg /* Truncate to match kernel interfaces; means occasional overflow 956d6c0b56eSmrg * misses, but that's generally not a big deal */ 957d6c0b56eSmrg target_msc &= 0xffffffff; 958d6c0b56eSmrg divisor &= 0xffffffff; 959d6c0b56eSmrg remainder &= 0xffffffff; 960d6c0b56eSmrg 961d6c0b56eSmrg /* Drawable not visible, return immediately */ 96235d5b7c7Smrg if (!crtc) 963d6c0b56eSmrg goto out_complete; 964d6c0b56eSmrg 965d6c0b56eSmrg msc_delta = amdgpu_get_msc_delta(draw, crtc); 966d6c0b56eSmrg 967d6c0b56eSmrg wait_info = calloc(1, sizeof(DRI2FrameEventRec)); 968d6c0b56eSmrg if (!wait_info) 969d6c0b56eSmrg goto out_complete; 970d6c0b56eSmrg 971d6c0b56eSmrg wait_info->drawable_id = draw->id; 972d6c0b56eSmrg wait_info->client = client; 973d6c0b56eSmrg wait_info->type = DRI2_WAITMSC; 974d6c0b56eSmrg wait_info->crtc = crtc; 975d6c0b56eSmrg 976d6c0b56eSmrg /* 977d6c0b56eSmrg * CRTC is in DPMS off state, calculate wait time from current time, 978d6c0b56eSmrg * target_msc and last vblank time/sequence when CRTC was turned off 979d6c0b56eSmrg */ 980d6c0b56eSmrg if (!amdgpu_crtc_is_enabled(crtc)) { 981d6c0b56eSmrg CARD32 delay; 982d6c0b56eSmrg target_msc -= msc_delta; 983d6c0b56eSmrg delay = amdgpu_dri2_extrapolate_msc_delay(crtc, &target_msc, 984d6c0b56eSmrg divisor, remainder); 985d6c0b56eSmrg amdgpu_dri2_schedule_event(delay, wait_info); 986d6c0b56eSmrg DRI2BlockClient(client, draw); 987d6c0b56eSmrg return TRUE; 988d6c0b56eSmrg } 989d6c0b56eSmrg 990d6c0b56eSmrg /* Get current count */ 99124b90cf4Smrg if (!drmmode_wait_vblank(crtc, DRM_VBLANK_RELATIVE, 0, 0, NULL, &seq)) { 992d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 993d6c0b56eSmrg "get vblank counter failed: %s\n", strerror(errno)); 994d6c0b56eSmrg goto out_complete; 995d6c0b56eSmrg } 996d6c0b56eSmrg 99724b90cf4Smrg current_msc = seq + msc_delta; 998d6c0b56eSmrg current_msc &= 0xffffffff; 999d6c0b56eSmrg 1000d6c0b56eSmrg drm_queue_seq = amdgpu_drm_queue_alloc(crtc, client, AMDGPU_DRM_QUEUE_ID_DEFAULT, 1001d6c0b56eSmrg wait_info, amdgpu_dri2_frame_event_handler, 100290f2b693Smrg amdgpu_dri2_frame_event_abort, FALSE); 1003504d986fSmrg if (drm_queue_seq == AMDGPU_DRM_QUEUE_ERROR) { 1004d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 1005d6c0b56eSmrg "Allocating DRM queue event entry failed.\n"); 1006d6c0b56eSmrg goto out_complete; 1007d6c0b56eSmrg } 1008d6c0b56eSmrg wait_info->drm_queue_seq = drm_queue_seq; 1009d6c0b56eSmrg 1010d6c0b56eSmrg /* 1011d6c0b56eSmrg * If divisor is zero, or current_msc is smaller than target_msc, 1012d6c0b56eSmrg * we just need to make sure target_msc passes before waking up the 1013d6c0b56eSmrg * client. 1014d6c0b56eSmrg */ 1015d6c0b56eSmrg if (divisor == 0 || current_msc < target_msc) { 1016d6c0b56eSmrg /* If target_msc already reached or passed, set it to 1017d6c0b56eSmrg * current_msc to ensure we return a reasonable value back 1018d6c0b56eSmrg * to the caller. This keeps the client from continually 1019d6c0b56eSmrg * sending us MSC targets from the past by forcibly updating 1020d6c0b56eSmrg * their count on this call. 1021d6c0b56eSmrg */ 1022d6c0b56eSmrg if (current_msc >= target_msc) 1023d6c0b56eSmrg target_msc = current_msc; 102424b90cf4Smrg if (!drmmode_wait_vblank(crtc, DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT, 102524b90cf4Smrg target_msc - msc_delta, drm_queue_seq, NULL, 102624b90cf4Smrg NULL)) { 1027d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 1028d6c0b56eSmrg "get vblank counter failed: %s\n", 1029d6c0b56eSmrg strerror(errno)); 1030d6c0b56eSmrg goto out_complete; 1031d6c0b56eSmrg } 1032d6c0b56eSmrg 1033d6c0b56eSmrg DRI2BlockClient(client, draw); 1034d6c0b56eSmrg return TRUE; 1035d6c0b56eSmrg } 1036d6c0b56eSmrg 1037d6c0b56eSmrg /* 1038d6c0b56eSmrg * If we get here, target_msc has already passed or we don't have one, 1039d6c0b56eSmrg * so we queue an event that will satisfy the divisor/remainder equation. 1040d6c0b56eSmrg */ 104124b90cf4Smrg target_msc = current_msc - (current_msc % divisor) + remainder - msc_delta; 1042d6c0b56eSmrg 1043d6c0b56eSmrg /* 1044d6c0b56eSmrg * If calculated remainder is larger than requested remainder, 1045d6c0b56eSmrg * it means we've passed the last point where 1046d6c0b56eSmrg * seq % divisor == remainder, so we need to wait for the next time 1047d6c0b56eSmrg * that will happen. 1048d6c0b56eSmrg */ 1049d6c0b56eSmrg if ((current_msc % divisor) >= remainder) 105024b90cf4Smrg target_msc += divisor; 1051d6c0b56eSmrg 105224b90cf4Smrg if (!drmmode_wait_vblank(crtc, DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT, 105324b90cf4Smrg target_msc, drm_queue_seq, NULL, NULL)) { 1054d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 1055d6c0b56eSmrg "get vblank counter failed: %s\n", strerror(errno)); 1056d6c0b56eSmrg goto out_complete; 1057d6c0b56eSmrg } 1058d6c0b56eSmrg 1059d6c0b56eSmrg DRI2BlockClient(client, draw); 1060d6c0b56eSmrg 1061d6c0b56eSmrg return TRUE; 1062d6c0b56eSmrg 1063d6c0b56eSmrgout_complete: 1064d6c0b56eSmrg if (wait_info) 1065d6c0b56eSmrg amdgpu_dri2_deferred_event(NULL, 0, wait_info); 106677d6d1ecSmrg else 106777d6d1ecSmrg DRI2WaitMSCComplete(client, draw, 0, 0, 0); 106877d6d1ecSmrg 1069d6c0b56eSmrg return TRUE; 1070d6c0b56eSmrg} 1071d6c0b56eSmrg 1072d6c0b56eSmrg/* 1073d6c0b56eSmrg * ScheduleSwap is responsible for requesting a DRM vblank event for the 1074d6c0b56eSmrg * appropriate frame. 1075d6c0b56eSmrg * 1076d6c0b56eSmrg * In the case of a blit (e.g. for a windowed swap) or buffer exchange, 1077d6c0b56eSmrg * the vblank requested can simply be the last queued swap frame + the swap 1078d6c0b56eSmrg * interval for the drawable. 1079d6c0b56eSmrg * 1080d6c0b56eSmrg * In the case of a page flip, we request an event for the last queued swap 1081d6c0b56eSmrg * frame + swap interval - 1, since we'll need to queue the flip for the frame 1082d6c0b56eSmrg * immediately following the received event. 1083d6c0b56eSmrg * 1084d6c0b56eSmrg * The client will be blocked if it tries to perform further GL commands 1085d6c0b56eSmrg * after queueing a swap, though in the Intel case after queueing a flip, the 1086d6c0b56eSmrg * client is free to queue more commands; they'll block in the kernel if 1087d6c0b56eSmrg * they access buffers busy with the flip. 1088d6c0b56eSmrg * 1089d6c0b56eSmrg * When the swap is complete, the driver should call into the server so it 1090d6c0b56eSmrg * can send any swap complete events that have been requested. 1091d6c0b56eSmrg */ 1092d6c0b56eSmrgstatic int amdgpu_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, 1093d6c0b56eSmrg DRI2BufferPtr front, DRI2BufferPtr back, 1094d6c0b56eSmrg CARD64 * target_msc, CARD64 divisor, 1095d6c0b56eSmrg CARD64 remainder, DRI2SwapEventPtr func, 1096d6c0b56eSmrg void *data) 1097d6c0b56eSmrg{ 1098d6c0b56eSmrg ScreenPtr screen = draw->pScreen; 1099d6c0b56eSmrg ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 110077d6d1ecSmrg xf86CrtcPtr crtc = amdgpu_dri2_drawable_crtc(draw); 1101d6c0b56eSmrg uint32_t msc_delta; 110224b90cf4Smrg drmVBlankSeqType type; 110324b90cf4Smrg uint32_t seq; 110424b90cf4Smrg int flip = 0; 1105d6c0b56eSmrg DRI2FrameEventPtr swap_info = NULL; 1106d6c0b56eSmrg uintptr_t drm_queue_seq; 110724b90cf4Smrg CARD64 current_msc, event_msc; 1108d6c0b56eSmrg BoxRec box; 1109d6c0b56eSmrg RegionRec region; 1110d6c0b56eSmrg 1111d6c0b56eSmrg /* Truncate to match kernel interfaces; means occasional overflow 1112d6c0b56eSmrg * misses, but that's generally not a big deal */ 1113d6c0b56eSmrg *target_msc &= 0xffffffff; 1114d6c0b56eSmrg divisor &= 0xffffffff; 1115d6c0b56eSmrg remainder &= 0xffffffff; 1116d6c0b56eSmrg 1117d6c0b56eSmrg /* amdgpu_dri2_frame_event_handler will get called some unknown time in the 1118d6c0b56eSmrg * future with these buffers. Take a reference to ensure that they won't 1119d6c0b56eSmrg * get destroyed before then. 1120d6c0b56eSmrg */ 1121d6c0b56eSmrg amdgpu_dri2_ref_buffer(front); 1122d6c0b56eSmrg amdgpu_dri2_ref_buffer(back); 1123d6c0b56eSmrg 1124d6c0b56eSmrg /* either off-screen or CRTC not usable... just complete the swap */ 112535d5b7c7Smrg if (!crtc) 1126d6c0b56eSmrg goto blit_fallback; 1127d6c0b56eSmrg 1128d6c0b56eSmrg msc_delta = amdgpu_get_msc_delta(draw, crtc); 1129d6c0b56eSmrg 1130d6c0b56eSmrg swap_info = calloc(1, sizeof(DRI2FrameEventRec)); 1131d6c0b56eSmrg if (!swap_info) 1132d6c0b56eSmrg goto blit_fallback; 1133d6c0b56eSmrg 1134d6c0b56eSmrg swap_info->type = DRI2_SWAP; 1135d6c0b56eSmrg swap_info->drawable_id = draw->id; 1136d6c0b56eSmrg swap_info->client = client; 1137d6c0b56eSmrg swap_info->event_complete = func; 1138d6c0b56eSmrg swap_info->event_data = data; 1139d6c0b56eSmrg swap_info->front = front; 1140d6c0b56eSmrg swap_info->back = back; 1141d6c0b56eSmrg swap_info->crtc = crtc; 1142d6c0b56eSmrg 1143d6c0b56eSmrg drm_queue_seq = amdgpu_drm_queue_alloc(crtc, client, AMDGPU_DRM_QUEUE_ID_DEFAULT, 1144d6c0b56eSmrg swap_info, amdgpu_dri2_frame_event_handler, 114590f2b693Smrg amdgpu_dri2_frame_event_abort, FALSE); 1146504d986fSmrg if (drm_queue_seq == AMDGPU_DRM_QUEUE_ERROR) { 1147d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 1148d6c0b56eSmrg "Allocating DRM queue entry failed.\n"); 1149d6c0b56eSmrg goto blit_fallback; 1150d6c0b56eSmrg } 1151d6c0b56eSmrg swap_info->drm_queue_seq = drm_queue_seq; 1152d6c0b56eSmrg 1153d6c0b56eSmrg /* 1154d6c0b56eSmrg * CRTC is in DPMS off state, fallback to blit, but calculate 1155d6c0b56eSmrg * wait time from current time, target_msc and last vblank 1156d6c0b56eSmrg * time/sequence when CRTC was turned off 1157d6c0b56eSmrg */ 1158d6c0b56eSmrg if (!amdgpu_crtc_is_enabled(crtc)) { 1159d6c0b56eSmrg CARD32 delay; 1160d6c0b56eSmrg *target_msc -= msc_delta; 1161d6c0b56eSmrg delay = amdgpu_dri2_extrapolate_msc_delay(crtc, target_msc, 1162d6c0b56eSmrg divisor, remainder); 1163d6c0b56eSmrg *target_msc += msc_delta; 1164d6c0b56eSmrg *target_msc &= 0xffffffff; 1165d6c0b56eSmrg amdgpu_dri2_schedule_event(delay, swap_info); 1166d6c0b56eSmrg return TRUE; 1167d6c0b56eSmrg } 1168d6c0b56eSmrg 1169d6c0b56eSmrg /* Get current count */ 117024b90cf4Smrg if (!drmmode_wait_vblank(crtc, DRM_VBLANK_RELATIVE, 0, 0, NULL, &seq)) { 1171d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 1172d6c0b56eSmrg "first get vblank counter failed: %s\n", 1173d6c0b56eSmrg strerror(errno)); 1174d6c0b56eSmrg goto blit_fallback; 1175d6c0b56eSmrg } 1176d6c0b56eSmrg 117724b90cf4Smrg current_msc = seq + msc_delta; 1178d6c0b56eSmrg current_msc &= 0xffffffff; 1179d6c0b56eSmrg 1180d6c0b56eSmrg /* Flips need to be submitted one frame before */ 118124b90cf4Smrg if (can_flip(crtc, draw, front, back)) { 1182d6c0b56eSmrg swap_info->type = DRI2_FLIP; 1183d6c0b56eSmrg flip = 1; 1184d6c0b56eSmrg } 1185d6c0b56eSmrg 1186d6c0b56eSmrg /* Correct target_msc by 'flip' if swap_info->type == DRI2_FLIP. 1187d6c0b56eSmrg * Do it early, so handling of different timing constraints 1188d6c0b56eSmrg * for divisor, remainder and msc vs. target_msc works. 1189d6c0b56eSmrg */ 1190d6c0b56eSmrg if (*target_msc > 0) 1191d6c0b56eSmrg *target_msc -= flip; 1192d6c0b56eSmrg 1193d6c0b56eSmrg /* 1194d6c0b56eSmrg * If divisor is zero, or current_msc is smaller than target_msc 1195d6c0b56eSmrg * we just need to make sure target_msc passes before initiating 1196d6c0b56eSmrg * the swap. 1197d6c0b56eSmrg */ 1198d6c0b56eSmrg if (divisor == 0 || current_msc < *target_msc) { 119924b90cf4Smrg type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; 1200d6c0b56eSmrg /* If non-pageflipping, but blitting/exchanging, we need to use 1201d6c0b56eSmrg * DRM_VBLANK_NEXTONMISS to avoid unreliable timestamping later 1202d6c0b56eSmrg * on. 1203d6c0b56eSmrg */ 1204d6c0b56eSmrg if (flip == 0) 120524b90cf4Smrg type |= DRM_VBLANK_NEXTONMISS; 1206d6c0b56eSmrg 1207d6c0b56eSmrg /* If target_msc already reached or passed, set it to 1208d6c0b56eSmrg * current_msc to ensure we return a reasonable value back 1209d6c0b56eSmrg * to the caller. This makes swap_interval logic more robust. 1210d6c0b56eSmrg */ 1211d6c0b56eSmrg if (current_msc >= *target_msc) 1212d6c0b56eSmrg *target_msc = current_msc; 1213d6c0b56eSmrg 121424b90cf4Smrg if (!drmmode_wait_vblank(crtc, type, *target_msc - msc_delta, 121524b90cf4Smrg drm_queue_seq, NULL, &seq)) { 1216d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 1217d6c0b56eSmrg "divisor 0 get vblank counter failed: %s\n", 1218d6c0b56eSmrg strerror(errno)); 1219d6c0b56eSmrg goto blit_fallback; 1220d6c0b56eSmrg } 1221d6c0b56eSmrg 122224b90cf4Smrg *target_msc = seq + flip + msc_delta; 1223d6c0b56eSmrg *target_msc &= 0xffffffff; 1224d6c0b56eSmrg swap_info->frame = *target_msc; 1225d6c0b56eSmrg 1226d6c0b56eSmrg return TRUE; 1227d6c0b56eSmrg } 1228d6c0b56eSmrg 1229d6c0b56eSmrg /* 1230d6c0b56eSmrg * If we get here, target_msc has already passed or we don't have one, 1231d6c0b56eSmrg * and we need to queue an event that will satisfy the divisor/remainder 1232d6c0b56eSmrg * equation. 1233d6c0b56eSmrg */ 123424b90cf4Smrg type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; 1235d6c0b56eSmrg if (flip == 0) 123624b90cf4Smrg type |= DRM_VBLANK_NEXTONMISS; 1237d6c0b56eSmrg 123824b90cf4Smrg event_msc = current_msc - (current_msc % divisor) + remainder - msc_delta; 1239d6c0b56eSmrg 1240d6c0b56eSmrg /* 1241d6c0b56eSmrg * If the calculated deadline vbl.request.sequence is smaller than 1242d6c0b56eSmrg * or equal to current_msc, it means we've passed the last point 1243d6c0b56eSmrg * when effective onset frame seq could satisfy 1244d6c0b56eSmrg * seq % divisor == remainder, so we need to wait for the next time 1245d6c0b56eSmrg * this will happen. 1246d6c0b56eSmrg 1247d6c0b56eSmrg * This comparison takes the 1 frame swap delay in pageflipping mode 1248d6c0b56eSmrg * into account, as well as a potential DRM_VBLANK_NEXTONMISS delay 1249d6c0b56eSmrg * if we are blitting/exchanging instead of flipping. 1250d6c0b56eSmrg */ 125124b90cf4Smrg if (event_msc <= current_msc) 125224b90cf4Smrg event_msc += divisor; 1253d6c0b56eSmrg 1254d6c0b56eSmrg /* Account for 1 frame extra pageflip delay if flip > 0 */ 125524b90cf4Smrg event_msc -= flip; 1256d6c0b56eSmrg 125724b90cf4Smrg if (!drmmode_wait_vblank(crtc, type, event_msc, drm_queue_seq, NULL, &seq)) { 1258d6c0b56eSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 1259d6c0b56eSmrg "final get vblank counter failed: %s\n", 1260d6c0b56eSmrg strerror(errno)); 1261d6c0b56eSmrg goto blit_fallback; 1262d6c0b56eSmrg } 1263d6c0b56eSmrg 1264d6c0b56eSmrg /* Adjust returned value for 1 fame pageflip offset of flip > 0 */ 126524b90cf4Smrg *target_msc = seq + flip + msc_delta; 1266d6c0b56eSmrg *target_msc &= 0xffffffff; 1267d6c0b56eSmrg swap_info->frame = *target_msc; 1268d6c0b56eSmrg 1269d6c0b56eSmrg return TRUE; 1270d6c0b56eSmrg 1271d6c0b56eSmrgblit_fallback: 1272d6c0b56eSmrg if (swap_info) { 1273d6c0b56eSmrg swap_info->type = DRI2_SWAP; 1274d6c0b56eSmrg amdgpu_dri2_schedule_event(FALLBACK_SWAP_DELAY, swap_info); 1275d6c0b56eSmrg } else { 1276d6c0b56eSmrg box.x1 = 0; 1277d6c0b56eSmrg box.y1 = 0; 1278d6c0b56eSmrg box.x2 = draw->width; 1279d6c0b56eSmrg box.y2 = draw->height; 1280d6c0b56eSmrg REGION_INIT(pScreen, ®ion, &box, 0); 1281d6c0b56eSmrg 128224b90cf4Smrg amdgpu_dri2_copy_region2(draw->pScreen, draw, ®ion, front, back); 1283d6c0b56eSmrg 1284d6c0b56eSmrg DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data); 1285d6c0b56eSmrg 1286d6c0b56eSmrg amdgpu_dri2_unref_buffer(front); 1287d6c0b56eSmrg amdgpu_dri2_unref_buffer(back); 1288d6c0b56eSmrg } 1289d6c0b56eSmrg 1290d6c0b56eSmrg *target_msc = 0; /* offscreen, so zero out target vblank count */ 1291d6c0b56eSmrg return TRUE; 1292d6c0b56eSmrg} 1293d6c0b56eSmrg 1294d6c0b56eSmrgBool amdgpu_dri2_screen_init(ScreenPtr pScreen) 1295d6c0b56eSmrg{ 1296d6c0b56eSmrg ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); 1297d6c0b56eSmrg AMDGPUInfoPtr info = AMDGPUPTR(pScrn); 1298d6c0b56eSmrg AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn); 1299d6c0b56eSmrg DRI2InfoRec dri2_info = { 0 }; 1300d6c0b56eSmrg const char *driverNames[2]; 1301d6c0b56eSmrg Bool scheduling_works = TRUE; 1302d6c0b56eSmrg 1303d6c0b56eSmrg if (!info->dri2.available) 1304d6c0b56eSmrg return FALSE; 1305d6c0b56eSmrg 1306d6c0b56eSmrg info->dri2.device_name = drmGetDeviceNameFromFd(pAMDGPUEnt->fd); 1307d6c0b56eSmrg 1308d6c0b56eSmrg dri2_info.driverName = SI_DRIVER_NAME; 1309d6c0b56eSmrg dri2_info.fd = pAMDGPUEnt->fd; 1310d6c0b56eSmrg dri2_info.deviceName = info->dri2.device_name; 1311d6c0b56eSmrg 1312d6c0b56eSmrg if (info->drmmode.count_crtcs > 2) { 1313d6c0b56eSmrg uint64_t cap_value; 1314d6c0b56eSmrg 1315d6c0b56eSmrg if (drmGetCap 1316d6c0b56eSmrg (pAMDGPUEnt->fd, DRM_CAP_VBLANK_HIGH_CRTC, &cap_value)) { 1317d6c0b56eSmrg xf86DrvMsg(pScrn->scrnIndex, X_WARNING, 1318d6c0b56eSmrg "You need a newer kernel " 1319d6c0b56eSmrg "for VBLANKs on CRTC > 1\n"); 1320d6c0b56eSmrg scheduling_works = FALSE; 1321d6c0b56eSmrg } else if (!cap_value) { 1322d6c0b56eSmrg xf86DrvMsg(pScrn->scrnIndex, X_WARNING, 1323d6c0b56eSmrg "Your kernel does not " 1324d6c0b56eSmrg "handle VBLANKs on CRTC > 1\n"); 1325d6c0b56eSmrg scheduling_works = FALSE; 1326d6c0b56eSmrg } 1327d6c0b56eSmrg } 1328d6c0b56eSmrg 1329d6c0b56eSmrg if (scheduling_works) { 1330d6c0b56eSmrg dri2_info.ScheduleSwap = amdgpu_dri2_schedule_swap; 1331d6c0b56eSmrg dri2_info.GetMSC = amdgpu_dri2_get_msc; 1332d6c0b56eSmrg dri2_info.ScheduleWaitMSC = amdgpu_dri2_schedule_wait_msc; 133324b90cf4Smrg dri2_info.numDrivers = ARRAY_SIZE(driverNames); 1334d6c0b56eSmrg dri2_info.driverNames = driverNames; 1335d6c0b56eSmrg driverNames[0] = driverNames[1] = dri2_info.driverName; 1336d6c0b56eSmrg 1337d6c0b56eSmrg if (DRI2InfoCnt == 0) { 1338d6c0b56eSmrg if (!dixRegisterPrivateKey(dri2_window_private_key, 1339d6c0b56eSmrg PRIVATE_WINDOW, 1340d6c0b56eSmrg sizeof(struct dri2_window_priv))) { 1341d6c0b56eSmrg xf86DrvMsg(pScrn->scrnIndex, X_WARNING, 1342d6c0b56eSmrg "Failed to get DRI2 window private\n"); 1343d6c0b56eSmrg return FALSE; 1344d6c0b56eSmrg } 1345d6c0b56eSmrg 1346d6c0b56eSmrg AddCallback(&ClientStateCallback, 1347d6c0b56eSmrg amdgpu_dri2_client_state_changed, 0); 1348d6c0b56eSmrg } 1349d6c0b56eSmrg 1350d6c0b56eSmrg DRI2InfoCnt++; 1351d6c0b56eSmrg } 1352d6c0b56eSmrg 1353d6c0b56eSmrg dri2_info.version = 9; 1354d6c0b56eSmrg dri2_info.CreateBuffer2 = amdgpu_dri2_create_buffer2; 1355d6c0b56eSmrg dri2_info.DestroyBuffer2 = amdgpu_dri2_destroy_buffer2; 1356d6c0b56eSmrg dri2_info.CopyRegion2 = amdgpu_dri2_copy_region2; 1357d6c0b56eSmrg 1358d6c0b56eSmrg info->dri2.enabled = DRI2ScreenInit(pScreen, &dri2_info); 1359d6c0b56eSmrg return info->dri2.enabled; 1360d6c0b56eSmrg} 1361d6c0b56eSmrg 1362d6c0b56eSmrgvoid amdgpu_dri2_close_screen(ScreenPtr pScreen) 1363d6c0b56eSmrg{ 1364d6c0b56eSmrg ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); 1365d6c0b56eSmrg AMDGPUInfoPtr info = AMDGPUPTR(pScrn); 1366d6c0b56eSmrg 1367d6c0b56eSmrg if (--DRI2InfoCnt == 0) 1368d6c0b56eSmrg DeleteCallback(&ClientStateCallback, 1369d6c0b56eSmrg amdgpu_dri2_client_state_changed, 0); 1370d6c0b56eSmrg 1371d6c0b56eSmrg DRI2CloseScreen(pScreen); 1372d6c0b56eSmrg drmFree(info->dri2.device_name); 1373d6c0b56eSmrg} 1374d6c0b56eSmrg 1375d6c0b56eSmrg#endif /* DRI2 */ 1376