1428d7b3dSmrg/**************************************************************************
2428d7b3dSmrg
3428d7b3dSmrgCopyright 2001 VA Linux Systems Inc., Fremont, California.
4428d7b3dSmrgCopyright © 2002 by David Dawes
5428d7b3dSmrg
6428d7b3dSmrgAll Rights Reserved.
7428d7b3dSmrg
8428d7b3dSmrgPermission is hereby granted, free of charge, to any person obtaining a
9428d7b3dSmrgcopy of this software and associated documentation files (the "Software"),
10428d7b3dSmrgto deal in the Software without restriction, including without limitation
11428d7b3dSmrgon the rights to use, copy, modify, merge, publish, distribute, sub
12428d7b3dSmrglicense, and/or sell copies of the Software, and to permit persons to whom
13428d7b3dSmrgthe Software is furnished to do so, subject to the following conditions:
14428d7b3dSmrg
15428d7b3dSmrgThe above copyright notice and this permission notice (including the next
16428d7b3dSmrgparagraph) shall be included in all copies or substantial portions of the
17428d7b3dSmrgSoftware.
18428d7b3dSmrg
19428d7b3dSmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20428d7b3dSmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21428d7b3dSmrgFITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
22428d7b3dSmrgATI, VA LINUX SYSTEMS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
23428d7b3dSmrgDAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24428d7b3dSmrgOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25428d7b3dSmrgUSE OR OTHER DEALINGS IN THE SOFTWARE.
26428d7b3dSmrg
27428d7b3dSmrg**************************************************************************/
28428d7b3dSmrg
29428d7b3dSmrg/*
30428d7b3dSmrg * Authors: Jeff Hartmann <jhartmann@valinux.com>
31428d7b3dSmrg *          David Dawes <dawes@xfree86.org>
32428d7b3dSmrg *          Keith Whitwell <keith@tungstengraphics.com>
33428d7b3dSmrg */
34428d7b3dSmrg
35428d7b3dSmrg#ifdef HAVE_CONFIG_H
36428d7b3dSmrg#include "config.h"
37428d7b3dSmrg#endif
38428d7b3dSmrg
39428d7b3dSmrg#include <errno.h>
40428d7b3dSmrg#include <time.h>
41428d7b3dSmrg#include <string.h>
42428d7b3dSmrg#include <unistd.h>
43428d7b3dSmrg#include <poll.h>
44428d7b3dSmrg
45428d7b3dSmrg#include "sna.h"
46428d7b3dSmrg#include "intel_options.h"
47428d7b3dSmrg
48428d7b3dSmrg#include <xf86drm.h>
49428d7b3dSmrg#include <i915_drm.h>
50428d7b3dSmrg#include <dri2.h>
51428d7b3dSmrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,12,99,901,0) && defined(COMPOSITE)
52428d7b3dSmrg#include <compositeext.h>
53428d7b3dSmrg#define CHECK_FOR_COMPOSITOR
54428d7b3dSmrg#endif
55428d7b3dSmrg
56428d7b3dSmrg#define DBG_CAN_FLIP 1
57428d7b3dSmrg#define DBG_CAN_XCHG 1
58428d7b3dSmrg
59428d7b3dSmrg#define DBG_FORCE_COPY -1 /* KGEM_BLT or KGEM_3D */
60428d7b3dSmrg
61428d7b3dSmrg#if DRI2INFOREC_VERSION < 2
62428d7b3dSmrg#error DRI2 version supported by the Xserver is too old
63428d7b3dSmrg#endif
64428d7b3dSmrg
65428d7b3dSmrgstatic inline struct kgem_bo *ref(struct kgem_bo *bo)
66428d7b3dSmrg{
67428d7b3dSmrg	return kgem_bo_reference(bo);
68428d7b3dSmrg}
69428d7b3dSmrg
70428d7b3dSmrgstruct sna_dri2_private {
71428d7b3dSmrg	PixmapPtr pixmap;
72428d7b3dSmrg	struct kgem_bo *bo;
73428d7b3dSmrg	DRI2Buffer2Ptr proxy;
74428d7b3dSmrg	bool stale;
75428d7b3dSmrg	uint32_t size;
76428d7b3dSmrg	int refcnt;
77428d7b3dSmrg};
78428d7b3dSmrg
79428d7b3dSmrgstatic inline struct sna_dri2_private *
80428d7b3dSmrgget_private(void *buffer)
81428d7b3dSmrg{
82428d7b3dSmrg	return (struct sna_dri2_private *)((DRI2Buffer2Ptr)buffer+1);
83428d7b3dSmrg}
84428d7b3dSmrg
85428d7b3dSmrg#if DRI2INFOREC_VERSION >= 4
86428d7b3dSmrgenum event_type {
87428d7b3dSmrg	WAITMSC = 0,
88428d7b3dSmrg	SWAP,
89428d7b3dSmrg	SWAP_WAIT,
90428d7b3dSmrg	SWAP_THROTTLE,
91428d7b3dSmrg	FLIP,
92428d7b3dSmrg	FLIP_THROTTLE,
93428d7b3dSmrg	FLIP_COMPLETE,
94428d7b3dSmrg	FLIP_ASYNC,
95428d7b3dSmrg};
96428d7b3dSmrg
97428d7b3dSmrgstruct dri_bo {
98428d7b3dSmrg	struct list link;
99428d7b3dSmrg	struct kgem_bo *bo;
100428d7b3dSmrg	uint32_t name;
101428d7b3dSmrg};
102428d7b3dSmrg
103428d7b3dSmrgstruct sna_dri2_event {
104428d7b3dSmrg	struct sna *sna;
105428d7b3dSmrg	DrawablePtr draw;
106428d7b3dSmrg	ClientPtr client;
107428d7b3dSmrg	enum event_type type;
108428d7b3dSmrg	xf86CrtcPtr crtc;
109428d7b3dSmrg	int pipe;
110428d7b3dSmrg	bool queued;
111428d7b3dSmrg
112428d7b3dSmrg	/* for swaps & flips only */
113428d7b3dSmrg	DRI2SwapEventPtr event_complete;
114428d7b3dSmrg	void *event_data;
115428d7b3dSmrg	DRI2BufferPtr front;
116428d7b3dSmrg	DRI2BufferPtr back;
117428d7b3dSmrg	struct kgem_bo *bo;
118428d7b3dSmrg
119428d7b3dSmrg	struct sna_dri2_event *chain;
120428d7b3dSmrg
121428d7b3dSmrg	struct list cache;
122428d7b3dSmrg	struct list link;
123428d7b3dSmrg
124428d7b3dSmrg	int mode;
125428d7b3dSmrg};
126428d7b3dSmrg
127428d7b3dSmrgstatic void sna_dri2_flip_event(struct sna_dri2_event *flip);
128428d7b3dSmrg
129428d7b3dSmrgstatic void
130428d7b3dSmrgsna_dri2_get_back(struct sna *sna,
131428d7b3dSmrg		  DrawablePtr draw,
132428d7b3dSmrg		  DRI2BufferPtr back,
133428d7b3dSmrg		  struct sna_dri2_event *info)
134428d7b3dSmrg{
135428d7b3dSmrg	struct kgem_bo *bo;
136428d7b3dSmrg	uint32_t name;
137428d7b3dSmrg	bool reuse;
138428d7b3dSmrg
139428d7b3dSmrg	DBG(("%s: draw size=%dx%d, buffer size=%dx%d\n",
140428d7b3dSmrg	     __FUNCTION__, draw->width, draw->height,
141428d7b3dSmrg	     get_private(back)->size & 0xffff, get_private(back)->size >> 16));
142428d7b3dSmrg	reuse = (draw->height << 16 | draw->width) == get_private(back)->size;
143428d7b3dSmrg	if (reuse) {
144428d7b3dSmrg		bo = get_private(back)->bo;
145428d7b3dSmrg		assert(bo->refcnt);
146428d7b3dSmrg		DBG(("%s: back buffer handle=%d, scanout?=%d, refcnt=%d\n",
147428d7b3dSmrg					__FUNCTION__, bo->handle, bo->active_scanout, get_private(back)->refcnt));
148428d7b3dSmrg		if (bo->active_scanout == 0) {
149428d7b3dSmrg			DBG(("%s: reuse unattached back\n", __FUNCTION__));
150428d7b3dSmrg			get_private(back)->stale = false;
151428d7b3dSmrg			return;
152428d7b3dSmrg		}
153428d7b3dSmrg	}
154428d7b3dSmrg
155428d7b3dSmrg	bo = NULL;
156428d7b3dSmrg	if (info) {
157428d7b3dSmrg		struct dri_bo *c;
158428d7b3dSmrg		list_for_each_entry(c, &info->cache, link) {
159428d7b3dSmrg			if (c->bo && c->bo->scanout == 0) {
160428d7b3dSmrg				bo = c->bo;
161428d7b3dSmrg				name = c->name;
162428d7b3dSmrg				DBG(("%s: reuse cache handle=%d\n", __FUNCTION__, bo->handle));
163428d7b3dSmrg				list_move_tail(&c->link, &info->cache);
164428d7b3dSmrg				c->bo = NULL;
165428d7b3dSmrg			}
166428d7b3dSmrg		}
167428d7b3dSmrg	}
168428d7b3dSmrg	if (bo == NULL) {
169428d7b3dSmrg		DBG(("%s: allocating new backbuffer\n", __FUNCTION__));
170428d7b3dSmrg		bo = kgem_create_2d(&sna->kgem,
171428d7b3dSmrg				    draw->width, draw->height, draw->bitsPerPixel,
172428d7b3dSmrg				    get_private(back)->bo->tiling,
173428d7b3dSmrg				    get_private(back)->bo->scanout ? CREATE_SCANOUT : 0);
174428d7b3dSmrg		if (bo == NULL)
175428d7b3dSmrg			return;
176428d7b3dSmrg
177428d7b3dSmrg		name = kgem_bo_flink(&sna->kgem, bo);
178428d7b3dSmrg		if (name == 0) {
179428d7b3dSmrg			kgem_bo_destroy(&sna->kgem, bo);
180428d7b3dSmrg			return;
181428d7b3dSmrg		}
182428d7b3dSmrg	}
183428d7b3dSmrg	assert(bo->active_scanout == 0);
184428d7b3dSmrg
185428d7b3dSmrg	if (info && reuse) {
186428d7b3dSmrg		bool found = false;
187428d7b3dSmrg		struct dri_bo *c;
188428d7b3dSmrg
189428d7b3dSmrg		list_for_each_entry_reverse(c, &info->cache, link) {
190428d7b3dSmrg			if (c->bo == NULL) {
191428d7b3dSmrg				found = true;
192428d7b3dSmrg				_list_del(&c->link);
193428d7b3dSmrg				break;
194428d7b3dSmrg			}
195428d7b3dSmrg		}
196428d7b3dSmrg		if (!found)
197428d7b3dSmrg			c = malloc(sizeof(*c));
198428d7b3dSmrg		if (c != NULL) {
199428d7b3dSmrg			c->bo = ref(get_private(back)->bo);
200428d7b3dSmrg			c->name = back->name;
201428d7b3dSmrg			list_add(&c->link, &info->cache);
202428d7b3dSmrg			DBG(("%s: cacheing handle=%d (name=%d)\n", __FUNCTION__, c->bo->handle, c->name));
203428d7b3dSmrg		}
204428d7b3dSmrg	}
205428d7b3dSmrg
206428d7b3dSmrg	assert(bo != get_private(back)->bo);
207428d7b3dSmrg	kgem_bo_destroy(&sna->kgem, get_private(back)->bo);
208428d7b3dSmrg
209428d7b3dSmrg	get_private(back)->bo = bo;
210428d7b3dSmrg	get_private(back)->size = draw->height << 16 | draw->width;
211428d7b3dSmrg	back->pitch = bo->pitch;
212428d7b3dSmrg	back->name = name;
213428d7b3dSmrg
214428d7b3dSmrg	get_private(back)->stale = false;
215428d7b3dSmrg}
216428d7b3dSmrg
217428d7b3dSmrgstruct dri2_window {
218428d7b3dSmrg	DRI2BufferPtr front;
219428d7b3dSmrg	struct sna_dri2_event *chain;
220428d7b3dSmrg	xf86CrtcPtr crtc;
221428d7b3dSmrg	int64_t msc_delta;
222428d7b3dSmrg};
223428d7b3dSmrg
224428d7b3dSmrgstatic struct dri2_window *dri2_window(WindowPtr win)
225428d7b3dSmrg{
226428d7b3dSmrg	assert(win->drawable.type != DRAWABLE_PIXMAP);
227428d7b3dSmrg	return ((void **)__get_private(win, sna_window_key))[1];
228428d7b3dSmrg}
229428d7b3dSmrg
230428d7b3dSmrgstatic struct sna_dri2_event *
231428d7b3dSmrgdri2_chain(DrawablePtr d)
232428d7b3dSmrg{
233428d7b3dSmrg	struct dri2_window *priv = dri2_window((WindowPtr)d);
234428d7b3dSmrg	assert(priv != NULL);
235428d7b3dSmrg	return priv->chain;
236428d7b3dSmrg}
237428d7b3dSmrginline static DRI2BufferPtr dri2_window_get_front(WindowPtr win)
238428d7b3dSmrg{
239428d7b3dSmrg	struct dri2_window *priv = dri2_window(win);
240428d7b3dSmrg	return priv ? priv->front : NULL;
241428d7b3dSmrg}
242428d7b3dSmrg#else
243428d7b3dSmrginline static void *dri2_window_get_front(WindowPtr win) { return NULL; }
244428d7b3dSmrg#endif
245428d7b3dSmrg
246428d7b3dSmrg#if DRI2INFOREC_VERSION < 6
247428d7b3dSmrg
248428d7b3dSmrg#define xorg_can_triple_buffer() 0
249428d7b3dSmrg#define swap_limit(d, l) false
250428d7b3dSmrg#define mark_stale(b)
251428d7b3dSmrg
252428d7b3dSmrg#else
253428d7b3dSmrg
254428d7b3dSmrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,15,99,904,0)
255428d7b3dSmrg/* Prime fixed for triple buffer support */
256428d7b3dSmrg#define xorg_can_triple_buffer() 1
257428d7b3dSmrg#elif XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1,12,99,901,0)
258428d7b3dSmrg/* Before numGPUScreens was introduced */
259428d7b3dSmrg#define xorg_can_triple_buffer() 1
260428d7b3dSmrg#else
261428d7b3dSmrg/* Subject to crashers when combining triple buffering and Prime */
262428d7b3dSmrginline static bool xorg_can_triple_buffer(void)
263428d7b3dSmrg{
264428d7b3dSmrg	return screenInfo.numGPUScreens == 0;
265428d7b3dSmrg}
266428d7b3dSmrg#endif
267428d7b3dSmrg
268428d7b3dSmrgstatic void
269428d7b3dSmrgmark_stale(DRI2BufferPtr back)
270428d7b3dSmrg{
271428d7b3dSmrg	/* If we have reuse notifications, we can track when the
272428d7b3dSmrg	 * client tries to present an old buffer (one that has not
273428d7b3dSmrg	 * been updated since the last swap) and avoid showing the
274428d7b3dSmrg	 * stale frame. (This is mostly useful for tracking down
275428d7b3dSmrg	 * driver bugs!)
276428d7b3dSmrg	 */
277428d7b3dSmrg	get_private(back)->stale = xorg_can_triple_buffer();
278428d7b3dSmrg}
279428d7b3dSmrg
280428d7b3dSmrgstatic Bool
281428d7b3dSmrgsna_dri2_swap_limit_validate(DrawablePtr draw, int swap_limit)
282428d7b3dSmrg{
283428d7b3dSmrg	DBG(("%s: swap limit set to %d\n", __FUNCTION__, swap_limit));
284428d7b3dSmrg	return swap_limit >= 1;
285428d7b3dSmrg}
286428d7b3dSmrg
287428d7b3dSmrgstatic void
288428d7b3dSmrgsna_dri2_reuse_buffer(DrawablePtr draw, DRI2BufferPtr buffer)
289428d7b3dSmrg{
290428d7b3dSmrg	DBG(("%s: reusing buffer pixmap=%ld, attachment=%d, handle=%d, name=%d\n",
291428d7b3dSmrg	     __FUNCTION__, get_drawable_pixmap(draw)->drawable.serialNumber,
292428d7b3dSmrg	     buffer->attachment, get_private(buffer)->bo->handle, buffer->name));
293428d7b3dSmrg	assert(get_private(buffer)->refcnt);
294428d7b3dSmrg	assert(get_private(buffer)->bo->refcnt > get_private(buffer)->bo->active_scanout);
295428d7b3dSmrg
296428d7b3dSmrg	if (buffer->attachment == DRI2BufferBackLeft &&
297428d7b3dSmrg	    draw->type != DRAWABLE_PIXMAP) {
298428d7b3dSmrg		DBG(("%s: replacing back buffer\n", __FUNCTION__));
299428d7b3dSmrg		sna_dri2_get_back(to_sna_from_drawable(draw), draw, buffer, dri2_chain(draw));
300428d7b3dSmrg
301428d7b3dSmrg		assert(kgem_bo_flink(&to_sna_from_drawable(draw)->kgem, get_private(buffer)->bo) == buffer->name);
302428d7b3dSmrg		assert(get_private(buffer)->bo->refcnt);
303428d7b3dSmrg		assert(get_private(buffer)->bo->active_scanout == 0);
304428d7b3dSmrg	}
305428d7b3dSmrg}
306428d7b3dSmrg
307428d7b3dSmrgstatic bool swap_limit(DrawablePtr draw, int limit)
308428d7b3dSmrg{
309428d7b3dSmrg	if (!xorg_can_triple_buffer())
310428d7b3dSmrg		return false;
311428d7b3dSmrg
312428d7b3dSmrg	DBG(("%s: draw=%ld setting swap limit to %d\n", __FUNCTION__, (long)draw->id, limit));
313428d7b3dSmrg	DRI2SwapLimit(draw, limit);
314428d7b3dSmrg	return true;
315428d7b3dSmrg}
316428d7b3dSmrg#endif
317428d7b3dSmrg
318428d7b3dSmrg#if DRI2INFOREC_VERSION < 10
319428d7b3dSmrg#undef USE_ASYNC_SWAP
320428d7b3dSmrg#define USE_ASYNC_SWAP 0
321428d7b3dSmrg#endif
322428d7b3dSmrg
323428d7b3dSmrg#define COLOR_PREFER_TILING_Y 0
324428d7b3dSmrg
325428d7b3dSmrg/* Prefer to enable TILING_Y if this buffer will never be a
326428d7b3dSmrg * candidate for pageflipping
327428d7b3dSmrg */
328428d7b3dSmrgstatic uint32_t color_tiling(struct sna *sna, DrawablePtr draw)
329428d7b3dSmrg{
330428d7b3dSmrg	uint32_t tiling;
331428d7b3dSmrg
332428d7b3dSmrg	if (COLOR_PREFER_TILING_Y &&
333428d7b3dSmrg	    (draw->width  != sna->front->drawable.width ||
334428d7b3dSmrg	     draw->height != sna->front->drawable.height))
335428d7b3dSmrg		tiling = I915_TILING_Y;
336428d7b3dSmrg	else
337428d7b3dSmrg		tiling = I915_TILING_X;
338428d7b3dSmrg
339428d7b3dSmrg	return kgem_choose_tiling(&sna->kgem, -tiling,
340428d7b3dSmrg				  draw->width,
341428d7b3dSmrg				  draw->height,
342428d7b3dSmrg				  draw->bitsPerPixel);
343428d7b3dSmrg}
344428d7b3dSmrg
345428d7b3dSmrgstatic uint32_t other_tiling(struct sna *sna, DrawablePtr draw)
346428d7b3dSmrg{
347428d7b3dSmrg	/* XXX Can mix color X / depth Y? */
348428d7b3dSmrg	return kgem_choose_tiling(&sna->kgem,
349428d7b3dSmrg				  sna->kgem.gen >= 040 ? -I915_TILING_Y : -I915_TILING_X,
350428d7b3dSmrg				  draw->width,
351428d7b3dSmrg				  draw->height,
352428d7b3dSmrg				  draw->bitsPerPixel);
353428d7b3dSmrg}
354428d7b3dSmrg
355428d7b3dSmrgstatic struct kgem_bo *sna_pixmap_set_dri(struct sna *sna,
356428d7b3dSmrg					  PixmapPtr pixmap)
357428d7b3dSmrg{
358428d7b3dSmrg	struct sna_pixmap *priv;
359428d7b3dSmrg	int tiling;
360428d7b3dSmrg
361428d7b3dSmrg	DBG(("%s: attaching DRI client to pixmap=%ld\n",
362428d7b3dSmrg	     __FUNCTION__, pixmap->drawable.serialNumber));
363428d7b3dSmrg
364428d7b3dSmrg	priv = sna_pixmap(pixmap);
365428d7b3dSmrg	if (priv != NULL && IS_STATIC_PTR(priv->ptr) && priv->cpu_bo) {
366428d7b3dSmrg		DBG(("%s: SHM or unattached Pixmap, BadAlloc\n", __FUNCTION__));
367428d7b3dSmrg		return NULL;
368428d7b3dSmrg	}
369428d7b3dSmrg
370428d7b3dSmrg	priv = sna_pixmap_move_to_gpu(pixmap,
371428d7b3dSmrg				      MOVE_READ | __MOVE_FORCE | __MOVE_DRI);
372428d7b3dSmrg	if (priv == NULL) {
373428d7b3dSmrg		DBG(("%s: failed to move to GPU, BadAlloc\n", __FUNCTION__));
374428d7b3dSmrg		return NULL;
375428d7b3dSmrg	}
376428d7b3dSmrg
377428d7b3dSmrg	assert(priv->flush == false);
378428d7b3dSmrg	assert(priv->cpu_damage == NULL);
379428d7b3dSmrg	assert(priv->gpu_bo);
380428d7b3dSmrg	assert(priv->gpu_bo->proxy == NULL);
381428d7b3dSmrg	assert(priv->gpu_bo->flush == false);
382428d7b3dSmrg
383428d7b3dSmrg	tiling = color_tiling(sna, &pixmap->drawable);
384428d7b3dSmrg	if (tiling < 0)
385428d7b3dSmrg		tiling = -tiling;
386428d7b3dSmrg	if (priv->gpu_bo->tiling != tiling)
387428d7b3dSmrg		sna_pixmap_change_tiling(pixmap, tiling);
388428d7b3dSmrg
389428d7b3dSmrg	return priv->gpu_bo;
390428d7b3dSmrg}
391428d7b3dSmrg
392428d7b3dSmrgpure static inline void *sna_pixmap_get_buffer(PixmapPtr pixmap)
393428d7b3dSmrg{
394428d7b3dSmrg	assert(pixmap->refcnt);
395428d7b3dSmrg	return ((void **)__get_private(pixmap, sna_pixmap_key))[2];
396428d7b3dSmrg}
397428d7b3dSmrg
398428d7b3dSmrgstatic inline void sna_pixmap_set_buffer(PixmapPtr pixmap, void *ptr)
399428d7b3dSmrg{
400428d7b3dSmrg	assert(pixmap->refcnt);
401428d7b3dSmrg	((void **)__get_private(pixmap, sna_pixmap_key))[2] = ptr;
402428d7b3dSmrg}
403428d7b3dSmrg
404428d7b3dSmrgvoid
405428d7b3dSmrgsna_dri2_pixmap_update_bo(struct sna *sna, PixmapPtr pixmap, struct kgem_bo *bo)
406428d7b3dSmrg{
407428d7b3dSmrg	DRI2BufferPtr buffer;
408428d7b3dSmrg	struct sna_dri2_private *private;
409428d7b3dSmrg
410428d7b3dSmrg	buffer = sna_pixmap_get_buffer(pixmap);
411428d7b3dSmrg	if (buffer == NULL)
412428d7b3dSmrg		return;
413428d7b3dSmrg
414428d7b3dSmrg	DBG(("%s: pixmap=%ld, old handle=%d, new handle=%d\n", __FUNCTION__,
415428d7b3dSmrg	     pixmap->drawable.serialNumber,
416428d7b3dSmrg	     get_private(buffer)->bo->handle,
417428d7b3dSmrg	     sna_pixmap(pixmap)->gpu_bo->handle));
418428d7b3dSmrg
419428d7b3dSmrg	private = get_private(buffer);
420428d7b3dSmrg	assert(private->pixmap == pixmap);
421428d7b3dSmrg
422428d7b3dSmrg	assert(bo != private->bo);
423428d7b3dSmrg	if (private->bo == bo)
424428d7b3dSmrg		return;
425428d7b3dSmrg
426428d7b3dSmrg	DBG(("%s: dropping flush hint from handle=%d\n", __FUNCTION__, private->bo->handle));
427428d7b3dSmrg	private->bo->flush = false;
428428d7b3dSmrg	kgem_bo_destroy(&sna->kgem, private->bo);
429428d7b3dSmrg
430428d7b3dSmrg	buffer->name = kgem_bo_flink(&sna->kgem, bo);
431428d7b3dSmrg	buffer->pitch = bo->pitch;
432428d7b3dSmrg	private->bo = ref(bo);
433428d7b3dSmrg
434428d7b3dSmrg	DBG(("%s: adding flush hint to handle=%d\n", __FUNCTION__, bo->handle));
435428d7b3dSmrg	bo->flush = true;
436428d7b3dSmrg	if (bo->exec)
437428d7b3dSmrg		sna->kgem.flush = 1;
438428d7b3dSmrg	assert(sna_pixmap(pixmap)->flush);
439428d7b3dSmrg
440428d7b3dSmrg	/* XXX DRI2InvalidateDrawable(&pixmap->drawable); */
441428d7b3dSmrg}
442428d7b3dSmrg
443428d7b3dSmrgstatic DRI2Buffer2Ptr
444428d7b3dSmrgsna_dri2_create_buffer(DrawablePtr draw,
445428d7b3dSmrg		       unsigned int attachment,
446428d7b3dSmrg		       unsigned int format)
447428d7b3dSmrg{
448428d7b3dSmrg	struct sna *sna = to_sna_from_drawable(draw);
449428d7b3dSmrg	DRI2Buffer2Ptr buffer;
450428d7b3dSmrg	struct sna_dri2_private *private;
451428d7b3dSmrg	PixmapPtr pixmap;
452428d7b3dSmrg	struct kgem_bo *bo;
453428d7b3dSmrg	unsigned flags = 0;
454428d7b3dSmrg	uint32_t size;
455428d7b3dSmrg	int bpp;
456428d7b3dSmrg
457428d7b3dSmrg	DBG(("%s pixmap=%ld, (attachment=%d, format=%d, drawable=%dx%d), window?=%d\n",
458428d7b3dSmrg	     __FUNCTION__,
459428d7b3dSmrg	     get_drawable_pixmap(draw)->drawable.serialNumber,
460428d7b3dSmrg	     attachment, format, draw->width, draw->height,
461428d7b3dSmrg	     draw->type != DRAWABLE_PIXMAP));
462428d7b3dSmrg
463428d7b3dSmrg	pixmap = NULL;
464428d7b3dSmrg	size = (uint32_t)draw->height << 16 | draw->width;
465428d7b3dSmrg	switch (attachment) {
466428d7b3dSmrg	case DRI2BufferFrontLeft:
467428d7b3dSmrg		pixmap = get_drawable_pixmap(draw);
468428d7b3dSmrg		buffer = NULL;
469428d7b3dSmrg		if (draw->type != DRAWABLE_PIXMAP)
470428d7b3dSmrg			buffer = dri2_window_get_front((WindowPtr)draw);
471428d7b3dSmrg		if (buffer == NULL)
472428d7b3dSmrg			buffer = sna_pixmap_get_buffer(pixmap);
473428d7b3dSmrg		if (buffer) {
474428d7b3dSmrg			private = get_private(buffer);
475428d7b3dSmrg
476428d7b3dSmrg			DBG(("%s: reusing front buffer attachment, win=%lu %dx%d, pixmap=%ld [%ld] %dx%d, handle=%d, name=%d\n",
477428d7b3dSmrg			     __FUNCTION__,
478428d7b3dSmrg			     draw->type != DRAWABLE_PIXMAP ? (long)draw->id : (long)0,
479428d7b3dSmrg			     draw->width, draw->height,
480428d7b3dSmrg			     pixmap->drawable.serialNumber,
481428d7b3dSmrg			     private->pixmap->drawable.serialNumber,
482428d7b3dSmrg			     pixmap->drawable.width,
483428d7b3dSmrg			     pixmap->drawable.height,
484428d7b3dSmrg			     private->bo->handle, buffer->name));
485428d7b3dSmrg
486428d7b3dSmrg			assert(private->pixmap == pixmap);
487428d7b3dSmrg			assert(sna_pixmap(pixmap)->flush);
488428d7b3dSmrg			assert(sna_pixmap(pixmap)->pinned & PIN_DRI2);
489428d7b3dSmrg			assert(kgem_bo_flink(&sna->kgem, private->bo) == buffer->name);
490428d7b3dSmrg
491428d7b3dSmrg			private->refcnt++;
492428d7b3dSmrg			return buffer;
493428d7b3dSmrg		}
494428d7b3dSmrg
495428d7b3dSmrg		bo = sna_pixmap_set_dri(sna, pixmap);
496428d7b3dSmrg		if (bo == NULL)
497428d7b3dSmrg			return NULL;
498428d7b3dSmrg
499428d7b3dSmrg		assert(sna_pixmap(pixmap) != NULL);
500428d7b3dSmrg
501428d7b3dSmrg		bo = ref(bo);
502428d7b3dSmrg		bpp = pixmap->drawable.bitsPerPixel;
503428d7b3dSmrg		if (pixmap == sna->front && !(sna->flags & SNA_LINEAR_FB))
504428d7b3dSmrg			flags |= CREATE_SCANOUT;
505428d7b3dSmrg		DBG(("%s: attaching to front buffer %dx%d [%p:%d], scanout? %d\n",
506428d7b3dSmrg		     __FUNCTION__,
507428d7b3dSmrg		     pixmap->drawable.width, pixmap->drawable.height,
508428d7b3dSmrg		     pixmap, pixmap->refcnt, flags & CREATE_SCANOUT));
509428d7b3dSmrg		size = (uint32_t)pixmap->drawable.height << 16 | pixmap->drawable.width;
510428d7b3dSmrg		break;
511428d7b3dSmrg
512428d7b3dSmrg	case DRI2BufferBackLeft:
513428d7b3dSmrg		if (draw->type != DRAWABLE_PIXMAP) {
514428d7b3dSmrg			if (dri2_window_get_front((WindowPtr)draw))
515428d7b3dSmrg				flags |= CREATE_SCANOUT;
516428d7b3dSmrg			if (draw->width  == sna->front->drawable.width &&
517428d7b3dSmrg			    draw->height == sna->front->drawable.height &&
518428d7b3dSmrg			    (sna->flags & (SNA_LINEAR_FB | SNA_NO_WAIT | SNA_NO_FLIP)) == 0)
519428d7b3dSmrg				flags |= CREATE_SCANOUT;
520428d7b3dSmrg		}
521428d7b3dSmrg	case DRI2BufferBackRight:
522428d7b3dSmrg	case DRI2BufferFrontRight:
523428d7b3dSmrg	case DRI2BufferFakeFrontLeft:
524428d7b3dSmrg	case DRI2BufferFakeFrontRight:
525428d7b3dSmrg		bpp = draw->bitsPerPixel;
526428d7b3dSmrg		DBG(("%s: creating back buffer %dx%d, suitable for scanout? %d\n",
527428d7b3dSmrg		     __FUNCTION__,
528428d7b3dSmrg		     draw->width, draw->height,
529428d7b3dSmrg		     flags & CREATE_SCANOUT));
530428d7b3dSmrg
531428d7b3dSmrg		bo = kgem_create_2d(&sna->kgem,
532428d7b3dSmrg				    draw->width,
533428d7b3dSmrg				    draw->height,
534428d7b3dSmrg				    draw->bitsPerPixel,
535428d7b3dSmrg				    color_tiling(sna, draw),
536428d7b3dSmrg				    flags);
537428d7b3dSmrg		break;
538428d7b3dSmrg
539428d7b3dSmrg	case DRI2BufferStencil:
540428d7b3dSmrg		/*
541428d7b3dSmrg		 * The stencil buffer has quirky pitch requirements.  From Vol
542428d7b3dSmrg		 * 2a, 11.5.6.2.1 3DSTATE_STENCIL_BUFFER, field "Surface
543428d7b3dSmrg		 * Pitch":
544428d7b3dSmrg		 *    The pitch must be set to 2x the value computed based on
545428d7b3dSmrg		 *    width, as the stencil buffer is stored with two rows
546428d7b3dSmrg		 *    interleaved.
547428d7b3dSmrg		 * To accomplish this, we resort to the nasty hack of doubling
548428d7b3dSmrg		 * the drm region's cpp and halving its height.
549428d7b3dSmrg		 *
550428d7b3dSmrg		 * If we neglect to double the pitch, then
551428d7b3dSmrg		 * drm_intel_gem_bo_map_gtt() maps the memory incorrectly.
552428d7b3dSmrg		 *
553428d7b3dSmrg		 * The alignment for W-tiling is quite different to the
554428d7b3dSmrg		 * nominal no-tiling case, so we have to account for
555428d7b3dSmrg		 * the tiled access pattern explicitly.
556428d7b3dSmrg		 *
557428d7b3dSmrg		 * The stencil buffer is W tiled. However, we request from
558428d7b3dSmrg		 * the kernel a non-tiled buffer because the kernel does
559428d7b3dSmrg		 * not understand W tiling and the GTT is incapable of
560428d7b3dSmrg		 * W fencing.
561428d7b3dSmrg		 */
562428d7b3dSmrg		bpp = format ? format : draw->bitsPerPixel;
563428d7b3dSmrg		bpp *= 2;
564428d7b3dSmrg		bo = kgem_create_2d(&sna->kgem,
565428d7b3dSmrg				    ALIGN(draw->width, 64),
566428d7b3dSmrg				    ALIGN((draw->height + 1) / 2, 64),
567428d7b3dSmrg				    bpp, I915_TILING_NONE, flags);
568428d7b3dSmrg		break;
569428d7b3dSmrg
570428d7b3dSmrg	case DRI2BufferDepth:
571428d7b3dSmrg	case DRI2BufferDepthStencil:
572428d7b3dSmrg	case DRI2BufferHiz:
573428d7b3dSmrg	case DRI2BufferAccum:
574428d7b3dSmrg		bpp = format ? format : draw->bitsPerPixel,
575428d7b3dSmrg		bo = kgem_create_2d(&sna->kgem,
576428d7b3dSmrg				    draw->width, draw->height, bpp,
577428d7b3dSmrg				    other_tiling(sna, draw),
578428d7b3dSmrg				    flags);
579428d7b3dSmrg		break;
580428d7b3dSmrg
581428d7b3dSmrg	default:
582428d7b3dSmrg		return NULL;
583428d7b3dSmrg	}
584428d7b3dSmrg	if (bo == NULL)
585428d7b3dSmrg		return NULL;
586428d7b3dSmrg
587428d7b3dSmrg	buffer = calloc(1, sizeof *buffer + sizeof *private);
588428d7b3dSmrg	if (buffer == NULL)
589428d7b3dSmrg		goto err;
590428d7b3dSmrg
591428d7b3dSmrg	private = get_private(buffer);
592428d7b3dSmrg	buffer->attachment = attachment;
593428d7b3dSmrg	buffer->pitch = bo->pitch;
594428d7b3dSmrg	buffer->cpp = bpp / 8;
595428d7b3dSmrg	buffer->driverPrivate = private;
596428d7b3dSmrg	buffer->format = format;
597428d7b3dSmrg	buffer->flags = 0;
598428d7b3dSmrg	buffer->name = kgem_bo_flink(&sna->kgem, bo);
599428d7b3dSmrg	private->refcnt = 1;
600428d7b3dSmrg	private->bo = bo;
601428d7b3dSmrg	private->pixmap = pixmap;
602428d7b3dSmrg	private->size = size;
603428d7b3dSmrg
604428d7b3dSmrg	if (buffer->name == 0)
605428d7b3dSmrg		goto err;
606428d7b3dSmrg
607428d7b3dSmrg	if (pixmap) {
608428d7b3dSmrg		struct sna_pixmap *priv;
609428d7b3dSmrg
610428d7b3dSmrg		assert(attachment == DRI2BufferFrontLeft);
611428d7b3dSmrg		assert(sna_pixmap_get_buffer(pixmap) == NULL);
612428d7b3dSmrg
613428d7b3dSmrg		sna_pixmap_set_buffer(pixmap, buffer);
614428d7b3dSmrg		assert(sna_pixmap_get_buffer(pixmap) == buffer);
615428d7b3dSmrg		pixmap->refcnt++;
616428d7b3dSmrg
617428d7b3dSmrg		priv = sna_pixmap(pixmap);
618428d7b3dSmrg		assert(priv->flush == false);
619428d7b3dSmrg		assert((priv->pinned & PIN_DRI2) == 0);
620428d7b3dSmrg
621428d7b3dSmrg		/* Don't allow this named buffer to be replaced */
622428d7b3dSmrg		priv->pinned |= PIN_DRI2;
623428d7b3dSmrg
624428d7b3dSmrg		/* We need to submit any modifications to and reads from this
625428d7b3dSmrg		 * buffer before we send any reply to the Client.
626428d7b3dSmrg		 *
627428d7b3dSmrg		 * As we don't track which Client, we flush for all.
628428d7b3dSmrg		 */
629428d7b3dSmrg		DBG(("%s: adding flush hint to handle=%d\n", __FUNCTION__, priv->gpu_bo->handle));
630428d7b3dSmrg		priv->gpu_bo->flush = true;
631428d7b3dSmrg		if (priv->gpu_bo->exec)
632428d7b3dSmrg			sna->kgem.flush = 1;
633428d7b3dSmrg
634428d7b3dSmrg		priv->flush |= 1;
635428d7b3dSmrg		if (draw->type == DRAWABLE_PIXMAP) {
636428d7b3dSmrg			/* DRI2 renders directly into GLXPixmaps, treat as hostile */
637428d7b3dSmrg			kgem_bo_unclean(&sna->kgem, priv->gpu_bo);
638428d7b3dSmrg			sna_damage_all(&priv->gpu_damage, pixmap);
639428d7b3dSmrg			priv->clear = false;
640428d7b3dSmrg			priv->cpu = false;
641428d7b3dSmrg			priv->flush |= 2;
642428d7b3dSmrg		}
643428d7b3dSmrg
644428d7b3dSmrg		sna_accel_watch_flush(sna, 1);
645428d7b3dSmrg	}
646428d7b3dSmrg
647428d7b3dSmrg	return buffer;
648428d7b3dSmrg
649428d7b3dSmrgerr:
650428d7b3dSmrg	kgem_bo_destroy(&sna->kgem, bo);
651428d7b3dSmrg	free(buffer);
652428d7b3dSmrg	return NULL;
653428d7b3dSmrg}
654428d7b3dSmrg
655428d7b3dSmrgstatic void _sna_dri2_destroy_buffer(struct sna *sna, DRI2Buffer2Ptr buffer)
656428d7b3dSmrg{
657428d7b3dSmrg	struct sna_dri2_private *private = get_private(buffer);
658428d7b3dSmrg
659428d7b3dSmrg	if (buffer == NULL)
660428d7b3dSmrg		return;
661428d7b3dSmrg
662428d7b3dSmrg	DBG(("%s: %p [handle=%d] -- refcnt=%d, pixmap=%ld\n",
663428d7b3dSmrg	     __FUNCTION__, buffer, private->bo->handle, private->refcnt,
664428d7b3dSmrg	     private->pixmap ? private->pixmap->drawable.serialNumber : 0));
665428d7b3dSmrg	assert(private->refcnt > 0);
666428d7b3dSmrg	if (--private->refcnt)
667428d7b3dSmrg		return;
668428d7b3dSmrg
669428d7b3dSmrg	assert(private->bo);
670428d7b3dSmrg
671428d7b3dSmrg	if (private->proxy) {
672428d7b3dSmrg		DBG(("%s: destroying proxy\n", __FUNCTION__));
673428d7b3dSmrg		_sna_dri2_destroy_buffer(sna, private->proxy);
674428d7b3dSmrg		private->pixmap = NULL;
675428d7b3dSmrg	}
676428d7b3dSmrg
677428d7b3dSmrg	if (private->pixmap) {
678428d7b3dSmrg		PixmapPtr pixmap = private->pixmap;
679428d7b3dSmrg		struct sna_pixmap *priv = sna_pixmap(pixmap);
680428d7b3dSmrg
681428d7b3dSmrg		assert(sna_pixmap_get_buffer(pixmap) == buffer);
682428d7b3dSmrg		assert(priv->gpu_bo == private->bo);
683428d7b3dSmrg		assert(priv->gpu_bo->flush);
684428d7b3dSmrg		assert(priv->pinned & PIN_DRI2);
685428d7b3dSmrg		assert(priv->flush);
686428d7b3dSmrg
687428d7b3dSmrg		/* Undo the DRI markings on this pixmap */
688428d7b3dSmrg		DBG(("%s: releasing last DRI pixmap=%ld, scanout?=%d\n",
689428d7b3dSmrg		     __FUNCTION__,
690428d7b3dSmrg		     pixmap->drawable.serialNumber,
691428d7b3dSmrg		     pixmap == sna->front));
692428d7b3dSmrg
693428d7b3dSmrg		list_del(&priv->flush_list);
694428d7b3dSmrg
695428d7b3dSmrg		DBG(("%s: dropping flush hint from handle=%d\n", __FUNCTION__, private->bo->handle));
696428d7b3dSmrg		priv->gpu_bo->flush = false;
697428d7b3dSmrg		priv->pinned &= ~PIN_DRI2;
698428d7b3dSmrg
699428d7b3dSmrg		priv->flush = false;
700428d7b3dSmrg		sna_accel_watch_flush(sna, -1);
701428d7b3dSmrg
702428d7b3dSmrg		sna_pixmap_set_buffer(pixmap, NULL);
703428d7b3dSmrg		pixmap->drawable.pScreen->DestroyPixmap(pixmap);
704428d7b3dSmrg	}
705428d7b3dSmrg	assert(private->bo->flush == false);
706428d7b3dSmrg
707428d7b3dSmrg	kgem_bo_destroy(&sna->kgem, private->bo);
708428d7b3dSmrg	free(buffer);
709428d7b3dSmrg}
710428d7b3dSmrg
711428d7b3dSmrgstatic void sna_dri2_destroy_buffer(DrawablePtr draw, DRI2Buffer2Ptr buffer)
712428d7b3dSmrg{
713428d7b3dSmrg	_sna_dri2_destroy_buffer(to_sna_from_drawable(draw), buffer);
714428d7b3dSmrg}
715428d7b3dSmrg
716428d7b3dSmrgstatic DRI2BufferPtr sna_dri2_reference_buffer(DRI2BufferPtr buffer)
717428d7b3dSmrg{
718428d7b3dSmrg	get_private(buffer)->refcnt++;
719428d7b3dSmrg	return buffer;
720428d7b3dSmrg}
721428d7b3dSmrg
722428d7b3dSmrgstatic inline void damage(PixmapPtr pixmap, struct sna_pixmap *priv, RegionPtr region)
723428d7b3dSmrg{
724428d7b3dSmrg	assert(priv->gpu_bo);
725428d7b3dSmrg	if (DAMAGE_IS_ALL(priv->gpu_damage))
726428d7b3dSmrg		goto done;
727428d7b3dSmrg
728428d7b3dSmrg	if (region == NULL) {
729428d7b3dSmrgdamage_all:
730428d7b3dSmrg		priv->gpu_damage = _sna_damage_all(priv->gpu_damage,
731428d7b3dSmrg						   pixmap->drawable.width,
732428d7b3dSmrg						   pixmap->drawable.height);
733428d7b3dSmrg		sna_damage_destroy(&priv->cpu_damage);
734428d7b3dSmrg		list_del(&priv->flush_list);
735428d7b3dSmrg	} else {
736428d7b3dSmrg		sna_damage_subtract(&priv->cpu_damage, region);
737428d7b3dSmrg		if (priv->cpu_damage == NULL)
738428d7b3dSmrg			goto damage_all;
739428d7b3dSmrg		sna_damage_add(&priv->gpu_damage, region);
740428d7b3dSmrg	}
741428d7b3dSmrgdone:
742428d7b3dSmrg	priv->cpu = false;
743428d7b3dSmrg	priv->clear = false;
744428d7b3dSmrg}
745428d7b3dSmrg
746428d7b3dSmrgstatic void set_bo(PixmapPtr pixmap, struct kgem_bo *bo)
747428d7b3dSmrg{
748428d7b3dSmrg	struct sna *sna = to_sna_from_pixmap(pixmap);
749428d7b3dSmrg	struct sna_pixmap *priv = sna_pixmap(pixmap);
750428d7b3dSmrg	RegionRec region;
751428d7b3dSmrg
752428d7b3dSmrg	DBG(("%s: pixmap=%ld, handle=%d\n",
753428d7b3dSmrg	     __FUNCTION__, pixmap->drawable.serialNumber, bo->handle));
754428d7b3dSmrg
755428d7b3dSmrg	assert(pixmap->drawable.width * pixmap->drawable.bitsPerPixel <= 8*bo->pitch);
756428d7b3dSmrg	assert(pixmap->drawable.height * bo->pitch <= kgem_bo_size(bo));
757428d7b3dSmrg	assert(bo->proxy == NULL);
758428d7b3dSmrg	assert(priv->pinned & PIN_DRI2);
759428d7b3dSmrg	assert((priv->pinned & (PIN_PRIME | PIN_DRI3)) == 0);
760428d7b3dSmrg	assert(priv->flush);
761428d7b3dSmrg
762428d7b3dSmrg	/* Post damage on the new front buffer so that listeners, such
763428d7b3dSmrg	 * as DisplayLink know take a copy and shove it over the USB,
764428d7b3dSmrg	 * also for software cursors and the like.
765428d7b3dSmrg	 */
766428d7b3dSmrg	region.extents.x1 = region.extents.y1 = 0;
767428d7b3dSmrg	region.extents.x2 = pixmap->drawable.width;
768428d7b3dSmrg	region.extents.y2 = pixmap->drawable.height;
769428d7b3dSmrg	region.data = NULL;
770428d7b3dSmrg	DamageRegionAppend(&pixmap->drawable, &region);
771428d7b3dSmrg
772428d7b3dSmrg	damage(pixmap, priv, NULL);
773428d7b3dSmrg
774428d7b3dSmrg	assert(bo->refcnt);
775428d7b3dSmrg	if (priv->move_to_gpu)
776428d7b3dSmrg		priv->move_to_gpu(sna, priv, 0);
777428d7b3dSmrg	if (priv->gpu_bo != bo) {
778428d7b3dSmrg		DBG(("%s: dropping flush hint from handle=%d\n", __FUNCTION__, priv->gpu_bo->handle));
779428d7b3dSmrg		priv->gpu_bo->flush = false;
780428d7b3dSmrg		if (priv->cow)
781428d7b3dSmrg			sna_pixmap_undo_cow(sna, priv, 0);
782428d7b3dSmrg		if (priv->gpu_bo) {
783428d7b3dSmrg			sna_pixmap_unmap(pixmap, priv);
784428d7b3dSmrg			kgem_bo_destroy(&sna->kgem, priv->gpu_bo);
785428d7b3dSmrg		}
786428d7b3dSmrg		DBG(("%s: adding flush hint to handle=%d\n", __FUNCTION__, bo->handle));
787428d7b3dSmrg		bo->flush = true;
788428d7b3dSmrg		if (bo->exec)
789428d7b3dSmrg			sna->kgem.flush = 1;
790428d7b3dSmrg		priv->gpu_bo = ref(bo);
791428d7b3dSmrg	}
792428d7b3dSmrg	if (bo->domain != DOMAIN_GPU)
793428d7b3dSmrg		bo->domain = DOMAIN_NONE;
794428d7b3dSmrg	assert(bo->flush);
795428d7b3dSmrg
796428d7b3dSmrg	DamageRegionProcessPending(&pixmap->drawable);
797428d7b3dSmrg}
798428d7b3dSmrg
799428d7b3dSmrgstatic void sna_dri2_select_mode(struct sna *sna, struct kgem_bo *dst, struct kgem_bo *src, bool sync)
800428d7b3dSmrg{
801428d7b3dSmrg	struct drm_i915_gem_busy busy;
802428d7b3dSmrg	int mode;
803428d7b3dSmrg
804428d7b3dSmrg	if (sna->kgem.gen < 060)
805428d7b3dSmrg		return;
806428d7b3dSmrg
807428d7b3dSmrg	if (sync) {
808428d7b3dSmrg		DBG(("%s: sync, force %s ring\n", __FUNCTION__,
809428d7b3dSmrg		     sna->kgem.gen >= 070 ? "BLT" : "RENDER"));
810428d7b3dSmrg		kgem_set_mode(&sna->kgem,
811428d7b3dSmrg			      sna->kgem.gen >= 070 ? KGEM_BLT : KGEM_RENDER,
812428d7b3dSmrg			      dst);
813428d7b3dSmrg		return;
814428d7b3dSmrg	}
815428d7b3dSmrg
816428d7b3dSmrg	if (DBG_FORCE_COPY != -1) {
817428d7b3dSmrg		DBG(("%s: forcing %d\n", __FUNCTION__, DBG_FORCE_COPY));
818428d7b3dSmrg		kgem_set_mode(&sna->kgem, DBG_FORCE_COPY, dst);
819428d7b3dSmrg		return;
820428d7b3dSmrg	}
821428d7b3dSmrg
822428d7b3dSmrg	if (sna->kgem.mode != KGEM_NONE) {
823428d7b3dSmrg		DBG(("%s: busy, not switching\n", __FUNCTION__));
824428d7b3dSmrg		return;
825428d7b3dSmrg	}
826428d7b3dSmrg
827428d7b3dSmrg	VG_CLEAR(busy);
828428d7b3dSmrg	busy.handle = src->handle;
829428d7b3dSmrg	if (drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GEM_BUSY, &busy))
830428d7b3dSmrg		return;
831428d7b3dSmrg
832428d7b3dSmrg	DBG(("%s: src handle=%d busy?=%x\n", __FUNCTION__, busy.handle, busy.busy));
833428d7b3dSmrg	if (busy.busy == 0) {
834428d7b3dSmrg		__kgem_bo_clear_busy(src);
835428d7b3dSmrg
836428d7b3dSmrg		busy.handle = dst->handle;
837428d7b3dSmrg		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GEM_BUSY, &busy))
838428d7b3dSmrg			return;
839428d7b3dSmrg
840428d7b3dSmrg		DBG(("%s: dst handle=%d busy?=%x\n", __FUNCTION__, busy.handle, busy.busy));
841428d7b3dSmrg		if (busy.busy == 0) {
842428d7b3dSmrg			__kgem_bo_clear_busy(dst);
843428d7b3dSmrg			DBG(("%s: src/dst is idle, using defaults\n", __FUNCTION__));
844428d7b3dSmrg			return;
845428d7b3dSmrg		}
846428d7b3dSmrg	}
847428d7b3dSmrg
848428d7b3dSmrg	/* Sandybridge introduced a separate ring which it uses to
849428d7b3dSmrg	 * perform blits. Switching rendering between rings incurs
850428d7b3dSmrg	 * a stall as we wait upon the old ring to finish and
851428d7b3dSmrg	 * flush its render cache before we can proceed on with
852428d7b3dSmrg	 * the operation on the new ring.
853428d7b3dSmrg	 *
854428d7b3dSmrg	 * As this buffer, we presume, has just been written to by
855428d7b3dSmrg	 * the DRI client using the RENDER ring, we want to perform
856428d7b3dSmrg	 * our operation on the same ring, and ideally on the same
857428d7b3dSmrg	 * ring as we will flip from (which should be the RENDER ring
858428d7b3dSmrg	 * as well).
859428d7b3dSmrg	 *
860428d7b3dSmrg	 * The ultimate question is whether preserving the ring outweighs
861428d7b3dSmrg	 * the cost of the query.
862428d7b3dSmrg	 */
863428d7b3dSmrg	mode = KGEM_RENDER;
864428d7b3dSmrg	if (busy.busy & (0xfffe << 16))
865428d7b3dSmrg		mode = KGEM_BLT;
866428d7b3dSmrg	kgem_bo_mark_busy(&sna->kgem, busy.handle == src->handle ? src : dst, mode);
867428d7b3dSmrg	_kgem_set_mode(&sna->kgem, mode);
868428d7b3dSmrg}
869428d7b3dSmrg
870428d7b3dSmrgstatic bool is_front(int attachment)
871428d7b3dSmrg{
872428d7b3dSmrg	return attachment == DRI2BufferFrontLeft;
873428d7b3dSmrg}
874428d7b3dSmrg
875428d7b3dSmrgstatic struct kgem_bo *
876428d7b3dSmrg__sna_dri2_copy_region(struct sna *sna, DrawablePtr draw, RegionPtr region,
877428d7b3dSmrg		      DRI2BufferPtr src, DRI2BufferPtr dst,
878428d7b3dSmrg		      bool sync)
879428d7b3dSmrg{
880428d7b3dSmrg	PixmapPtr pixmap = get_drawable_pixmap(draw);
881428d7b3dSmrg	DrawableRec scratch, *src_draw = &pixmap->drawable, *dst_draw = &pixmap->drawable;
882428d7b3dSmrg	struct sna_dri2_private *src_priv = get_private(src);
883428d7b3dSmrg	struct sna_dri2_private *dst_priv = get_private(dst);
884428d7b3dSmrg	pixman_region16_t clip;
885428d7b3dSmrg	struct kgem_bo *bo = NULL;
886428d7b3dSmrg	struct kgem_bo *src_bo;
887428d7b3dSmrg	struct kgem_bo *dst_bo;
888428d7b3dSmrg	const BoxRec *boxes;
889428d7b3dSmrg	int16_t dx, dy, sx, sy;
890428d7b3dSmrg	unsigned flags;
891428d7b3dSmrg	int n;
892428d7b3dSmrg
893428d7b3dSmrg	/* To hide a stale DRI2Buffer, one may choose to substitute
894428d7b3dSmrg	 * pixmap->gpu_bo instead of dst/src->bo, however you then run
895428d7b3dSmrg	 * the risk of copying around invalid data. So either you may not
896428d7b3dSmrg	 * see the results of the copy, or you may see the wrong pixels.
897428d7b3dSmrg	 * Either way you eventually lose.
898428d7b3dSmrg	 *
899428d7b3dSmrg	 * We also have to be careful in case that the stale buffers are
900428d7b3dSmrg	 * now attached to invalid (non-DRI) pixmaps.
901428d7b3dSmrg	 */
902428d7b3dSmrg
903428d7b3dSmrg	assert(is_front(dst->attachment) || is_front(src->attachment));
904428d7b3dSmrg	assert(dst->attachment != src->attachment);
905428d7b3dSmrg
906428d7b3dSmrg	clip.extents.x1 = draw->x;
907428d7b3dSmrg	clip.extents.y1 = draw->y;
908428d7b3dSmrg	clip.extents.x2 = draw->x + draw->width;
909428d7b3dSmrg	clip.extents.y2 = draw->y + draw->height;
910428d7b3dSmrg	clip.data = NULL;
911428d7b3dSmrg
912428d7b3dSmrg	if (region) {
913428d7b3dSmrg		pixman_region_translate(region, draw->x, draw->y);
914428d7b3dSmrg		pixman_region_intersect(&clip, &clip, region);
915428d7b3dSmrg		region = &clip;
916428d7b3dSmrg	}
917428d7b3dSmrg
918428d7b3dSmrg	if (clip.extents.x1 >= clip.extents.x2 ||
919428d7b3dSmrg	    clip.extents.y1 >= clip.extents.y2) {
920428d7b3dSmrg		DBG(("%s: all clipped\n", __FUNCTION__));
921428d7b3dSmrg		return NULL;
922428d7b3dSmrg	}
923428d7b3dSmrg
924428d7b3dSmrg	sx = sy = dx = dy = 0;
925428d7b3dSmrg	if (is_front(dst->attachment)) {
926428d7b3dSmrg		sx = -draw->x;
927428d7b3dSmrg		sy = -draw->y;
928428d7b3dSmrg	} else {
929428d7b3dSmrg		dx = -draw->x;
930428d7b3dSmrg		dy = -draw->y;
931428d7b3dSmrg	}
932428d7b3dSmrg	if (draw->type == DRAWABLE_WINDOW) {
933428d7b3dSmrg		WindowPtr win = (WindowPtr)draw;
934428d7b3dSmrg		int16_t tx, ty;
935428d7b3dSmrg
936428d7b3dSmrg		if (is_clipped(&win->clipList, draw)) {
937428d7b3dSmrg			DBG(("%s: draw=(%d, %d), delta=(%d, %d), draw=(%d, %d),(%d, %d), clip.extents=(%d, %d), (%d, %d)\n",
938428d7b3dSmrg			     __FUNCTION__, draw->x, draw->y,
939428d7b3dSmrg			     get_drawable_dx(draw), get_drawable_dy(draw),
940428d7b3dSmrg			     clip.extents.x1, clip.extents.y1,
941428d7b3dSmrg			     clip.extents.x2, clip.extents.y2,
942428d7b3dSmrg			     win->clipList.extents.x1, win->clipList.extents.y1,
943428d7b3dSmrg			     win->clipList.extents.x2, win->clipList.extents.y2));
944428d7b3dSmrg
945428d7b3dSmrg			assert(region == NULL || region == &clip);
946428d7b3dSmrg			pixman_region_intersect(&clip, &win->clipList, &clip);
947428d7b3dSmrg			if (!pixman_region_not_empty(&clip)) {
948428d7b3dSmrg				DBG(("%s: all clipped\n", __FUNCTION__));
949428d7b3dSmrg				return NULL;
950428d7b3dSmrg			}
951428d7b3dSmrg
952428d7b3dSmrg			region = &clip;
953428d7b3dSmrg		}
954428d7b3dSmrg
955428d7b3dSmrg		if (get_drawable_deltas(draw, pixmap, &tx, &ty)) {
956428d7b3dSmrg			if (is_front(dst->attachment)) {
957428d7b3dSmrg				pixman_region_translate(region ?: &clip, tx, ty);
958428d7b3dSmrg				sx -= tx;
959428d7b3dSmrg				sy -= ty;
960428d7b3dSmrg			} else {
961428d7b3dSmrg				sx += tx;
962428d7b3dSmrg				sy += ty;
963428d7b3dSmrg			}
964428d7b3dSmrg		}
965428d7b3dSmrg	} else
966428d7b3dSmrg		sync = false;
967428d7b3dSmrg
968428d7b3dSmrg	scratch.x = scratch.y = 0;
969428d7b3dSmrg	scratch.width = scratch.height = 0;
970428d7b3dSmrg	scratch.depth = draw->depth;
971428d7b3dSmrg	scratch.bitsPerPixel = draw->bitsPerPixel;
972428d7b3dSmrg
973428d7b3dSmrg	src_bo = src_priv->bo;
974428d7b3dSmrg	assert(src_bo->refcnt);
975428d7b3dSmrg	if (is_front(src->attachment)) {
976428d7b3dSmrg		struct sna_pixmap *priv;
977428d7b3dSmrg
978428d7b3dSmrg		priv = sna_pixmap_move_to_gpu(pixmap, MOVE_READ);
979428d7b3dSmrg		if (priv)
980428d7b3dSmrg			src_bo = priv->gpu_bo;
981428d7b3dSmrg		DBG(("%s: updated FrontLeft src_bo from handle=%d to handle=%d\n",
982428d7b3dSmrg		     __FUNCTION__, src_priv->bo->handle, src_bo->handle));
983428d7b3dSmrg		assert(src_bo->refcnt);
984428d7b3dSmrg	} else {
985428d7b3dSmrg		RegionRec source;
986428d7b3dSmrg
987428d7b3dSmrg		scratch.width = src_priv->size & 0xffff;
988428d7b3dSmrg		scratch.height = src_priv->size >> 16;
989428d7b3dSmrg		src_draw = &scratch;
990428d7b3dSmrg
991428d7b3dSmrg		DBG(("%s: source size %dx%d, region size %dx%d\n",
992428d7b3dSmrg		     __FUNCTION__,
993428d7b3dSmrg		     scratch.width, scratch.height,
994428d7b3dSmrg		     clip.extents.x2 - clip.extents.x1,
995428d7b3dSmrg		     clip.extents.y2 - clip.extents.y1));
996428d7b3dSmrg
997428d7b3dSmrg		source.extents.x1 = -sx;
998428d7b3dSmrg		source.extents.y1 = -sy;
999428d7b3dSmrg		source.extents.x2 = source.extents.x1 + scratch.width;
1000428d7b3dSmrg		source.extents.y2 = source.extents.y1 + scratch.height;
1001428d7b3dSmrg		source.data = NULL;
1002428d7b3dSmrg
1003428d7b3dSmrg		assert(region == NULL || region == &clip);
1004428d7b3dSmrg		pixman_region_intersect(&clip, &clip, &source);
1005428d7b3dSmrg
1006428d7b3dSmrg	}
1007428d7b3dSmrg
1008428d7b3dSmrg	dst_bo = dst_priv->bo;
1009428d7b3dSmrg	assert(dst_bo->refcnt);
1010428d7b3dSmrg	if (is_front(dst->attachment)) {
1011428d7b3dSmrg		struct sna_pixmap *priv;
1012428d7b3dSmrg		struct list shadow;
1013428d7b3dSmrg
1014428d7b3dSmrg		/* Preserve the CRTC shadow overrides */
1015428d7b3dSmrg		sna_shadow_steal_crtcs(sna, &shadow);
1016428d7b3dSmrg
1017428d7b3dSmrg		flags = MOVE_WRITE | __MOVE_FORCE;
1018428d7b3dSmrg		if (clip.data)
1019428d7b3dSmrg			flags |= MOVE_READ;
1020428d7b3dSmrg
1021428d7b3dSmrg		assert(region == NULL || region == &clip);
1022428d7b3dSmrg		priv = sna_pixmap_move_area_to_gpu(pixmap, &clip.extents, flags);
1023428d7b3dSmrg		if (priv) {
1024428d7b3dSmrg			damage(pixmap, priv, region);
1025428d7b3dSmrg			dst_bo = priv->gpu_bo;
1026428d7b3dSmrg		}
1027428d7b3dSmrg		DBG(("%s: updated FrontLeft dst_bo from handle=%d to handle=%d\n",
1028428d7b3dSmrg		     __FUNCTION__, dst_priv->bo->handle, dst_bo->handle));
1029428d7b3dSmrg		assert(dst_bo->refcnt);
1030428d7b3dSmrg
1031428d7b3dSmrg		sna_shadow_unsteal_crtcs(sna, &shadow);
1032428d7b3dSmrg	} else {
1033428d7b3dSmrg		RegionRec target;
1034428d7b3dSmrg
1035428d7b3dSmrg		scratch.width = dst_priv->size & 0xffff;
1036428d7b3dSmrg		scratch.height = dst_priv->size >> 16;
1037428d7b3dSmrg		dst_draw = &scratch;
1038428d7b3dSmrg
1039428d7b3dSmrg		DBG(("%s: target size %dx%d, region size %dx%d\n",
1040428d7b3dSmrg		     __FUNCTION__,
1041428d7b3dSmrg		     scratch.width, scratch.height,
1042428d7b3dSmrg		     clip.extents.x2 - clip.extents.x1,
1043428d7b3dSmrg		     clip.extents.y2 - clip.extents.y1));
1044428d7b3dSmrg
1045428d7b3dSmrg		target.extents.x1 = -dx;
1046428d7b3dSmrg		target.extents.y1 = -dy;
1047428d7b3dSmrg		target.extents.x2 = target.extents.x1 + scratch.width;
1048428d7b3dSmrg		target.extents.y2 = target.extents.y1 + scratch.height;
1049428d7b3dSmrg		target.data = NULL;
1050428d7b3dSmrg
1051428d7b3dSmrg		assert(region == NULL || region == &clip);
1052428d7b3dSmrg		pixman_region_intersect(&clip, &clip, &target);
1053428d7b3dSmrg
1054428d7b3dSmrg		sync = false;
1055428d7b3dSmrg	}
1056428d7b3dSmrg
1057428d7b3dSmrg	if (!wedged(sna)) {
1058428d7b3dSmrg		xf86CrtcPtr crtc;
1059428d7b3dSmrg
1060428d7b3dSmrg		crtc = NULL;
1061428d7b3dSmrg		if (sync && sna_pixmap_is_scanout(sna, pixmap))
1062428d7b3dSmrg			crtc = sna_covering_crtc(sna, &clip.extents, NULL);
1063428d7b3dSmrg		sna_dri2_select_mode(sna, dst_bo, src_bo, crtc != NULL);
1064428d7b3dSmrg
1065428d7b3dSmrg		sync = (crtc != NULL&&
1066428d7b3dSmrg			sna_wait_for_scanline(sna, pixmap, crtc,
1067428d7b3dSmrg					      &clip.extents));
1068428d7b3dSmrg	}
1069428d7b3dSmrg
1070428d7b3dSmrg	if (region) {
1071428d7b3dSmrg		boxes = region_rects(region);
1072428d7b3dSmrg		n = region_num_rects(region);
1073428d7b3dSmrg		assert(n);
1074428d7b3dSmrg	} else {
1075428d7b3dSmrg		region = &clip;
1076428d7b3dSmrg		boxes = &clip.extents;
1077428d7b3dSmrg		n = 1;
1078428d7b3dSmrg	}
1079428d7b3dSmrg	DamageRegionAppend(&pixmap->drawable, region);
1080428d7b3dSmrg
1081428d7b3dSmrg
1082428d7b3dSmrg	DBG(("%s: copying [(%d, %d), (%d, %d)]x%d src=(%d, %d), dst=(%d, %d)\n",
1083428d7b3dSmrg	     __FUNCTION__,
1084428d7b3dSmrg	     boxes[0].x1, boxes[0].y1,
1085428d7b3dSmrg	     boxes[0].x2, boxes[0].y2,
1086428d7b3dSmrg	     n, sx, sy, dx, dy));
1087428d7b3dSmrg
1088428d7b3dSmrg	flags = COPY_LAST;
1089428d7b3dSmrg	if (sync)
1090428d7b3dSmrg		flags |= COPY_SYNC;
1091428d7b3dSmrg	if (!sna->render.copy_boxes(sna, GXcopy,
1092428d7b3dSmrg				    src_draw, src_bo, sx, sy,
1093428d7b3dSmrg				    dst_draw, dst_bo, dx, dy,
1094428d7b3dSmrg				    boxes, n, flags))
1095428d7b3dSmrg		memcpy_copy_boxes(sna, GXcopy,
1096428d7b3dSmrg				  src_draw, src_bo, sx, sy,
1097428d7b3dSmrg				  dst_draw, dst_bo, dx, dy,
1098428d7b3dSmrg				  boxes, n, flags);
1099428d7b3dSmrg
1100428d7b3dSmrg	DBG(("%s: flushing? %d\n", __FUNCTION__, sync));
1101428d7b3dSmrg	if (sync) { /* STAT! */
1102428d7b3dSmrg		struct kgem_request *rq = sna->kgem.next_request;
1103428d7b3dSmrg		kgem_submit(&sna->kgem);
1104428d7b3dSmrg		if (rq->bo) {
1105428d7b3dSmrg			bo = ref(rq->bo);
1106428d7b3dSmrg			DBG(("%s: recording sync fence handle=%d\n", __FUNCTION__, bo->handle));
1107428d7b3dSmrg		}
1108428d7b3dSmrg	}
1109428d7b3dSmrg
1110428d7b3dSmrg	DamageRegionProcessPending(&pixmap->drawable);
1111428d7b3dSmrg
1112428d7b3dSmrg	if (clip.data)
1113428d7b3dSmrg		pixman_region_fini(&clip);
1114428d7b3dSmrg
1115428d7b3dSmrg	return bo;
1116428d7b3dSmrg}
1117428d7b3dSmrg
1118428d7b3dSmrgstatic void
1119428d7b3dSmrgsna_dri2_copy_region(DrawablePtr draw,
1120428d7b3dSmrg		     RegionPtr region,
1121428d7b3dSmrg		     DRI2BufferPtr dst,
1122428d7b3dSmrg		     DRI2BufferPtr src)
1123428d7b3dSmrg{
1124428d7b3dSmrg	PixmapPtr pixmap = get_drawable_pixmap(draw);
1125428d7b3dSmrg	struct sna *sna = to_sna_from_pixmap(pixmap);
1126428d7b3dSmrg
1127428d7b3dSmrg	DBG(("%s: pixmap=%ld, src=%u (refs=%d/%d, flush=%d, attach=%d) , dst=%u (refs=%d/%d, flush=%d, attach=%d)\n",
1128428d7b3dSmrg	     __FUNCTION__,
1129428d7b3dSmrg	     pixmap->drawable.serialNumber,
1130428d7b3dSmrg	     get_private(src)->bo->handle,
1131428d7b3dSmrg	     get_private(src)->refcnt,
1132428d7b3dSmrg	     get_private(src)->bo->refcnt,
1133428d7b3dSmrg	     get_private(src)->bo->flush,
1134428d7b3dSmrg	     src->attachment,
1135428d7b3dSmrg	     get_private(dst)->bo->handle,
1136428d7b3dSmrg	     get_private(dst)->refcnt,
1137428d7b3dSmrg	     get_private(dst)->bo->refcnt,
1138428d7b3dSmrg	     get_private(dst)->bo->flush,
1139428d7b3dSmrg	     dst->attachment));
1140428d7b3dSmrg
1141428d7b3dSmrg	assert(src != dst);
1142428d7b3dSmrg
1143428d7b3dSmrg	assert(get_private(src)->refcnt);
1144428d7b3dSmrg	assert(get_private(dst)->refcnt);
1145428d7b3dSmrg
1146428d7b3dSmrg	assert(get_private(src)->bo->refcnt);
1147428d7b3dSmrg	assert(get_private(dst)->bo->refcnt);
1148428d7b3dSmrg
1149428d7b3dSmrg	DBG(("%s: region (%d, %d), (%d, %d) x %d\n",
1150428d7b3dSmrg	     __FUNCTION__,
1151428d7b3dSmrg	     region->extents.x1, region->extents.y1,
1152428d7b3dSmrg	     region->extents.x2, region->extents.y2,
1153428d7b3dSmrg	     region_num_rects(region)));
1154428d7b3dSmrg
1155428d7b3dSmrg	__sna_dri2_copy_region(sna, draw, region, src, dst, false);
1156428d7b3dSmrg}
1157428d7b3dSmrg
1158428d7b3dSmrginline static uint32_t pipe_select(int pipe)
1159428d7b3dSmrg{
1160428d7b3dSmrg	/* The third pipe was introduced with IvyBridge long after
1161428d7b3dSmrg	 * multiple pipe support was added to the kernel, hence
1162428d7b3dSmrg	 * we can safely ignore the capability check - if we have more
1163428d7b3dSmrg	 * than two pipes, we can assume that they are fully supported.
1164428d7b3dSmrg	 */
1165428d7b3dSmrg	if (pipe > 1)
1166428d7b3dSmrg		return pipe << DRM_VBLANK_HIGH_CRTC_SHIFT;
1167428d7b3dSmrg	else if (pipe > 0)
1168428d7b3dSmrg		return DRM_VBLANK_SECONDARY;
1169428d7b3dSmrg	else
1170428d7b3dSmrg		return 0;
1171428d7b3dSmrg}
1172428d7b3dSmrg
1173428d7b3dSmrgstatic inline int sna_wait_vblank(struct sna *sna, union drm_wait_vblank *vbl, int pipe)
1174428d7b3dSmrg{
1175428d7b3dSmrg	DBG(("%s(pipe=%d, waiting until seq=%u%s)\n",
1176428d7b3dSmrg	     __FUNCTION__, pipe, vbl->request.sequence,
1177428d7b3dSmrg	     vbl->request.type & DRM_VBLANK_RELATIVE ? " [relative]" : ""));
1178428d7b3dSmrg	assert(pipe != -1);
1179428d7b3dSmrg
1180428d7b3dSmrg	vbl->request.type |= pipe_select(pipe);
1181428d7b3dSmrg	return drmIoctl(sna->kgem.fd, DRM_IOCTL_WAIT_VBLANK, vbl);
1182428d7b3dSmrg}
1183428d7b3dSmrg
1184428d7b3dSmrg#if DRI2INFOREC_VERSION >= 4
1185428d7b3dSmrg
1186428d7b3dSmrgstatic void dri2_window_attach(WindowPtr win, struct dri2_window *priv)
1187428d7b3dSmrg{
1188428d7b3dSmrg	assert(win->drawable.type == DRAWABLE_WINDOW);
1189428d7b3dSmrg	assert(dri2_window(win) == NULL);
1190428d7b3dSmrg	((void **)__get_private(win, sna_window_key))[1] = priv;
1191428d7b3dSmrg	assert(dri2_window(win) == priv);
1192428d7b3dSmrg}
1193428d7b3dSmrg
1194428d7b3dSmrgstatic uint64_t
1195428d7b3dSmrgdraw_current_msc(DrawablePtr draw, xf86CrtcPtr crtc, uint64_t msc)
1196428d7b3dSmrg{
1197428d7b3dSmrg	struct dri2_window *priv;
1198428d7b3dSmrg
1199428d7b3dSmrg	if (draw->type != DRAWABLE_WINDOW)
1200428d7b3dSmrg		return msc;
1201428d7b3dSmrg
1202428d7b3dSmrg	priv = dri2_window((WindowPtr)draw);
1203428d7b3dSmrg	if (priv == NULL) {
1204428d7b3dSmrg		priv = malloc(sizeof(*priv));
1205428d7b3dSmrg		if (priv != NULL) {
1206428d7b3dSmrg			priv->front = NULL;
1207428d7b3dSmrg			priv->crtc = crtc;
1208428d7b3dSmrg			priv->msc_delta = 0;
1209428d7b3dSmrg			priv->chain = NULL;
1210428d7b3dSmrg			dri2_window_attach((WindowPtr)draw, priv);
1211428d7b3dSmrg		}
1212428d7b3dSmrg	} else {
1213428d7b3dSmrg		if (priv->crtc != crtc) {
1214428d7b3dSmrg			const struct ust_msc *last = sna_crtc_last_swap(priv->crtc);
1215428d7b3dSmrg			const struct ust_msc *this = sna_crtc_last_swap(crtc);
1216428d7b3dSmrg			DBG(("%s: Window transferring from pipe=%d [msc=%llu] to pipe=%d [msc=%llu], delta now %lld\n",
1217428d7b3dSmrg			     __FUNCTION__,
1218428d7b3dSmrg			     sna_crtc_to_pipe(priv->crtc), (long long)last->msc,
1219428d7b3dSmrg			     sna_crtc_to_pipe(crtc), (long long)this->msc,
1220428d7b3dSmrg			     (long long)(priv->msc_delta + this->msc - last->msc)));
1221428d7b3dSmrg			priv->msc_delta += this->msc - last->msc;
1222428d7b3dSmrg			priv->crtc = crtc;
1223428d7b3dSmrg		}
1224428d7b3dSmrg		msc -= priv->msc_delta;
1225428d7b3dSmrg	}
1226428d7b3dSmrg	return  msc;
1227428d7b3dSmrg}
1228428d7b3dSmrg
1229428d7b3dSmrgstatic uint32_t
1230428d7b3dSmrgdraw_target_seq(DrawablePtr draw, uint64_t msc)
1231428d7b3dSmrg{
1232428d7b3dSmrg	struct dri2_window *priv = dri2_window((WindowPtr)draw);
1233428d7b3dSmrg	if (priv == NULL)
1234428d7b3dSmrg		return msc;
1235428d7b3dSmrg	DBG(("%s: converting target_msc=%llu to seq %u\n",
1236428d7b3dSmrg	     __FUNCTION__, (long long)msc, (unsigned)(msc + priv->msc_delta)));
1237428d7b3dSmrg	return msc + priv->msc_delta;
1238428d7b3dSmrg}
1239428d7b3dSmrg
1240428d7b3dSmrgstatic xf86CrtcPtr
1241428d7b3dSmrgsna_dri2_get_crtc(DrawablePtr draw)
1242428d7b3dSmrg{
1243428d7b3dSmrg	if (draw->type == DRAWABLE_PIXMAP)
1244428d7b3dSmrg		return NULL;
1245428d7b3dSmrg
1246428d7b3dSmrg	/* Make sure the CRTC is valid and this is the real front buffer */
1247428d7b3dSmrg	return sna_covering_crtc(to_sna_from_drawable(draw),
1248428d7b3dSmrg				 &((WindowPtr)draw)->clipList.extents,
1249428d7b3dSmrg				 NULL);
1250428d7b3dSmrg}
1251428d7b3dSmrg
1252428d7b3dSmrgstatic void
1253428d7b3dSmrgsna_dri2_remove_event(WindowPtr win, struct sna_dri2_event *info)
1254428d7b3dSmrg{
1255428d7b3dSmrg	struct dri2_window *priv;
1256428d7b3dSmrg	struct sna_dri2_event *chain;
1257428d7b3dSmrg
1258428d7b3dSmrg	assert(win->drawable.type == DRAWABLE_WINDOW);
1259428d7b3dSmrg	DBG(("%s: remove[%p] from window %ld, active? %d\n",
1260428d7b3dSmrg	     __FUNCTION__, info, (long)win->drawable.id, info->draw != NULL));
1261428d7b3dSmrg
1262428d7b3dSmrg	priv = dri2_window(win);
1263428d7b3dSmrg	assert(priv);
1264428d7b3dSmrg	assert(priv->chain != NULL);
1265428d7b3dSmrg
1266428d7b3dSmrg	if (priv->chain == info) {
1267428d7b3dSmrg		priv->chain = info->chain;
1268428d7b3dSmrg		return;
1269428d7b3dSmrg	}
1270428d7b3dSmrg
1271428d7b3dSmrg	chain = priv->chain;
1272428d7b3dSmrg	while (chain->chain != info)
1273428d7b3dSmrg		chain = chain->chain;
1274428d7b3dSmrg	assert(chain != info);
1275428d7b3dSmrg	assert(info->chain != chain);
1276428d7b3dSmrg	chain->chain = info->chain;
1277428d7b3dSmrg}
1278428d7b3dSmrg
1279428d7b3dSmrgstatic void
1280428d7b3dSmrgsna_dri2_event_free(struct sna_dri2_event *info)
1281428d7b3dSmrg{
1282428d7b3dSmrg	DrawablePtr draw = info->draw;
1283428d7b3dSmrg
1284428d7b3dSmrg	DBG(("%s(draw?=%d)\n", __FUNCTION__, draw != NULL));
1285428d7b3dSmrg	if (draw && draw->type == DRAWABLE_WINDOW)
1286428d7b3dSmrg		sna_dri2_remove_event((WindowPtr)draw, info);
1287428d7b3dSmrg
1288428d7b3dSmrg	_sna_dri2_destroy_buffer(info->sna, info->front);
1289428d7b3dSmrg	_sna_dri2_destroy_buffer(info->sna, info->back);
1290428d7b3dSmrg
1291428d7b3dSmrg	while (!list_is_empty(&info->cache)) {
1292428d7b3dSmrg		struct dri_bo *c;
1293428d7b3dSmrg
1294428d7b3dSmrg		c = list_first_entry(&info->cache, struct dri_bo, link);
1295428d7b3dSmrg		list_del(&c->link);
1296428d7b3dSmrg
1297428d7b3dSmrg		DBG(("%s: releasing cached handle=%d\n", __FUNCTION__, c->bo ? c->bo->handle : 0));
1298428d7b3dSmrg		if (c->bo)
1299428d7b3dSmrg			kgem_bo_destroy(&info->sna->kgem, c->bo);
1300428d7b3dSmrg
1301428d7b3dSmrg		free(c);
1302428d7b3dSmrg	}
1303428d7b3dSmrg
1304428d7b3dSmrg	if (info->bo) {
1305428d7b3dSmrg		DBG(("%s: releasing batch handle=%d\n", __FUNCTION__, info->bo->handle));
1306428d7b3dSmrg		kgem_bo_destroy(&info->sna->kgem, info->bo);
1307428d7b3dSmrg	}
1308428d7b3dSmrg
1309428d7b3dSmrg	_list_del(&info->link);
1310428d7b3dSmrg	free(info);
1311428d7b3dSmrg}
1312428d7b3dSmrg
1313428d7b3dSmrgstatic void
1314428d7b3dSmrgsna_dri2_client_gone(CallbackListPtr *list, void *closure, void *data)
1315428d7b3dSmrg{
1316428d7b3dSmrg	NewClientInfoRec *clientinfo = data;
1317428d7b3dSmrg	ClientPtr client = clientinfo->client;
1318428d7b3dSmrg	struct sna_client *priv = sna_client(client);
1319428d7b3dSmrg	struct sna *sna = closure;
1320428d7b3dSmrg
1321428d7b3dSmrg	if (priv->events.next == NULL)
1322428d7b3dSmrg		return;
1323428d7b3dSmrg
1324428d7b3dSmrg	if (client->clientState != ClientStateGone)
1325428d7b3dSmrg		return;
1326428d7b3dSmrg
1327428d7b3dSmrg	DBG(("%s(active?=%d)\n", __FUNCTION__,
1328428d7b3dSmrg	     !list_is_empty(&priv->events)));
1329428d7b3dSmrg
1330428d7b3dSmrg	while (!list_is_empty(&priv->events)) {
1331428d7b3dSmrg		struct sna_dri2_event *event;
1332428d7b3dSmrg
1333428d7b3dSmrg		event = list_first_entry(&priv->events, struct sna_dri2_event, link);
1334428d7b3dSmrg		assert(event->client == client);
1335428d7b3dSmrg
1336428d7b3dSmrg		if (event->queued) {
1337428d7b3dSmrg			if (event->draw)
1338428d7b3dSmrg				sna_dri2_remove_event((WindowPtr)event->draw,
1339428d7b3dSmrg						      event);
1340428d7b3dSmrg			event->client = NULL;
1341428d7b3dSmrg			event->draw = NULL;
1342428d7b3dSmrg			list_del(&event->link);
1343428d7b3dSmrg		} else
1344428d7b3dSmrg			sna_dri2_event_free(event);
1345428d7b3dSmrg	}
1346428d7b3dSmrg
1347428d7b3dSmrg	if (--sna->dri2.client_count == 0)
1348428d7b3dSmrg		DeleteCallback(&ClientStateCallback, sna_dri2_client_gone, sna);
1349428d7b3dSmrg}
1350428d7b3dSmrg
1351428d7b3dSmrgstatic bool add_event_to_client(struct sna_dri2_event *info, struct sna *sna, ClientPtr client)
1352428d7b3dSmrg{
1353428d7b3dSmrg	struct sna_client *priv = sna_client(client);
1354428d7b3dSmrg
1355428d7b3dSmrg	if (priv->events.next == NULL) {
1356428d7b3dSmrg		if (sna->dri2.client_count++ == 0 &&
1357428d7b3dSmrg		    !AddCallback(&ClientStateCallback, sna_dri2_client_gone, sna))
1358428d7b3dSmrg			return false;
1359428d7b3dSmrg
1360428d7b3dSmrg		list_init(&priv->events);
1361428d7b3dSmrg	}
1362428d7b3dSmrg
1363428d7b3dSmrg	list_add(&info->link, &priv->events);
1364428d7b3dSmrg	info->client = client;
1365428d7b3dSmrg	return true;
1366428d7b3dSmrg}
1367428d7b3dSmrg
1368428d7b3dSmrgstatic struct sna_dri2_event *
1369428d7b3dSmrgsna_dri2_add_event(struct sna *sna, DrawablePtr draw, ClientPtr client)
1370428d7b3dSmrg{
1371428d7b3dSmrg	struct dri2_window *priv;
1372428d7b3dSmrg	struct sna_dri2_event *info, *chain;
1373428d7b3dSmrg
1374428d7b3dSmrg	assert(draw->type == DRAWABLE_WINDOW);
1375428d7b3dSmrg	DBG(("%s: adding event to window %ld)\n",
1376428d7b3dSmrg	     __FUNCTION__, (long)draw->id));
1377428d7b3dSmrg
1378428d7b3dSmrg	priv = dri2_window((WindowPtr)draw);
1379428d7b3dSmrg	if (priv == NULL)
1380428d7b3dSmrg		return NULL;
1381428d7b3dSmrg
1382428d7b3dSmrg	info = calloc(1, sizeof(struct sna_dri2_event));
1383428d7b3dSmrg	if (info == NULL)
1384428d7b3dSmrg		return NULL;
1385428d7b3dSmrg
1386428d7b3dSmrg	list_init(&info->cache);
1387428d7b3dSmrg	info->sna = sna;
1388428d7b3dSmrg	info->draw = draw;
1389428d7b3dSmrg	info->crtc = priv->crtc;
1390428d7b3dSmrg	info->pipe = sna_crtc_to_pipe(priv->crtc);
1391428d7b3dSmrg
1392428d7b3dSmrg	if (!add_event_to_client(info, sna, client)) {
1393428d7b3dSmrg		free(info);
1394428d7b3dSmrg		return NULL;
1395428d7b3dSmrg	}
1396428d7b3dSmrg
1397428d7b3dSmrg	assert(priv->chain != info);
1398428d7b3dSmrg
1399428d7b3dSmrg	if (priv->chain == NULL) {
1400428d7b3dSmrg		priv->chain = info;
1401428d7b3dSmrg		return info;
1402428d7b3dSmrg	}
1403428d7b3dSmrg
1404428d7b3dSmrg	chain = priv->chain;
1405428d7b3dSmrg	while (chain->chain != NULL)
1406428d7b3dSmrg		chain = chain->chain;
1407428d7b3dSmrg
1408428d7b3dSmrg	assert(chain != info);
1409428d7b3dSmrg	chain->chain = info;
1410428d7b3dSmrg	return info;
1411428d7b3dSmrg}
1412428d7b3dSmrg
1413428d7b3dSmrgvoid sna_dri2_decouple_window(WindowPtr win)
1414428d7b3dSmrg{
1415428d7b3dSmrg	struct dri2_window *priv;
1416428d7b3dSmrg
1417428d7b3dSmrg	priv = dri2_window(win);
1418428d7b3dSmrg	if (priv == NULL)
1419428d7b3dSmrg		return;
1420428d7b3dSmrg
1421428d7b3dSmrg	DBG(("%s: window=%ld\n", __FUNCTION__, win->drawable.id));
1422428d7b3dSmrg
1423428d7b3dSmrg	if (priv->front) {
1424428d7b3dSmrg		struct sna *sna = to_sna_from_drawable(&win->drawable);
1425428d7b3dSmrg		assert(priv->crtc);
1426428d7b3dSmrg		sna_shadow_unset_crtc(sna, priv->crtc);
1427428d7b3dSmrg		_sna_dri2_destroy_buffer(sna, priv->front);
1428428d7b3dSmrg		priv->front = NULL;
1429428d7b3dSmrg	}
1430428d7b3dSmrg}
1431428d7b3dSmrg
1432428d7b3dSmrgvoid sna_dri2_destroy_window(WindowPtr win)
1433428d7b3dSmrg{
1434428d7b3dSmrg	struct dri2_window *priv;
1435428d7b3dSmrg
1436428d7b3dSmrg	priv = dri2_window(win);
1437428d7b3dSmrg	if (priv == NULL)
1438428d7b3dSmrg		return;
1439428d7b3dSmrg
1440428d7b3dSmrg	DBG(("%s: window=%ld\n", __FUNCTION__, win->drawable.id));
1441428d7b3dSmrg
1442428d7b3dSmrg	if (priv->front) {
1443428d7b3dSmrg		struct sna *sna = to_sna_from_drawable(&win->drawable);
1444428d7b3dSmrg		assert(priv->crtc);
1445428d7b3dSmrg		sna_shadow_unset_crtc(sna, priv->crtc);
1446428d7b3dSmrg		_sna_dri2_destroy_buffer(sna, priv->front);
1447428d7b3dSmrg	}
1448428d7b3dSmrg
1449428d7b3dSmrg	if (priv->chain) {
1450428d7b3dSmrg		struct sna_dri2_event *info, *chain;
1451428d7b3dSmrg
1452428d7b3dSmrg		DBG(("%s: freeing chain\n", __FUNCTION__));
1453428d7b3dSmrg
1454428d7b3dSmrg		chain = priv->chain;
1455428d7b3dSmrg		while ((info = chain)) {
1456428d7b3dSmrg			info->draw = NULL;
1457428d7b3dSmrg			info->client = NULL;
1458428d7b3dSmrg			list_del(&info->link);
1459428d7b3dSmrg
1460428d7b3dSmrg			chain = info->chain;
1461428d7b3dSmrg			info->chain = NULL;
1462428d7b3dSmrg
1463428d7b3dSmrg			if (!info->queued)
1464428d7b3dSmrg				sna_dri2_event_free(info);
1465428d7b3dSmrg		}
1466428d7b3dSmrg	}
1467428d7b3dSmrg
1468428d7b3dSmrg	free(priv);
1469428d7b3dSmrg}
1470428d7b3dSmrg
1471428d7b3dSmrgstatic void
1472428d7b3dSmrgsna_dri2_flip_handler(struct drm_event_vblank *event, void *data)
1473428d7b3dSmrg{
1474428d7b3dSmrg	DBG(("%s: sequence=%d\n", __FUNCTION__, event->sequence));
1475428d7b3dSmrg	sna_dri2_flip_event(data);
1476428d7b3dSmrg}
1477428d7b3dSmrg
1478428d7b3dSmrgstatic bool
1479428d7b3dSmrgsna_dri2_flip(struct sna_dri2_event *info)
1480428d7b3dSmrg{
1481428d7b3dSmrg	struct kgem_bo *bo = get_private(info->back)->bo;
1482428d7b3dSmrg	struct kgem_bo *tmp_bo;
1483428d7b3dSmrg	uint32_t tmp_name;
1484428d7b3dSmrg	int tmp_pitch;
1485428d7b3dSmrg
1486428d7b3dSmrg	DBG(("%s(type=%d)\n", __FUNCTION__, info->type));
1487428d7b3dSmrg
1488428d7b3dSmrg	assert(sna_pixmap_get_buffer(info->sna->front) == info->front);
1489428d7b3dSmrg	assert(get_drawable_pixmap(info->draw)->drawable.height * bo->pitch <= kgem_bo_size(bo));
1490428d7b3dSmrg	assert(bo->refcnt);
1491428d7b3dSmrg
1492428d7b3dSmrg	if (!sna_page_flip(info->sna, bo, sna_dri2_flip_handler,
1493428d7b3dSmrg			   info->type == FLIP_ASYNC ? NULL : info))
1494428d7b3dSmrg		return false;
1495428d7b3dSmrg
1496428d7b3dSmrg	assert(info->sna->dri2.flip_pending == NULL ||
1497428d7b3dSmrg	       info->sna->dri2.flip_pending == info);
1498428d7b3dSmrg	if (info->type != FLIP_ASYNC)
1499428d7b3dSmrg		info->sna->dri2.flip_pending = info;
1500428d7b3dSmrg
1501428d7b3dSmrg	DBG(("%s: marked handle=%d as scanout, swap front (handle=%d, name=%d) and back (handle=%d, name=%d)\n",
1502428d7b3dSmrg	     __FUNCTION__, bo->handle,
1503428d7b3dSmrg	     get_private(info->front)->bo->handle, info->front->name,
1504428d7b3dSmrg	     get_private(info->back)->bo->handle, info->back->name));
1505428d7b3dSmrg
1506428d7b3dSmrg	tmp_bo = get_private(info->front)->bo;
1507428d7b3dSmrg	tmp_name = info->front->name;
1508428d7b3dSmrg	tmp_pitch = info->front->pitch;
1509428d7b3dSmrg
1510428d7b3dSmrg	set_bo(info->sna->front, bo);
1511428d7b3dSmrg
1512428d7b3dSmrg	info->front->name = info->back->name;
1513428d7b3dSmrg	info->front->pitch = info->back->pitch;
1514428d7b3dSmrg	get_private(info->front)->bo = bo;
1515428d7b3dSmrg
1516428d7b3dSmrg	info->back->name = tmp_name;
1517428d7b3dSmrg	info->back->pitch = tmp_pitch;
1518428d7b3dSmrg	get_private(info->back)->bo = tmp_bo;
1519428d7b3dSmrg	mark_stale(info->back);
1520428d7b3dSmrg
1521428d7b3dSmrg	assert(get_private(info->front)->bo->refcnt);
1522428d7b3dSmrg	assert(get_private(info->back)->bo->refcnt);
1523428d7b3dSmrg	assert(get_private(info->front)->bo != get_private(info->back)->bo);
1524428d7b3dSmrg
1525428d7b3dSmrg	info->queued = true;
1526428d7b3dSmrg	return true;
1527428d7b3dSmrg}
1528428d7b3dSmrg
1529428d7b3dSmrgstatic bool
1530428d7b3dSmrgcan_flip(struct sna * sna,
1531428d7b3dSmrg	 DrawablePtr draw,
1532428d7b3dSmrg	 DRI2BufferPtr front,
1533428d7b3dSmrg	 DRI2BufferPtr back,
1534428d7b3dSmrg	 xf86CrtcPtr crtc)
1535428d7b3dSmrg{
1536428d7b3dSmrg	WindowPtr win = (WindowPtr)draw;
1537428d7b3dSmrg	PixmapPtr pixmap;
1538428d7b3dSmrg
1539428d7b3dSmrg	assert((sna->flags & SNA_NO_WAIT) == 0);
1540428d7b3dSmrg
1541428d7b3dSmrg	if (!DBG_CAN_FLIP)
1542428d7b3dSmrg		return false;
1543428d7b3dSmrg
1544428d7b3dSmrg	if (draw->type == DRAWABLE_PIXMAP)
1545428d7b3dSmrg		return false;
1546428d7b3dSmrg
1547428d7b3dSmrg	if (!sna->mode.front_active) {
1548428d7b3dSmrg		DBG(("%s: no, active CRTC\n", __FUNCTION__));
1549428d7b3dSmrg		return false;
1550428d7b3dSmrg	}
1551428d7b3dSmrg
1552428d7b3dSmrg	assert(sna->scrn->vtSema);
1553428d7b3dSmrg
1554428d7b3dSmrg	if ((sna->flags & (SNA_HAS_FLIP | SNA_HAS_ASYNC_FLIP)) == 0) {
1555428d7b3dSmrg		DBG(("%s: no, pageflips disabled\n", __FUNCTION__));
1556428d7b3dSmrg		return false;
1557428d7b3dSmrg	}
1558428d7b3dSmrg
1559428d7b3dSmrg	if (front->format != back->format) {
1560428d7b3dSmrg		DBG(("%s: no, format mismatch, front = %d, back = %d\n",
1561428d7b3dSmrg		     __FUNCTION__, front->format, back->format));
1562428d7b3dSmrg		return false;
1563428d7b3dSmrg	}
1564428d7b3dSmrg
1565428d7b3dSmrg	if (sna->mode.shadow_active) {
1566428d7b3dSmrg		DBG(("%s: no, shadow enabled\n", __FUNCTION__));
1567428d7b3dSmrg		return false;
1568428d7b3dSmrg	}
1569428d7b3dSmrg
1570428d7b3dSmrg	if (!sna_crtc_is_on(crtc)) {
1571428d7b3dSmrg		DBG(("%s: ref-pipe=%d is disabled\n", __FUNCTION__, sna_crtc_to_pipe(crtc)));
1572428d7b3dSmrg		return false;
1573428d7b3dSmrg	}
1574428d7b3dSmrg
1575428d7b3dSmrg	pixmap = get_window_pixmap(win);
1576428d7b3dSmrg	if (pixmap != sna->front) {
1577428d7b3dSmrg		DBG(("%s: no, window (pixmap=%ld) is not attached to the front buffer (pixmap=%ld)\n",
1578428d7b3dSmrg		     __FUNCTION__, pixmap->drawable.serialNumber, sna->front->drawable.serialNumber));
1579428d7b3dSmrg		return false;
1580428d7b3dSmrg	}
1581428d7b3dSmrg
1582428d7b3dSmrg	if (sna_pixmap_get_buffer(pixmap) != front) {
1583428d7b3dSmrg		DBG(("%s: no, DRI2 drawable is no longer attached (old name=%d, new name=%d) to pixmap=%ld\n",
1584428d7b3dSmrg		     __FUNCTION__, front->name,
1585428d7b3dSmrg		     sna_pixmap_get_buffer(pixmap) ? ((DRI2BufferPtr)sna_pixmap_get_buffer(pixmap))->name : 0,
1586428d7b3dSmrg		     pixmap->drawable.serialNumber));
1587428d7b3dSmrg		return false;
1588428d7b3dSmrg	}
1589428d7b3dSmrg
1590428d7b3dSmrg	assert(get_private(front)->pixmap == sna->front);
1591428d7b3dSmrg	assert(sna_pixmap(sna->front)->gpu_bo == get_private(front)->bo);
1592428d7b3dSmrg
1593428d7b3dSmrg	if (!get_private(back)->bo->scanout) {
1594428d7b3dSmrg		DBG(("%s: no, DRI2 drawable was too small at time of creation)\n",
1595428d7b3dSmrg		     __FUNCTION__));
1596428d7b3dSmrg		return false;
1597428d7b3dSmrg	}
1598428d7b3dSmrg
1599428d7b3dSmrg	if (get_private(back)->size != get_private(front)->size) {
1600428d7b3dSmrg		DBG(("%s: no, DRI2 drawable does not fit into scanout\n",
1601428d7b3dSmrg		     __FUNCTION__));
1602428d7b3dSmrg		return false;
1603428d7b3dSmrg	}
1604428d7b3dSmrg
1605428d7b3dSmrg	DBG(("%s: window size: %dx%d, clip=(%d, %d), (%d, %d) x %d\n",
1606428d7b3dSmrg	     __FUNCTION__,
1607428d7b3dSmrg	     win->drawable.width, win->drawable.height,
1608428d7b3dSmrg	     win->clipList.extents.x1, win->clipList.extents.y1,
1609428d7b3dSmrg	     win->clipList.extents.x2, win->clipList.extents.y2,
1610428d7b3dSmrg	     region_num_rects(&win->clipList)));
1611428d7b3dSmrg	if (!RegionEqual(&win->clipList, &draw->pScreen->root->winSize)) {
1612428d7b3dSmrg		DBG(("%s: no, window is clipped: clip region=(%d, %d), (%d, %d), root size=(%d, %d), (%d, %d)\n",
1613428d7b3dSmrg		     __FUNCTION__,
1614428d7b3dSmrg		     win->clipList.extents.x1,
1615428d7b3dSmrg		     win->clipList.extents.y1,
1616428d7b3dSmrg		     win->clipList.extents.x2,
1617428d7b3dSmrg		     win->clipList.extents.y2,
1618428d7b3dSmrg		     draw->pScreen->root->winSize.extents.x1,
1619428d7b3dSmrg		     draw->pScreen->root->winSize.extents.y1,
1620428d7b3dSmrg		     draw->pScreen->root->winSize.extents.x2,
1621428d7b3dSmrg		     draw->pScreen->root->winSize.extents.y2));
1622428d7b3dSmrg		return false;
1623428d7b3dSmrg	}
1624428d7b3dSmrg
1625428d7b3dSmrg	if (draw->x != 0 || draw->y != 0 ||
1626428d7b3dSmrg#ifdef COMPOSITE
1627428d7b3dSmrg	    draw->x != pixmap->screen_x ||
1628428d7b3dSmrg	    draw->y != pixmap->screen_y ||
1629428d7b3dSmrg#endif
1630428d7b3dSmrg	    draw->width != pixmap->drawable.width ||
1631428d7b3dSmrg	    draw->height != pixmap->drawable.height) {
1632428d7b3dSmrg		DBG(("%s: no, window is not full size (%dx%d)!=(%dx%d)\n",
1633428d7b3dSmrg		     __FUNCTION__,
1634428d7b3dSmrg		     draw->width, draw->height,
1635428d7b3dSmrg		     pixmap->drawable.width,
1636428d7b3dSmrg		     pixmap->drawable.height));
1637428d7b3dSmrg		return false;
1638428d7b3dSmrg	}
1639428d7b3dSmrg
1640428d7b3dSmrg	/* prevent an implicit tiling mode change */
1641428d7b3dSmrg	if (get_private(back)->bo->tiling > I915_TILING_X) {
1642428d7b3dSmrg		DBG(("%s -- no, tiling mismatch: front %d, back=%d, want-tiled?=%d\n",
1643428d7b3dSmrg		     __FUNCTION__,
1644428d7b3dSmrg		     get_private(front)->bo->tiling,
1645428d7b3dSmrg		     get_private(back)->bo->tiling,
1646428d7b3dSmrg		     !!(sna->flags & SNA_LINEAR_FB)));
1647428d7b3dSmrg		return false;
1648428d7b3dSmrg	}
1649428d7b3dSmrg
1650428d7b3dSmrg	if (get_private(front)->bo->pitch != get_private(back)->bo->pitch) {
1651428d7b3dSmrg		DBG(("%s -- no, pitch mismatch: front %d, back=%d\n",
1652428d7b3dSmrg		     __FUNCTION__,
1653428d7b3dSmrg		     get_private(front)->bo->pitch,
1654428d7b3dSmrg		     get_private(back)->bo->pitch));
1655428d7b3dSmrg		return false;
1656428d7b3dSmrg	}
1657428d7b3dSmrg
1658428d7b3dSmrg	if (sna_pixmap(pixmap)->pinned & ~(PIN_DRI2 | PIN_SCANOUT)) {
1659428d7b3dSmrg		DBG(("%s -- no, pinned: front %x\n",
1660428d7b3dSmrg		     __FUNCTION__, sna_pixmap(pixmap)->pinned));
1661428d7b3dSmrg		return false;
1662428d7b3dSmrg	}
1663428d7b3dSmrg
1664428d7b3dSmrg	DBG(("%s: yes, pixmap=%ld\n", __FUNCTION__, pixmap->drawable.serialNumber));
1665428d7b3dSmrg	assert(dri2_window(win)->front == NULL);
1666428d7b3dSmrg	return true;
1667428d7b3dSmrg}
1668428d7b3dSmrg
1669428d7b3dSmrgstatic bool
1670428d7b3dSmrgcan_xchg(struct sna *sna,
1671428d7b3dSmrg	 DrawablePtr draw,
1672428d7b3dSmrg	 DRI2BufferPtr front,
1673428d7b3dSmrg	 DRI2BufferPtr back)
1674428d7b3dSmrg{
1675428d7b3dSmrg	WindowPtr win = (WindowPtr)draw;
1676428d7b3dSmrg	PixmapPtr pixmap;
1677428d7b3dSmrg
1678428d7b3dSmrg	if (!DBG_CAN_XCHG)
1679428d7b3dSmrg		return false;
1680428d7b3dSmrg
1681428d7b3dSmrg	if (draw->type == DRAWABLE_PIXMAP)
1682428d7b3dSmrg		return false;
1683428d7b3dSmrg
1684428d7b3dSmrg	if (front->format != back->format) {
1685428d7b3dSmrg		DBG(("%s: no, format mismatch, front = %d, back = %d\n",
1686428d7b3dSmrg		     __FUNCTION__, front->format, back->format));
1687428d7b3dSmrg		return false;
1688428d7b3dSmrg	}
1689428d7b3dSmrg
1690428d7b3dSmrg	pixmap = get_window_pixmap(win);
1691428d7b3dSmrg	if (get_private(front)->pixmap != pixmap) {
1692428d7b3dSmrg		DBG(("%s: no, DRI2 drawable is no longer attached, old pixmap=%ld, now pixmap=%ld\n",
1693428d7b3dSmrg		     __FUNCTION__,
1694428d7b3dSmrg		     get_private(front)->pixmap->drawable.serialNumber,
1695428d7b3dSmrg		     pixmap->drawable.serialNumber));
1696428d7b3dSmrg		return false;
1697428d7b3dSmrg	}
1698428d7b3dSmrg
1699428d7b3dSmrg	DBG(("%s: window size: %dx%d, clip=(%d, %d), (%d, %d) x %d, pixmap size=%dx%d\n",
1700428d7b3dSmrg	     __FUNCTION__,
1701428d7b3dSmrg	     win->drawable.width, win->drawable.height,
1702428d7b3dSmrg	     win->clipList.extents.x1, win->clipList.extents.y1,
1703428d7b3dSmrg	     win->clipList.extents.x2, win->clipList.extents.y2,
1704428d7b3dSmrg	     region_num_rects(&win->clipList),
1705428d7b3dSmrg	     pixmap->drawable.width,
1706428d7b3dSmrg	     pixmap->drawable.height));
1707428d7b3dSmrg	if (is_clipped(&win->clipList, &pixmap->drawable)) {
1708428d7b3dSmrg		DBG(("%s: no, %dx%d window is clipped: clip region=(%d, %d), (%d, %d)\n",
1709428d7b3dSmrg		     __FUNCTION__,
1710428d7b3dSmrg		     draw->width, draw->height,
1711428d7b3dSmrg		     win->clipList.extents.x1,
1712428d7b3dSmrg		     win->clipList.extents.y1,
1713428d7b3dSmrg		     win->clipList.extents.x2,
1714428d7b3dSmrg		     win->clipList.extents.y2));
1715428d7b3dSmrg		return false;
1716428d7b3dSmrg	}
1717428d7b3dSmrg
1718428d7b3dSmrg	if (get_private(back)->size != get_private(front)->size) {
1719428d7b3dSmrg		DBG(("%s: no, back buffer %dx%d does not match front buffer %dx%d\n",
1720428d7b3dSmrg		     __FUNCTION__,
1721428d7b3dSmrg		     get_private(back)->size & 0x7fff, (get_private(back)->size >> 16) & 0x7fff,
1722428d7b3dSmrg		     get_private(front)->size & 0x7fff, (get_private(front)->size >> 16) & 0x7fff));
1723428d7b3dSmrg		return false;
1724428d7b3dSmrg	}
1725428d7b3dSmrg
1726428d7b3dSmrg	if (pixmap == sna->front && !(sna->flags & SNA_TEAR_FREE) && sna->mode.front_active) {
1727428d7b3dSmrg		DBG(("%s: no, front buffer, requires flipping\n",
1728428d7b3dSmrg		     __FUNCTION__));
1729428d7b3dSmrg		return false;
1730428d7b3dSmrg	}
1731428d7b3dSmrg
1732428d7b3dSmrg	if (sna_pixmap(pixmap)->pinned & ~(PIN_DRI2 | PIN_SCANOUT)) {
1733428d7b3dSmrg		DBG(("%s: no, pinned: %x\n",
1734428d7b3dSmrg		     __FUNCTION__, sna_pixmap(pixmap)->pinned));
1735428d7b3dSmrg		return false;
1736428d7b3dSmrg	}
1737428d7b3dSmrg
1738428d7b3dSmrg	DBG(("%s: yes, pixmap=%ld\n", __FUNCTION__, pixmap->drawable.serialNumber));
1739428d7b3dSmrg	return true;
1740428d7b3dSmrg}
1741428d7b3dSmrg
1742428d7b3dSmrgstatic bool
1743428d7b3dSmrgoverlaps_other_crtc(struct sna *sna, xf86CrtcPtr desired)
1744428d7b3dSmrg{
1745428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
1746428d7b3dSmrg	int c;
1747428d7b3dSmrg
1748428d7b3dSmrg	for (c = 0; c < sna->mode.num_real_crtc; c++) {
1749428d7b3dSmrg		xf86CrtcPtr crtc = config->crtc[c];
1750428d7b3dSmrg
1751428d7b3dSmrg		if (crtc == desired)
1752428d7b3dSmrg			continue;
1753428d7b3dSmrg
1754428d7b3dSmrg		if (!crtc->enabled)
1755428d7b3dSmrg			continue;
1756428d7b3dSmrg
1757428d7b3dSmrg		if (desired->bounds.x1 < crtc->bounds.x2 &&
1758428d7b3dSmrg		    desired->bounds.x2 > crtc->bounds.x1 &&
1759428d7b3dSmrg		    desired->bounds.y1 < crtc->bounds.y2 &&
1760428d7b3dSmrg		    desired->bounds.y2 > crtc->bounds.y1)
1761428d7b3dSmrg			return true;
1762428d7b3dSmrg	}
1763428d7b3dSmrg
1764428d7b3dSmrg	return false;
1765428d7b3dSmrg}
1766428d7b3dSmrg
1767428d7b3dSmrgstatic bool
1768428d7b3dSmrgcan_xchg_crtc(struct sna *sna,
1769428d7b3dSmrg	      DrawablePtr draw,
1770428d7b3dSmrg	      DRI2BufferPtr front,
1771428d7b3dSmrg	      DRI2BufferPtr back,
1772428d7b3dSmrg	      xf86CrtcPtr crtc)
1773428d7b3dSmrg{
1774428d7b3dSmrg	WindowPtr win = (WindowPtr)draw;
1775428d7b3dSmrg	PixmapPtr pixmap;
1776428d7b3dSmrg
1777428d7b3dSmrg	if (!DBG_CAN_XCHG)
1778428d7b3dSmrg		return false;
1779428d7b3dSmrg
1780428d7b3dSmrg	if ((sna->flags & SNA_TEAR_FREE) == 0) {
1781428d7b3dSmrg		DBG(("%s: no, requires TearFree\n",
1782428d7b3dSmrg		     __FUNCTION__));
1783428d7b3dSmrg		return false;
1784428d7b3dSmrg	}
1785428d7b3dSmrg
1786428d7b3dSmrg	if (draw->type == DRAWABLE_PIXMAP)
1787428d7b3dSmrg		return false;
1788428d7b3dSmrg
1789428d7b3dSmrg	if (front->format != back->format) {
1790428d7b3dSmrg		DBG(("%s: no, format mismatch, front = %d, back = %d\n",
1791428d7b3dSmrg		     __FUNCTION__, front->format, back->format));
1792428d7b3dSmrg		return false;
1793428d7b3dSmrg	}
1794428d7b3dSmrg
1795428d7b3dSmrg	if (memcmp(&win->clipList.extents, &crtc->bounds, sizeof(crtc->bounds))) {
1796428d7b3dSmrg		DBG(("%s: no, window [(%d, %d), (%d, %d)] does not cover CRTC [(%d, %d), (%d, %d)]\n",
1797428d7b3dSmrg		     __FUNCTION__,
1798428d7b3dSmrg		     win->clipList.extents.x1, win->clipList.extents.y1,
1799428d7b3dSmrg		     win->clipList.extents.x2, win->clipList.extents.y2,
1800428d7b3dSmrg		     crtc->bounds.x1, crtc->bounds.y1,
1801428d7b3dSmrg		     crtc->bounds.x2, crtc->bounds.y2));
1802428d7b3dSmrg		return false;
1803428d7b3dSmrg	}
1804428d7b3dSmrg
1805428d7b3dSmrg	if (sna_crtc_is_transformed(crtc)) {
1806428d7b3dSmrg		DBG(("%s: no, CRTC is rotated\n", __FUNCTION__));
1807428d7b3dSmrg		return false;
1808428d7b3dSmrg	}
1809428d7b3dSmrg
1810428d7b3dSmrg	pixmap = get_window_pixmap(win);
1811428d7b3dSmrg	if (pixmap != sna->front) {
1812428d7b3dSmrg		DBG(("%s: no, not attached to front buffer\n", __FUNCTION__));
1813428d7b3dSmrg		return false;
1814428d7b3dSmrg	}
1815428d7b3dSmrg
1816428d7b3dSmrg	if (get_private(front)->pixmap != pixmap) {
1817428d7b3dSmrg		DBG(("%s: no, DRI2 drawable is no longer attached, old pixmap=%ld, now pixmap=%ld\n",
1818428d7b3dSmrg		     __FUNCTION__,
1819428d7b3dSmrg		     get_private(front)->pixmap->drawable.serialNumber,
1820428d7b3dSmrg		     pixmap->drawable.serialNumber));
1821428d7b3dSmrg		return false;
1822428d7b3dSmrg	}
1823428d7b3dSmrg
1824428d7b3dSmrg	DBG(("%s: window size: %dx%d, clip=(%d, %d), (%d, %d) x %d\n",
1825428d7b3dSmrg	     __FUNCTION__,
1826428d7b3dSmrg	     win->drawable.width, win->drawable.height,
1827428d7b3dSmrg	     win->clipList.extents.x1, win->clipList.extents.y1,
1828428d7b3dSmrg	     win->clipList.extents.x2, win->clipList.extents.y2,
1829428d7b3dSmrg	     region_num_rects(&win->clipList)));
1830428d7b3dSmrg	if (is_clipped(&win->clipList, &win->drawable)) {
1831428d7b3dSmrg		DBG(("%s: no, %dx%d window is clipped: clip region=(%d, %d), (%d, %d)\n",
1832428d7b3dSmrg		     __FUNCTION__,
1833428d7b3dSmrg		     draw->width, draw->height,
1834428d7b3dSmrg		     win->clipList.extents.x1,
1835428d7b3dSmrg		     win->clipList.extents.y1,
1836428d7b3dSmrg		     win->clipList.extents.x2,
1837428d7b3dSmrg		     win->clipList.extents.y2));
1838428d7b3dSmrg		return false;
1839428d7b3dSmrg	}
1840428d7b3dSmrg
1841428d7b3dSmrg	if (overlaps_other_crtc(sna, crtc)) {
1842428d7b3dSmrg		DBG(("%s: no, overlaps other CRTC\n", __FUNCTION__));
1843428d7b3dSmrg		return false;
1844428d7b3dSmrg	}
1845428d7b3dSmrg
1846428d7b3dSmrg	if (get_private(back)->size != (draw->height << 16 | draw->width)) {
1847428d7b3dSmrg		DBG(("%s: no, DRI2 buffers does not fit window\n",
1848428d7b3dSmrg		     __FUNCTION__));
1849428d7b3dSmrg		return false;
1850428d7b3dSmrg	}
1851428d7b3dSmrg
1852428d7b3dSmrg	assert(win != win->drawable.pScreen->root);
1853428d7b3dSmrg	DBG(("%s: yes, pixmap=%ld\n", __FUNCTION__, pixmap->drawable.serialNumber));
1854428d7b3dSmrg	return true;
1855428d7b3dSmrg}
1856428d7b3dSmrg
1857428d7b3dSmrgstatic void
1858428d7b3dSmrgsna_dri2_xchg(DrawablePtr draw, DRI2BufferPtr front, DRI2BufferPtr back)
1859428d7b3dSmrg{
1860428d7b3dSmrg	WindowPtr win = (WindowPtr)draw;
1861428d7b3dSmrg	struct kgem_bo *back_bo, *front_bo;
1862428d7b3dSmrg	PixmapPtr pixmap;
1863428d7b3dSmrg	int tmp;
1864428d7b3dSmrg
1865428d7b3dSmrg	assert(draw->type != DRAWABLE_PIXMAP);
1866428d7b3dSmrg	pixmap = get_window_pixmap(win);
1867428d7b3dSmrg
1868428d7b3dSmrg	back_bo = get_private(back)->bo;
1869428d7b3dSmrg	front_bo = get_private(front)->bo;
1870428d7b3dSmrg	assert(front_bo != back_bo);
1871428d7b3dSmrg
1872428d7b3dSmrg	DBG(("%s: win=%ld, exchange front=%d/%d and back=%d/%d, pixmap=%ld %dx%d\n",
1873428d7b3dSmrg	     __FUNCTION__, win->drawable.id,
1874428d7b3dSmrg	     front_bo->handle, front->name,
1875428d7b3dSmrg	     back_bo->handle, back->name,
1876428d7b3dSmrg	     pixmap->drawable.serialNumber,
1877428d7b3dSmrg	     pixmap->drawable.width,
1878428d7b3dSmrg	     pixmap->drawable.height));
1879428d7b3dSmrg
1880428d7b3dSmrg	DBG(("%s: back_bo pitch=%d, size=%d, ref=%d, active_scanout?=%d\n",
1881428d7b3dSmrg	     __FUNCTION__, back_bo->pitch, kgem_bo_size(back_bo), back_bo->refcnt, back_bo->active_scanout));
1882428d7b3dSmrg	DBG(("%s: front_bo pitch=%d, size=%d, ref=%d, active_scanout?=%d\n",
1883428d7b3dSmrg	     __FUNCTION__, front_bo->pitch, kgem_bo_size(front_bo), front_bo->refcnt, front_bo->active_scanout));
1884428d7b3dSmrg	assert(front_bo->refcnt);
1885428d7b3dSmrg	assert(back_bo->refcnt);
1886428d7b3dSmrg
1887428d7b3dSmrg	assert(sna_pixmap_get_buffer(pixmap) == front);
1888428d7b3dSmrg
1889428d7b3dSmrg	assert(pixmap->drawable.height * back_bo->pitch <= kgem_bo_size(back_bo));
1890428d7b3dSmrg	assert(pixmap->drawable.height * front_bo->pitch <= kgem_bo_size(front_bo));
1891428d7b3dSmrg
1892428d7b3dSmrg	set_bo(pixmap, back_bo);
1893428d7b3dSmrg
1894428d7b3dSmrg	get_private(front)->bo = back_bo;
1895428d7b3dSmrg	get_private(back)->bo = front_bo;
1896428d7b3dSmrg	mark_stale(back);
1897428d7b3dSmrg
1898428d7b3dSmrg	tmp = front->name;
1899428d7b3dSmrg	front->name = back->name;
1900428d7b3dSmrg	back->name = tmp;
1901428d7b3dSmrg
1902428d7b3dSmrg	tmp = front->pitch;
1903428d7b3dSmrg	front->pitch = back->pitch;
1904428d7b3dSmrg	back->pitch = tmp;
1905428d7b3dSmrg
1906428d7b3dSmrg	assert(front_bo->refcnt);
1907428d7b3dSmrg	assert(back_bo->refcnt);
1908428d7b3dSmrg
1909428d7b3dSmrg	assert(get_private(front)->bo == sna_pixmap(pixmap)->gpu_bo);
1910428d7b3dSmrg}
1911428d7b3dSmrg
1912428d7b3dSmrgstatic void sna_dri2_xchg_crtc(struct sna *sna, DrawablePtr draw, xf86CrtcPtr crtc, DRI2BufferPtr front, DRI2BufferPtr back)
1913428d7b3dSmrg{
1914428d7b3dSmrg	WindowPtr win = (WindowPtr)draw;
1915428d7b3dSmrg	DRI2Buffer2Ptr tmp;
1916428d7b3dSmrg	struct kgem_bo *bo;
1917428d7b3dSmrg
1918428d7b3dSmrg	DBG(("%s: exchange front=%d/%d and back=%d/%d, win id=%lu, pixmap=%ld %dx%d\n",
1919428d7b3dSmrg	     __FUNCTION__,
1920428d7b3dSmrg	     get_private(front)->bo->handle, front->name,
1921428d7b3dSmrg	     get_private(back)->bo->handle, back->name,
1922428d7b3dSmrg	     win->drawable.id,
1923428d7b3dSmrg	     get_window_pixmap(win)->drawable.serialNumber,
1924428d7b3dSmrg	     get_window_pixmap(win)->drawable.width,
1925428d7b3dSmrg	     get_window_pixmap(win)->drawable.height));
1926428d7b3dSmrg
1927428d7b3dSmrg	DamageRegionAppend(&win->drawable, &win->clipList);
1928428d7b3dSmrg	sna_shadow_set_crtc(sna, crtc, get_private(back)->bo);
1929428d7b3dSmrg	DamageRegionProcessPending(&win->drawable);
1930428d7b3dSmrg
1931428d7b3dSmrg	assert(dri2_window(win)->front == NULL);
1932428d7b3dSmrg
1933428d7b3dSmrg	tmp = calloc(1, sizeof(*tmp) + sizeof(struct sna_dri2_private));
1934428d7b3dSmrg	if (tmp == NULL) {
1935428d7b3dSmrg		back->attachment = -1;
1936428d7b3dSmrg		if (get_private(back)->proxy == NULL) {
1937428d7b3dSmrg			get_private(back)->pixmap = get_window_pixmap(win);
1938428d7b3dSmrg			get_private(back)->proxy = sna_dri2_reference_buffer(sna_pixmap_get_buffer(get_private(back)->pixmap));
1939428d7b3dSmrg		}
1940428d7b3dSmrg		dri2_window(win)->front = sna_dri2_reference_buffer(back);
1941428d7b3dSmrg		return;
1942428d7b3dSmrg	}
1943428d7b3dSmrg
1944428d7b3dSmrg	*tmp = *back;
1945428d7b3dSmrg	tmp->attachment = DRI2BufferFrontLeft;
1946428d7b3dSmrg	tmp->driverPrivate = tmp + 1;
1947428d7b3dSmrg	get_private(tmp)->refcnt = 1;
1948428d7b3dSmrg	get_private(tmp)->bo = get_private(back)->bo;
1949428d7b3dSmrg	get_private(tmp)->size = get_private(back)->size;
1950428d7b3dSmrg	get_private(tmp)->pixmap = get_window_pixmap(win);
1951428d7b3dSmrg	get_private(tmp)->proxy = sna_dri2_reference_buffer(sna_pixmap_get_buffer(get_private(tmp)->pixmap));
1952428d7b3dSmrg	dri2_window(win)->front = tmp;
1953428d7b3dSmrg
1954428d7b3dSmrg	DBG(("%s: allocating new backbuffer\n", __FUNCTION__));
1955428d7b3dSmrg	back->name = 0;
1956428d7b3dSmrg	bo = kgem_create_2d(&sna->kgem,
1957428d7b3dSmrg			    draw->width, draw->height, draw->bitsPerPixel,
1958428d7b3dSmrg			    get_private(back)->bo->tiling,
1959428d7b3dSmrg			    CREATE_SCANOUT);
1960428d7b3dSmrg	if (bo != NULL) {
1961428d7b3dSmrg		get_private(back)->bo = bo;
1962428d7b3dSmrg		back->pitch = bo->pitch;
1963428d7b3dSmrg		back->name = kgem_bo_flink(&sna->kgem, bo);
1964428d7b3dSmrg	}
1965428d7b3dSmrg	if (back->name == 0) {
1966428d7b3dSmrg		if (bo != NULL)
1967428d7b3dSmrg			kgem_bo_destroy(&sna->kgem, bo);
1968428d7b3dSmrg		get_private(back)->bo = NULL;
1969428d7b3dSmrg		back->attachment = -1;
1970428d7b3dSmrg	}
1971428d7b3dSmrg}
1972428d7b3dSmrg
1973428d7b3dSmrgstatic void frame_swap_complete(struct sna_dri2_event *frame, int type)
1974428d7b3dSmrg{
1975428d7b3dSmrg	const struct ust_msc *swap;
1976428d7b3dSmrg
1977428d7b3dSmrg	if (frame->draw == NULL)
1978428d7b3dSmrg		return;
1979428d7b3dSmrg
1980428d7b3dSmrg	assert(frame->client);
1981428d7b3dSmrg
1982428d7b3dSmrg	swap = sna_crtc_last_swap(frame->crtc);
1983428d7b3dSmrg	DBG(("%s(type=%d): draw=%ld, pipe=%d, frame=%lld [msc=%lld], tv=%d.%06d\n",
1984428d7b3dSmrg	     __FUNCTION__, type, (long)frame->draw, frame->pipe,
1985428d7b3dSmrg	     (long long)swap->msc,
1986428d7b3dSmrg	     (long long)draw_current_msc(frame->draw, frame->crtc, swap->msc),
1987428d7b3dSmrg	     swap->tv_sec, swap->tv_usec));
1988428d7b3dSmrg
1989428d7b3dSmrg	DRI2SwapComplete(frame->client, frame->draw,
1990428d7b3dSmrg			 draw_current_msc(frame->draw, frame->crtc, swap->msc),
1991428d7b3dSmrg			 swap->tv_sec, swap->tv_usec,
1992428d7b3dSmrg			 type, frame->event_complete, frame->event_data);
1993428d7b3dSmrg}
1994428d7b3dSmrg
1995428d7b3dSmrgstatic void fake_swap_complete(struct sna *sna, ClientPtr client,
1996428d7b3dSmrg			       DrawablePtr draw, xf86CrtcPtr crtc,
1997428d7b3dSmrg			       int type, DRI2SwapEventPtr func, void *data)
1998428d7b3dSmrg{
1999428d7b3dSmrg	const struct ust_msc *swap;
2000428d7b3dSmrg
2001428d7b3dSmrg	swap = sna_crtc_last_swap(crtc);
2002428d7b3dSmrg	DBG(("%s(type=%d): draw=%ld, pipe=%d, frame=%lld [msc %lld], tv=%d.%06d\n",
2003428d7b3dSmrg	     __FUNCTION__, type, (long)draw->id, crtc ? sna_crtc_to_pipe(crtc) : -1,
2004428d7b3dSmrg	     (long long)swap->msc,
2005428d7b3dSmrg	     (long long)draw_current_msc(draw, crtc, swap->msc),
2006428d7b3dSmrg	     swap->tv_sec, swap->tv_usec));
2007428d7b3dSmrg
2008428d7b3dSmrg	DRI2SwapComplete(client, draw,
2009428d7b3dSmrg			 draw_current_msc(draw, crtc, swap->msc),
2010428d7b3dSmrg			 swap->tv_sec, swap->tv_usec,
2011428d7b3dSmrg			 type, func, data);
2012428d7b3dSmrg}
2013428d7b3dSmrg
2014428d7b3dSmrgstatic void chain_swap(struct sna_dri2_event *chain)
2015428d7b3dSmrg{
2016428d7b3dSmrg	union drm_wait_vblank vbl;
2017428d7b3dSmrg
2018428d7b3dSmrg	if (chain->draw == NULL) {
2019428d7b3dSmrg		sna_dri2_event_free(chain);
2020428d7b3dSmrg		return;
2021428d7b3dSmrg	}
2022428d7b3dSmrg
2023428d7b3dSmrg	if (chain->queued) /* too early! */
2024428d7b3dSmrg		return;
2025428d7b3dSmrg
2026428d7b3dSmrg	assert(chain == dri2_chain(chain->draw));
2027428d7b3dSmrg	DBG(("%s: chaining draw=%ld, type=%d\n",
2028428d7b3dSmrg	     __FUNCTION__, (long)chain->draw->id, chain->type));
2029428d7b3dSmrg	chain->queued = true;
2030428d7b3dSmrg
2031428d7b3dSmrg	switch (chain->type) {
2032428d7b3dSmrg	case SWAP_THROTTLE:
2033428d7b3dSmrg		DBG(("%s: emitting chained vsync'ed blit\n", __FUNCTION__));
2034428d7b3dSmrg		if (chain->sna->mode.shadow &&
2035428d7b3dSmrg		    !chain->sna->mode.shadow_damage) {
2036428d7b3dSmrg			/* recursed from wait_for_shadow(), simply requeue */
2037428d7b3dSmrg			DBG(("%s -- recursed from wait_for_shadow(), requeuing\n", __FUNCTION__));
2038428d7b3dSmrg			VG_CLEAR(vbl);
2039428d7b3dSmrg			vbl.request.type =
2040428d7b3dSmrg				DRM_VBLANK_RELATIVE |
2041428d7b3dSmrg				DRM_VBLANK_EVENT;
2042428d7b3dSmrg			vbl.request.sequence = 1;
2043428d7b3dSmrg			vbl.request.signal = (uintptr_t)chain;
2044428d7b3dSmrg
2045428d7b3dSmrg			if (!sna_wait_vblank(chain->sna, &vbl, chain->pipe))
2046428d7b3dSmrg				return;
2047428d7b3dSmrg
2048428d7b3dSmrg			DBG(("%s -- requeue failed, errno=%d\n", __FUNCTION__, errno));
2049428d7b3dSmrg		}
2050428d7b3dSmrg
2051428d7b3dSmrg		if (can_xchg(chain->sna, chain->draw, chain->front, chain->back)) {
2052428d7b3dSmrg			sna_dri2_xchg(chain->draw, chain->front, chain->back);
2053428d7b3dSmrg		} else if (can_xchg_crtc(chain->sna, chain->draw, chain->front, chain->back, chain->crtc)) {
2054428d7b3dSmrg			sna_dri2_xchg_crtc(chain->sna, chain->draw, chain->crtc, chain->front, chain->back);
2055428d7b3dSmrg		} else {
2056428d7b3dSmrg			assert(chain->queued);
2057428d7b3dSmrg			chain->bo = __sna_dri2_copy_region(chain->sna, chain->draw, NULL,
2058428d7b3dSmrg							   chain->back, chain->front,
2059428d7b3dSmrg							   true);
2060428d7b3dSmrg		}
2061428d7b3dSmrg	case SWAP:
2062428d7b3dSmrg		break;
2063428d7b3dSmrg	default:
2064428d7b3dSmrg		return;
2065428d7b3dSmrg	}
2066428d7b3dSmrg
2067428d7b3dSmrg	VG_CLEAR(vbl);
2068428d7b3dSmrg	vbl.request.type =
2069428d7b3dSmrg		DRM_VBLANK_RELATIVE |
2070428d7b3dSmrg		DRM_VBLANK_EVENT;
2071428d7b3dSmrg	vbl.request.sequence = 1;
2072428d7b3dSmrg	vbl.request.signal = (uintptr_t)chain;
2073428d7b3dSmrg	if (sna_wait_vblank(chain->sna, &vbl, chain->pipe)) {
2074428d7b3dSmrg		DBG(("%s: vblank wait failed, unblocking client\n", __FUNCTION__));
2075428d7b3dSmrg		frame_swap_complete(chain, DRI2_BLIT_COMPLETE);
2076428d7b3dSmrg		sna_dri2_event_free(chain);
2077428d7b3dSmrg	} else {
2078428d7b3dSmrg		if (chain->type == SWAP_THROTTLE && !swap_limit(chain->draw, 2)) {
2079428d7b3dSmrg			DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__));
2080428d7b3dSmrg			frame_swap_complete(chain, DRI2_BLIT_COMPLETE);
2081428d7b3dSmrg		}
2082428d7b3dSmrg	}
2083428d7b3dSmrg}
2084428d7b3dSmrg
2085428d7b3dSmrgstatic inline bool rq_is_busy(struct kgem *kgem, struct kgem_bo *bo)
2086428d7b3dSmrg{
2087428d7b3dSmrg	if (bo == NULL)
2088428d7b3dSmrg		return false;
2089428d7b3dSmrg
2090428d7b3dSmrg	DBG(("%s: handle=%d, domain: %d exec? %d, rq? %d\n", __FUNCTION__,
2091428d7b3dSmrg	     bo->handle, bo->domain, bo->exec != NULL, bo->rq != NULL));
2092428d7b3dSmrg	assert(bo->refcnt);
2093428d7b3dSmrg
2094428d7b3dSmrg	if (bo->exec)
2095428d7b3dSmrg		return true;
2096428d7b3dSmrg
2097428d7b3dSmrg	if (bo->rq == NULL)
2098428d7b3dSmrg		return false;
2099428d7b3dSmrg
2100428d7b3dSmrg	return __kgem_busy(kgem, bo->handle);
2101428d7b3dSmrg}
2102428d7b3dSmrg
2103428d7b3dSmrgstatic bool sna_dri2_blit_complete(struct sna *sna,
2104428d7b3dSmrg				   struct sna_dri2_event *info)
2105428d7b3dSmrg{
2106428d7b3dSmrg	if (rq_is_busy(&sna->kgem, info->bo)) {
2107428d7b3dSmrg		union drm_wait_vblank vbl;
2108428d7b3dSmrg
2109428d7b3dSmrg		DBG(("%s: vsync'ed blit is still busy, postponing\n",
2110428d7b3dSmrg		     __FUNCTION__));
2111428d7b3dSmrg
2112428d7b3dSmrg		VG_CLEAR(vbl);
2113428d7b3dSmrg		vbl.request.type =
2114428d7b3dSmrg			DRM_VBLANK_RELATIVE |
2115428d7b3dSmrg			DRM_VBLANK_EVENT;
2116428d7b3dSmrg		vbl.request.sequence = 1;
2117428d7b3dSmrg		vbl.request.signal = (uintptr_t)info;
2118428d7b3dSmrg		assert(info->queued);
2119428d7b3dSmrg		if (!sna_wait_vblank(sna, &vbl, info->pipe))
2120428d7b3dSmrg			return false;
2121428d7b3dSmrg	}
2122428d7b3dSmrg
2123428d7b3dSmrg	DBG(("%s: blit finished\n", __FUNCTION__));
2124428d7b3dSmrg	return true;
2125428d7b3dSmrg}
2126428d7b3dSmrg
2127428d7b3dSmrgvoid sna_dri2_vblank_handler(struct drm_event_vblank *event)
2128428d7b3dSmrg{
2129428d7b3dSmrg	struct sna_dri2_event *info = (void *)(uintptr_t)event->user_data;
2130428d7b3dSmrg	struct sna *sna = info->sna;
2131428d7b3dSmrg	DrawablePtr draw;
2132428d7b3dSmrg	union drm_wait_vblank vbl;
2133428d7b3dSmrg	uint64_t msc;
2134428d7b3dSmrg
2135428d7b3dSmrg	DBG(("%s(type=%d, sequence=%d)\n", __FUNCTION__, info->type, event->sequence));
2136428d7b3dSmrg	assert(info->queued);
2137428d7b3dSmrg	msc = sna_crtc_record_event(info->crtc, event);
2138428d7b3dSmrg
2139428d7b3dSmrg	draw = info->draw;
2140428d7b3dSmrg	if (draw == NULL) {
2141428d7b3dSmrg		DBG(("%s -- drawable gone\n", __FUNCTION__));
2142428d7b3dSmrg		goto done;
2143428d7b3dSmrg	}
2144428d7b3dSmrg
2145428d7b3dSmrg	switch (info->type) {
2146428d7b3dSmrg	case FLIP:
2147428d7b3dSmrg		/* If we can still flip... */
2148428d7b3dSmrg		if (can_flip(sna, draw, info->front, info->back, info->crtc) &&
2149428d7b3dSmrg		    sna_dri2_flip(info))
2150428d7b3dSmrg			return;
2151428d7b3dSmrg
2152428d7b3dSmrg		/* else fall through to blit */
2153428d7b3dSmrg	case SWAP:
2154428d7b3dSmrg		assert(info->queued);
2155428d7b3dSmrg		if (sna->mode.shadow && !sna->mode.shadow_damage) {
2156428d7b3dSmrg			/* recursed from wait_for_shadow(), simply requeue */
2157428d7b3dSmrg			DBG(("%s -- recursed from wait_for_shadow(), requeuing\n", __FUNCTION__));
2158428d7b3dSmrg
2159428d7b3dSmrg		} else if (can_xchg(info->sna, draw, info->front, info->back)) {
2160428d7b3dSmrg			sna_dri2_xchg(draw, info->front, info->back);
2161428d7b3dSmrg			info->type = SWAP_WAIT;
2162428d7b3dSmrg		} else if (can_xchg_crtc(sna, draw, info->front, info->back, info->crtc)) {
2163428d7b3dSmrg			sna_dri2_xchg_crtc(sna, draw, info->crtc, info->front, info->back);
2164428d7b3dSmrg			info->type = SWAP_WAIT;
2165428d7b3dSmrg		}  else {
2166428d7b3dSmrg			assert(info->queued);
2167428d7b3dSmrg			info->bo = __sna_dri2_copy_region(sna, draw, NULL,
2168428d7b3dSmrg							  info->back, info->front, true);
2169428d7b3dSmrg			info->type = SWAP_WAIT;
2170428d7b3dSmrg		}
2171428d7b3dSmrg
2172428d7b3dSmrg		VG_CLEAR(vbl);
2173428d7b3dSmrg		vbl.request.type =
2174428d7b3dSmrg			DRM_VBLANK_RELATIVE |
2175428d7b3dSmrg			DRM_VBLANK_EVENT;
2176428d7b3dSmrg		vbl.request.sequence = 1;
2177428d7b3dSmrg		vbl.request.signal = (uintptr_t)info;
2178428d7b3dSmrg
2179428d7b3dSmrg		assert(info->queued);
2180428d7b3dSmrg		if (!sna_wait_vblank(sna, &vbl, info->pipe))
2181428d7b3dSmrg			return;
2182428d7b3dSmrg
2183428d7b3dSmrg		DBG(("%s -- requeue failed, errno=%d\n", __FUNCTION__, errno));
2184428d7b3dSmrg		/* fall through to SwapComplete */
2185428d7b3dSmrg	case SWAP_WAIT:
2186428d7b3dSmrg		if (!sna_dri2_blit_complete(sna, info))
2187428d7b3dSmrg			return;
2188428d7b3dSmrg
2189428d7b3dSmrg		DBG(("%s: swap complete, unblocking client (frame=%d, tv=%d.%06d)\n", __FUNCTION__,
2190428d7b3dSmrg		     event->sequence, event->tv_sec, event->tv_usec));
2191428d7b3dSmrg		frame_swap_complete(info, DRI2_BLIT_COMPLETE);
2192428d7b3dSmrg		break;
2193428d7b3dSmrg
2194428d7b3dSmrg	case SWAP_THROTTLE:
2195428d7b3dSmrg		DBG(("%s: %d complete, frame=%d tv=%d.%06d\n",
2196428d7b3dSmrg		     __FUNCTION__, info->type,
2197428d7b3dSmrg		     event->sequence, event->tv_sec, event->tv_usec));
2198428d7b3dSmrg
2199428d7b3dSmrg		if (xorg_can_triple_buffer()) {
2200428d7b3dSmrg			if (!sna_dri2_blit_complete(sna, info))
2201428d7b3dSmrg				return;
2202428d7b3dSmrg
2203428d7b3dSmrg			DBG(("%s: triple buffer swap complete, unblocking client (frame=%d, tv=%d.%06d)\n", __FUNCTION__,
2204428d7b3dSmrg			     event->sequence, event->tv_sec, event->tv_usec));
2205428d7b3dSmrg			frame_swap_complete(info, DRI2_BLIT_COMPLETE);
2206428d7b3dSmrg		}
2207428d7b3dSmrg		break;
2208428d7b3dSmrg
2209428d7b3dSmrg	case WAITMSC:
2210428d7b3dSmrg		assert(info->client);
2211428d7b3dSmrg		DRI2WaitMSCComplete(info->client, draw, msc,
2212428d7b3dSmrg				    event->tv_sec, event->tv_usec);
2213428d7b3dSmrg		break;
2214428d7b3dSmrg	default:
2215428d7b3dSmrg		xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING,
2216428d7b3dSmrg			   "%s: unknown vblank event received\n", __func__);
2217428d7b3dSmrg		/* Unknown type */
2218428d7b3dSmrg		break;
2219428d7b3dSmrg	}
2220428d7b3dSmrg
2221428d7b3dSmrg	if (info->chain) {
2222428d7b3dSmrg		assert(info->chain != info);
2223428d7b3dSmrg		assert(info->draw == draw);
2224428d7b3dSmrg		sna_dri2_remove_event((WindowPtr)draw, info);
2225428d7b3dSmrg		chain_swap(info->chain);
2226428d7b3dSmrg		info->draw = NULL;
2227428d7b3dSmrg	}
2228428d7b3dSmrg
2229428d7b3dSmrgdone:
2230428d7b3dSmrg	sna_dri2_event_free(info);
2231428d7b3dSmrg	DBG(("%s complete\n", __FUNCTION__));
2232428d7b3dSmrg}
2233428d7b3dSmrg
2234428d7b3dSmrgstatic bool
2235428d7b3dSmrgsna_dri2_immediate_blit(struct sna *sna,
2236428d7b3dSmrg			struct sna_dri2_event *info,
2237428d7b3dSmrg			bool sync, bool event)
2238428d7b3dSmrg{
2239428d7b3dSmrg	DrawablePtr draw = info->draw;
2240428d7b3dSmrg	bool ret = false;
2241428d7b3dSmrg
2242428d7b3dSmrg	if (sna->flags & SNA_NO_WAIT)
2243428d7b3dSmrg		sync = false;
2244428d7b3dSmrg
2245428d7b3dSmrg	DBG(("%s: emitting immediate blit, throttling client, synced? %d, chained? %d, send-event? %d\n",
2246428d7b3dSmrg	     __FUNCTION__, sync, dri2_chain(draw) != info,
2247428d7b3dSmrg	     event));
2248428d7b3dSmrg
2249428d7b3dSmrg	info->type = SWAP_THROTTLE;
2250428d7b3dSmrg	if (!sync || dri2_chain(draw) == info) {
2251428d7b3dSmrg		DBG(("%s: no pending blit, starting chain\n",
2252428d7b3dSmrg		     __FUNCTION__));
2253428d7b3dSmrg
2254428d7b3dSmrg		info->queued = true;
2255428d7b3dSmrg		info->bo = __sna_dri2_copy_region(sna, draw, NULL,
2256428d7b3dSmrg						  info->back,
2257428d7b3dSmrg						  info->front,
2258428d7b3dSmrg						  sync);
2259428d7b3dSmrg		if (event) {
2260428d7b3dSmrg			if (sync) {
2261428d7b3dSmrg				union drm_wait_vblank vbl;
2262428d7b3dSmrg
2263428d7b3dSmrg				VG_CLEAR(vbl);
2264428d7b3dSmrg				vbl.request.type =
2265428d7b3dSmrg					DRM_VBLANK_RELATIVE |
2266428d7b3dSmrg					DRM_VBLANK_EVENT;
2267428d7b3dSmrg				vbl.request.sequence = 1;
2268428d7b3dSmrg				vbl.request.signal = (uintptr_t)info;
2269428d7b3dSmrg				ret = !sna_wait_vblank(sna, &vbl, info->pipe);
2270428d7b3dSmrg				if (ret)
2271428d7b3dSmrg					event = !swap_limit(draw, 2);
2272428d7b3dSmrg			}
2273428d7b3dSmrg			if (event) {
2274428d7b3dSmrg				DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__));
2275428d7b3dSmrg				frame_swap_complete(info, DRI2_BLIT_COMPLETE);
2276428d7b3dSmrg			}
2277428d7b3dSmrg		}
2278428d7b3dSmrg	} else {
2279428d7b3dSmrg		DBG(("%s: pending blit, chained\n", __FUNCTION__));
2280428d7b3dSmrg		ret = true;
2281428d7b3dSmrg	}
2282428d7b3dSmrg
2283428d7b3dSmrg	DBG(("%s: continue? %d\n", __FUNCTION__, ret));
2284428d7b3dSmrg	return ret;
2285428d7b3dSmrg}
2286428d7b3dSmrg
2287428d7b3dSmrgstatic bool
2288428d7b3dSmrgsna_dri2_flip_continue(struct sna_dri2_event *info)
2289428d7b3dSmrg{
2290428d7b3dSmrg	DBG(("%s(mode=%d)\n", __FUNCTION__, info->mode));
2291428d7b3dSmrg
2292428d7b3dSmrg	if (info->mode > 0){
2293428d7b3dSmrg		struct kgem_bo *bo = get_private(info->front)->bo;
2294428d7b3dSmrg
2295428d7b3dSmrg		info->type = info->mode;
2296428d7b3dSmrg
2297428d7b3dSmrg		if (bo != sna_pixmap(info->sna->front)->gpu_bo)
2298428d7b3dSmrg			return false;
2299428d7b3dSmrg
2300428d7b3dSmrg		if (!sna_page_flip(info->sna, bo, sna_dri2_flip_handler, info))
2301428d7b3dSmrg			return false;
2302428d7b3dSmrg
2303428d7b3dSmrg		assert(info->sna->dri2.flip_pending == NULL ||
2304428d7b3dSmrg		       info->sna->dri2.flip_pending == info);
2305428d7b3dSmrg		info->sna->dri2.flip_pending = info;
2306428d7b3dSmrg		assert(info->queued);
2307428d7b3dSmrg	} else {
2308428d7b3dSmrg		info->type = -info->mode;
2309428d7b3dSmrg
2310428d7b3dSmrg		if (!info->draw)
2311428d7b3dSmrg			return false;
2312428d7b3dSmrg
2313428d7b3dSmrg		if (!can_flip(info->sna, info->draw, info->front, info->back, info->crtc))
2314428d7b3dSmrg			return false;
2315428d7b3dSmrg
2316428d7b3dSmrg		assert(sna_pixmap_get_buffer(get_drawable_pixmap(info->draw)) == info->front);
2317428d7b3dSmrg		if (!sna_dri2_flip(info))
2318428d7b3dSmrg			return false;
2319428d7b3dSmrg
2320428d7b3dSmrg		if (!xorg_can_triple_buffer()) {
2321428d7b3dSmrg			sna_dri2_get_back(info->sna, info->draw, info->back, info);
2322428d7b3dSmrg			DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__));
2323428d7b3dSmrg			frame_swap_complete(info, DRI2_FLIP_COMPLETE);
2324428d7b3dSmrg		}
2325428d7b3dSmrg	}
2326428d7b3dSmrg
2327428d7b3dSmrg	info->mode = 0;
2328428d7b3dSmrg	return true;
2329428d7b3dSmrg}
2330428d7b3dSmrg
2331428d7b3dSmrgstatic void chain_flip(struct sna *sna)
2332428d7b3dSmrg{
2333428d7b3dSmrg	struct sna_dri2_event *chain = sna->dri2.flip_pending;
2334428d7b3dSmrg
2335428d7b3dSmrg	assert(chain->type == FLIP);
2336428d7b3dSmrg	DBG(("%s: chaining type=%d, cancelled?=%d\n",
2337428d7b3dSmrg	     __FUNCTION__, chain->type, chain->draw == NULL));
2338428d7b3dSmrg
2339428d7b3dSmrg	sna->dri2.flip_pending = NULL;
2340428d7b3dSmrg	if (chain->draw == NULL) {
2341428d7b3dSmrg		sna_dri2_event_free(chain);
2342428d7b3dSmrg		return;
2343428d7b3dSmrg	}
2344428d7b3dSmrg
2345428d7b3dSmrg	assert(chain == dri2_chain(chain->draw));
2346428d7b3dSmrg	assert(!chain->queued);
2347428d7b3dSmrg	chain->queued = true;
2348428d7b3dSmrg
2349428d7b3dSmrg	if (can_flip(sna, chain->draw, chain->front, chain->back, chain->crtc) &&
2350428d7b3dSmrg	    sna_dri2_flip(chain)) {
2351428d7b3dSmrg		DBG(("%s: performing chained flip\n", __FUNCTION__));
2352428d7b3dSmrg	} else {
2353428d7b3dSmrg		DBG(("%s: emitting chained vsync'ed blit\n", __FUNCTION__));
2354428d7b3dSmrg		chain->bo = __sna_dri2_copy_region(sna, chain->draw, NULL,
2355428d7b3dSmrg						  chain->back, chain->front,
2356428d7b3dSmrg						  true);
2357428d7b3dSmrg
2358428d7b3dSmrg		if (xorg_can_triple_buffer()) {
2359428d7b3dSmrg			union drm_wait_vblank vbl;
2360428d7b3dSmrg
2361428d7b3dSmrg			VG_CLEAR(vbl);
2362428d7b3dSmrg
2363428d7b3dSmrg			chain->type = SWAP_WAIT;
2364428d7b3dSmrg			vbl.request.type =
2365428d7b3dSmrg				DRM_VBLANK_RELATIVE |
2366428d7b3dSmrg				DRM_VBLANK_EVENT;
2367428d7b3dSmrg			vbl.request.sequence = 1;
2368428d7b3dSmrg			vbl.request.signal = (uintptr_t)chain;
2369428d7b3dSmrg
2370428d7b3dSmrg			assert(chain->queued);
2371428d7b3dSmrg			if (!sna_wait_vblank(sna, &vbl, chain->pipe))
2372428d7b3dSmrg				return;
2373428d7b3dSmrg		}
2374428d7b3dSmrg
2375428d7b3dSmrg		DBG(("%s: fake triple buffering (or vblank wait failed), unblocking client\n", __FUNCTION__));
2376428d7b3dSmrg		frame_swap_complete(chain, DRI2_BLIT_COMPLETE);
2377428d7b3dSmrg		sna_dri2_event_free(chain);
2378428d7b3dSmrg	}
2379428d7b3dSmrg}
2380428d7b3dSmrg
2381428d7b3dSmrgstatic void sna_dri2_flip_event(struct sna_dri2_event *flip)
2382428d7b3dSmrg{
2383428d7b3dSmrg	struct sna *sna = flip->sna;
2384428d7b3dSmrg
2385428d7b3dSmrg	DBG(("%s(pipe=%d, event=%d)\n", __FUNCTION__, flip->pipe, flip->type));
2386428d7b3dSmrg	assert(flip->queued);
2387428d7b3dSmrg
2388428d7b3dSmrg	if (sna->dri2.flip_pending == flip)
2389428d7b3dSmrg		sna->dri2.flip_pending = NULL;
2390428d7b3dSmrg
2391428d7b3dSmrg	/* We assume our flips arrive in order, so we don't check the frame */
2392428d7b3dSmrg	switch (flip->type) {
2393428d7b3dSmrg	case FLIP:
2394428d7b3dSmrg		DBG(("%s: swap complete, unblocking client\n", __FUNCTION__));
2395428d7b3dSmrg		frame_swap_complete(flip, DRI2_FLIP_COMPLETE);
2396428d7b3dSmrg		sna_dri2_event_free(flip);
2397428d7b3dSmrg
2398428d7b3dSmrg		if (sna->dri2.flip_pending)
2399428d7b3dSmrg			chain_flip(sna);
2400428d7b3dSmrg		break;
2401428d7b3dSmrg
2402428d7b3dSmrg	case FLIP_THROTTLE:
2403428d7b3dSmrg		DBG(("%s: triple buffer swap complete, unblocking client\n", __FUNCTION__));
2404428d7b3dSmrg		frame_swap_complete(flip, DRI2_FLIP_COMPLETE);
2405428d7b3dSmrg	case FLIP_COMPLETE:
2406428d7b3dSmrg		if (sna->dri2.flip_pending) {
2407428d7b3dSmrg			sna_dri2_event_free(flip);
2408428d7b3dSmrg			chain_flip(sna);
2409428d7b3dSmrg		} else if (!flip->mode) {
2410428d7b3dSmrg			DBG(("%s: flip chain complete\n", __FUNCTION__));
2411428d7b3dSmrg
2412428d7b3dSmrg			if (flip->chain) {
2413428d7b3dSmrg				sna_dri2_remove_event((WindowPtr)flip->draw,
2414428d7b3dSmrg						      flip);
2415428d7b3dSmrg				chain_swap(flip->chain);
2416428d7b3dSmrg				flip->draw = NULL;
2417428d7b3dSmrg			}
2418428d7b3dSmrg
2419428d7b3dSmrg			sna_dri2_event_free(flip);
2420428d7b3dSmrg		} else if (!sna_dri2_flip_continue(flip)) {
2421428d7b3dSmrg			DBG(("%s: no longer able to flip\n", __FUNCTION__));
2422428d7b3dSmrg			if (flip->draw == NULL || !sna_dri2_immediate_blit(sna, flip, false, flip->mode < 0))
2423428d7b3dSmrg				sna_dri2_event_free(flip);
2424428d7b3dSmrg		}
2425428d7b3dSmrg		break;
2426428d7b3dSmrg
2427428d7b3dSmrg	default: /* Unknown type */
2428428d7b3dSmrg		xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING,
2429428d7b3dSmrg			   "%s: unknown vblank event received\n", __func__);
2430428d7b3dSmrg		sna_dri2_event_free(flip);
2431428d7b3dSmrg		if (sna->dri2.flip_pending)
2432428d7b3dSmrg			chain_flip(sna);
2433428d7b3dSmrg		break;
2434428d7b3dSmrg	}
2435428d7b3dSmrg}
2436428d7b3dSmrg
2437428d7b3dSmrgstatic uint64_t
2438428d7b3dSmrgget_current_msc(struct sna *sna, DrawablePtr draw, xf86CrtcPtr crtc)
2439428d7b3dSmrg{
2440428d7b3dSmrg	union drm_wait_vblank vbl;
2441428d7b3dSmrg	uint64_t ret = -1;
2442428d7b3dSmrg
2443428d7b3dSmrg	VG_CLEAR(vbl);
2444428d7b3dSmrg	vbl.request.type = _DRM_VBLANK_RELATIVE;
2445428d7b3dSmrg	vbl.request.sequence = 0;
2446428d7b3dSmrg	if (sna_wait_vblank(sna, &vbl, sna_crtc_to_pipe(crtc)) == 0)
2447428d7b3dSmrg		ret = sna_crtc_record_vblank(crtc, &vbl);
2448428d7b3dSmrg
2449428d7b3dSmrg	return draw_current_msc(draw, crtc, ret);
2450428d7b3dSmrg}
2451428d7b3dSmrg
2452428d7b3dSmrg#if defined(CHECK_FOR_COMPOSITOR)
2453428d7b3dSmrgstatic Bool find(pointer value, XID id, pointer cdata)
2454428d7b3dSmrg{
2455428d7b3dSmrg	return TRUE;
2456428d7b3dSmrg}
2457428d7b3dSmrg#endif
2458428d7b3dSmrg
2459428d7b3dSmrgstatic int use_triple_buffer(struct sna *sna, ClientPtr client, bool async)
2460428d7b3dSmrg{
2461428d7b3dSmrg	if ((sna->flags & SNA_TRIPLE_BUFFER) == 0) {
2462428d7b3dSmrg		DBG(("%s: triple buffer disabled, using FLIP\n", __FUNCTION__));
2463428d7b3dSmrg		return FLIP;
2464428d7b3dSmrg	}
2465428d7b3dSmrg
2466428d7b3dSmrg	if (async) {
2467428d7b3dSmrg		DBG(("%s: running async, using %s\n", __FUNCTION__,
2468428d7b3dSmrg		     sna->flags & SNA_HAS_ASYNC_FLIP ? "FLIP_ASYNC" : "FLIP_COMPLETE"));
2469428d7b3dSmrg		return sna->flags & SNA_HAS_ASYNC_FLIP ? FLIP_ASYNC : FLIP_COMPLETE;
2470428d7b3dSmrg	}
2471428d7b3dSmrg
2472428d7b3dSmrg	if (xorg_can_triple_buffer()) {
2473428d7b3dSmrg		DBG(("%s: triple buffer enabled, using FLIP_THROTTLE\n", __FUNCTION__));
2474428d7b3dSmrg		return FLIP_THROTTLE;
2475428d7b3dSmrg	}
2476428d7b3dSmrg
2477428d7b3dSmrg#if defined(CHECK_FOR_COMPOSITOR)
2478428d7b3dSmrg	/* Hack: Disable triple buffering for compositors */
2479428d7b3dSmrg	{
2480428d7b3dSmrg		struct sna_client *priv = sna_client(client);
2481428d7b3dSmrg		if (priv->is_compositor == 0)
2482428d7b3dSmrg			priv->is_compositor =
2483428d7b3dSmrg				LookupClientResourceComplex(client,
2484428d7b3dSmrg							    CompositeClientWindowType+1,
2485428d7b3dSmrg							    find, NULL) ? FLIP : FLIP_COMPLETE;
2486428d7b3dSmrg
2487428d7b3dSmrg		DBG(("%s: fake triple buffer enabled?=%d using %s\n", __FUNCTION__,
2488428d7b3dSmrg		     priv->is_compositor != FLIP, priv->is_compositor == FLIP ? "FLIP" : "FLIP_COMPLETE"));
2489428d7b3dSmrg		return priv->is_compositor;
2490428d7b3dSmrg	}
2491428d7b3dSmrg#else
2492428d7b3dSmrg	DBG(("%s: fake triple buffer enabled, using FLIP_COMPLETE\n", __FUNCTION__));
2493428d7b3dSmrg	return FLIP_COMPLETE;
2494428d7b3dSmrg#endif
2495428d7b3dSmrg}
2496428d7b3dSmrg
2497428d7b3dSmrgstatic bool immediate_swap(struct sna *sna,
2498428d7b3dSmrg			   uint64_t target_msc,
2499428d7b3dSmrg			   uint64_t divisor,
2500428d7b3dSmrg			   DrawablePtr draw,
2501428d7b3dSmrg			   xf86CrtcPtr crtc,
2502428d7b3dSmrg			   uint64_t *current_msc)
2503428d7b3dSmrg{
2504428d7b3dSmrg	if (divisor == 0) {
2505428d7b3dSmrg		*current_msc = -1;
2506428d7b3dSmrg
2507428d7b3dSmrg		if (sna->flags & SNA_NO_WAIT) {
2508428d7b3dSmrg			DBG(("%s: yes, waits are disabled\n", __FUNCTION__));
2509428d7b3dSmrg			return true;
2510428d7b3dSmrg		}
2511428d7b3dSmrg
2512428d7b3dSmrg		if (target_msc)
2513428d7b3dSmrg			*current_msc = get_current_msc(sna, draw, crtc);
2514428d7b3dSmrg
2515428d7b3dSmrg		DBG(("%s: current_msc=%ld, target_msc=%ld -- %s\n",
2516428d7b3dSmrg		     __FUNCTION__, (long)*current_msc, (long)target_msc,
2517428d7b3dSmrg		     (*current_msc >= target_msc - 1) ? "yes" : "no"));
2518428d7b3dSmrg		return *current_msc >= target_msc - 1;
2519428d7b3dSmrg	}
2520428d7b3dSmrg
2521428d7b3dSmrg	DBG(("%s: explicit waits requests, divisor=%ld\n",
2522428d7b3dSmrg	     __FUNCTION__, (long)divisor));
2523428d7b3dSmrg	*current_msc = get_current_msc(sna, draw, crtc);
2524428d7b3dSmrg	return false;
2525428d7b3dSmrg}
2526428d7b3dSmrg
2527428d7b3dSmrgstatic bool
2528428d7b3dSmrgsna_dri2_schedule_flip(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc,
2529428d7b3dSmrg		       DRI2BufferPtr front, DRI2BufferPtr back,
2530428d7b3dSmrg		       CARD64 *target_msc, CARD64 divisor, CARD64 remainder,
2531428d7b3dSmrg		       DRI2SwapEventPtr func, void *data)
2532428d7b3dSmrg{
2533428d7b3dSmrg	struct sna *sna = to_sna_from_drawable(draw);
2534428d7b3dSmrg	struct sna_dri2_event *info;
2535428d7b3dSmrg	uint64_t current_msc;
2536428d7b3dSmrg
2537428d7b3dSmrg	if (immediate_swap(sna, *target_msc, divisor, draw, crtc, &current_msc)) {
2538428d7b3dSmrg		int type;
2539428d7b3dSmrg
2540428d7b3dSmrg		info = sna->dri2.flip_pending;
2541428d7b3dSmrg		DBG(("%s: performing immediate swap on pipe %d, pending? %d, mode: %d, continuation? %d\n",
2542428d7b3dSmrg		     __FUNCTION__, sna_crtc_to_pipe(crtc),
2543428d7b3dSmrg		     info != NULL, info ? info->mode : 0,
2544428d7b3dSmrg		     info && info->draw == draw));
2545428d7b3dSmrg
2546428d7b3dSmrg		if (info && info->draw == draw) {
2547428d7b3dSmrg			assert(info->type != FLIP);
2548428d7b3dSmrg			assert(info->front == front);
2549428d7b3dSmrg			if (info->back != back) {
2550428d7b3dSmrg				_sna_dri2_destroy_buffer(sna, info->back);
2551428d7b3dSmrg				info->back = sna_dri2_reference_buffer(back);
2552428d7b3dSmrg			}
2553428d7b3dSmrg			if (info->mode || current_msc >= *target_msc) {
2554428d7b3dSmrg				DBG(("%s: executing xchg of pending flip\n",
2555428d7b3dSmrg				     __FUNCTION__));
2556428d7b3dSmrg				sna_dri2_xchg(draw, front, back);
2557428d7b3dSmrg				info->mode = type = FLIP_COMPLETE;
2558428d7b3dSmrg				goto new_back;
2559428d7b3dSmrg			} else {
2560428d7b3dSmrg				DBG(("%s: chaining flip\n", __FUNCTION__));
2561428d7b3dSmrg				type = FLIP_THROTTLE;
2562428d7b3dSmrg				if (xorg_can_triple_buffer())
2563428d7b3dSmrg					info->mode = -type;
2564428d7b3dSmrg				else
2565428d7b3dSmrg					info->mode = -FLIP_COMPLETE;
2566428d7b3dSmrg				goto out;
2567428d7b3dSmrg			}
2568428d7b3dSmrg		}
2569428d7b3dSmrg
2570428d7b3dSmrg		info = sna_dri2_add_event(sna, draw, client);
2571428d7b3dSmrg		if (info == NULL)
2572428d7b3dSmrg			return false;
2573428d7b3dSmrg
2574428d7b3dSmrg		assert(info->crtc == crtc);
2575428d7b3dSmrg		info->event_complete = func;
2576428d7b3dSmrg		info->event_data = data;
2577428d7b3dSmrg
2578428d7b3dSmrg		info->front = sna_dri2_reference_buffer(front);
2579428d7b3dSmrg		info->back = sna_dri2_reference_buffer(back);
2580428d7b3dSmrg
2581428d7b3dSmrg		if (sna->dri2.flip_pending) {
2582428d7b3dSmrg			/* We need to first wait (one vblank) for the
2583428d7b3dSmrg			 * async flips to complete before this client
2584428d7b3dSmrg			 * can take over.
2585428d7b3dSmrg			 */
2586428d7b3dSmrg			DBG(("%s: queueing flip after pending completion\n",
2587428d7b3dSmrg			     __FUNCTION__));
2588428d7b3dSmrg			info->type = type = FLIP;
2589428d7b3dSmrg			sna->dri2.flip_pending = info;
2590428d7b3dSmrg			assert(info->queued);
2591428d7b3dSmrg			current_msc++;
2592428d7b3dSmrg		} else {
2593428d7b3dSmrg			info->type = type = use_triple_buffer(sna, client, *target_msc == 0);
2594428d7b3dSmrg			if (!sna_dri2_flip(info)) {
2595428d7b3dSmrg				DBG(("%s: flip failed, falling back\n", __FUNCTION__));
2596428d7b3dSmrg				sna_dri2_event_free(info);
2597428d7b3dSmrg				return false;
2598428d7b3dSmrg			}
2599428d7b3dSmrg		}
2600428d7b3dSmrg
2601428d7b3dSmrg		swap_limit(draw, 1 + (type == FLIP_THROTTLE));
2602428d7b3dSmrg		if (type >= FLIP_COMPLETE) {
2603428d7b3dSmrgnew_back:
2604428d7b3dSmrg			if (!xorg_can_triple_buffer())
2605428d7b3dSmrg				sna_dri2_get_back(sna, draw, back, info);
2606428d7b3dSmrg			DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__));
2607428d7b3dSmrg			frame_swap_complete(info, DRI2_EXCHANGE_COMPLETE);
2608428d7b3dSmrg			if (info->type == FLIP_ASYNC)
2609428d7b3dSmrg				sna_dri2_event_free(info);
2610428d7b3dSmrg		}
2611428d7b3dSmrgout:
2612428d7b3dSmrg		DBG(("%s: target_msc=%llu\n", __FUNCTION__, current_msc + 1));
2613428d7b3dSmrg		*target_msc = current_msc + 1;
2614428d7b3dSmrg		return true;
2615428d7b3dSmrg	}
2616428d7b3dSmrg
2617428d7b3dSmrg	info = sna_dri2_add_event(sna, draw, client);
2618428d7b3dSmrg	if (info == NULL)
2619428d7b3dSmrg		return false;
2620428d7b3dSmrg
2621428d7b3dSmrg	assert(info->crtc == crtc);
2622428d7b3dSmrg	info->event_complete = func;
2623428d7b3dSmrg	info->event_data = data;
2624428d7b3dSmrg	info->type = FLIP;
2625428d7b3dSmrg
2626428d7b3dSmrg	info->front = sna_dri2_reference_buffer(front);
2627428d7b3dSmrg	info->back = sna_dri2_reference_buffer(back);
2628428d7b3dSmrg
2629428d7b3dSmrg	/*
2630428d7b3dSmrg	 * If divisor is zero, or current_msc is smaller than target_msc
2631428d7b3dSmrg	 * we just need to make sure target_msc passes before initiating
2632428d7b3dSmrg	 * the swap.
2633428d7b3dSmrg	 */
2634428d7b3dSmrg	if (divisor && current_msc >= *target_msc) {
2635428d7b3dSmrg		DBG(("%s: missed target, queueing event for next: current=%lld, target=%lld, divisor=%lld, remainder=%lld\n",
2636428d7b3dSmrg		     __FUNCTION__,
2637428d7b3dSmrg		     (long long)current_msc,
2638428d7b3dSmrg		     (long long)*target_msc,
2639428d7b3dSmrg		     (long long)divisor,
2640428d7b3dSmrg		     (long long)remainder));
2641428d7b3dSmrg
2642428d7b3dSmrg		*target_msc = current_msc + remainder - current_msc % divisor;
2643428d7b3dSmrg		if (*target_msc <= current_msc)
2644428d7b3dSmrg			*target_msc += divisor;
2645428d7b3dSmrg	}
2646428d7b3dSmrg
2647428d7b3dSmrg	if (*target_msc <= current_msc + 1) {
2648428d7b3dSmrg		if (!sna_dri2_flip(info)) {
2649428d7b3dSmrg			sna_dri2_event_free(info);
2650428d7b3dSmrg			return false;
2651428d7b3dSmrg		}
2652428d7b3dSmrg		*target_msc = current_msc + 1;
2653428d7b3dSmrg	} else {
2654428d7b3dSmrg		union drm_wait_vblank vbl;
2655428d7b3dSmrg
2656428d7b3dSmrg		VG_CLEAR(vbl);
2657428d7b3dSmrg
2658428d7b3dSmrg		vbl.request.type =
2659428d7b3dSmrg			DRM_VBLANK_ABSOLUTE |
2660428d7b3dSmrg			DRM_VBLANK_EVENT;
2661428d7b3dSmrg
2662428d7b3dSmrg		/* Account for 1 frame extra pageflip delay */
2663428d7b3dSmrg		vbl.reply.sequence = draw_target_seq(draw, *target_msc - 1);
2664428d7b3dSmrg		vbl.request.signal = (uintptr_t)info;
2665428d7b3dSmrg
2666428d7b3dSmrg		info->queued = true;
2667428d7b3dSmrg		if (sna_wait_vblank(sna, &vbl, info->pipe)) {
2668428d7b3dSmrg			sna_dri2_event_free(info);
2669428d7b3dSmrg			return false;
2670428d7b3dSmrg		}
2671428d7b3dSmrg	}
2672428d7b3dSmrg
2673428d7b3dSmrg	DBG(("%s: reported target_msc=%llu\n", __FUNCTION__, *target_msc));
2674428d7b3dSmrg	swap_limit(draw, 1);
2675428d7b3dSmrg	return true;
2676428d7b3dSmrg}
2677428d7b3dSmrg
2678428d7b3dSmrgstatic bool
2679428d7b3dSmrgsna_dri2_schedule_xchg(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc,
2680428d7b3dSmrg		       DRI2BufferPtr front, DRI2BufferPtr back,
2681428d7b3dSmrg		       CARD64 *target_msc, CARD64 divisor, CARD64 remainder,
2682428d7b3dSmrg		       DRI2SwapEventPtr func, void *data)
2683428d7b3dSmrg{
2684428d7b3dSmrg	struct sna *sna = to_sna_from_drawable(draw);
2685428d7b3dSmrg	uint64_t current_msc;
2686428d7b3dSmrg	bool sync, event;
2687428d7b3dSmrg
2688428d7b3dSmrg	if (!immediate_swap(sna, *target_msc, divisor, draw, crtc, &current_msc))
2689428d7b3dSmrg		return false;
2690428d7b3dSmrg
2691428d7b3dSmrg	sync = current_msc < *target_msc;
2692428d7b3dSmrg	event = dri2_chain(draw) == NULL;
2693428d7b3dSmrg	if (!sync || event) {
2694428d7b3dSmrg		DBG(("%s: performing immediate xchg on pipe %d\n",
2695428d7b3dSmrg		     __FUNCTION__, sna_crtc_to_pipe(crtc)));
2696428d7b3dSmrg		sna_dri2_xchg(draw, front, back);
2697428d7b3dSmrg	}
2698428d7b3dSmrg	if (sync) {
2699428d7b3dSmrg		struct sna_dri2_event *info;
2700428d7b3dSmrg
2701428d7b3dSmrg		info = sna_dri2_add_event(sna, draw, client);
2702428d7b3dSmrg		if (!info)
2703428d7b3dSmrg			goto complete;
2704428d7b3dSmrg
2705428d7b3dSmrg		info->event_complete = func;
2706428d7b3dSmrg		info->event_data = data;
2707428d7b3dSmrg
2708428d7b3dSmrg		info->front = sna_dri2_reference_buffer(front);
2709428d7b3dSmrg		info->back = sna_dri2_reference_buffer(back);
2710428d7b3dSmrg		info->type = SWAP_THROTTLE;
2711428d7b3dSmrg
2712428d7b3dSmrg		if (event) {
2713428d7b3dSmrg			union drm_wait_vblank vbl;
2714428d7b3dSmrg
2715428d7b3dSmrg			VG_CLEAR(vbl);
2716428d7b3dSmrg			vbl.request.type =
2717428d7b3dSmrg				DRM_VBLANK_RELATIVE |
2718428d7b3dSmrg				DRM_VBLANK_EVENT;
2719428d7b3dSmrg			vbl.request.sequence = 1;
2720428d7b3dSmrg			vbl.request.signal = (uintptr_t)info;
2721428d7b3dSmrg
2722428d7b3dSmrg			info->queued = true;
2723428d7b3dSmrg			if (sna_wait_vblank(sna, &vbl, info->pipe)) {
2724428d7b3dSmrg				sna_dri2_event_free(info);
2725428d7b3dSmrg				goto complete;
2726428d7b3dSmrg			}
2727428d7b3dSmrg
2728428d7b3dSmrg			swap_limit(draw, 2);
2729428d7b3dSmrg		}
2730428d7b3dSmrg	} else {
2731428d7b3dSmrgcomplete:
2732428d7b3dSmrg		fake_swap_complete(sna, client, draw, crtc, DRI2_EXCHANGE_COMPLETE, func, data);
2733428d7b3dSmrg	}
2734428d7b3dSmrg
2735428d7b3dSmrg	*target_msc = current_msc + 1;
2736428d7b3dSmrg	return true;
2737428d7b3dSmrg}
2738428d7b3dSmrg
2739428d7b3dSmrgstatic bool
2740428d7b3dSmrgsna_dri2_schedule_xchg_crtc(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc,
2741428d7b3dSmrg			    DRI2BufferPtr front, DRI2BufferPtr back,
2742428d7b3dSmrg			    CARD64 *target_msc, CARD64 divisor, CARD64 remainder,
2743428d7b3dSmrg			    DRI2SwapEventPtr func, void *data)
2744428d7b3dSmrg{
2745428d7b3dSmrg	struct sna *sna = to_sna_from_drawable(draw);
2746428d7b3dSmrg	uint64_t current_msc;
2747428d7b3dSmrg	bool sync, event;
2748428d7b3dSmrg
2749428d7b3dSmrg	if (!immediate_swap(sna, *target_msc, divisor, draw, crtc, &current_msc))
2750428d7b3dSmrg		return false;
2751428d7b3dSmrg
2752428d7b3dSmrg	sync = current_msc < *target_msc;
2753428d7b3dSmrg	event = dri2_chain(draw) == NULL;
2754428d7b3dSmrg	if (!sync || event) {
2755428d7b3dSmrg		DBG(("%s: performing immediate xchg only on pipe %d\n",
2756428d7b3dSmrg		     __FUNCTION__, sna_crtc_to_pipe(crtc)));
2757428d7b3dSmrg		sna_dri2_xchg_crtc(sna, draw, crtc, front, back);
2758428d7b3dSmrg	}
2759428d7b3dSmrg	if (sync) {
2760428d7b3dSmrg		struct sna_dri2_event *info;
2761428d7b3dSmrg
2762428d7b3dSmrg		info = sna_dri2_add_event(sna, draw, client);
2763428d7b3dSmrg		if (!info)
2764428d7b3dSmrg			goto complete;
2765428d7b3dSmrg
2766428d7b3dSmrg		info->event_complete = func;
2767428d7b3dSmrg		info->event_data = data;
2768428d7b3dSmrg
2769428d7b3dSmrg		info->front = sna_dri2_reference_buffer(front);
2770428d7b3dSmrg		info->back = sna_dri2_reference_buffer(back);
2771428d7b3dSmrg		info->type = SWAP_THROTTLE;
2772428d7b3dSmrg
2773428d7b3dSmrg		if (event) {
2774428d7b3dSmrg			union drm_wait_vblank vbl;
2775428d7b3dSmrg
2776428d7b3dSmrg			VG_CLEAR(vbl);
2777428d7b3dSmrg			vbl.request.type =
2778428d7b3dSmrg				DRM_VBLANK_RELATIVE |
2779428d7b3dSmrg				DRM_VBLANK_EVENT;
2780428d7b3dSmrg			vbl.request.sequence = 1;
2781428d7b3dSmrg			vbl.request.signal = (uintptr_t)info;
2782428d7b3dSmrg
2783428d7b3dSmrg			info->queued = true;
2784428d7b3dSmrg			if (sna_wait_vblank(sna, &vbl, info->pipe)) {
2785428d7b3dSmrg				sna_dri2_event_free(info);
2786428d7b3dSmrg				goto complete;
2787428d7b3dSmrg			}
2788428d7b3dSmrg
2789428d7b3dSmrg			swap_limit(draw, 2);
2790428d7b3dSmrg		}
2791428d7b3dSmrg	} else {
2792428d7b3dSmrgcomplete:
2793428d7b3dSmrg		fake_swap_complete(sna, client, draw, crtc, DRI2_EXCHANGE_COMPLETE, func, data);
2794428d7b3dSmrg	}
2795428d7b3dSmrg
2796428d7b3dSmrg	*target_msc = current_msc + 1;
2797428d7b3dSmrg	return true;
2798428d7b3dSmrg}
2799428d7b3dSmrg
2800428d7b3dSmrgstatic bool has_pending_events(struct sna *sna)
2801428d7b3dSmrg{
2802428d7b3dSmrg	struct pollfd pfd;
2803428d7b3dSmrg	pfd.fd = sna->kgem.fd;
2804428d7b3dSmrg	pfd.events = POLLIN;
2805428d7b3dSmrg	return poll(&pfd, 1, 0) == 1;
2806428d7b3dSmrg}
2807428d7b3dSmrg
2808428d7b3dSmrg/*
2809428d7b3dSmrg * ScheduleSwap is responsible for requesting a DRM vblank event for the
2810428d7b3dSmrg * appropriate frame.
2811428d7b3dSmrg *
2812428d7b3dSmrg * In the case of a blit (e.g. for a windowed swap) or buffer exchange,
2813428d7b3dSmrg * the vblank requested can simply be the last queued swap frame + the swap
2814428d7b3dSmrg * interval for the drawable.
2815428d7b3dSmrg *
2816428d7b3dSmrg * In the case of a page flip, we request an event for the last queued swap
2817428d7b3dSmrg * frame + swap interval - 1, since we'll need to queue the flip for the frame
2818428d7b3dSmrg * immediately following the received event.
2819428d7b3dSmrg *
2820428d7b3dSmrg * The client will be blocked if it tries to perform further GL commands
2821428d7b3dSmrg * after queueing a swap, though in the Intel case after queueing a flip, the
2822428d7b3dSmrg * client is free to queue more commands; they'll block in the kernel if
2823428d7b3dSmrg * they access buffers busy with the flip.
2824428d7b3dSmrg *
2825428d7b3dSmrg * When the swap is complete, the driver should call into the server so it
2826428d7b3dSmrg * can send any swap complete events that have been requested.
2827428d7b3dSmrg */
2828428d7b3dSmrgstatic int
2829428d7b3dSmrgsna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
2830428d7b3dSmrg		       DRI2BufferPtr back, CARD64 *target_msc, CARD64 divisor,
2831428d7b3dSmrg		       CARD64 remainder, DRI2SwapEventPtr func, void *data)
2832428d7b3dSmrg{
2833428d7b3dSmrg	struct sna *sna = to_sna_from_drawable(draw);
2834428d7b3dSmrg	union drm_wait_vblank vbl;
2835428d7b3dSmrg	xf86CrtcPtr crtc = NULL;
2836428d7b3dSmrg	struct sna_dri2_event *info = NULL;
2837428d7b3dSmrg	int type = DRI2_EXCHANGE_COMPLETE;
2838428d7b3dSmrg	CARD64 current_msc;
2839428d7b3dSmrg
2840428d7b3dSmrg	DBG(("%s: draw=%lu %dx%d, pixmap=%ld %dx%d, back=%u (refs=%d/%d, flush=%d) , front=%u (refs=%d/%d, flush=%d)\n",
2841428d7b3dSmrg	     __FUNCTION__,
2842428d7b3dSmrg	     (long)draw->id, draw->width, draw->height,
2843428d7b3dSmrg	     get_drawable_pixmap(draw)->drawable.serialNumber,
2844428d7b3dSmrg	     get_drawable_pixmap(draw)->drawable.width,
2845428d7b3dSmrg	     get_drawable_pixmap(draw)->drawable.height,
2846428d7b3dSmrg	     get_private(back)->bo->handle,
2847428d7b3dSmrg	     get_private(back)->refcnt,
2848428d7b3dSmrg	     get_private(back)->bo->refcnt,
2849428d7b3dSmrg	     get_private(back)->bo->flush,
2850428d7b3dSmrg	     get_private(front)->bo->handle,
2851428d7b3dSmrg	     get_private(front)->refcnt,
2852428d7b3dSmrg	     get_private(front)->bo->refcnt,
2853428d7b3dSmrg	     get_private(front)->bo->flush));
2854428d7b3dSmrg
2855428d7b3dSmrg	DBG(("%s(target_msc=%llu, divisor=%llu, remainder=%llu)\n",
2856428d7b3dSmrg	     __FUNCTION__,
2857428d7b3dSmrg	     (long long)*target_msc,
2858428d7b3dSmrg	     (long long)divisor,
2859428d7b3dSmrg	     (long long)remainder));
2860428d7b3dSmrg
2861428d7b3dSmrg	assert(get_private(front)->refcnt);
2862428d7b3dSmrg	assert(get_private(back)->refcnt);
2863428d7b3dSmrg
2864428d7b3dSmrg	assert(get_private(front)->bo->refcnt);
2865428d7b3dSmrg	assert(get_private(back)->bo->refcnt);
2866428d7b3dSmrg
2867428d7b3dSmrg	if (get_private(front)->pixmap != get_drawable_pixmap(draw)) {
2868428d7b3dSmrg		DBG(("%s: decoupled DRI2 front pixmap=%ld, actual pixmap=%ld\n",
2869428d7b3dSmrg		     __FUNCTION__,
2870428d7b3dSmrg		     get_private(front)->pixmap->drawable.serialNumber,
2871428d7b3dSmrg		     get_drawable_pixmap(draw)->drawable.serialNumber));
2872428d7b3dSmrg		goto skip;
2873428d7b3dSmrg	}
2874428d7b3dSmrg
2875428d7b3dSmrg	if (get_private(back)->stale) {
2876428d7b3dSmrg		DBG(("%s: stale back buffer\n", __FUNCTION__));
2877428d7b3dSmrg		goto skip;
2878428d7b3dSmrg	}
2879428d7b3dSmrg
2880428d7b3dSmrg	assert(sna_pixmap_from_drawable(draw)->flush);
2881428d7b3dSmrg
2882428d7b3dSmrg	if (draw->type != DRAWABLE_PIXMAP) {
2883428d7b3dSmrg		WindowPtr win = (WindowPtr)draw;
2884428d7b3dSmrg		struct dri2_window *priv = dri2_window(win);
2885428d7b3dSmrg		if (priv->front) {
2886428d7b3dSmrg			assert(front == priv->front);
2887428d7b3dSmrg			assert(get_private(priv->front)->refcnt > 1);
2888428d7b3dSmrg			get_private(priv->front)->refcnt--;
2889428d7b3dSmrg			priv->front = NULL;
2890428d7b3dSmrg		}
2891428d7b3dSmrg		if (win->clipList.extents.x2 <= win->clipList.extents.x1 ||
2892428d7b3dSmrg		    win->clipList.extents.y2 <= win->clipList.extents.y1) {
2893428d7b3dSmrg			DBG(("%s: window clipped (%d, %d), (%d, %d)\n",
2894428d7b3dSmrg			     __FUNCTION__,
2895428d7b3dSmrg			     win->clipList.extents.x1,
2896428d7b3dSmrg			     win->clipList.extents.y1,
2897428d7b3dSmrg			     win->clipList.extents.x2,
2898428d7b3dSmrg			     win->clipList.extents.y2));
2899428d7b3dSmrg			goto skip;
2900428d7b3dSmrg		}
2901428d7b3dSmrg	}
2902428d7b3dSmrg
2903428d7b3dSmrg	/* Drawable not displayed... just complete the swap */
2904428d7b3dSmrg	if ((sna->flags & SNA_NO_WAIT) == 0)
2905428d7b3dSmrg		crtc = sna_dri2_get_crtc(draw);
2906428d7b3dSmrg	if (crtc == NULL) {
2907428d7b3dSmrg		DBG(("%s: off-screen, immediate update\n", __FUNCTION__));
2908428d7b3dSmrg		goto blit;
2909428d7b3dSmrg	}
2910428d7b3dSmrg
2911428d7b3dSmrg	assert(draw->type != DRAWABLE_PIXMAP);
2912428d7b3dSmrg
2913428d7b3dSmrg	while (dri2_chain(draw) && has_pending_events(sna)) {
2914428d7b3dSmrg		DBG(("%s: flushing pending events\n", __FUNCTION__));
2915428d7b3dSmrg		sna_mode_wakeup(sna);
2916428d7b3dSmrg	}
2917428d7b3dSmrg
2918428d7b3dSmrg	if (can_xchg(sna, draw, front, back) &&
2919428d7b3dSmrg	    sna_dri2_schedule_xchg(client, draw, crtc, front, back,
2920428d7b3dSmrg				   target_msc, divisor, remainder,
2921428d7b3dSmrg				   func, data))
2922428d7b3dSmrg		return TRUE;
2923428d7b3dSmrg
2924428d7b3dSmrg	if (can_xchg_crtc(sna, draw, front, back, crtc) &&
2925428d7b3dSmrg	    sna_dri2_schedule_xchg_crtc(client, draw, crtc, front, back,
2926428d7b3dSmrg					target_msc, divisor, remainder,
2927428d7b3dSmrg					func, data))
2928428d7b3dSmrg		return TRUE;
2929428d7b3dSmrg
2930428d7b3dSmrg	if (can_flip(sna, draw, front, back, crtc) &&
2931428d7b3dSmrg	    sna_dri2_schedule_flip(client, draw, crtc, front, back,
2932428d7b3dSmrg				  target_msc, divisor, remainder,
2933428d7b3dSmrg				  func, data))
2934428d7b3dSmrg		return TRUE;
2935428d7b3dSmrg
2936428d7b3dSmrg	VG_CLEAR(vbl);
2937428d7b3dSmrg
2938428d7b3dSmrg	info = sna_dri2_add_event(sna, draw, client);
2939428d7b3dSmrg	if (!info)
2940428d7b3dSmrg		goto blit;
2941428d7b3dSmrg
2942428d7b3dSmrg	assert(info->crtc == crtc);
2943428d7b3dSmrg	info->event_complete = func;
2944428d7b3dSmrg	info->event_data = data;
2945428d7b3dSmrg
2946428d7b3dSmrg	info->front = sna_dri2_reference_buffer(front);
2947428d7b3dSmrg	info->back = sna_dri2_reference_buffer(back);
2948428d7b3dSmrg
2949428d7b3dSmrg	if (immediate_swap(sna, *target_msc, divisor, draw, crtc, &current_msc)) {
2950428d7b3dSmrg		bool sync = current_msc < *target_msc;
2951428d7b3dSmrg		if (!sna_dri2_immediate_blit(sna, info, sync, true))
2952428d7b3dSmrg			sna_dri2_event_free(info);
2953428d7b3dSmrg		*target_msc = current_msc + sync;
2954428d7b3dSmrg		return TRUE;
2955428d7b3dSmrg	}
2956428d7b3dSmrg
2957428d7b3dSmrg	vbl.request.type =
2958428d7b3dSmrg		DRM_VBLANK_ABSOLUTE |
2959428d7b3dSmrg		DRM_VBLANK_EVENT;
2960428d7b3dSmrg	vbl.request.signal = (uintptr_t)info;
2961428d7b3dSmrg
2962428d7b3dSmrg	/*
2963428d7b3dSmrg	 * If divisor is zero, or current_msc is smaller than target_msc
2964428d7b3dSmrg	 * we just need to make sure target_msc passes before initiating
2965428d7b3dSmrg	 * the swap.
2966428d7b3dSmrg	 */
2967428d7b3dSmrg	info->type = SWAP;
2968428d7b3dSmrg	info->queued = true;
2969428d7b3dSmrg	if (divisor && current_msc >= *target_msc) {
2970428d7b3dSmrg		DBG(("%s: missed target, queueing event for next: current=%lld, target=%lld, divisor=%lld, remainder=%lld\n",
2971428d7b3dSmrg		     __FUNCTION__,
2972428d7b3dSmrg		     (long long)current_msc,
2973428d7b3dSmrg		     (long long)*target_msc,
2974428d7b3dSmrg		     (long long)divisor,
2975428d7b3dSmrg		     (long long)remainder));
2976428d7b3dSmrg
2977428d7b3dSmrg		*target_msc = current_msc + remainder - current_msc % divisor;
2978428d7b3dSmrg		if (*target_msc <= current_msc)
2979428d7b3dSmrg			*target_msc += divisor;
2980428d7b3dSmrg	}
2981428d7b3dSmrg	vbl.request.sequence = draw_target_seq(draw, *target_msc - 1);
2982428d7b3dSmrg	if (*target_msc <= current_msc + 1) {
2983428d7b3dSmrg		DBG(("%s: performing blit before queueing\n", __FUNCTION__));
2984428d7b3dSmrg		assert(info->queued);
2985428d7b3dSmrg		info->bo = __sna_dri2_copy_region(sna, draw, NULL,
2986428d7b3dSmrg						  back, front,
2987428d7b3dSmrg						  true);
2988428d7b3dSmrg		info->type = SWAP_WAIT;
2989428d7b3dSmrg
2990428d7b3dSmrg		vbl.request.type =
2991428d7b3dSmrg			DRM_VBLANK_RELATIVE |
2992428d7b3dSmrg			DRM_VBLANK_EVENT;
2993428d7b3dSmrg		vbl.request.sequence = 1;
2994428d7b3dSmrg		*target_msc = current_msc + 1;
2995428d7b3dSmrg	}
2996428d7b3dSmrg
2997428d7b3dSmrg	assert(info->queued);
2998428d7b3dSmrg	if (sna_wait_vblank(sna, &vbl, info->pipe))
2999428d7b3dSmrg		goto blit;
3000428d7b3dSmrg
3001428d7b3dSmrg	DBG(("%s: reported target_msc=%llu\n", __FUNCTION__, *target_msc));
3002428d7b3dSmrg	swap_limit(draw, 1 + (info->type == SWAP_WAIT));
3003428d7b3dSmrg	return TRUE;
3004428d7b3dSmrg
3005428d7b3dSmrgblit:
3006428d7b3dSmrg	DBG(("%s -- blit\n", __FUNCTION__));
3007428d7b3dSmrg	if (info)
3008428d7b3dSmrg		sna_dri2_event_free(info);
3009428d7b3dSmrg	if (can_xchg(sna, draw, front, back)) {
3010428d7b3dSmrg		sna_dri2_xchg(draw, front, back);
3011428d7b3dSmrg	} else {
3012428d7b3dSmrg		__sna_dri2_copy_region(sna, draw, NULL, back, front, false);
3013428d7b3dSmrg		type = DRI2_BLIT_COMPLETE;
3014428d7b3dSmrg	}
3015428d7b3dSmrgskip:
3016428d7b3dSmrg	DBG(("%s: unable to show frame, unblocking client\n", __FUNCTION__));
3017428d7b3dSmrg	if (crtc == NULL)
3018428d7b3dSmrg		crtc = sna_mode_first_crtc(sna);
3019428d7b3dSmrg	fake_swap_complete(sna, client, draw, crtc, type, func, data);
3020428d7b3dSmrg	*target_msc = 0; /* offscreen, so zero out target vblank count */
3021428d7b3dSmrg	return TRUE;
3022428d7b3dSmrg}
3023428d7b3dSmrg
3024428d7b3dSmrg/*
3025428d7b3dSmrg * Get current frame count and frame count timestamp, based on drawable's
3026428d7b3dSmrg * crtc.
3027428d7b3dSmrg */
3028428d7b3dSmrgstatic int
3029428d7b3dSmrgsna_dri2_get_msc(DrawablePtr draw, CARD64 *ust, CARD64 *msc)
3030428d7b3dSmrg{
3031428d7b3dSmrg	struct sna *sna = to_sna_from_drawable(draw);
3032428d7b3dSmrg	xf86CrtcPtr crtc = sna_dri2_get_crtc(draw);
3033428d7b3dSmrg	const struct ust_msc *swap;
3034428d7b3dSmrg
3035428d7b3dSmrg	DBG(("%s(draw=%ld, pipe=%d)\n", __FUNCTION__, draw->id,
3036428d7b3dSmrg	     crtc ? sna_crtc_to_pipe(crtc) : -1));
3037428d7b3dSmrg
3038428d7b3dSmrg	if (crtc != NULL) {
3039428d7b3dSmrg		union drm_wait_vblank vbl;
3040428d7b3dSmrg
3041428d7b3dSmrg		VG_CLEAR(vbl);
3042428d7b3dSmrg		vbl.request.type = _DRM_VBLANK_RELATIVE;
3043428d7b3dSmrg		vbl.request.sequence = 0;
3044428d7b3dSmrg		if (sna_wait_vblank(sna, &vbl, sna_crtc_to_pipe(crtc)) == 0)
3045428d7b3dSmrg			sna_crtc_record_vblank(crtc, &vbl);
3046428d7b3dSmrg	} else
3047428d7b3dSmrg		/* Drawable not displayed, make up a *monotonic* value */
3048428d7b3dSmrg		crtc = sna_mode_first_crtc(sna);
3049428d7b3dSmrg
3050428d7b3dSmrg	swap = sna_crtc_last_swap(crtc);
3051428d7b3dSmrg	*msc = draw_current_msc(draw, crtc, swap->msc);
3052428d7b3dSmrg	*ust = ust64(swap->tv_sec, swap->tv_usec);
3053428d7b3dSmrg	DBG(("%s: msc=%llu, ust=%llu\n", __FUNCTION__,
3054428d7b3dSmrg	     (long long)*msc, (long long)*ust));
3055428d7b3dSmrg	return TRUE;
3056428d7b3dSmrg}
3057428d7b3dSmrg
3058428d7b3dSmrg/*
3059428d7b3dSmrg * Request a DRM event when the requested conditions will be satisfied.
3060428d7b3dSmrg *
3061428d7b3dSmrg * We need to handle the event and ask the server to wake up the client when
3062428d7b3dSmrg * we receive it.
3063428d7b3dSmrg */
3064428d7b3dSmrgstatic int
3065428d7b3dSmrgsna_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
3066428d7b3dSmrg			   CARD64 divisor, CARD64 remainder)
3067428d7b3dSmrg{
3068428d7b3dSmrg	struct sna *sna = to_sna_from_drawable(draw);
3069428d7b3dSmrg	struct sna_dri2_event *info = NULL;
3070428d7b3dSmrg	xf86CrtcPtr crtc;
3071428d7b3dSmrg	CARD64 current_msc;
3072428d7b3dSmrg	union drm_wait_vblank vbl;
3073428d7b3dSmrg	const struct ust_msc *swap;
3074428d7b3dSmrg	int pipe;
3075428d7b3dSmrg
3076428d7b3dSmrg	crtc = sna_dri2_get_crtc(draw);
3077428d7b3dSmrg	DBG(("%s(pipe=%d, target_msc=%llu, divisor=%llu, rem=%llu)\n",
3078428d7b3dSmrg	     __FUNCTION__, crtc ? sna_crtc_to_pipe(crtc) : -1,
3079428d7b3dSmrg	     (long long)target_msc,
3080428d7b3dSmrg	     (long long)divisor,
3081428d7b3dSmrg	     (long long)remainder));
3082428d7b3dSmrg
3083428d7b3dSmrg	/* Drawable not visible, return immediately */
3084428d7b3dSmrg	if (crtc == NULL)
3085428d7b3dSmrg		goto out_complete;
3086428d7b3dSmrg
3087428d7b3dSmrg	pipe = sna_crtc_to_pipe(crtc);
3088428d7b3dSmrg
3089428d7b3dSmrg	VG_CLEAR(vbl);
3090428d7b3dSmrg
3091428d7b3dSmrg	/* Get current count */
3092428d7b3dSmrg	vbl.request.type = _DRM_VBLANK_RELATIVE;
3093428d7b3dSmrg	vbl.request.sequence = 0;
3094428d7b3dSmrg	if (sna_wait_vblank(sna, &vbl, pipe))
3095428d7b3dSmrg		goto out_complete;
3096428d7b3dSmrg
3097428d7b3dSmrg	current_msc = draw_current_msc(draw, crtc, sna_crtc_record_vblank(crtc, &vbl));
3098428d7b3dSmrg
3099428d7b3dSmrg	/* If target_msc already reached or passed, set it to
3100428d7b3dSmrg	 * current_msc to ensure we return a reasonable value back
3101428d7b3dSmrg	 * to the caller. This keeps the client from continually
3102428d7b3dSmrg	 * sending us MSC targets from the past by forcibly updating
3103428d7b3dSmrg	 * their count on this call.
3104428d7b3dSmrg	 */
3105428d7b3dSmrg	if (divisor == 0 && current_msc >= target_msc)
3106428d7b3dSmrg		goto out_complete;
3107428d7b3dSmrg
3108428d7b3dSmrg	info = sna_dri2_add_event(sna, draw, client);
3109428d7b3dSmrg	if (!info)
3110428d7b3dSmrg		goto out_complete;
3111428d7b3dSmrg
3112428d7b3dSmrg	assert(info->crtc == crtc);
3113428d7b3dSmrg	info->type = WAITMSC;
3114428d7b3dSmrg
3115428d7b3dSmrg	vbl.request.signal = (uintptr_t)info;
3116428d7b3dSmrg	vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
3117428d7b3dSmrg	/*
3118428d7b3dSmrg	 * If divisor is zero, or current_msc is smaller than target_msc,
3119428d7b3dSmrg	 * we just need to make sure target_msc passes before waking up the
3120428d7b3dSmrg	 * client. Otherwise, compute the next msc to match divisor/remainder.
3121428d7b3dSmrg	 */
3122428d7b3dSmrg	if (divisor && current_msc >= target_msc) {
3123428d7b3dSmrg		DBG(("%s: missed target, queueing event for next: current=%lld, target=%lld, divisor=%lld, remainder=%lld\n",
3124428d7b3dSmrg		     __FUNCTION__,
3125428d7b3dSmrg		     (long long)current_msc,
3126428d7b3dSmrg		     (long long)target_msc,
3127428d7b3dSmrg		     (long long)divisor,
3128428d7b3dSmrg		     (long long)remainder));
3129428d7b3dSmrg		target_msc = current_msc + remainder - current_msc % divisor;
3130428d7b3dSmrg		if (target_msc <= current_msc)
3131428d7b3dSmrg			target_msc += divisor;
3132428d7b3dSmrg	}
3133428d7b3dSmrg	vbl.request.sequence = draw_target_seq(draw, target_msc);
3134428d7b3dSmrg
3135428d7b3dSmrg	info->queued = true;
3136428d7b3dSmrg	if (sna_wait_vblank(sna, &vbl, pipe))
3137428d7b3dSmrg		goto out_free_info;
3138428d7b3dSmrg
3139428d7b3dSmrg	DRI2BlockClient(client, draw);
3140428d7b3dSmrg	return TRUE;
3141428d7b3dSmrg
3142428d7b3dSmrgout_free_info:
3143428d7b3dSmrg	sna_dri2_event_free(info);
3144428d7b3dSmrgout_complete:
3145428d7b3dSmrg	if (crtc == NULL)
3146428d7b3dSmrg		crtc = sna_mode_first_crtc(sna);
3147428d7b3dSmrg	swap = sna_crtc_last_swap(crtc);
3148428d7b3dSmrg	DRI2WaitMSCComplete(client, draw,
3149428d7b3dSmrg			    draw_current_msc(draw, crtc, swap->msc),
3150428d7b3dSmrg			    swap->tv_sec, swap->tv_usec);
3151428d7b3dSmrg	return TRUE;
3152428d7b3dSmrg}
3153428d7b3dSmrg#else
3154428d7b3dSmrgvoid sna_dri2_destroy_window(WindowPtr win) { }
3155428d7b3dSmrgvoid sna_dri2_decouple_window(WindowPtr win) { }
3156428d7b3dSmrg#endif
3157428d7b3dSmrg
3158428d7b3dSmrgstatic bool has_i830_dri(void)
3159428d7b3dSmrg{
3160428d7b3dSmrg	return access(DRI_DRIVER_PATH "/i830_dri.so", R_OK) == 0;
3161428d7b3dSmrg}
3162428d7b3dSmrg
3163428d7b3dSmrgstatic int
3164428d7b3dSmrgnamecmp(const char *s1, const char *s2)
3165428d7b3dSmrg{
3166428d7b3dSmrg	char c1, c2;
3167428d7b3dSmrg
3168428d7b3dSmrg	if (!s1 || *s1 == 0) {
3169428d7b3dSmrg		if (!s2 || *s2 == 0)
3170428d7b3dSmrg			return 0;
3171428d7b3dSmrg		else
3172428d7b3dSmrg			return 1;
3173428d7b3dSmrg	}
3174428d7b3dSmrg
3175428d7b3dSmrg	while (*s1 == '_' || *s1 == ' ' || *s1 == '\t')
3176428d7b3dSmrg		s1++;
3177428d7b3dSmrg
3178428d7b3dSmrg	while (*s2 == '_' || *s2 == ' ' || *s2 == '\t')
3179428d7b3dSmrg		s2++;
3180428d7b3dSmrg
3181428d7b3dSmrg	c1 = isupper(*s1) ? tolower(*s1) : *s1;
3182428d7b3dSmrg	c2 = isupper(*s2) ? tolower(*s2) : *s2;
3183428d7b3dSmrg	while (c1 == c2) {
3184428d7b3dSmrg		if (c1 == '\0')
3185428d7b3dSmrg			return 0;
3186428d7b3dSmrg
3187428d7b3dSmrg		s1++;
3188428d7b3dSmrg		while (*s1 == '_' || *s1 == ' ' || *s1 == '\t')
3189428d7b3dSmrg			s1++;
3190428d7b3dSmrg
3191428d7b3dSmrg		s2++;
3192428d7b3dSmrg		while (*s2 == '_' || *s2 == ' ' || *s2 == '\t')
3193428d7b3dSmrg			s2++;
3194428d7b3dSmrg
3195428d7b3dSmrg		c1 = isupper(*s1) ? tolower(*s1) : *s1;
3196428d7b3dSmrg		c2 = isupper(*s2) ? tolower(*s2) : *s2;
3197428d7b3dSmrg	}
3198428d7b3dSmrg
3199428d7b3dSmrg	return c1 - c2;
3200428d7b3dSmrg}
3201428d7b3dSmrg
3202428d7b3dSmrgstatic bool is_level(const char **str)
3203428d7b3dSmrg{
3204428d7b3dSmrg	const char *s = *str;
3205428d7b3dSmrg	char *end;
3206428d7b3dSmrg	unsigned val;
3207428d7b3dSmrg
3208428d7b3dSmrg	if (s == NULL || *s == '\0')
3209428d7b3dSmrg		return true;
3210428d7b3dSmrg
3211428d7b3dSmrg	if (namecmp(s, "on") == 0)
3212428d7b3dSmrg		return true;
3213428d7b3dSmrg	if (namecmp(s, "true") == 0)
3214428d7b3dSmrg		return true;
3215428d7b3dSmrg	if (namecmp(s, "yes") == 0)
3216428d7b3dSmrg		return true;
3217428d7b3dSmrg
3218428d7b3dSmrg	if (namecmp(s, "0") == 0)
3219428d7b3dSmrg		return true;
3220428d7b3dSmrg	if (namecmp(s, "off") == 0)
3221428d7b3dSmrg		return true;
3222428d7b3dSmrg	if (namecmp(s, "false") == 0)
3223428d7b3dSmrg		return true;
3224428d7b3dSmrg	if (namecmp(s, "no") == 0)
3225428d7b3dSmrg		return true;
3226428d7b3dSmrg
3227428d7b3dSmrg	val = strtoul(s, &end, 0);
3228428d7b3dSmrg	if (val && *end == '\0')
3229428d7b3dSmrg		return true;
3230428d7b3dSmrg	if (val && *end == ':')
3231428d7b3dSmrg		*str = end + 1;
3232428d7b3dSmrg	return false;
3233428d7b3dSmrg}
3234428d7b3dSmrg
3235428d7b3dSmrgstatic const char *dri_driver_name(struct sna *sna)
3236428d7b3dSmrg{
3237428d7b3dSmrg	const char *s = xf86GetOptValString(sna->Options, OPTION_DRI);
3238428d7b3dSmrg
3239428d7b3dSmrg	if (is_level(&s)) {
3240428d7b3dSmrg		if (sna->kgem.gen < 030)
3241428d7b3dSmrg			return has_i830_dri() ? "i830" : "i915";
3242428d7b3dSmrg		else if (sna->kgem.gen < 040)
3243428d7b3dSmrg			return "i915";
3244428d7b3dSmrg		else
3245428d7b3dSmrg			return "i965";
3246428d7b3dSmrg	}
3247428d7b3dSmrg
3248428d7b3dSmrg	return s;
3249428d7b3dSmrg}
3250428d7b3dSmrg
3251428d7b3dSmrgbool sna_dri2_open(struct sna *sna, ScreenPtr screen)
3252428d7b3dSmrg{
3253428d7b3dSmrg	DRI2InfoRec info;
3254428d7b3dSmrg	int major = 1, minor = 0;
3255428d7b3dSmrg#if DRI2INFOREC_VERSION >= 4
3256428d7b3dSmrg	const char *driverNames[2];
3257428d7b3dSmrg#endif
3258428d7b3dSmrg
3259428d7b3dSmrg	DBG(("%s()\n", __FUNCTION__));
3260428d7b3dSmrg
3261428d7b3dSmrg	if (wedged(sna)) {
3262428d7b3dSmrg		xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING,
3263428d7b3dSmrg			   "loading DRI2 whilst the GPU is wedged.\n");
3264428d7b3dSmrg	}
3265428d7b3dSmrg
3266428d7b3dSmrg	if (xf86LoaderCheckSymbol("DRI2Version"))
3267428d7b3dSmrg		DRI2Version(&major, &minor);
3268428d7b3dSmrg
3269428d7b3dSmrg	if (minor < 1) {
3270428d7b3dSmrg		xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING,
3271428d7b3dSmrg			   "DRI2 requires DRI2 module version 1.1.0 or later\n");
3272428d7b3dSmrg		return false;
3273428d7b3dSmrg	}
3274428d7b3dSmrg
3275428d7b3dSmrg	memset(&info, '\0', sizeof(info));
3276428d7b3dSmrg	info.fd = sna->kgem.fd;
3277428d7b3dSmrg	info.driverName = dri_driver_name(sna);
3278428d7b3dSmrg	info.deviceName = intel_get_client_name(sna->dev);
3279428d7b3dSmrg
3280428d7b3dSmrg	DBG(("%s: loading dri driver '%s' [gen=%d] for device '%s'\n",
3281428d7b3dSmrg	     __FUNCTION__, info.driverName, sna->kgem.gen, info.deviceName));
3282428d7b3dSmrg
3283428d7b3dSmrg#if DRI2INFOREC_VERSION == 2
3284428d7b3dSmrg	/* The ABI between 2 and 3 was broken so we could get rid of
3285428d7b3dSmrg	 * the multi-buffer alloc functions.  Make sure we indicate the
3286428d7b3dSmrg	 * right version so DRI2 can reject us if it's version 3 or above. */
3287428d7b3dSmrg	info.version = 2;
3288428d7b3dSmrg#else
3289428d7b3dSmrg	info.version = 3;
3290428d7b3dSmrg#endif
3291428d7b3dSmrg	info.CreateBuffer = sna_dri2_create_buffer;
3292428d7b3dSmrg	info.DestroyBuffer = sna_dri2_destroy_buffer;
3293428d7b3dSmrg
3294428d7b3dSmrg	info.CopyRegion = sna_dri2_copy_region;
3295428d7b3dSmrg#if DRI2INFOREC_VERSION >= 4
3296428d7b3dSmrg	info.version = 4;
3297428d7b3dSmrg	info.ScheduleSwap = sna_dri2_schedule_swap;
3298428d7b3dSmrg	info.GetMSC = sna_dri2_get_msc;
3299428d7b3dSmrg	info.ScheduleWaitMSC = sna_dri2_schedule_wait_msc;
3300428d7b3dSmrg	info.numDrivers = 2;
3301428d7b3dSmrg	info.driverNames = driverNames;
3302428d7b3dSmrg	driverNames[0] = info.driverName;
3303428d7b3dSmrg	driverNames[1] = info.driverName;
3304428d7b3dSmrg#endif
3305428d7b3dSmrg
3306428d7b3dSmrg#if DRI2INFOREC_VERSION >= 6
3307428d7b3dSmrg	if (xorg_can_triple_buffer()) {
3308428d7b3dSmrg		info.version = 6;
3309428d7b3dSmrg		info.SwapLimitValidate = sna_dri2_swap_limit_validate;
3310428d7b3dSmrg		info.ReuseBufferNotify = sna_dri2_reuse_buffer;
3311428d7b3dSmrg	}
3312428d7b3dSmrg#endif
3313428d7b3dSmrg
3314428d7b3dSmrg#if USE_ASYNC_SWAP
3315428d7b3dSmrg	info.version = 10;
3316428d7b3dSmrg	info.scheduleSwap0 = 1;
3317428d7b3dSmrg#endif
3318428d7b3dSmrg
3319428d7b3dSmrg	return DRI2ScreenInit(screen, &info);
3320428d7b3dSmrg}
3321428d7b3dSmrg
3322428d7b3dSmrgvoid sna_dri2_close(struct sna *sna, ScreenPtr screen)
3323428d7b3dSmrg{
3324428d7b3dSmrg	DBG(("%s()\n", __FUNCTION__));
3325428d7b3dSmrg	DRI2CloseScreen(screen);
3326428d7b3dSmrg}
3327