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, &region);
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, &region, &box, 0);
73124b90cf4Smrg			amdgpu_dri2_copy_region2(drawable->pScreen, drawable, &region,
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, &region, &box, 0);
1281d6c0b56eSmrg
128224b90cf4Smrg		amdgpu_dri2_copy_region2(draw->pScreen, draw, &region, 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