1428d7b3dSmrg/*
2428d7b3dSmrg * Copyright © 2007 Red Hat, Inc.
3428d7b3dSmrg * Copyright © 2013-2014 Intel Corporation
4428d7b3dSmrg *
5428d7b3dSmrg * Permission is hereby granted, free of charge, to any person obtaining a
6428d7b3dSmrg * copy of this software and associated documentation files (the "Software"),
7428d7b3dSmrg * to deal in the Software without restriction, including without limitation
8428d7b3dSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9428d7b3dSmrg * and/or sell copies of the Software, and to permit persons to whom the
10428d7b3dSmrg * Software is furnished to do so, subject to the following conditions:
11428d7b3dSmrg *
12428d7b3dSmrg * The above copyright notice and this permission notice (including the next
13428d7b3dSmrg * paragraph) shall be included in all copies or substantial portions of the
14428d7b3dSmrg * Software.
15428d7b3dSmrg *
16428d7b3dSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17428d7b3dSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18428d7b3dSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19428d7b3dSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20428d7b3dSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21428d7b3dSmrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22428d7b3dSmrg * SOFTWARE.
23428d7b3dSmrg *
24428d7b3dSmrg * Authors:
25428d7b3dSmrg *    Dave Airlie <airlied@redhat.com>
26428d7b3dSmrg *
27428d7b3dSmrg */
28428d7b3dSmrg
29428d7b3dSmrg#ifdef HAVE_CONFIG_H
30428d7b3dSmrg#include "config.h"
31428d7b3dSmrg#endif
32428d7b3dSmrg
33428d7b3dSmrg#include <stdint.h>
34428d7b3dSmrg#include <sys/types.h>
35428d7b3dSmrg#include <sys/stat.h>
36428d7b3dSmrg#include <sys/mman.h>
37428d7b3dSmrg#include <fcntl.h>
38428d7b3dSmrg#include <unistd.h>
39428d7b3dSmrg#include <errno.h>
40428d7b3dSmrg#include <poll.h>
41428d7b3dSmrg#include <ctype.h>
42428d7b3dSmrg
43428d7b3dSmrg#include "sna.h"
44428d7b3dSmrg#include "sna_reg.h"
45428d7b3dSmrg#include "fb/fbpict.h"
46428d7b3dSmrg#include "intel_options.h"
47428d7b3dSmrg#include "backlight.h"
48428d7b3dSmrg
49428d7b3dSmrg#include <xf86Crtc.h>
50428d7b3dSmrg#include <xf86RandR12.h>
51428d7b3dSmrg#include <cursorstr.h>
52428d7b3dSmrg
53428d7b3dSmrg#if XF86_CRTC_VERSION >= 3
54428d7b3dSmrg#define HAS_GAMMA 1
55428d7b3dSmrg#else
56428d7b3dSmrg#define HAS_GAMMA 0
57428d7b3dSmrg#endif
58428d7b3dSmrg
59428d7b3dSmrg#include <X11/Xatom.h>
60428d7b3dSmrg#if defined(HAVE_X11_EXTENSIONS_DPMSCONST_H)
61428d7b3dSmrg#include <X11/extensions/dpmsconst.h>
62428d7b3dSmrg#else
63428d7b3dSmrg#define DPMSModeOn 0
64428d7b3dSmrg#define DPMSModeOff 3
65428d7b3dSmrg#endif
66428d7b3dSmrg#include <xf86DDC.h> /* for xf86InterpretEDID */
67428d7b3dSmrg
68428d7b3dSmrg#include <xf86drm.h>
69428d7b3dSmrg
70428d7b3dSmrg#ifdef HAVE_VALGRIND
71428d7b3dSmrg#include <valgrind.h>
72428d7b3dSmrg#include <memcheck.h>
73428d7b3dSmrg#endif
74428d7b3dSmrg
75428d7b3dSmrg/* Minor discrepancy between 32-bit/64-bit ABI in old kernels */
76428d7b3dSmrgunion compat_mode_get_connector{
77428d7b3dSmrg	struct drm_mode_get_connector conn;
78428d7b3dSmrg	uint32_t pad[20];
79428d7b3dSmrg};
80428d7b3dSmrg
81428d7b3dSmrg#define KNOWN_MODE_FLAGS ((1<<14)-1)
82428d7b3dSmrg
83428d7b3dSmrg#ifndef MONITOR_EDID_COMPLETE_RAWDATA
84428d7b3dSmrg#define MONITOR_EDID_COMPLETE_RAWDATA 1
85428d7b3dSmrg#endif
86428d7b3dSmrg
87428d7b3dSmrg#ifndef DEFAULT_DPI
88428d7b3dSmrg#define DEFAULT_DPI 96
89428d7b3dSmrg#endif
90428d7b3dSmrg
91428d7b3dSmrg#define DRM_MODE_PAGE_FLIP_ASYNC 0x02
92428d7b3dSmrg
93428d7b3dSmrg#define DRM_CLIENT_CAP_UNIVERSAL_PLANES 2
94428d7b3dSmrg#define DRM_PLANE_TYPE_OVERLAY 0
95428d7b3dSmrg#define DRM_PLANE_TYPE_PRIMARY 1
96428d7b3dSmrg#define DRM_PLANE_TYPE_CURSOR  2
97428d7b3dSmrg
98428d7b3dSmrg#define LOCAL_IOCTL_MODE_OBJ_GETPROPERTIES DRM_IOWR(0xb9, struct local_mode_obj_get_properties)
99428d7b3dSmrgstruct local_mode_obj_get_properties {
100428d7b3dSmrg	uint64_t props_ptr;
101428d7b3dSmrg	uint64_t prop_values_ptr;
102428d7b3dSmrg	uint32_t count_props;
103428d7b3dSmrg	uint32_t obj_id;
104428d7b3dSmrg	uint32_t obj_type;
105428d7b3dSmrg	uint32_t pad;
106428d7b3dSmrg};
107428d7b3dSmrg#define LOCAL_MODE_OBJECT_PLANE 0xeeeeeeee
108428d7b3dSmrg
109428d7b3dSmrg#if 0
110428d7b3dSmrg#define __DBG DBG
111428d7b3dSmrg#else
112428d7b3dSmrg#define __DBG(x)
113428d7b3dSmrg#endif
114428d7b3dSmrg
115428d7b3dSmrgextern XF86ConfigPtr xf86configptr;
116428d7b3dSmrg
117428d7b3dSmrgstruct sna_crtc {
118428d7b3dSmrg	xf86CrtcPtr base;
119428d7b3dSmrg	struct drm_mode_modeinfo kmode;
120428d7b3dSmrg	int dpms_mode;
121428d7b3dSmrg	PixmapPtr slave_pixmap;
122428d7b3dSmrg	DamagePtr slave_damage;
123428d7b3dSmrg	struct kgem_bo *bo, *shadow_bo, *client_bo;
124428d7b3dSmrg	struct sna_cursor *cursor;
125428d7b3dSmrg	unsigned int last_cursor_size;
126428d7b3dSmrg	uint32_t offset;
127428d7b3dSmrg	bool shadow;
128428d7b3dSmrg	bool fallback_shadow;
129428d7b3dSmrg	bool transform;
130428d7b3dSmrg	bool flip_pending;
131428d7b3dSmrg	uint8_t id;
132428d7b3dSmrg	uint8_t pipe;
133428d7b3dSmrg
134428d7b3dSmrg	RegionRec client_damage; /* XXX overlap with shadow damage? */
135428d7b3dSmrg
136428d7b3dSmrg	uint16_t shadow_bo_width, shadow_bo_height;
137428d7b3dSmrg
138428d7b3dSmrg	uint32_t rotation;
139428d7b3dSmrg	struct plane {
140428d7b3dSmrg		uint32_t id;
141428d7b3dSmrg		struct {
142428d7b3dSmrg			uint32_t prop;
143428d7b3dSmrg			uint32_t supported;
144428d7b3dSmrg			uint32_t current;
145428d7b3dSmrg		} rotation;
146428d7b3dSmrg	} primary, sprite;
147428d7b3dSmrg
148428d7b3dSmrg	uint32_t mode_serial, flip_serial;
149428d7b3dSmrg
150428d7b3dSmrg	uint32_t last_seq, wrap_seq;
151428d7b3dSmrg	struct ust_msc swap;
152428d7b3dSmrg
153428d7b3dSmrg	sna_flip_handler_t flip_handler;
154428d7b3dSmrg	struct kgem_bo *flip_bo;
155428d7b3dSmrg	void *flip_data;
156428d7b3dSmrg
157428d7b3dSmrg	struct list shadow_link;
158428d7b3dSmrg};
159428d7b3dSmrg
160428d7b3dSmrgstruct sna_property {
161428d7b3dSmrg	drmModePropertyPtr kprop;
162428d7b3dSmrg	int num_atoms; /* if range prop, num_atoms == 1; if enum prop, num_atoms == num_enums + 1 */
163428d7b3dSmrg	Atom *atoms;
164428d7b3dSmrg};
165428d7b3dSmrg
166428d7b3dSmrgstruct sna_output {
167428d7b3dSmrg	xf86OutputPtr base;
168428d7b3dSmrg	unsigned id;
169428d7b3dSmrg	unsigned serial;
170428d7b3dSmrg
171428d7b3dSmrg	unsigned possible_encoders;
172428d7b3dSmrg	unsigned attached_encoders;
173428d7b3dSmrg
174428d7b3dSmrg	unsigned int is_panel : 1;
175428d7b3dSmrg	unsigned int add_default_modes : 1;
176428d7b3dSmrg
177428d7b3dSmrg	uint32_t edid_idx;
178428d7b3dSmrg	uint32_t edid_blob_id;
179428d7b3dSmrg	uint32_t edid_len;
180428d7b3dSmrg	void *edid_raw;
181428d7b3dSmrg
182428d7b3dSmrg	bool has_panel_limits;
183428d7b3dSmrg	int panel_hdisplay;
184428d7b3dSmrg	int panel_vdisplay;
185428d7b3dSmrg
186428d7b3dSmrg	uint32_t dpms_id;
187428d7b3dSmrg	int dpms_mode;
188428d7b3dSmrg	struct backlight backlight;
189428d7b3dSmrg	int backlight_active_level;
190428d7b3dSmrg
191428d7b3dSmrg	int num_modes;
192428d7b3dSmrg	struct drm_mode_modeinfo *modes;
193428d7b3dSmrg
194428d7b3dSmrg	int num_props;
195428d7b3dSmrg	uint32_t *prop_ids;
196428d7b3dSmrg	uint64_t *prop_values;
197428d7b3dSmrg	struct sna_property *props;
198428d7b3dSmrg};
199428d7b3dSmrg
200428d7b3dSmrgenum { /* XXX copied from hw/xfree86/modes/xf86Crtc.c */
201428d7b3dSmrg	OPTION_PREFERRED_MODE,
202428d7b3dSmrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,14,99,1,0)
203428d7b3dSmrg	OPTION_ZOOM_MODES,
204428d7b3dSmrg#endif
205428d7b3dSmrg	OPTION_POSITION,
206428d7b3dSmrg	OPTION_BELOW,
207428d7b3dSmrg	OPTION_RIGHT_OF,
208428d7b3dSmrg	OPTION_ABOVE,
209428d7b3dSmrg	OPTION_LEFT_OF,
210428d7b3dSmrg	OPTION_ENABLE,
211428d7b3dSmrg	OPTION_DISABLE,
212428d7b3dSmrg	OPTION_MIN_CLOCK,
213428d7b3dSmrg	OPTION_MAX_CLOCK,
214428d7b3dSmrg	OPTION_IGNORE,
215428d7b3dSmrg	OPTION_ROTATE,
216428d7b3dSmrg	OPTION_PANNING,
217428d7b3dSmrg	OPTION_PRIMARY,
218428d7b3dSmrg	OPTION_DEFAULT_MODES,
219428d7b3dSmrg};
220428d7b3dSmrg
221428d7b3dSmrgstatic void sna_crtc_disable_cursor(struct sna *sna, struct sna_crtc *crtc);
222428d7b3dSmrg
223428d7b3dSmrgstatic bool is_zaphod(ScrnInfoPtr scrn)
224428d7b3dSmrg{
225428d7b3dSmrg	return xf86IsEntityShared(scrn->entityList[0]);
226428d7b3dSmrg}
227428d7b3dSmrg
228428d7b3dSmrginline static unsigned count_to_mask(int x)
229428d7b3dSmrg{
230428d7b3dSmrg	return (1 << x) - 1;
231428d7b3dSmrg}
232428d7b3dSmrg
233428d7b3dSmrgstatic inline struct sna_output *to_sna_output(xf86OutputPtr output)
234428d7b3dSmrg{
235428d7b3dSmrg	return output->driver_private;
236428d7b3dSmrg}
237428d7b3dSmrg
238428d7b3dSmrgstatic inline int to_connector_id(xf86OutputPtr output)
239428d7b3dSmrg{
240428d7b3dSmrg	assert(to_sna_output(output));
241428d7b3dSmrg	assert(to_sna_output(output)->id);
242428d7b3dSmrg	return to_sna_output(output)->id;
243428d7b3dSmrg}
244428d7b3dSmrg
245428d7b3dSmrgstatic inline struct sna_crtc *to_sna_crtc(xf86CrtcPtr crtc)
246428d7b3dSmrg{
247428d7b3dSmrg	return crtc->driver_private;
248428d7b3dSmrg}
249428d7b3dSmrg
250428d7b3dSmrgstatic inline bool event_pending(int fd)
251428d7b3dSmrg{
252428d7b3dSmrg	struct pollfd pfd;
253428d7b3dSmrg	pfd.fd = fd;
254428d7b3dSmrg	pfd.events = POLLIN;
255428d7b3dSmrg	return poll(&pfd, 1, 0) == 1;
256428d7b3dSmrg}
257428d7b3dSmrg
258428d7b3dSmrgstatic bool sna_mode_wait_for_event(struct sna *sna)
259428d7b3dSmrg{
260428d7b3dSmrg	struct pollfd pfd;
261428d7b3dSmrg	pfd.fd = sna->kgem.fd;
262428d7b3dSmrg	pfd.events = POLLIN;
263428d7b3dSmrg	return poll(&pfd, 1, -1) == 1;
264428d7b3dSmrg}
265428d7b3dSmrg
266428d7b3dSmrgstatic inline uint32_t fb_id(struct kgem_bo *bo)
267428d7b3dSmrg{
268428d7b3dSmrg	return bo->delta;
269428d7b3dSmrg}
270428d7b3dSmrg
271428d7b3dSmrguint32_t sna_crtc_id(xf86CrtcPtr crtc)
272428d7b3dSmrg{
273428d7b3dSmrg	if (to_sna_crtc(crtc) == NULL)
274428d7b3dSmrg		return 0;
275428d7b3dSmrg	return to_sna_crtc(crtc)->id;
276428d7b3dSmrg}
277428d7b3dSmrg
278428d7b3dSmrgint sna_crtc_to_pipe(xf86CrtcPtr crtc)
279428d7b3dSmrg{
280428d7b3dSmrg	assert(to_sna_crtc(crtc));
281428d7b3dSmrg	return to_sna_crtc(crtc)->pipe;
282428d7b3dSmrg}
283428d7b3dSmrg
284428d7b3dSmrguint32_t sna_crtc_to_sprite(xf86CrtcPtr crtc)
285428d7b3dSmrg{
286428d7b3dSmrg	assert(to_sna_crtc(crtc));
287428d7b3dSmrg	return to_sna_crtc(crtc)->sprite.id;
288428d7b3dSmrg}
289428d7b3dSmrg
290428d7b3dSmrgbool sna_crtc_is_on(xf86CrtcPtr crtc)
291428d7b3dSmrg{
292428d7b3dSmrg	assert(to_sna_crtc(crtc));
293428d7b3dSmrg	return to_sna_crtc(crtc)->bo != NULL;
294428d7b3dSmrg}
295428d7b3dSmrg
296428d7b3dSmrgbool sna_crtc_is_transformed(xf86CrtcPtr crtc)
297428d7b3dSmrg{
298428d7b3dSmrg	assert(to_sna_crtc(crtc));
299428d7b3dSmrg	return to_sna_crtc(crtc)->transform;
300428d7b3dSmrg}
301428d7b3dSmrg
302428d7b3dSmrgstatic inline uint64_t msc64(struct sna_crtc *sna_crtc, uint32_t seq)
303428d7b3dSmrg{
304428d7b3dSmrg	if (seq < sna_crtc->last_seq) {
305428d7b3dSmrg		if (sna_crtc->last_seq - seq > 0x40000000) {
306428d7b3dSmrg			sna_crtc->wrap_seq++;
307428d7b3dSmrg			DBG(("%s: pipe=%d wrapped; was %u, now %u, wraps=%u\n",
308428d7b3dSmrg			     __FUNCTION__, sna_crtc->pipe,
309428d7b3dSmrg			     sna_crtc->last_seq, seq, sna_crtc->wrap_seq));
310428d7b3dSmrg		} else  {
311428d7b3dSmrg			ERR(("%s: pipe=%d msc went backwards; was %u, now %u\n",
312428d7b3dSmrg			     __FUNCTION__, sna_crtc->pipe, sna_crtc->last_seq, seq));
313428d7b3dSmrg			seq = sna_crtc->last_seq;
314428d7b3dSmrg		}
315428d7b3dSmrg	}
316428d7b3dSmrg	sna_crtc->last_seq = seq;
317428d7b3dSmrg	return (uint64_t)sna_crtc->wrap_seq << 32 | seq;
318428d7b3dSmrg}
319428d7b3dSmrg
320428d7b3dSmrguint64_t sna_crtc_record_swap(xf86CrtcPtr crtc,
321428d7b3dSmrg			      int tv_sec, int tv_usec, unsigned seq)
322428d7b3dSmrg{
323428d7b3dSmrg	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
324428d7b3dSmrg	assert(sna_crtc);
325428d7b3dSmrg	DBG(("%s: recording last swap on pipe=%d, frame %d, time %d.%06d\n",
326428d7b3dSmrg	     __FUNCTION__, sna_crtc->pipe, seq, tv_sec, tv_usec));
327428d7b3dSmrg	sna_crtc->swap.tv_sec = tv_sec;
328428d7b3dSmrg	sna_crtc->swap.tv_usec = tv_usec;
329428d7b3dSmrg	return sna_crtc->swap.msc = msc64(sna_crtc, seq);
330428d7b3dSmrg}
331428d7b3dSmrg
332428d7b3dSmrgconst struct ust_msc *sna_crtc_last_swap(xf86CrtcPtr crtc)
333428d7b3dSmrg{
334428d7b3dSmrg	static struct ust_msc zero;
335428d7b3dSmrg
336428d7b3dSmrg	if (crtc == NULL) {
337428d7b3dSmrg		return &zero;
338428d7b3dSmrg	} else {
339428d7b3dSmrg		struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
340428d7b3dSmrg		assert(sna_crtc);
341428d7b3dSmrg		return &sna_crtc->swap;
342428d7b3dSmrg	}
343428d7b3dSmrg}
344428d7b3dSmrg
345428d7b3dSmrgxf86CrtcPtr sna_mode_first_crtc(struct sna *sna)
346428d7b3dSmrg{
347428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
348428d7b3dSmrg	if (sna->mode.num_real_crtc)
349428d7b3dSmrg		return config->crtc[0];
350428d7b3dSmrg	else
351428d7b3dSmrg		return NULL;
352428d7b3dSmrg}
353428d7b3dSmrg
354428d7b3dSmrg#ifndef NDEBUG
355428d7b3dSmrgstatic void gem_close(int fd, uint32_t handle);
356428d7b3dSmrgstatic void assert_scanout(struct kgem *kgem, struct kgem_bo *bo,
357428d7b3dSmrg			   int width, int height)
358428d7b3dSmrg{
359428d7b3dSmrg	struct drm_mode_fb_cmd info;
360428d7b3dSmrg
361428d7b3dSmrg	assert(bo->scanout);
362428d7b3dSmrg
363428d7b3dSmrg	VG_CLEAR(info);
364428d7b3dSmrg	info.fb_id = fb_id(bo);
365428d7b3dSmrg
366428d7b3dSmrg	assert(drmIoctl(kgem->fd, DRM_IOCTL_MODE_GETFB, &info) == 0);
367428d7b3dSmrg	gem_close(kgem->fd, info.handle);
368428d7b3dSmrg
369428d7b3dSmrg	assert(width == info.width && height == info.height);
370428d7b3dSmrg}
371428d7b3dSmrg#else
372428d7b3dSmrg#define assert_scanout(k, b, w, h)
373428d7b3dSmrg#endif
374428d7b3dSmrg
375428d7b3dSmrgstatic unsigned get_fb(struct sna *sna, struct kgem_bo *bo,
376428d7b3dSmrg		       int width, int height)
377428d7b3dSmrg{
378428d7b3dSmrg	ScrnInfoPtr scrn = sna->scrn;
379428d7b3dSmrg	struct drm_mode_fb_cmd arg;
380428d7b3dSmrg
381428d7b3dSmrg	assert(bo->refcnt);
382428d7b3dSmrg	assert(bo->proxy == NULL);
383428d7b3dSmrg	assert(!bo->snoop);
384428d7b3dSmrg	assert(8*bo->pitch >= width * scrn->bitsPerPixel);
385428d7b3dSmrg	assert(height * bo->pitch <= kgem_bo_size(bo)); /* XXX crtc offset */
386428d7b3dSmrg	if (fb_id(bo)) {
387428d7b3dSmrg		DBG(("%s: reusing fb=%d for handle=%d\n",
388428d7b3dSmrg		     __FUNCTION__, fb_id(bo), bo->handle));
389428d7b3dSmrg		assert_scanout(&sna->kgem, bo, width, height);
390428d7b3dSmrg		return fb_id(bo);
391428d7b3dSmrg	}
392428d7b3dSmrg
393428d7b3dSmrg	DBG(("%s: create fb %dx%d@%d/%d\n",
394428d7b3dSmrg	     __FUNCTION__, width, height, scrn->depth, scrn->bitsPerPixel));
395428d7b3dSmrg
396428d7b3dSmrg	assert(bo->tiling != I915_TILING_Y);
397428d7b3dSmrg	assert((bo->pitch & 63) == 0);
398428d7b3dSmrg
399428d7b3dSmrg	VG_CLEAR(arg);
400428d7b3dSmrg	arg.width = width;
401428d7b3dSmrg	arg.height = height;
402428d7b3dSmrg	arg.pitch = bo->pitch;
403428d7b3dSmrg	arg.bpp = scrn->bitsPerPixel;
404428d7b3dSmrg	arg.depth = scrn->depth;
405428d7b3dSmrg	arg.handle = bo->handle;
406428d7b3dSmrg
407428d7b3dSmrg	assert(sna->scrn->vtSema); /* must be master */
408428d7b3dSmrg	if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_ADDFB, &arg)) {
409428d7b3dSmrg		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
410428d7b3dSmrg			   "%s: failed to add fb: %dx%d depth=%d, bpp=%d, pitch=%d: %d\n",
411428d7b3dSmrg			   __FUNCTION__, width, height,
412428d7b3dSmrg			   scrn->depth, scrn->bitsPerPixel, bo->pitch, errno);
413428d7b3dSmrg		return 0;
414428d7b3dSmrg	}
415428d7b3dSmrg	assert(arg.fb_id != 0);
416428d7b3dSmrg
417428d7b3dSmrg	DBG(("%s: attached fb=%d to handle=%d\n",
418428d7b3dSmrg	     __FUNCTION__, arg.fb_id, arg.handle));
419428d7b3dSmrg
420428d7b3dSmrg	bo->scanout = true;
421428d7b3dSmrg	return bo->delta = arg.fb_id;
422428d7b3dSmrg}
423428d7b3dSmrg
424428d7b3dSmrgstatic uint32_t gem_create(int fd, int size)
425428d7b3dSmrg{
426428d7b3dSmrg	struct drm_i915_gem_create create;
427428d7b3dSmrg
428428d7b3dSmrg	assert((size & 4095) == 0);
429428d7b3dSmrg
430428d7b3dSmrg	VG_CLEAR(create);
431428d7b3dSmrg	create.handle = 0;
432428d7b3dSmrg	create.size = size;
433428d7b3dSmrg	(void)drmIoctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create);
434428d7b3dSmrg
435428d7b3dSmrg	return create.handle;
436428d7b3dSmrg}
437428d7b3dSmrg
438428d7b3dSmrgstatic void *gem_mmap(int fd, int handle, int size)
439428d7b3dSmrg{
440428d7b3dSmrg	struct drm_i915_gem_mmap_gtt mmap_arg;
441428d7b3dSmrg	void *ptr;
442428d7b3dSmrg
443428d7b3dSmrg	VG_CLEAR(mmap_arg);
444428d7b3dSmrg	mmap_arg.handle = handle;
445428d7b3dSmrg	if (drmIoctl(fd, DRM_IOCTL_I915_GEM_MMAP_GTT, &mmap_arg))
446428d7b3dSmrg		return NULL;
447428d7b3dSmrg
448428d7b3dSmrg	ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mmap_arg.offset);
449428d7b3dSmrg	if (ptr == MAP_FAILED)
450428d7b3dSmrg		return NULL;
451428d7b3dSmrg
452428d7b3dSmrg	return ptr;
453428d7b3dSmrg}
454428d7b3dSmrg
455428d7b3dSmrgstatic void gem_close(int fd, uint32_t handle)
456428d7b3dSmrg{
457428d7b3dSmrg	struct drm_gem_close close;
458428d7b3dSmrg
459428d7b3dSmrg	VG_CLEAR(close);
460428d7b3dSmrg	close.handle = handle;
461428d7b3dSmrg	(void)drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &close);
462428d7b3dSmrg}
463428d7b3dSmrg
464428d7b3dSmrg#define BACKLIGHT_NAME             "Backlight"
465428d7b3dSmrg#define BACKLIGHT_DEPRECATED_NAME  "BACKLIGHT"
466428d7b3dSmrgstatic Atom backlight_atom, backlight_deprecated_atom;
467428d7b3dSmrg
468428d7b3dSmrg#if HAVE_UDEV
469428d7b3dSmrgstatic void
470428d7b3dSmrgsna_backlight_uevent(int fd, void *closure)
471428d7b3dSmrg{
472428d7b3dSmrg	struct sna *sna = closure;
473428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
474428d7b3dSmrg	int i;
475428d7b3dSmrg
476428d7b3dSmrg	DBG(("%s()\n", __FUNCTION__));
477428d7b3dSmrg
478428d7b3dSmrg	/* Drain the event queue */
479428d7b3dSmrg	while (event_pending(fd)) {
480428d7b3dSmrg		struct udev_device *dev;
481428d7b3dSmrg
482428d7b3dSmrg		DBG(("%s: waiting for uevent\n", __FUNCTION__));
483428d7b3dSmrg		dev = udev_monitor_receive_device(sna->mode.backlight_monitor);
484428d7b3dSmrg		if (dev == NULL)
485428d7b3dSmrg			break;
486428d7b3dSmrg
487428d7b3dSmrg		udev_device_unref(dev);
488428d7b3dSmrg	}
489428d7b3dSmrg
490428d7b3dSmrg	/* Query all backlights for any changes */
491428d7b3dSmrg	DBG(("%s: probing backlights for changes\n", __FUNCTION__));
492428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_output; i++) {
493428d7b3dSmrg		xf86OutputPtr output = config->output[i];
494428d7b3dSmrg		struct sna_output *sna_output = to_sna_output(output);
495428d7b3dSmrg		int val;
496428d7b3dSmrg
497428d7b3dSmrg		if (sna_output->dpms_mode != DPMSModeOn)
498428d7b3dSmrg			continue;
499428d7b3dSmrg
500428d7b3dSmrg		assert(output->randr_output);
501428d7b3dSmrg
502428d7b3dSmrg		val = backlight_get(&sna_output->backlight);
503428d7b3dSmrg		if (val < 0)
504428d7b3dSmrg			continue;
505428d7b3dSmrg		DBG(("%s(%s): backlight '%s' was %d, now %d\n",
506428d7b3dSmrg		     __FUNCTION__, output->name, sna_output->backlight.iface,
507428d7b3dSmrg		     sna_output->backlight_active_level, val));
508428d7b3dSmrg
509428d7b3dSmrg		if (val == sna_output->backlight_active_level)
510428d7b3dSmrg			continue;
511428d7b3dSmrg
512428d7b3dSmrg		sna_output->backlight_active_level = val;
513428d7b3dSmrg
514428d7b3dSmrg		if (output->randr_output) {
515428d7b3dSmrg			DBG(("%s(%s): sending change notification\n", __FUNCTION__, output->name));
516428d7b3dSmrg			RRChangeOutputProperty(output->randr_output,
517428d7b3dSmrg					       backlight_atom, XA_INTEGER,
518428d7b3dSmrg					       32, PropModeReplace, 1, &val,
519428d7b3dSmrg					       TRUE, FALSE);
520428d7b3dSmrg			RRChangeOutputProperty(output->randr_output,
521428d7b3dSmrg					       backlight_deprecated_atom, XA_INTEGER,
522428d7b3dSmrg					       32, PropModeReplace, 1, &val,
523428d7b3dSmrg					       TRUE, FALSE);
524428d7b3dSmrg		}
525428d7b3dSmrg	}
526428d7b3dSmrg}
527428d7b3dSmrg
528428d7b3dSmrgstatic void sna_backlight_pre_init(struct sna *sna)
529428d7b3dSmrg{
530428d7b3dSmrg	struct udev *u;
531428d7b3dSmrg	struct udev_monitor *mon;
532428d7b3dSmrg
533428d7b3dSmrg#if !USE_BACKLIGHT
534428d7b3dSmrg	return;
535428d7b3dSmrg#endif
536428d7b3dSmrg
537428d7b3dSmrg	u = udev_new();
538428d7b3dSmrg	if (!u)
539428d7b3dSmrg		return;
540428d7b3dSmrg
541428d7b3dSmrg	mon = udev_monitor_new_from_netlink(u, "udev");
542428d7b3dSmrg	if (!mon)
543428d7b3dSmrg		goto free_udev;
544428d7b3dSmrg
545428d7b3dSmrg	if (udev_monitor_filter_add_match_subsystem_devtype(mon, "backlight", NULL))
546428d7b3dSmrg		goto free_monitor;
547428d7b3dSmrg
548428d7b3dSmrg	if (udev_monitor_enable_receiving(mon))
549428d7b3dSmrg		goto free_monitor;
550428d7b3dSmrg
551428d7b3dSmrg	sna->mode.backlight_handler =
552428d7b3dSmrg		xf86AddGeneralHandler(udev_monitor_get_fd(mon),
553428d7b3dSmrg				      sna_backlight_uevent, sna);
554428d7b3dSmrg	if (!sna->mode.backlight_handler)
555428d7b3dSmrg		goto free_monitor;
556428d7b3dSmrg
557428d7b3dSmrg	DBG(("%s: installed backlight monitor\n", __FUNCTION__));
558428d7b3dSmrg	sna->mode.backlight_monitor = mon;
559428d7b3dSmrg
560428d7b3dSmrg	return;
561428d7b3dSmrg
562428d7b3dSmrgfree_monitor:
563428d7b3dSmrg	udev_monitor_unref(mon);
564428d7b3dSmrgfree_udev:
565428d7b3dSmrg	udev_unref(u);
566428d7b3dSmrg}
567428d7b3dSmrg
568428d7b3dSmrgstatic void sna_backlight_drain_uevents(struct sna *sna)
569428d7b3dSmrg{
570428d7b3dSmrg	if (sna->mode.backlight_monitor == NULL)
571428d7b3dSmrg		return;
572428d7b3dSmrg
573428d7b3dSmrg	sna_backlight_uevent(udev_monitor_get_fd(sna->mode.backlight_monitor),
574428d7b3dSmrg			     sna);
575428d7b3dSmrg}
576428d7b3dSmrg
577428d7b3dSmrgstatic void sna_backlight_close(struct sna *sna)
578428d7b3dSmrg{
579428d7b3dSmrg	struct udev *u;
580428d7b3dSmrg
581428d7b3dSmrg	if (sna->mode.backlight_handler == NULL)
582428d7b3dSmrg		return;
583428d7b3dSmrg
584428d7b3dSmrg	xf86RemoveGeneralHandler(sna->mode.backlight_handler);
585428d7b3dSmrg
586428d7b3dSmrg	u = udev_monitor_get_udev(sna->mode.backlight_monitor);
587428d7b3dSmrg	udev_monitor_unref(sna->mode.backlight_monitor);
588428d7b3dSmrg	udev_unref(u);
589428d7b3dSmrg
590428d7b3dSmrg	sna->mode.backlight_handler = NULL;
591428d7b3dSmrg	sna->mode.backlight_monitor = NULL;
592428d7b3dSmrg}
593428d7b3dSmrg#else
594428d7b3dSmrgstatic void sna_backlight_pre_init(struct sna *sna) { }
595428d7b3dSmrgstatic void sna_backlight_drain_uevents(struct sna *sna) { }
596428d7b3dSmrgstatic void sna_backlight_close(struct sna *sna) { }
597428d7b3dSmrg#endif
598428d7b3dSmrg
599428d7b3dSmrgstatic void
600428d7b3dSmrgsna_output_backlight_disable(struct sna_output *sna_output)
601428d7b3dSmrg{
602428d7b3dSmrg	xf86OutputPtr output = sna_output->base;
603428d7b3dSmrg
604428d7b3dSmrg	xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
605428d7b3dSmrg		   "Failed to set backlight %s for output %s, disabling\n",
606428d7b3dSmrg		   sna_output->backlight.iface, output->name);
607428d7b3dSmrg	backlight_disable(&sna_output->backlight);
608428d7b3dSmrg	if (output->randr_output) {
609428d7b3dSmrg		RRDeleteOutputProperty(output->randr_output, backlight_atom);
610428d7b3dSmrg		RRDeleteOutputProperty(output->randr_output, backlight_deprecated_atom);
611428d7b3dSmrg	}
612428d7b3dSmrg}
613428d7b3dSmrg
614428d7b3dSmrgstatic int
615428d7b3dSmrgsna_output_backlight_set(struct sna_output *sna_output, int level)
616428d7b3dSmrg{
617428d7b3dSmrg	int ret = 0;
618428d7b3dSmrg
619428d7b3dSmrg	DBG(("%s(%s) level=%d, max=%d\n", __FUNCTION__,
620428d7b3dSmrg	     sna_output->base->name, level, sna_output->backlight.max));
621428d7b3dSmrg
622428d7b3dSmrg	if (backlight_set(&sna_output->backlight, level)) {
623428d7b3dSmrg		sna_output_backlight_disable(sna_output);
624428d7b3dSmrg		ret = -1;
625428d7b3dSmrg	}
626428d7b3dSmrg
627428d7b3dSmrg	/* Consume the uevent notification now so that we don't misconstrue
628428d7b3dSmrg	 * the change latter when we wake up and the output is in a different
629428d7b3dSmrg	 * state.
630428d7b3dSmrg	 */
631428d7b3dSmrg	sna_backlight_drain_uevents(to_sna(sna_output->base->scrn));
632428d7b3dSmrg	return ret;
633428d7b3dSmrg}
634428d7b3dSmrg
635428d7b3dSmrgstatic void
636428d7b3dSmrgsna_output_backlight_off(struct sna_output *sna_output)
637428d7b3dSmrg{
638428d7b3dSmrg	DBG(("%s(%s)\n", __FUNCTION__, sna_output->base->name));
639428d7b3dSmrg	backlight_off(&sna_output->backlight);
640428d7b3dSmrg	sna_output_backlight_set(sna_output, 0);
641428d7b3dSmrg}
642428d7b3dSmrg
643428d7b3dSmrgstatic void
644428d7b3dSmrgsna_output_backlight_on(struct sna_output *sna_output)
645428d7b3dSmrg{
646428d7b3dSmrg	DBG(("%s(%s)\n", __FUNCTION__, sna_output->base->name));
647428d7b3dSmrg	sna_output_backlight_set(sna_output,
648428d7b3dSmrg				 sna_output->backlight_active_level);
649428d7b3dSmrg	if (backlight_on(&sna_output->backlight) < 0)
650428d7b3dSmrg		sna_output_backlight_disable(sna_output);
651428d7b3dSmrg}
652428d7b3dSmrg
653428d7b3dSmrgstatic int
654428d7b3dSmrgsna_output_backlight_get(xf86OutputPtr output)
655428d7b3dSmrg{
656428d7b3dSmrg	struct sna_output *sna_output = output->driver_private;
657428d7b3dSmrg	int level = backlight_get(&sna_output->backlight);
658428d7b3dSmrg	DBG(("%s(%s) level=%d, max=%d\n", __FUNCTION__,
659428d7b3dSmrg	     output->name, level, sna_output->backlight.max));
660428d7b3dSmrg	return level;
661428d7b3dSmrg}
662428d7b3dSmrg
663428d7b3dSmrgstatic char *
664428d7b3dSmrghas_user_backlight_override(xf86OutputPtr output)
665428d7b3dSmrg{
666428d7b3dSmrg	struct sna *sna = to_sna(output->scrn);
667428d7b3dSmrg	const char *str;
668428d7b3dSmrg
669428d7b3dSmrg	str = xf86GetOptValString(sna->Options, OPTION_BACKLIGHT);
670428d7b3dSmrg	if (str == NULL)
671428d7b3dSmrg		return NULL;
672428d7b3dSmrg
673428d7b3dSmrg	DBG(("%s(%s) requested %s\n", __FUNCTION__, output->name, str));
674428d7b3dSmrg	if (*str == '\0')
675428d7b3dSmrg		return (char *)str;
676428d7b3dSmrg
677428d7b3dSmrg	if (backlight_exists(str) == BL_NONE) {
678428d7b3dSmrg		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
679428d7b3dSmrg			   "Unrecognised backlight control interface '%s'\n",
680428d7b3dSmrg			   str);
681428d7b3dSmrg		return NULL;
682428d7b3dSmrg	}
683428d7b3dSmrg
684428d7b3dSmrg	return strdup(str);
685428d7b3dSmrg}
686428d7b3dSmrg
687428d7b3dSmrgstatic void
688428d7b3dSmrgsna_output_backlight_init(xf86OutputPtr output)
689428d7b3dSmrg{
690428d7b3dSmrg	struct sna_output *sna_output = output->driver_private;
691428d7b3dSmrg	struct pci_device *pci;
692428d7b3dSmrg	MessageType from;
693428d7b3dSmrg	char *best_iface;
694428d7b3dSmrg
695428d7b3dSmrg#if !USE_BACKLIGHT
696428d7b3dSmrg	return;
697428d7b3dSmrg#endif
698428d7b3dSmrg
699428d7b3dSmrg	from = X_CONFIG;
700428d7b3dSmrg	best_iface = has_user_backlight_override(output);
701428d7b3dSmrg	if (best_iface)
702428d7b3dSmrg		goto done;
703428d7b3dSmrg
704428d7b3dSmrg	/* XXX detect right backlight for multi-GPU/panels */
705428d7b3dSmrg	from = X_PROBED;
706428d7b3dSmrg	pci = xf86GetPciInfoForEntity(to_sna(output->scrn)->pEnt->index);
707428d7b3dSmrg	if (pci != NULL)
708428d7b3dSmrg		best_iface = backlight_find_for_device(pci);
709428d7b3dSmrg
710428d7b3dSmrgdone:
711428d7b3dSmrg	DBG(("%s(%s) opening backlight %s\n", __FUNCTION__,
712428d7b3dSmrg	     output->name, best_iface ?: "none"));
713428d7b3dSmrg	sna_output->backlight_active_level =
714428d7b3dSmrg		backlight_open(&sna_output->backlight, best_iface);
715428d7b3dSmrg	DBG(("%s(%s): initial backlight value %d\n",
716428d7b3dSmrg	     __FUNCTION__, output->name, sna_output->backlight_active_level));
717428d7b3dSmrg	if (sna_output->backlight_active_level < 0)
718428d7b3dSmrg		return;
719428d7b3dSmrg
720428d7b3dSmrg	switch (sna_output->backlight.type) {
721428d7b3dSmrg	case BL_FIRMWARE: best_iface = (char *)"firmware"; break;
722428d7b3dSmrg	case BL_PLATFORM: best_iface = (char *)"platform"; break;
723428d7b3dSmrg	case BL_RAW: best_iface = (char *)"raw"; break;
724428d7b3dSmrg	default: best_iface = (char *)"unknown"; break;
725428d7b3dSmrg	}
726428d7b3dSmrg	xf86DrvMsg(output->scrn->scrnIndex, from,
727428d7b3dSmrg		   "Found backlight control interface %s (type '%s') for output %s\n",
728428d7b3dSmrg		   sna_output->backlight.iface, best_iface, output->name);
729428d7b3dSmrg}
730428d7b3dSmrg
731428d7b3dSmrgstatic char *canonical_kmode_name(const struct drm_mode_modeinfo *kmode)
732428d7b3dSmrg{
733428d7b3dSmrg	char tmp[32], *buf;
734428d7b3dSmrg	int len;
735428d7b3dSmrg
736428d7b3dSmrg	len = sprintf(tmp, "%dx%d%s",
737428d7b3dSmrg		      kmode->hdisplay, kmode->vdisplay,
738428d7b3dSmrg		      kmode->flags & V_INTERLACE ? "i" : "");
739428d7b3dSmrg	if ((unsigned)len >= sizeof(tmp))
740428d7b3dSmrg		return NULL;
741428d7b3dSmrg
742428d7b3dSmrg	buf = malloc(len + 1);
743428d7b3dSmrg	if (buf == NULL)
744428d7b3dSmrg		return NULL;
745428d7b3dSmrg
746428d7b3dSmrg	return memcpy(buf, tmp, len + 1);
747428d7b3dSmrg}
748428d7b3dSmrg
749428d7b3dSmrgstatic char *get_kmode_name(const struct drm_mode_modeinfo *kmode)
750428d7b3dSmrg{
751428d7b3dSmrg	if (*kmode->name == '\0')
752428d7b3dSmrg		return canonical_kmode_name(kmode);
753428d7b3dSmrg
754428d7b3dSmrg	return strdup(kmode->name);
755428d7b3dSmrg}
756428d7b3dSmrg
757428d7b3dSmrgstatic DisplayModePtr
758428d7b3dSmrgmode_from_kmode(ScrnInfoPtr scrn,
759428d7b3dSmrg		const struct drm_mode_modeinfo *kmode,
760428d7b3dSmrg		DisplayModePtr mode)
761428d7b3dSmrg{
762428d7b3dSmrg	DBG(("kmode: %s, clock=%d, %d %d %d %d %d, %d %d %d %d %d, flags=%x, type=%x\n",
763428d7b3dSmrg	     kmode->name, kmode->clock,
764428d7b3dSmrg	     kmode->hdisplay, kmode->hsync_start, kmode->hsync_end, kmode->htotal, kmode->hskew,
765428d7b3dSmrg	     kmode->vdisplay, kmode->vsync_start, kmode->vsync_end, kmode->vtotal, kmode->vscan,
766428d7b3dSmrg	     kmode->flags, kmode->type));
767428d7b3dSmrg
768428d7b3dSmrg	mode->status = MODE_OK;
769428d7b3dSmrg
770428d7b3dSmrg	mode->Clock = kmode->clock;
771428d7b3dSmrg
772428d7b3dSmrg	mode->HDisplay = kmode->hdisplay;
773428d7b3dSmrg	mode->HSyncStart = kmode->hsync_start;
774428d7b3dSmrg	mode->HSyncEnd = kmode->hsync_end;
775428d7b3dSmrg	mode->HTotal = kmode->htotal;
776428d7b3dSmrg	mode->HSkew = kmode->hskew;
777428d7b3dSmrg
778428d7b3dSmrg	mode->VDisplay = kmode->vdisplay;
779428d7b3dSmrg	mode->VSyncStart = kmode->vsync_start;
780428d7b3dSmrg	mode->VSyncEnd = kmode->vsync_end;
781428d7b3dSmrg	mode->VTotal = kmode->vtotal;
782428d7b3dSmrg	mode->VScan = kmode->vscan;
783428d7b3dSmrg
784428d7b3dSmrg	mode->Flags = kmode->flags;
785428d7b3dSmrg	mode->name = get_kmode_name(kmode);
786428d7b3dSmrg
787428d7b3dSmrg	if (kmode->type & DRM_MODE_TYPE_DRIVER)
788428d7b3dSmrg		mode->type = M_T_DRIVER;
789428d7b3dSmrg	if (kmode->type & DRM_MODE_TYPE_PREFERRED)
790428d7b3dSmrg		mode->type |= M_T_PREFERRED;
791428d7b3dSmrg
792428d7b3dSmrg	if (mode->status == MODE_OK && kmode->flags & ~KNOWN_MODE_FLAGS)
793428d7b3dSmrg		mode->status = MODE_BAD; /* unknown flags => unhandled */
794428d7b3dSmrg
795428d7b3dSmrg	xf86SetModeCrtc(mode, scrn->adjustFlags);
796428d7b3dSmrg	return mode;
797428d7b3dSmrg}
798428d7b3dSmrg
799428d7b3dSmrgstatic void
800428d7b3dSmrgmode_to_kmode(struct drm_mode_modeinfo *kmode, DisplayModePtr mode)
801428d7b3dSmrg{
802428d7b3dSmrg	memset(kmode, 0, sizeof(*kmode));
803428d7b3dSmrg
804428d7b3dSmrg	kmode->clock = mode->Clock;
805428d7b3dSmrg	kmode->hdisplay = mode->HDisplay;
806428d7b3dSmrg	kmode->hsync_start = mode->HSyncStart;
807428d7b3dSmrg	kmode->hsync_end = mode->HSyncEnd;
808428d7b3dSmrg	kmode->htotal = mode->HTotal;
809428d7b3dSmrg	kmode->hskew = mode->HSkew;
810428d7b3dSmrg
811428d7b3dSmrg	kmode->vdisplay = mode->VDisplay;
812428d7b3dSmrg	kmode->vsync_start = mode->VSyncStart;
813428d7b3dSmrg	kmode->vsync_end = mode->VSyncEnd;
814428d7b3dSmrg	kmode->vtotal = mode->VTotal;
815428d7b3dSmrg	kmode->vscan = mode->VScan;
816428d7b3dSmrg
817428d7b3dSmrg	kmode->flags = mode->Flags;
818428d7b3dSmrg	if (mode->name)
819428d7b3dSmrg		strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN);
820428d7b3dSmrg	kmode->name[DRM_DISPLAY_MODE_LEN-1] = 0;
821428d7b3dSmrg}
822428d7b3dSmrg
823428d7b3dSmrgstatic void
824428d7b3dSmrgsna_crtc_force_outputs_on(xf86CrtcPtr crtc)
825428d7b3dSmrg{
826428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
827428d7b3dSmrg	int i;
828428d7b3dSmrg
829428d7b3dSmrg	assert(to_sna_crtc(crtc));
830428d7b3dSmrg	DBG(("%s(pipe=%d), currently? %d\n", __FUNCTION__,
831428d7b3dSmrg	     to_sna_crtc(crtc)->pipe, to_sna_crtc(crtc)->dpms_mode));
832428d7b3dSmrg
833428d7b3dSmrg	/* DPMS handling by the kernel is inconsistent, so after setting a
834428d7b3dSmrg	 * mode on an output presume that we intend for it to be on, or that
835428d7b3dSmrg	 * the kernel will force it on.
836428d7b3dSmrg	 *
837428d7b3dSmrg	 * So force DPMS to be on for all connected outputs, and restore
838428d7b3dSmrg	 * the backlight.
839428d7b3dSmrg	 */
840428d7b3dSmrg	for (i = 0; i < config->num_output; i++) {
841428d7b3dSmrg		xf86OutputPtr output = config->output[i];
842428d7b3dSmrg
843428d7b3dSmrg		if (output->crtc != crtc)
844428d7b3dSmrg			continue;
845428d7b3dSmrg
846428d7b3dSmrg		output->funcs->dpms(output, DPMSModeOn);
847428d7b3dSmrg	}
848428d7b3dSmrg
849428d7b3dSmrg	to_sna_crtc(crtc)->dpms_mode = DPMSModeOn;
850428d7b3dSmrg#if XF86_CRTC_VERSION >= 3
851428d7b3dSmrg	crtc->active = TRUE;
852428d7b3dSmrg#endif
853428d7b3dSmrg}
854428d7b3dSmrg
855428d7b3dSmrgstatic void
856428d7b3dSmrgsna_crtc_force_outputs_off(xf86CrtcPtr crtc)
857428d7b3dSmrg{
858428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
859428d7b3dSmrg	int i;
860428d7b3dSmrg
861428d7b3dSmrg	assert(to_sna_crtc(crtc));
862428d7b3dSmrg	DBG(("%s(pipe=%d), currently? %d\n", __FUNCTION__,
863428d7b3dSmrg	     to_sna_crtc(crtc)->pipe, to_sna_crtc(crtc)->dpms_mode));
864428d7b3dSmrg
865428d7b3dSmrg	/* DPMS handling by the kernel is inconsistent, so after setting a
866428d7b3dSmrg	 * mode on an output presume that we intend for it to be on, or that
867428d7b3dSmrg	 * the kernel will force it on.
868428d7b3dSmrg	 *
869428d7b3dSmrg	 * So force DPMS to be on for all connected outputs, and restore
870428d7b3dSmrg	 * the backlight.
871428d7b3dSmrg	 */
872428d7b3dSmrg	for (i = 0; i < config->num_output; i++) {
873428d7b3dSmrg		xf86OutputPtr output = config->output[i];
874428d7b3dSmrg
875428d7b3dSmrg		if (output->crtc != crtc)
876428d7b3dSmrg			continue;
877428d7b3dSmrg
878428d7b3dSmrg		output->funcs->dpms(output, DPMSModeOff);
879428d7b3dSmrg	}
880428d7b3dSmrg
881428d7b3dSmrg	to_sna_crtc(crtc)->dpms_mode = DPMSModeOff;
882428d7b3dSmrg}
883428d7b3dSmrg
884428d7b3dSmrgstatic unsigned
885428d7b3dSmrgrotation_reduce(struct plane *p, unsigned rotation)
886428d7b3dSmrg{
887428d7b3dSmrg	unsigned unsupported_rotations = rotation & ~p->rotation.supported;
888428d7b3dSmrg
889428d7b3dSmrg	if (unsupported_rotations == 0)
890428d7b3dSmrg		return rotation;
891428d7b3dSmrg
892428d7b3dSmrg#define RR_Reflect_XY (RR_Reflect_X | RR_Reflect_Y)
893428d7b3dSmrg
894428d7b3dSmrg	if ((unsupported_rotations & RR_Reflect_XY) == RR_Reflect_XY &&
895428d7b3dSmrg	    p->rotation.supported& RR_Rotate_180) {
896428d7b3dSmrg		rotation &= ~RR_Reflect_XY;
897428d7b3dSmrg		rotation ^= RR_Rotate_180;
898428d7b3dSmrg	}
899428d7b3dSmrg
900428d7b3dSmrg	if ((unsupported_rotations & RR_Rotate_180) &&
901428d7b3dSmrg	    (p->rotation.supported& RR_Reflect_XY) == RR_Reflect_XY) {
902428d7b3dSmrg		rotation ^= RR_Reflect_XY;
903428d7b3dSmrg		rotation &= ~RR_Rotate_180;
904428d7b3dSmrg	}
905428d7b3dSmrg
906428d7b3dSmrg#undef RR_Reflect_XY
907428d7b3dSmrg
908428d7b3dSmrg	return rotation;
909428d7b3dSmrg}
910428d7b3dSmrg
911428d7b3dSmrgstatic bool
912428d7b3dSmrgrotation_set(struct sna *sna, struct plane *p, uint32_t desired)
913428d7b3dSmrg{
914428d7b3dSmrg#define LOCAL_IOCTL_MODE_OBJ_SETPROPERTY DRM_IOWR(0xbA, struct local_mode_obj_set_property)
915428d7b3dSmrg	struct local_mode_obj_set_property {
916428d7b3dSmrg		uint64_t value;
917428d7b3dSmrg		uint32_t prop_id;
918428d7b3dSmrg		uint32_t obj_id;
919428d7b3dSmrg		uint32_t obj_type;
920428d7b3dSmrg		uint32_t pad;
921428d7b3dSmrg	} prop;
922428d7b3dSmrg
923428d7b3dSmrg	if (desired == p->rotation.current)
924428d7b3dSmrg		return true;
925428d7b3dSmrg
926428d7b3dSmrg	if ((desired & p->rotation.supported) == 0) {
927428d7b3dSmrg		errno = EINVAL;
928428d7b3dSmrg		return false;
929428d7b3dSmrg	}
930428d7b3dSmrg
931428d7b3dSmrg	DBG(("%s: obj=%d, type=%x prop=%d set-rotation=%x\n",
932428d7b3dSmrg	     __FUNCTION__, p->id, LOCAL_MODE_OBJECT_PLANE, p->rotation.prop, desired));
933428d7b3dSmrg
934428d7b3dSmrg	assert(p->id);
935428d7b3dSmrg	assert(p->rotation.prop);
936428d7b3dSmrg
937428d7b3dSmrg	VG_CLEAR(prop);
938428d7b3dSmrg	prop.obj_id = p->id;
939428d7b3dSmrg	prop.obj_type = LOCAL_MODE_OBJECT_PLANE;
940428d7b3dSmrg	prop.prop_id = p->rotation.prop;
941428d7b3dSmrg	prop.value = desired;
942428d7b3dSmrg
943428d7b3dSmrg	if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_OBJ_SETPROPERTY, &prop))
944428d7b3dSmrg		return false;
945428d7b3dSmrg
946428d7b3dSmrg	p->rotation.current = desired;
947428d7b3dSmrg	return true;
948428d7b3dSmrg}
949428d7b3dSmrg
950428d7b3dSmrgstatic void
951428d7b3dSmrgrotation_reset(struct plane *p)
952428d7b3dSmrg{
953428d7b3dSmrg	if (p->rotation.prop == 0)
954428d7b3dSmrg		return;
955428d7b3dSmrg
956428d7b3dSmrg	p->rotation.current = 0;
957428d7b3dSmrg}
958428d7b3dSmrg
959428d7b3dSmrgbool sna_crtc_set_sprite_rotation(xf86CrtcPtr crtc, uint32_t rotation)
960428d7b3dSmrg{
961428d7b3dSmrg	assert(to_sna_crtc(crtc));
962428d7b3dSmrg	DBG(("%s: CRTC:%d [pipe=%d], sprite=%u set-rotation=%x\n",
963428d7b3dSmrg	     __FUNCTION__,
964428d7b3dSmrg	     to_sna_crtc(crtc)->id, to_sna_crtc(crtc)->pipe, to_sna_crtc(crtc)->sprite.id,
965428d7b3dSmrg	     rotation));
966428d7b3dSmrg
967428d7b3dSmrg	return rotation_set(to_sna(crtc->scrn),
968428d7b3dSmrg			    &to_sna_crtc(crtc)->sprite,
969428d7b3dSmrg			    rotation_reduce(&to_sna_crtc(crtc)->sprite, rotation));
970428d7b3dSmrg}
971428d7b3dSmrg
972428d7b3dSmrgstatic bool
973428d7b3dSmrgsna_crtc_apply(xf86CrtcPtr crtc)
974428d7b3dSmrg{
975428d7b3dSmrg	struct sna *sna = to_sna(crtc->scrn);
976428d7b3dSmrg	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
977428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
978428d7b3dSmrg	struct drm_mode_crtc arg;
979428d7b3dSmrg	uint32_t output_ids[32];
980428d7b3dSmrg	int output_count = 0;
981428d7b3dSmrg	int i;
982428d7b3dSmrg
983428d7b3dSmrg	DBG(("%s CRTC:%d [pipe=%d], handle=%d\n", __FUNCTION__, sna_crtc->id, sna_crtc->pipe, sna_crtc->bo->handle));
984428d7b3dSmrg	if (!sna_crtc->kmode.clock) {
985428d7b3dSmrg		ERR(("%s(CRTC:%d [pipe=%d]): attempted to set an invalid mode\n",
986428d7b3dSmrg		     __FUNCTION__, sna_crtc->id, sna_crtc->pipe));
987428d7b3dSmrg		return false;
988428d7b3dSmrg	}
989428d7b3dSmrg
990428d7b3dSmrg	assert(sna->mode.num_real_output < ARRAY_SIZE(output_ids));
991428d7b3dSmrg	sna_crtc_disable_cursor(sna, sna_crtc);
992428d7b3dSmrg
993428d7b3dSmrg	if (!rotation_set(sna, &sna_crtc->primary, sna_crtc->rotation)) {
994428d7b3dSmrg		ERR(("%s: set-primary-rotation failed (rotation-id=%d, rotation=%d) on CRTC:%d [pipe=%d], errno=%d\n",
995428d7b3dSmrg		     __FUNCTION__, sna_crtc->primary.rotation.prop, sna_crtc->rotation, sna_crtc->id, sna_crtc->pipe, errno));
996428d7b3dSmrg		sna_crtc->primary.rotation.supported &= ~sna_crtc->rotation;
997428d7b3dSmrg		return false;
998428d7b3dSmrg	}
999428d7b3dSmrg	DBG(("%s: CRTC:%d [pipe=%d] primary rotation set to %x\n",
1000428d7b3dSmrg	     __FUNCTION__, sna_crtc->id, sna_crtc->pipe, sna_crtc->rotation));
1001428d7b3dSmrg
1002428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_output; i++) {
1003428d7b3dSmrg		xf86OutputPtr output = config->output[i];
1004428d7b3dSmrg
1005428d7b3dSmrg		/* Make sure we mark the output as off (and save the backlight)
1006428d7b3dSmrg		 * before the kernel turns it off due to changing the pipe.
1007428d7b3dSmrg		 * This is necessary as the kernel may turn off the backlight
1008428d7b3dSmrg		 * and we lose track of the user settings.
1009428d7b3dSmrg		 */
1010428d7b3dSmrg		if (output->crtc == NULL)
1011428d7b3dSmrg			output->funcs->dpms(output, DPMSModeOff);
1012428d7b3dSmrg
1013428d7b3dSmrg		if (output->crtc != crtc)
1014428d7b3dSmrg			continue;
1015428d7b3dSmrg
1016428d7b3dSmrg		/* Skip over any hotunplugged outputs so that we can
1017428d7b3dSmrg		 * recover in cases where the previous mode is now
1018428d7b3dSmrg		 * only partially valid.
1019428d7b3dSmrg		 */
1020428d7b3dSmrg		if (!to_sna_output(output)->id)
1021428d7b3dSmrg			continue;
1022428d7b3dSmrg
1023428d7b3dSmrg		DBG(("%s: attaching output '%s' %d [%d] to crtc:%d (pipe %d) (possible crtc:%x, possible clones:%x)\n",
1024428d7b3dSmrg		     __FUNCTION__, output->name, i, to_connector_id(output),
1025428d7b3dSmrg		     sna_crtc->id, sna_crtc->pipe,
1026428d7b3dSmrg		     (uint32_t)output->possible_crtcs,
1027428d7b3dSmrg		     (uint32_t)output->possible_clones));
1028428d7b3dSmrg
1029428d7b3dSmrg		assert(output->possible_crtcs & (1 << sna_crtc->pipe) ||
1030428d7b3dSmrg		       is_zaphod(crtc->scrn));
1031428d7b3dSmrg
1032428d7b3dSmrg		output_ids[output_count] = to_connector_id(output);
1033428d7b3dSmrg		if (++output_count == ARRAY_SIZE(output_ids)) {
1034428d7b3dSmrg			DBG(("%s: too many outputs (%d) for me!\n",
1035428d7b3dSmrg			     __FUNCTION__, output_count));
1036428d7b3dSmrg			errno = EINVAL;
1037428d7b3dSmrg			return false;
1038428d7b3dSmrg		}
1039428d7b3dSmrg	}
1040428d7b3dSmrg	if (output_count == 0) {
1041428d7b3dSmrg		DBG(("%s: no outputs\n", __FUNCTION__));
1042428d7b3dSmrg		errno = EINVAL;
1043428d7b3dSmrg		return false;
1044428d7b3dSmrg	}
1045428d7b3dSmrg
1046428d7b3dSmrg	VG_CLEAR(arg);
1047428d7b3dSmrg	arg.crtc_id = sna_crtc->id;
1048428d7b3dSmrg	arg.fb_id = fb_id(sna_crtc->bo);
1049428d7b3dSmrg	if (sna_crtc->transform || sna_crtc->slave_pixmap) {
1050428d7b3dSmrg		arg.x = 0;
1051428d7b3dSmrg		arg.y = 0;
1052428d7b3dSmrg		sna_crtc->offset = 0;
1053428d7b3dSmrg	} else {
1054428d7b3dSmrg		arg.x = crtc->x;
1055428d7b3dSmrg		arg.y = crtc->y;
1056428d7b3dSmrg		sna_crtc->offset = arg.y << 16 | arg.x;
1057428d7b3dSmrg	}
1058428d7b3dSmrg	arg.set_connectors_ptr = (uintptr_t)output_ids;
1059428d7b3dSmrg	arg.count_connectors = output_count;
1060428d7b3dSmrg	arg.mode = sna_crtc->kmode;
1061428d7b3dSmrg	arg.mode_valid = 1;
1062428d7b3dSmrg
1063428d7b3dSmrg	DBG(("%s: applying crtc [%d, pipe=%d] mode=%dx%d+%d+%d@%d, fb=%d%s%s update to %d outputs [%d...]\n",
1064428d7b3dSmrg	     __FUNCTION__, sna_crtc->id, sna_crtc->pipe,
1065428d7b3dSmrg	     arg.mode.hdisplay,
1066428d7b3dSmrg	     arg.mode.vdisplay,
1067428d7b3dSmrg	     arg.x, arg.y,
1068428d7b3dSmrg	     arg.mode.clock,
1069428d7b3dSmrg	     arg.fb_id,
1070428d7b3dSmrg	     sna_crtc->shadow ? " [shadow]" : "",
1071428d7b3dSmrg	     sna_crtc->transform ? " [transformed]" : "",
1072428d7b3dSmrg	     output_count, output_count ? output_ids[0] : 0));
1073428d7b3dSmrg
1074428d7b3dSmrg	if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_SETCRTC, &arg))
1075428d7b3dSmrg		return false;
1076428d7b3dSmrg
1077428d7b3dSmrg	sna_crtc->mode_serial++;
1078428d7b3dSmrg	sna_crtc_force_outputs_on(crtc);
1079428d7b3dSmrg	return true;
1080428d7b3dSmrg}
1081428d7b3dSmrg
1082428d7b3dSmrgstatic bool overlap(const BoxRec *a, const BoxRec *b)
1083428d7b3dSmrg{
1084428d7b3dSmrg	if (a->x1 >= b->x2)
1085428d7b3dSmrg		return false;
1086428d7b3dSmrg	if (a->x2 <= b->x1)
1087428d7b3dSmrg		return false;
1088428d7b3dSmrg
1089428d7b3dSmrg	if (a->y1 >= b->y2)
1090428d7b3dSmrg		return false;
1091428d7b3dSmrg	if (a->y2 <= b->y1)
1092428d7b3dSmrg		return false;
1093428d7b3dSmrg
1094428d7b3dSmrg	return true;
1095428d7b3dSmrg}
1096428d7b3dSmrg
1097428d7b3dSmrgstatic bool wait_for_shadow(struct sna *sna,
1098428d7b3dSmrg			    struct sna_pixmap *priv,
1099428d7b3dSmrg			    unsigned flags)
1100428d7b3dSmrg{
1101428d7b3dSmrg	PixmapPtr pixmap = priv->pixmap;
1102428d7b3dSmrg	DamagePtr damage;
1103428d7b3dSmrg	struct kgem_bo *bo, *tmp;
1104428d7b3dSmrg	int flip_active;
1105428d7b3dSmrg	bool ret = true;
1106428d7b3dSmrg
1107428d7b3dSmrg	DBG(("%s: flags=%x, flips=%d, handle=%d, shadow=%d\n",
1108428d7b3dSmrg	     __FUNCTION__, flags, sna->mode.flip_active,
1109428d7b3dSmrg	     priv->gpu_bo->handle, sna->mode.shadow->handle));
1110428d7b3dSmrg
1111428d7b3dSmrg	assert(priv->move_to_gpu_data == sna);
1112428d7b3dSmrg	assert(sna->mode.shadow != priv->gpu_bo);
1113428d7b3dSmrg
1114428d7b3dSmrg	if (flags == 0 || pixmap != sna->front || !sna->mode.shadow_damage)
1115428d7b3dSmrg		goto done;
1116428d7b3dSmrg
1117428d7b3dSmrg	if ((flags & MOVE_WRITE) == 0) {
1118428d7b3dSmrg		if ((flags & __MOVE_SCANOUT) == 0) {
1119428d7b3dSmrg			struct sna_crtc *crtc;
1120428d7b3dSmrg
1121428d7b3dSmrg			list_for_each_entry(crtc, &sna->mode.shadow_crtc, shadow_link) {
1122428d7b3dSmrg				if (overlap(&sna->mode.shadow_region.extents,
1123428d7b3dSmrg					    &crtc->base->bounds)) {
1124428d7b3dSmrg					DrawableRec draw;
1125428d7b3dSmrg					RegionRec region;
1126428d7b3dSmrg
1127428d7b3dSmrg					draw.width = crtc->base->mode.HDisplay;
1128428d7b3dSmrg					draw.height = crtc->base->mode.VDisplay;
1129428d7b3dSmrg					draw.depth = sna->front->drawable.depth;
1130428d7b3dSmrg					draw.bitsPerPixel = sna->front->drawable.bitsPerPixel;
1131428d7b3dSmrg
1132428d7b3dSmrg					DBG(("%s: copying replaced CRTC: (%d, %d), (%d, %d), handle=%d\n",
1133428d7b3dSmrg					     __FUNCTION__,
1134428d7b3dSmrg					     crtc->base->bounds.x1,
1135428d7b3dSmrg					     crtc->base->bounds.y1,
1136428d7b3dSmrg					     crtc->base->bounds.x2,
1137428d7b3dSmrg					     crtc->base->bounds.y2,
1138428d7b3dSmrg					     crtc->client_bo->handle));
1139428d7b3dSmrg
1140428d7b3dSmrg					ret &= sna->render.copy_boxes(sna, GXcopy,
1141428d7b3dSmrg								      &draw, crtc->client_bo, -crtc->base->bounds.x1, -crtc->base->bounds.y1,
1142428d7b3dSmrg								      &pixmap->drawable, priv->gpu_bo, 0, 0,
1143428d7b3dSmrg								      &crtc->base->bounds, 1,
1144428d7b3dSmrg								      0);
1145428d7b3dSmrg
1146428d7b3dSmrg					region.extents = crtc->base->bounds;
1147428d7b3dSmrg					region.data = NULL;
1148428d7b3dSmrg					RegionSubtract(&sna->mode.shadow_region, &sna->mode.shadow_region, &region);
1149428d7b3dSmrg				}
1150428d7b3dSmrg			}
1151428d7b3dSmrg		}
1152428d7b3dSmrg
1153428d7b3dSmrg		return ret;
1154428d7b3dSmrg	}
1155428d7b3dSmrg
1156428d7b3dSmrg	assert(sna->mode.shadow_active);
1157428d7b3dSmrg
1158428d7b3dSmrg	damage = sna->mode.shadow_damage;
1159428d7b3dSmrg	sna->mode.shadow_damage = NULL;
1160428d7b3dSmrg
1161428d7b3dSmrg	flip_active = sna->mode.flip_active;
1162428d7b3dSmrg	if (flip_active) {
1163428d7b3dSmrg		struct sna_crtc *crtc;
1164428d7b3dSmrg		list_for_each_entry(crtc, &sna->mode.shadow_crtc, shadow_link)
1165428d7b3dSmrg			flip_active -= crtc->flip_pending;
1166428d7b3dSmrg		DBG(("%s: %d flips still pending, shadow flip_active=%d\n",
1167428d7b3dSmrg		     __FUNCTION__, sna->mode.flip_active, flip_active));
1168428d7b3dSmrg	}
1169428d7b3dSmrg	if (flip_active) {
1170428d7b3dSmrg		/* raw cmd to avoid setting wedged in the middle of an op */
1171428d7b3dSmrg		drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GEM_THROTTLE, 0);
1172428d7b3dSmrg		sna->kgem.need_throttle = false;
1173428d7b3dSmrg
1174428d7b3dSmrg		while (flip_active && sna_mode_wakeup(sna)) {
1175428d7b3dSmrg			struct sna_crtc *crtc;
1176428d7b3dSmrg
1177428d7b3dSmrg			flip_active = sna->mode.flip_active;
1178428d7b3dSmrg			list_for_each_entry(crtc, &sna->mode.shadow_crtc, shadow_link)
1179428d7b3dSmrg				flip_active -= crtc->flip_pending;
1180428d7b3dSmrg		}
1181428d7b3dSmrg		DBG(("%s: after waiting %d flips outstanding, flip_active=%d\n",
1182428d7b3dSmrg		     __FUNCTION__, sna->mode.flip_active, flip_active));
1183428d7b3dSmrg	}
1184428d7b3dSmrg
1185428d7b3dSmrg	bo = sna->mode.shadow;
1186428d7b3dSmrg	if (flip_active) {
1187428d7b3dSmrg		bo = kgem_create_2d(&sna->kgem,
1188428d7b3dSmrg				    pixmap->drawable.width,
1189428d7b3dSmrg				    pixmap->drawable.height,
1190428d7b3dSmrg				    pixmap->drawable.bitsPerPixel,
1191428d7b3dSmrg				    priv->gpu_bo->tiling,
1192428d7b3dSmrg				    CREATE_EXACT | CREATE_SCANOUT);
1193428d7b3dSmrg		if (bo != NULL) {
1194428d7b3dSmrg			DBG(("%s: replacing still-attached GPU bo handle=%d, flips=%d\n",
1195428d7b3dSmrg			     __FUNCTION__, priv->gpu_bo->tiling, sna->mode.flip_active));
1196428d7b3dSmrg
1197428d7b3dSmrg			RegionUninit(&sna->mode.shadow_region);
1198428d7b3dSmrg			sna->mode.shadow_region.extents.x1 = 0;
1199428d7b3dSmrg			sna->mode.shadow_region.extents.y1 = 0;
1200428d7b3dSmrg			sna->mode.shadow_region.extents.x2 = pixmap->drawable.width;
1201428d7b3dSmrg			sna->mode.shadow_region.extents.y2 = pixmap->drawable.height;
1202428d7b3dSmrg			sna->mode.shadow_region.data = NULL;
1203428d7b3dSmrg		} else {
1204428d7b3dSmrg			while (sna->mode.flip_active &&
1205428d7b3dSmrg			       sna_mode_wait_for_event(sna))
1206428d7b3dSmrg				sna_mode_wakeup(sna);
1207428d7b3dSmrg
1208428d7b3dSmrg			bo = sna->mode.shadow;
1209428d7b3dSmrg		}
1210428d7b3dSmrg	}
1211428d7b3dSmrg
1212428d7b3dSmrg	if (bo->refcnt > 1) {
1213428d7b3dSmrg		bo = kgem_create_2d(&sna->kgem,
1214428d7b3dSmrg				    pixmap->drawable.width,
1215428d7b3dSmrg				    pixmap->drawable.height,
1216428d7b3dSmrg				    pixmap->drawable.bitsPerPixel,
1217428d7b3dSmrg				    priv->gpu_bo->tiling,
1218428d7b3dSmrg				    CREATE_EXACT | CREATE_SCANOUT);
1219428d7b3dSmrg		if (bo != NULL) {
1220428d7b3dSmrg			DBG(("%s: replacing exported GPU bo\n",
1221428d7b3dSmrg			     __FUNCTION__));
1222428d7b3dSmrg
1223428d7b3dSmrg			RegionUninit(&sna->mode.shadow_region);
1224428d7b3dSmrg			sna->mode.shadow_region.extents.x1 = 0;
1225428d7b3dSmrg			sna->mode.shadow_region.extents.y1 = 0;
1226428d7b3dSmrg			sna->mode.shadow_region.extents.x2 = pixmap->drawable.width;
1227428d7b3dSmrg			sna->mode.shadow_region.extents.y2 = pixmap->drawable.height;
1228428d7b3dSmrg			sna->mode.shadow_region.data = NULL;
1229428d7b3dSmrg		} else
1230428d7b3dSmrg			bo = sna->mode.shadow;
1231428d7b3dSmrg	}
1232428d7b3dSmrg
1233428d7b3dSmrg	sna->mode.shadow_damage = damage;
1234428d7b3dSmrg
1235428d7b3dSmrg	RegionSubtract(&sna->mode.shadow_region,
1236428d7b3dSmrg		       &sna->mode.shadow_region,
1237428d7b3dSmrg		       &sna->mode.shadow_cancel);
1238428d7b3dSmrg
1239428d7b3dSmrg	while (!list_is_empty(&sna->mode.shadow_crtc)) {
1240428d7b3dSmrg		struct sna_crtc *crtc =
1241428d7b3dSmrg			list_first_entry(&sna->mode.shadow_crtc, struct sna_crtc, shadow_link);
1242428d7b3dSmrg		if (overlap(&crtc->base->bounds,
1243428d7b3dSmrg			    &sna->mode.shadow_region.extents)) {
1244428d7b3dSmrg			RegionRec region;
1245428d7b3dSmrg			DrawableRec draw;
1246428d7b3dSmrg
1247428d7b3dSmrg			draw.width = crtc->base->mode.HDisplay;
1248428d7b3dSmrg			draw.height = crtc->base->mode.VDisplay;
1249428d7b3dSmrg			draw.depth = sna->front->drawable.depth;
1250428d7b3dSmrg			draw.bitsPerPixel = sna->front->drawable.bitsPerPixel;
1251428d7b3dSmrg
1252428d7b3dSmrg			DBG(("%s: copying replaced CRTC: (%d, %d), (%d, %d), handle=%d\n",
1253428d7b3dSmrg			     __FUNCTION__,
1254428d7b3dSmrg			     crtc->base->bounds.x1,
1255428d7b3dSmrg			     crtc->base->bounds.y1,
1256428d7b3dSmrg			     crtc->base->bounds.x2,
1257428d7b3dSmrg			     crtc->base->bounds.y2,
1258428d7b3dSmrg			     crtc->client_bo->handle));
1259428d7b3dSmrg
1260428d7b3dSmrg			ret = sna->render.copy_boxes(sna, GXcopy,
1261428d7b3dSmrg						     &draw, crtc->client_bo, -crtc->base->bounds.x1, -crtc->base->bounds.y1,
1262428d7b3dSmrg						     &pixmap->drawable, bo, 0, 0,
1263428d7b3dSmrg						     &crtc->base->bounds, 1,
1264428d7b3dSmrg						     0);
1265428d7b3dSmrg
1266428d7b3dSmrg
1267428d7b3dSmrg			region.extents = crtc->base->bounds;
1268428d7b3dSmrg			region.data = NULL;
1269428d7b3dSmrg			RegionSubtract(&sna->mode.shadow_region, &sna->mode.shadow_region, &region);
1270428d7b3dSmrg		}
1271428d7b3dSmrg
1272428d7b3dSmrg		kgem_bo_destroy(&sna->kgem, crtc->client_bo);
1273428d7b3dSmrg		crtc->client_bo = NULL;
1274428d7b3dSmrg		list_del(&crtc->shadow_link);
1275428d7b3dSmrg	}
1276428d7b3dSmrg
1277428d7b3dSmrg	if (RegionNotEmpty(&sna->mode.shadow_region)) {
1278428d7b3dSmrg		DBG(("%s: copying existing GPU damage: %dx(%d, %d), (%d, %d)\n",
1279428d7b3dSmrg		     __FUNCTION__, region_num_rects(&sna->mode.shadow_region),
1280428d7b3dSmrg		     sna->mode.shadow_region.extents.x1,
1281428d7b3dSmrg		     sna->mode.shadow_region.extents.y1,
1282428d7b3dSmrg		     sna->mode.shadow_region.extents.x2,
1283428d7b3dSmrg		     sna->mode.shadow_region.extents.y2));
1284428d7b3dSmrg		ret = sna->render.copy_boxes(sna, GXcopy,
1285428d7b3dSmrg					     &pixmap->drawable, priv->gpu_bo, 0, 0,
1286428d7b3dSmrg					     &pixmap->drawable, bo, 0, 0,
1287428d7b3dSmrg					     region_rects(&sna->mode.shadow_region),
1288428d7b3dSmrg					     region_num_rects(&sna->mode.shadow_region),
1289428d7b3dSmrg					     0);
1290428d7b3dSmrg	}
1291428d7b3dSmrg
1292428d7b3dSmrg	if (priv->cow)
1293428d7b3dSmrg		sna_pixmap_undo_cow(sna, priv, 0);
1294428d7b3dSmrg
1295428d7b3dSmrg	sna_pixmap_unmap(pixmap, priv);
1296428d7b3dSmrg
1297428d7b3dSmrg	DBG(("%s: setting front pixmap to handle=%d\n", __FUNCTION__, bo->handle));
1298428d7b3dSmrg	tmp = priv->gpu_bo;
1299428d7b3dSmrg	priv->gpu_bo = bo;
1300428d7b3dSmrg	if (bo != sna->mode.shadow)
1301428d7b3dSmrg		kgem_bo_destroy(&sna->kgem, sna->mode.shadow);
1302428d7b3dSmrg	sna->mode.shadow = tmp;
1303428d7b3dSmrg
1304428d7b3dSmrg	sna_dri2_pixmap_update_bo(sna, pixmap, bo);
1305428d7b3dSmrg
1306428d7b3dSmrgdone:
1307428d7b3dSmrg	RegionEmpty(&sna->mode.shadow_cancel);
1308428d7b3dSmrg	RegionEmpty(&sna->mode.shadow_region);
1309428d7b3dSmrg	sna->mode.shadow_dirty = false;
1310428d7b3dSmrg
1311428d7b3dSmrg	priv->move_to_gpu_data = NULL;
1312428d7b3dSmrg	priv->move_to_gpu = NULL;
1313428d7b3dSmrg
1314428d7b3dSmrg	return ret;
1315428d7b3dSmrg}
1316428d7b3dSmrg
1317428d7b3dSmrgbool sna_pixmap_discard_shadow_damage(struct sna_pixmap *priv,
1318428d7b3dSmrg				      const RegionRec *region)
1319428d7b3dSmrg{
1320428d7b3dSmrg	struct sna *sna;
1321428d7b3dSmrg
1322428d7b3dSmrg	if (priv->move_to_gpu != wait_for_shadow)
1323428d7b3dSmrg		return false;
1324428d7b3dSmrg
1325428d7b3dSmrg	sna = priv->move_to_gpu_data;
1326428d7b3dSmrg	if (region) {
1327428d7b3dSmrg		DBG(("%s: discarding region %dx[(%d, %d), (%d, %d)] from damage %dx[(%d, %d], (%d, %d)]\n",
1328428d7b3dSmrg		     __FUNCTION__,
1329428d7b3dSmrg		     region_num_rects(region),
1330428d7b3dSmrg		     region->extents.x1, region->extents.y1,
1331428d7b3dSmrg		     region->extents.x2, region->extents.y2,
1332428d7b3dSmrg		     region_num_rects(&sna->mode.shadow_region),
1333428d7b3dSmrg		     sna->mode.shadow_region.extents.x1, sna->mode.shadow_region.extents.y1,
1334428d7b3dSmrg		     sna->mode.shadow_region.extents.x2, sna->mode.shadow_region.extents.y2));
1335428d7b3dSmrg
1336428d7b3dSmrg		RegionSubtract(&sna->mode.shadow_region,
1337428d7b3dSmrg			       &sna->mode.shadow_region,
1338428d7b3dSmrg			       (RegionPtr)region);
1339428d7b3dSmrg		RegionUnion(&sna->mode.shadow_cancel,
1340428d7b3dSmrg			    &sna->mode.shadow_cancel,
1341428d7b3dSmrg			    (RegionPtr)region);
1342428d7b3dSmrg	} else {
1343428d7b3dSmrg		DBG(("%s: discarding all damage %dx[(%d, %d], (%d, %d)]\n",
1344428d7b3dSmrg		     __FUNCTION__,
1345428d7b3dSmrg		     region_num_rects(&sna->mode.shadow_region),
1346428d7b3dSmrg		     sna->mode.shadow_region.extents.x1, sna->mode.shadow_region.extents.y1,
1347428d7b3dSmrg		     sna->mode.shadow_region.extents.x2, sna->mode.shadow_region.extents.y2));
1348428d7b3dSmrg		RegionEmpty(&sna->mode.shadow_region);
1349428d7b3dSmrg
1350428d7b3dSmrg		RegionUninit(&sna->mode.shadow_cancel);
1351428d7b3dSmrg		sna->mode.shadow_cancel.extents.x1 = 0;
1352428d7b3dSmrg		sna->mode.shadow_cancel.extents.y1 = 0;
1353428d7b3dSmrg		sna->mode.shadow_cancel.extents.x2 = sna->front->drawable.width;
1354428d7b3dSmrg		sna->mode.shadow_cancel.extents.y2 = sna->front->drawable.height;
1355428d7b3dSmrg		sna->mode.shadow_cancel.data = NULL;
1356428d7b3dSmrg	}
1357428d7b3dSmrg
1358428d7b3dSmrg	return RegionNil(&sna->mode.shadow_region);
1359428d7b3dSmrg}
1360428d7b3dSmrg
1361428d7b3dSmrgstatic bool sna_mode_enable_shadow(struct sna *sna)
1362428d7b3dSmrg{
1363428d7b3dSmrg	ScreenPtr screen = sna->scrn->pScreen;
1364428d7b3dSmrg
1365428d7b3dSmrg	DBG(("%s\n", __FUNCTION__));
1366428d7b3dSmrg	assert(sna->mode.shadow == NULL);
1367428d7b3dSmrg	assert(sna->mode.shadow_damage == NULL);
1368428d7b3dSmrg	assert(sna->mode.shadow_active == 0);
1369428d7b3dSmrg
1370428d7b3dSmrg	sna->mode.shadow_damage = DamageCreate(NULL, NULL,
1371428d7b3dSmrg					       DamageReportNone, TRUE,
1372428d7b3dSmrg					       screen, screen);
1373428d7b3dSmrg	if (!sna->mode.shadow_damage)
1374428d7b3dSmrg		return false;
1375428d7b3dSmrg
1376428d7b3dSmrg	DamageRegister(&sna->front->drawable, sna->mode.shadow_damage);
1377428d7b3dSmrg	return true;
1378428d7b3dSmrg}
1379428d7b3dSmrg
1380428d7b3dSmrgstatic void sna_mode_disable_shadow(struct sna *sna)
1381428d7b3dSmrg{
1382428d7b3dSmrg	struct sna_pixmap *priv;
1383428d7b3dSmrg
1384428d7b3dSmrg	if (!sna->mode.shadow_damage)
1385428d7b3dSmrg		return;
1386428d7b3dSmrg
1387428d7b3dSmrg	DBG(("%s\n", __FUNCTION__));
1388428d7b3dSmrg
1389428d7b3dSmrg	priv = sna_pixmap(sna->front);
1390428d7b3dSmrg	if (priv->move_to_gpu == wait_for_shadow)
1391428d7b3dSmrg		priv->move_to_gpu(sna, priv, 0);
1392428d7b3dSmrg
1393428d7b3dSmrg	DamageUnregister(&sna->front->drawable, sna->mode.shadow_damage);
1394428d7b3dSmrg	DamageDestroy(sna->mode.shadow_damage);
1395428d7b3dSmrg	sna->mode.shadow_damage = NULL;
1396428d7b3dSmrg
1397428d7b3dSmrg	if (sna->mode.shadow) {
1398428d7b3dSmrg		kgem_bo_destroy(&sna->kgem, sna->mode.shadow);
1399428d7b3dSmrg		sna->mode.shadow = NULL;
1400428d7b3dSmrg	}
1401428d7b3dSmrg
1402428d7b3dSmrg	assert(sna->mode.shadow_active == 0);
1403428d7b3dSmrg	sna->mode.shadow_dirty = false;
1404428d7b3dSmrg}
1405428d7b3dSmrg
1406428d7b3dSmrgstatic void sna_crtc_slave_damage(DamagePtr damage, RegionPtr region, void *closure)
1407428d7b3dSmrg{
1408428d7b3dSmrg	struct sna_crtc *crtc = closure;
1409428d7b3dSmrg	struct sna *sna = to_sna(crtc->base->scrn);
1410428d7b3dSmrg	RegionPtr scr;
1411428d7b3dSmrg
1412428d7b3dSmrg	DBG(("%s: pushing damage [(%d, %d), (%d, %d) x %d] to CRTC [pipe=%d] (%d, %d)\n",
1413428d7b3dSmrg	     __FUNCTION__,
1414428d7b3dSmrg	     region->extents.x1, region->extents.y1, region->extents.x2, region->extents.y2,
1415428d7b3dSmrg	     region_num_rects(region),
1416428d7b3dSmrg	     crtc->pipe, crtc->base->x, crtc->base->y));
1417428d7b3dSmrg
1418428d7b3dSmrg	assert(crtc->slave_damage == damage);
1419428d7b3dSmrg	assert(sna->mode.shadow_damage);
1420428d7b3dSmrg
1421428d7b3dSmrg	RegionTranslate(region, crtc->base->x, crtc->base->y);
1422428d7b3dSmrg	scr = DamageRegion(sna->mode.shadow_damage);
1423428d7b3dSmrg	RegionUnion(scr, scr, region);
1424428d7b3dSmrg	RegionTranslate(region, -crtc->base->x, -crtc->base->y);
1425428d7b3dSmrg}
1426428d7b3dSmrg
1427428d7b3dSmrgstatic bool sna_crtc_enable_shadow(struct sna *sna, struct sna_crtc *crtc)
1428428d7b3dSmrg{
1429428d7b3dSmrg	if (crtc->shadow) {
1430428d7b3dSmrg		assert(sna->mode.shadow_damage && sna->mode.shadow_active);
1431428d7b3dSmrg		return true;
1432428d7b3dSmrg	}
1433428d7b3dSmrg
1434428d7b3dSmrg	DBG(("%s: enabling for crtc %d\n", __FUNCTION__, crtc->id));
1435428d7b3dSmrg
1436428d7b3dSmrg	if (!sna->mode.shadow_active) {
1437428d7b3dSmrg		if (!sna_mode_enable_shadow(sna))
1438428d7b3dSmrg			return false;
1439428d7b3dSmrg		assert(sna->mode.shadow_damage);
1440428d7b3dSmrg		assert(sna->mode.shadow == NULL);
1441428d7b3dSmrg	}
1442428d7b3dSmrg
1443428d7b3dSmrg	if (crtc->slave_pixmap) {
1444428d7b3dSmrg		assert(crtc->slave_damage == NULL);
1445428d7b3dSmrg
1446428d7b3dSmrg		crtc->slave_damage = DamageCreate(sna_crtc_slave_damage, NULL,
1447428d7b3dSmrg						  DamageReportRawRegion, TRUE,
1448428d7b3dSmrg						  sna->scrn->pScreen, crtc);
1449428d7b3dSmrg		if (crtc->slave_damage == NULL) {
1450428d7b3dSmrg			if (!--sna->mode.shadow_active)
1451428d7b3dSmrg				sna_mode_disable_shadow(sna);
1452428d7b3dSmrg			return false;
1453428d7b3dSmrg		}
1454428d7b3dSmrg
1455428d7b3dSmrg		DamageRegister(&crtc->slave_pixmap->drawable, crtc->slave_damage);
1456428d7b3dSmrg	}
1457428d7b3dSmrg
1458428d7b3dSmrg	crtc->shadow = true;
1459428d7b3dSmrg	sna->mode.shadow_active++;
1460428d7b3dSmrg	return true;
1461428d7b3dSmrg}
1462428d7b3dSmrg
1463428d7b3dSmrgstatic void sna_crtc_disable_override(struct sna *sna, struct sna_crtc *crtc)
1464428d7b3dSmrg{
1465428d7b3dSmrg	if (crtc->client_bo == NULL)
1466428d7b3dSmrg		return;
1467428d7b3dSmrg
1468428d7b3dSmrg	if (!crtc->transform) {
1469428d7b3dSmrg		DrawableRec tmp;
1470428d7b3dSmrg
1471428d7b3dSmrg		tmp.width = crtc->base->mode.HDisplay;
1472428d7b3dSmrg		tmp.height = crtc->base->mode.VDisplay;
1473428d7b3dSmrg		tmp.depth = sna->front->drawable.depth;
1474428d7b3dSmrg		tmp.bitsPerPixel = sna->front->drawable.bitsPerPixel;
1475428d7b3dSmrg
1476428d7b3dSmrg		sna->render.copy_boxes(sna, GXcopy,
1477428d7b3dSmrg				       &tmp, crtc->client_bo, -crtc->base->bounds.x1, -crtc->base->bounds.y1,
1478428d7b3dSmrg				       &sna->front->drawable, __sna_pixmap_get_bo(sna->front), 0, 0,
1479428d7b3dSmrg				       &crtc->base->bounds, 1, 0);
1480428d7b3dSmrg		list_del(&crtc->shadow_link);
1481428d7b3dSmrg	}
1482428d7b3dSmrg	kgem_bo_destroy(&sna->kgem, crtc->client_bo);
1483428d7b3dSmrg	crtc->client_bo = NULL;
1484428d7b3dSmrg}
1485428d7b3dSmrg
1486428d7b3dSmrgstatic void sna_crtc_disable_shadow(struct sna *sna, struct sna_crtc *crtc)
1487428d7b3dSmrg{
1488428d7b3dSmrg	crtc->fallback_shadow = false;
1489428d7b3dSmrg	if (!crtc->shadow)
1490428d7b3dSmrg		return;
1491428d7b3dSmrg
1492428d7b3dSmrg	DBG(("%s: disabling for crtc %d\n", __FUNCTION__, crtc->id));
1493428d7b3dSmrg	assert(sna->mode.shadow_active > 0);
1494428d7b3dSmrg
1495428d7b3dSmrg	if (crtc->slave_damage) {
1496428d7b3dSmrg		assert(crtc->slave_pixmap);
1497428d7b3dSmrg		DamageUnregister(&crtc->slave_pixmap->drawable, crtc->slave_damage);
1498428d7b3dSmrg		DamageDestroy(crtc->slave_damage);
1499428d7b3dSmrg		crtc->slave_damage = NULL;
1500428d7b3dSmrg	}
1501428d7b3dSmrg
1502428d7b3dSmrg	sna_crtc_disable_override(sna, crtc);
1503428d7b3dSmrg
1504428d7b3dSmrg	if (!--sna->mode.shadow_active)
1505428d7b3dSmrg		sna_mode_disable_shadow(sna);
1506428d7b3dSmrg
1507428d7b3dSmrg	crtc->shadow = false;
1508428d7b3dSmrg}
1509428d7b3dSmrg
1510428d7b3dSmrgstatic void
1511428d7b3dSmrg__sna_crtc_disable(struct sna *sna, struct sna_crtc *sna_crtc)
1512428d7b3dSmrg{
1513428d7b3dSmrg	sna_crtc->mode_serial++;
1514428d7b3dSmrg
1515428d7b3dSmrg	sna_crtc_disable_cursor(sna, sna_crtc);
1516428d7b3dSmrg	rotation_set(sna, &sna_crtc->primary, RR_Rotate_0);
1517428d7b3dSmrg	sna_crtc_disable_shadow(sna, sna_crtc);
1518428d7b3dSmrg
1519428d7b3dSmrg	if (sna_crtc->bo) {
1520428d7b3dSmrg		assert(sna_crtc->bo->active_scanout);
1521428d7b3dSmrg		assert(sna_crtc->bo->refcnt >= sna_crtc->bo->active_scanout);
1522428d7b3dSmrg		sna_crtc->bo->active_scanout--;
1523428d7b3dSmrg		kgem_bo_destroy(&sna->kgem, sna_crtc->bo);
1524428d7b3dSmrg		sna_crtc->bo = NULL;
1525428d7b3dSmrg
1526428d7b3dSmrg		assert(sna->mode.front_active);
1527428d7b3dSmrg		sna->mode.front_active--;
1528428d7b3dSmrg		sna->mode.dirty = true;
1529428d7b3dSmrg	}
1530428d7b3dSmrg
1531428d7b3dSmrg	if (sna_crtc->shadow_bo) {
1532428d7b3dSmrg		kgem_bo_destroy(&sna->kgem, sna_crtc->shadow_bo);
1533428d7b3dSmrg		sna_crtc->shadow_bo = NULL;
1534428d7b3dSmrg	}
1535428d7b3dSmrg	sna_crtc->transform = false;
1536428d7b3dSmrg
1537428d7b3dSmrg	assert(!sna_crtc->shadow);
1538428d7b3dSmrg}
1539428d7b3dSmrg
1540428d7b3dSmrgstatic void
1541428d7b3dSmrgsna_crtc_disable(xf86CrtcPtr crtc)
1542428d7b3dSmrg{
1543428d7b3dSmrg	struct sna *sna = to_sna(crtc->scrn);
1544428d7b3dSmrg	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
1545428d7b3dSmrg	struct drm_mode_crtc arg;
1546428d7b3dSmrg
1547428d7b3dSmrg	if (sna_crtc == NULL)
1548428d7b3dSmrg		return;
1549428d7b3dSmrg
1550428d7b3dSmrg	DBG(("%s: disabling crtc [%d, pipe=%d]\n", __FUNCTION__,
1551428d7b3dSmrg	     sna_crtc->id, sna_crtc->pipe));
1552428d7b3dSmrg
1553428d7b3dSmrg	sna_crtc_force_outputs_off(crtc);
1554428d7b3dSmrg	assert(sna_crtc->dpms_mode == DPMSModeOff);
1555428d7b3dSmrg
1556428d7b3dSmrg	memset(&arg, 0, sizeof(arg));
1557428d7b3dSmrg	arg.crtc_id = sna_crtc->id;
1558428d7b3dSmrg	(void)drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_SETCRTC, &arg);
1559428d7b3dSmrg
1560428d7b3dSmrg	__sna_crtc_disable(sna, sna_crtc);
1561428d7b3dSmrg}
1562428d7b3dSmrg
1563428d7b3dSmrgstatic void update_flush_interval(struct sna *sna)
1564428d7b3dSmrg{
1565428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
1566428d7b3dSmrg	int i, max_vrefresh = 0;
1567428d7b3dSmrg
1568428d7b3dSmrg	DBG(("%s: front_active=%d\n", __FUNCTION__, sna->mode.front_active));
1569428d7b3dSmrg
1570428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_crtc; i++) {
1571428d7b3dSmrg		xf86CrtcPtr crtc = config->crtc[i];
1572428d7b3dSmrg
1573428d7b3dSmrg		assert(to_sna_crtc(crtc) != NULL);
1574428d7b3dSmrg
1575428d7b3dSmrg		if (!crtc->enabled) {
1576428d7b3dSmrg			DBG(("%s: CRTC:%d (pipe %d) disabled\n",
1577428d7b3dSmrg			     __FUNCTION__,i, to_sna_crtc(crtc)->pipe));
1578428d7b3dSmrg			assert(to_sna_crtc(crtc)->bo == NULL);
1579428d7b3dSmrg			continue;
1580428d7b3dSmrg		}
1581428d7b3dSmrg
1582428d7b3dSmrg		if (to_sna_crtc(crtc)->dpms_mode != DPMSModeOn) {
1583428d7b3dSmrg			DBG(("%s: CRTC:%d (pipe %d) turned off\n",
1584428d7b3dSmrg			     __FUNCTION__,i, to_sna_crtc(crtc)->pipe));
1585428d7b3dSmrg			continue;
1586428d7b3dSmrg		}
1587428d7b3dSmrg
1588428d7b3dSmrg		DBG(("%s: CRTC:%d (pipe %d) vrefresh=%f\n",
1589428d7b3dSmrg		     __FUNCTION__, i, to_sna_crtc(crtc)->pipe,
1590428d7b3dSmrg		     xf86ModeVRefresh(&crtc->mode)));
1591428d7b3dSmrg		max_vrefresh = max(max_vrefresh, xf86ModeVRefresh(&crtc->mode));
1592428d7b3dSmrg	}
1593428d7b3dSmrg
1594428d7b3dSmrg	if (max_vrefresh == 0) {
1595428d7b3dSmrg		assert(sna->mode.front_active == 0);
1596428d7b3dSmrg		sna->vblank_interval = 0;
1597428d7b3dSmrg	} else
1598428d7b3dSmrg		sna->vblank_interval = 1000 / max_vrefresh; /* Hz -> ms */
1599428d7b3dSmrg
1600428d7b3dSmrg	DBG(("max_vrefresh=%d, vblank_interval=%d ms\n",
1601428d7b3dSmrg	       max_vrefresh, sna->vblank_interval));
1602428d7b3dSmrg}
1603428d7b3dSmrg
1604428d7b3dSmrgstatic struct kgem_bo *sna_create_bo_for_fbcon(struct sna *sna,
1605428d7b3dSmrg					       const struct drm_mode_fb_cmd *fbcon)
1606428d7b3dSmrg{
1607428d7b3dSmrg	struct drm_gem_flink flink;
1608428d7b3dSmrg	struct kgem_bo *bo;
1609428d7b3dSmrg	int ret;
1610428d7b3dSmrg
1611428d7b3dSmrg	/* Create a new reference for the fbcon so that we can track it
1612428d7b3dSmrg	 * using a normal bo and so that when we call gem_close on it we
1613428d7b3dSmrg	 * delete our reference and not fbcon's!
1614428d7b3dSmrg	 */
1615428d7b3dSmrg	VG_CLEAR(flink);
1616428d7b3dSmrg	flink.handle = fbcon->handle;
1617428d7b3dSmrg	ret = drmIoctl(sna->kgem.fd, DRM_IOCTL_GEM_FLINK, &flink);
1618428d7b3dSmrg	if (ret)
1619428d7b3dSmrg		return NULL;
1620428d7b3dSmrg
1621428d7b3dSmrg	bo = kgem_create_for_name(&sna->kgem, flink.name);
1622428d7b3dSmrg	if (bo == NULL)
1623428d7b3dSmrg		return NULL;
1624428d7b3dSmrg
1625428d7b3dSmrg	bo->pitch = fbcon->pitch;
1626428d7b3dSmrg	return bo;
1627428d7b3dSmrg}
1628428d7b3dSmrg
1629428d7b3dSmrg/* Copy the current framebuffer contents into the front-buffer for a seamless
1630428d7b3dSmrg * transition from e.g. plymouth.
1631428d7b3dSmrg */
1632428d7b3dSmrgvoid sna_copy_fbcon(struct sna *sna)
1633428d7b3dSmrg{
1634428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
1635428d7b3dSmrg	struct drm_mode_fb_cmd fbcon;
1636428d7b3dSmrg	PixmapRec scratch;
1637428d7b3dSmrg	struct sna_pixmap *priv;
1638428d7b3dSmrg	struct kgem_bo *bo;
1639428d7b3dSmrg	BoxRec box;
1640428d7b3dSmrg	bool ok;
1641428d7b3dSmrg	int sx, sy;
1642428d7b3dSmrg	int dx, dy;
1643428d7b3dSmrg	int i;
1644428d7b3dSmrg
1645428d7b3dSmrg	if (wedged(sna))
1646428d7b3dSmrg		return;
1647428d7b3dSmrg
1648428d7b3dSmrg	DBG(("%s\n", __FUNCTION__));
1649428d7b3dSmrg	assert((sna->flags & SNA_IS_HOSTED) == 0);
1650428d7b3dSmrg
1651428d7b3dSmrg	priv = sna_pixmap_move_to_gpu(sna->front, MOVE_WRITE | __MOVE_SCANOUT);
1652428d7b3dSmrg	if (priv == NULL)
1653428d7b3dSmrg		return;
1654428d7b3dSmrg
1655428d7b3dSmrg	/* Scan the connectors for a framebuffer and assume that is the fbcon */
1656428d7b3dSmrg	VG_CLEAR(fbcon);
1657428d7b3dSmrg	fbcon.fb_id = 0;
1658428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_crtc; i++) {
1659428d7b3dSmrg		struct sna_crtc *crtc = to_sna_crtc(config->crtc[i]);
1660428d7b3dSmrg		struct drm_mode_crtc mode;
1661428d7b3dSmrg
1662428d7b3dSmrg		assert(crtc != NULL);
1663428d7b3dSmrg
1664428d7b3dSmrg		VG_CLEAR(mode);
1665428d7b3dSmrg		mode.crtc_id = crtc->id;
1666428d7b3dSmrg		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCRTC, &mode))
1667428d7b3dSmrg			continue;
1668428d7b3dSmrg		if (!mode.fb_id)
1669428d7b3dSmrg			continue;
1670428d7b3dSmrg
1671428d7b3dSmrg		fbcon.fb_id = mode.fb_id;
1672428d7b3dSmrg		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETFB, &fbcon)) {
1673428d7b3dSmrg			fbcon.fb_id = 0;
1674428d7b3dSmrg			continue;
1675428d7b3dSmrg		}
1676428d7b3dSmrg		break;
1677428d7b3dSmrg	}
1678428d7b3dSmrg	if (fbcon.fb_id == 0) {
1679428d7b3dSmrg		DBG(("%s: no fbcon found\n", __FUNCTION__));
1680428d7b3dSmrg		return;
1681428d7b3dSmrg	}
1682428d7b3dSmrg
1683428d7b3dSmrg	if (fbcon.fb_id == fb_id(priv->gpu_bo)) {
1684428d7b3dSmrg		DBG(("%s: fb already installed as scanout\n", __FUNCTION__));
1685428d7b3dSmrg		return;
1686428d7b3dSmrg	}
1687428d7b3dSmrg
1688428d7b3dSmrg	DBG(("%s: found fbcon, size=%dx%d, depth=%d, bpp=%d\n",
1689428d7b3dSmrg	     __FUNCTION__, fbcon.width, fbcon.height, fbcon.depth, fbcon.bpp));
1690428d7b3dSmrg
1691428d7b3dSmrg	bo = sna_create_bo_for_fbcon(sna, &fbcon);
1692428d7b3dSmrg	if (bo == NULL)
1693428d7b3dSmrg		return;
1694428d7b3dSmrg
1695428d7b3dSmrg	DBG(("%s: fbcon handle=%d\n", __FUNCTION__, bo->handle));
1696428d7b3dSmrg
1697428d7b3dSmrg	scratch.drawable.width = fbcon.width;
1698428d7b3dSmrg	scratch.drawable.height = fbcon.height;
1699428d7b3dSmrg	scratch.drawable.depth = fbcon.depth;
1700428d7b3dSmrg	scratch.drawable.bitsPerPixel = fbcon.bpp;
1701428d7b3dSmrg	scratch.devPrivate.ptr = NULL;
1702428d7b3dSmrg
1703428d7b3dSmrg	box.x1 = box.y1 = 0;
1704428d7b3dSmrg	box.x2 = min(fbcon.width, sna->front->drawable.width);
1705428d7b3dSmrg	box.y2 = min(fbcon.height, sna->front->drawable.height);
1706428d7b3dSmrg
1707428d7b3dSmrg	sx = dx = 0;
1708428d7b3dSmrg	if (box.x2 < (uint16_t)fbcon.width)
1709428d7b3dSmrg		sx = (fbcon.width - box.x2) / 2;
1710428d7b3dSmrg	if (box.x2 < sna->front->drawable.width)
1711428d7b3dSmrg		dx = (sna->front->drawable.width - box.x2) / 2;
1712428d7b3dSmrg
1713428d7b3dSmrg	sy = dy = 0;
1714428d7b3dSmrg	if (box.y2 < (uint16_t)fbcon.height)
1715428d7b3dSmrg		sy = (fbcon.height - box.y2) / 2;
1716428d7b3dSmrg	if (box.y2 < sna->front->drawable.height)
1717428d7b3dSmrg		dy = (sna->front->drawable.height - box.y2) / 2;
1718428d7b3dSmrg
1719428d7b3dSmrg	ok = sna->render.copy_boxes(sna, GXcopy,
1720428d7b3dSmrg				    &scratch.drawable, bo, sx, sy,
1721428d7b3dSmrg				    &sna->front->drawable, priv->gpu_bo, dx, dy,
1722428d7b3dSmrg				    &box, 1, 0);
1723428d7b3dSmrg	if (!DAMAGE_IS_ALL(priv->gpu_damage))
1724428d7b3dSmrg		sna_damage_add_box(&priv->gpu_damage, &box);
1725428d7b3dSmrg
1726428d7b3dSmrg	kgem_bo_destroy(&sna->kgem, bo);
1727428d7b3dSmrg
1728428d7b3dSmrg#if ABI_VIDEODRV_VERSION >= SET_ABI_VERSION(10, 0)
1729428d7b3dSmrg	sna->scrn->pScreen->canDoBGNoneRoot = ok;
1730428d7b3dSmrg#endif
1731428d7b3dSmrg}
1732428d7b3dSmrg
1733428d7b3dSmrgstatic bool use_shadow(struct sna *sna, xf86CrtcPtr crtc)
1734428d7b3dSmrg{
1735428d7b3dSmrg	RRTransformPtr transform;
1736428d7b3dSmrg	PictTransform crtc_to_fb;
1737428d7b3dSmrg	struct pict_f_transform f_crtc_to_fb, f_fb_to_crtc;
1738428d7b3dSmrg	unsigned pitch_limit;
1739428d7b3dSmrg	struct sna_pixmap *priv;
1740428d7b3dSmrg	BoxRec b;
1741428d7b3dSmrg
1742428d7b3dSmrg	assert(sna->scrn->virtualX && sna->scrn->virtualY);
1743428d7b3dSmrg
1744428d7b3dSmrg	if (sna->flags & SNA_FORCE_SHADOW) {
1745428d7b3dSmrg		DBG(("%s: forcing shadow\n", __FUNCTION__));
1746428d7b3dSmrg		return true;
1747428d7b3dSmrg	}
1748428d7b3dSmrg
1749428d7b3dSmrg	if (to_sna_crtc(crtc)->fallback_shadow) {
1750428d7b3dSmrg		DBG(("%s: fallback shadow\n", __FUNCTION__));
1751428d7b3dSmrg		return true;
1752428d7b3dSmrg	}
1753428d7b3dSmrg
1754428d7b3dSmrg	if (sna->flags & SNA_TEAR_FREE && to_sna_crtc(crtc)->slave_pixmap) {
1755428d7b3dSmrg		DBG(("%s: TearFree shadow required\n", __FUNCTION__));
1756428d7b3dSmrg		return true;
1757428d7b3dSmrg	}
1758428d7b3dSmrg
1759428d7b3dSmrg	if (sna->scrn->virtualX > sna->mode.max_crtc_width ||
1760428d7b3dSmrg	    sna->scrn->virtualY > sna->mode.max_crtc_height) {
1761428d7b3dSmrg		DBG(("%s: framebuffer too large (%dx%d) > (%dx%d)\n",
1762428d7b3dSmrg		    __FUNCTION__,
1763428d7b3dSmrg		    sna->scrn->virtualX, sna->scrn->virtualY,
1764428d7b3dSmrg		    sna->mode.max_crtc_width, sna->mode.max_crtc_height));
1765428d7b3dSmrg		return true;
1766428d7b3dSmrg	}
1767428d7b3dSmrg
1768428d7b3dSmrg	priv = sna_pixmap_force_to_gpu(sna->front, MOVE_READ | __MOVE_SCANOUT);
1769428d7b3dSmrg	if (priv == NULL)
1770428d7b3dSmrg		return true; /* maybe we can create a bo for the scanout? */
1771428d7b3dSmrg
1772428d7b3dSmrg	if (sna->kgem.gen == 071)
1773428d7b3dSmrg		pitch_limit = priv->gpu_bo->tiling ? 16 * 1024 : 32 * 1024;
1774428d7b3dSmrg	else if ((sna->kgem.gen >> 3) > 4)
1775428d7b3dSmrg		pitch_limit = 32 * 1024;
1776428d7b3dSmrg	else if ((sna->kgem.gen >> 3) == 4)
1777428d7b3dSmrg		pitch_limit = priv->gpu_bo->tiling ? 16 * 1024 : 32 * 1024;
1778428d7b3dSmrg	else if ((sna->kgem.gen >> 3) == 3)
1779428d7b3dSmrg		pitch_limit = priv->gpu_bo->tiling ? 8 * 1024 : 16 * 1024;
1780428d7b3dSmrg	else
1781428d7b3dSmrg		pitch_limit = 8 * 1024;
1782428d7b3dSmrg	DBG(("%s: gpu bo handle=%d tiling=%d pitch=%d, limit=%d\n", __FUNCTION__, priv->gpu_bo->handle, priv->gpu_bo->tiling, priv->gpu_bo->pitch, pitch_limit));
1783428d7b3dSmrg	if (priv->gpu_bo->pitch > pitch_limit)
1784428d7b3dSmrg		return true;
1785428d7b3dSmrg
1786428d7b3dSmrg	if (priv->gpu_bo->tiling && sna->flags & SNA_LINEAR_FB) {
1787428d7b3dSmrg		DBG(("%s: gpu bo is tiled, need linear, forcing shadow\n", __FUNCTION__));
1788428d7b3dSmrg		return true;
1789428d7b3dSmrg	}
1790428d7b3dSmrg
1791428d7b3dSmrg	transform = NULL;
1792428d7b3dSmrg	if (crtc->transformPresent)
1793428d7b3dSmrg		transform = &crtc->transform;
1794428d7b3dSmrg	if (RRTransformCompute(crtc->x, crtc->y,
1795428d7b3dSmrg			       crtc->mode.HDisplay, crtc->mode.VDisplay,
1796428d7b3dSmrg			       crtc->rotation, transform,
1797428d7b3dSmrg			       &crtc_to_fb,
1798428d7b3dSmrg			       &f_crtc_to_fb,
1799428d7b3dSmrg			       &f_fb_to_crtc)) {
1800428d7b3dSmrg		bool needs_transform = true;
1801428d7b3dSmrg		unsigned rotation = rotation_reduce(&to_sna_crtc(crtc)->primary, crtc->rotation);
1802428d7b3dSmrg		DBG(("%s: natively supported rotation? rotation=%x & supported=%x == %d\n",
1803428d7b3dSmrg		     __FUNCTION__, crtc->rotation, to_sna_crtc(crtc)->primary.rotation.supported,
1804428d7b3dSmrg		     !!(crtc->rotation & to_sna_crtc(crtc)->primary.rotation.supported)));
1805428d7b3dSmrg		if (to_sna_crtc(crtc)->primary.rotation.supported & rotation)
1806428d7b3dSmrg			needs_transform = RRTransformCompute(crtc->x, crtc->y,
1807428d7b3dSmrg							     crtc->mode.HDisplay, crtc->mode.VDisplay,
1808428d7b3dSmrg							     RR_Rotate_0, transform,
1809428d7b3dSmrg							     NULL, NULL, NULL);
1810428d7b3dSmrg		if (needs_transform) {
1811428d7b3dSmrg			DBG(("%s: RandR transform present\n", __FUNCTION__));
1812428d7b3dSmrg			return true;
1813428d7b3dSmrg		}
1814428d7b3dSmrg	}
1815428d7b3dSmrg
1816428d7b3dSmrg	/* And finally check that it is entirely visible */
1817428d7b3dSmrg	b.x1 = b.y1 = 0;
1818428d7b3dSmrg	b.x2 = crtc->mode.HDisplay;
1819428d7b3dSmrg	b.y2 = crtc->mode.VDisplay;
1820428d7b3dSmrg	pixman_f_transform_bounds(&f_crtc_to_fb, &b);
1821428d7b3dSmrg	DBG(("%s? bounds (%d, %d), (%d, %d), framebufer %dx%d\n",
1822428d7b3dSmrg	     __FUNCTION__, b.x1, b.y1, b.x2, b.y2,
1823428d7b3dSmrg		 sna->scrn->virtualX, sna->scrn->virtualY));
1824428d7b3dSmrg
1825428d7b3dSmrg	if  (b.x1 < 0 || b.y1 < 0 ||
1826428d7b3dSmrg	     b.x2 > sna->scrn->virtualX ||
1827428d7b3dSmrg	     b.y2 > sna->scrn->virtualY) {
1828428d7b3dSmrg		DBG(("%s: scanout is partly outside the framebuffer\n",
1829428d7b3dSmrg		     __FUNCTION__));
1830428d7b3dSmrg		return true;
1831428d7b3dSmrg	}
1832428d7b3dSmrg
1833428d7b3dSmrg	return false;
1834428d7b3dSmrg}
1835428d7b3dSmrg
1836428d7b3dSmrgstatic void set_shadow(struct sna *sna, RegionPtr region)
1837428d7b3dSmrg{
1838428d7b3dSmrg	struct sna_pixmap *priv = sna_pixmap(sna->front);
1839428d7b3dSmrg
1840428d7b3dSmrg	assert(priv->gpu_bo);
1841428d7b3dSmrg	assert(sna->mode.shadow);
1842428d7b3dSmrg
1843428d7b3dSmrg	DBG(("%s: waiting for region %dx[(%d, %d), (%d, %d)], front handle=%d, shadow handle=%d\n",
1844428d7b3dSmrg	     __FUNCTION__,
1845428d7b3dSmrg	     region_num_rects(region),
1846428d7b3dSmrg	     region->extents.x1, region->extents.y1,
1847428d7b3dSmrg	     region->extents.x2, region->extents.y2,
1848428d7b3dSmrg	     priv->gpu_bo->handle, sna->mode.shadow->handle));
1849428d7b3dSmrg
1850428d7b3dSmrg	assert(priv->pinned & PIN_SCANOUT);
1851428d7b3dSmrg	assert((priv->pinned & PIN_PRIME) == 0);
1852428d7b3dSmrg	assert(sna->mode.shadow != priv->gpu_bo);
1853428d7b3dSmrg
1854428d7b3dSmrg	RegionCopy(&sna->mode.shadow_region, region);
1855428d7b3dSmrg
1856428d7b3dSmrg	priv->move_to_gpu = wait_for_shadow;
1857428d7b3dSmrg	priv->move_to_gpu_data = sna;
1858428d7b3dSmrg}
1859428d7b3dSmrg
1860428d7b3dSmrgstatic struct kgem_bo *
1861428d7b3dSmrgget_scanout_bo(struct sna *sna, PixmapPtr pixmap)
1862428d7b3dSmrg{
1863428d7b3dSmrg	struct sna_pixmap *priv;
1864428d7b3dSmrg
1865428d7b3dSmrg	priv = sna_pixmap_force_to_gpu(pixmap, MOVE_READ | __MOVE_SCANOUT);
1866428d7b3dSmrg	if (!priv)
1867428d7b3dSmrg		return NULL;
1868428d7b3dSmrg
1869428d7b3dSmrg	if (priv->gpu_bo->pitch & 63) {
1870428d7b3dSmrg		struct kgem_bo *tmp;
1871428d7b3dSmrg		BoxRec b;
1872428d7b3dSmrg
1873428d7b3dSmrg		DBG(("%s: converting to scanout bo due to bad pitch [%d]\n",
1874428d7b3dSmrg		     __FUNCTION__, priv->gpu_bo->pitch));
1875428d7b3dSmrg
1876428d7b3dSmrg		if (priv->pinned) {
1877428d7b3dSmrg			DBG(("%s: failed as the Pixmap is already pinned [%x]\n",
1878428d7b3dSmrg			     __FUNCTION__, priv->pinned));
1879428d7b3dSmrg			return NULL;
1880428d7b3dSmrg		}
1881428d7b3dSmrg
1882428d7b3dSmrg		tmp = kgem_create_2d(&sna->kgem,
1883428d7b3dSmrg				     pixmap->drawable.width,
1884428d7b3dSmrg				     pixmap->drawable.height,
1885428d7b3dSmrg				     sna->scrn->bitsPerPixel,
1886428d7b3dSmrg				     priv->gpu_bo->tiling,
1887428d7b3dSmrg				     CREATE_EXACT | CREATE_SCANOUT);
1888428d7b3dSmrg		if (tmp == NULL) {
1889428d7b3dSmrg			DBG(("%s: allocation failed\n", __FUNCTION__));
1890428d7b3dSmrg			return NULL;
1891428d7b3dSmrg		}
1892428d7b3dSmrg
1893428d7b3dSmrg		b.x1 = 0;
1894428d7b3dSmrg		b.y1 = 0;
1895428d7b3dSmrg		b.x2 = pixmap->drawable.width;
1896428d7b3dSmrg		b.y2 = pixmap->drawable.height;
1897428d7b3dSmrg
1898428d7b3dSmrg		if (sna->render.copy_boxes(sna, GXcopy,
1899428d7b3dSmrg					   &pixmap->drawable, priv->gpu_bo, 0, 0,
1900428d7b3dSmrg					   &pixmap->drawable, tmp, 0, 0,
1901428d7b3dSmrg					   &b, 1, COPY_LAST)) {
1902428d7b3dSmrg			DBG(("%s: copy failed\n", __FUNCTION__));
1903428d7b3dSmrg			kgem_bo_destroy(&sna->kgem, tmp);
1904428d7b3dSmrg			return NULL;
1905428d7b3dSmrg		}
1906428d7b3dSmrg
1907428d7b3dSmrg		kgem_bo_destroy(&sna->kgem, priv->gpu_bo);
1908428d7b3dSmrg		priv->gpu_bo = tmp;
1909428d7b3dSmrg	}
1910428d7b3dSmrg
1911428d7b3dSmrg	priv->pinned |= PIN_SCANOUT;
1912428d7b3dSmrg	return priv->gpu_bo;
1913428d7b3dSmrg}
1914428d7b3dSmrg
1915428d7b3dSmrgstatic struct kgem_bo *sna_crtc_attach(xf86CrtcPtr crtc)
1916428d7b3dSmrg{
1917428d7b3dSmrg	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
1918428d7b3dSmrg	ScrnInfoPtr scrn = crtc->scrn;
1919428d7b3dSmrg	struct sna *sna = to_sna(scrn);
1920428d7b3dSmrg	struct kgem_bo *bo;
1921428d7b3dSmrg
1922428d7b3dSmrg	sna_crtc->transform = false;
1923428d7b3dSmrg	sna_crtc->rotation = RR_Rotate_0;
1924428d7b3dSmrg
1925428d7b3dSmrg	if (use_shadow(sna, crtc)) {
1926428d7b3dSmrg		unsigned long tiled_limit;
1927428d7b3dSmrg		int tiling;
1928428d7b3dSmrg
1929428d7b3dSmrgforce_shadow:
1930428d7b3dSmrg		if (!sna_crtc_enable_shadow(sna, sna_crtc)) {
1931428d7b3dSmrg			DBG(("%s: failed to enable crtc shadow\n", __FUNCTION__));
1932428d7b3dSmrg			return NULL;
1933428d7b3dSmrg		}
1934428d7b3dSmrg
1935428d7b3dSmrg		DBG(("%s: attaching to per-crtc pixmap %dx%d\n",
1936428d7b3dSmrg		     __FUNCTION__, crtc->mode.HDisplay, crtc->mode.VDisplay));
1937428d7b3dSmrg
1938428d7b3dSmrg		bo = sna_crtc->shadow_bo;
1939428d7b3dSmrg		if (bo) {
1940428d7b3dSmrg			if (sna_crtc->shadow_bo_width == crtc->mode.HDisplay &&
1941428d7b3dSmrg			    sna_crtc->shadow_bo_height == crtc->mode.VDisplay) {
1942428d7b3dSmrg				DBG(("%s: reusing current shadow bo handle=%d\n",
1943428d7b3dSmrg				     __FUNCTION__, bo->handle));
1944428d7b3dSmrg				goto out_shadow;
1945428d7b3dSmrg			}
1946428d7b3dSmrg
1947428d7b3dSmrg			kgem_bo_destroy(&sna->kgem, bo);
1948428d7b3dSmrg			sna_crtc->shadow_bo = NULL;
1949428d7b3dSmrg		}
1950428d7b3dSmrg
1951428d7b3dSmrg		tiling = I915_TILING_X;
1952428d7b3dSmrg		if (sna->kgem.gen == 071)
1953428d7b3dSmrg			tiled_limit = 16 * 1024 * 8;
1954428d7b3dSmrg		else if ((sna->kgem.gen >> 3) > 4)
1955428d7b3dSmrg			tiled_limit = 32 * 1024 * 8;
1956428d7b3dSmrg		else if ((sna->kgem.gen >> 3) == 4)
1957428d7b3dSmrg			tiled_limit = 16 * 1024 * 8;
1958428d7b3dSmrg		else
1959428d7b3dSmrg			tiled_limit = 8 * 1024 * 8;
1960428d7b3dSmrg		if ((unsigned long)crtc->mode.HDisplay * scrn->bitsPerPixel > tiled_limit)
1961428d7b3dSmrg			tiling = I915_TILING_NONE;
1962428d7b3dSmrg		if (sna->flags & SNA_LINEAR_FB)
1963428d7b3dSmrg			tiling = I915_TILING_NONE;
1964428d7b3dSmrg
1965428d7b3dSmrg		bo = kgem_create_2d(&sna->kgem,
1966428d7b3dSmrg				    crtc->mode.HDisplay, crtc->mode.VDisplay,
1967428d7b3dSmrg				    scrn->bitsPerPixel,
1968428d7b3dSmrg				    tiling, CREATE_SCANOUT);
1969428d7b3dSmrg		if (bo == NULL) {
1970428d7b3dSmrg			DBG(("%s: failed to allocate crtc scanout\n", __FUNCTION__));
1971428d7b3dSmrg			return NULL;
1972428d7b3dSmrg		}
1973428d7b3dSmrg
1974428d7b3dSmrg		if (!get_fb(sna, bo, crtc->mode.HDisplay, crtc->mode.VDisplay)) {
1975428d7b3dSmrg			DBG(("%s: failed to bind fb for crtc scanout\n", __FUNCTION__));
1976428d7b3dSmrg			kgem_bo_destroy(&sna->kgem, bo);
1977428d7b3dSmrg			return NULL;
1978428d7b3dSmrg		}
1979428d7b3dSmrg
1980428d7b3dSmrg		if (__sna_pixmap_get_bo(sna->front) && !crtc->transformPresent) {
1981428d7b3dSmrg			DrawableRec tmp;
1982428d7b3dSmrg			BoxRec b;
1983428d7b3dSmrg
1984428d7b3dSmrg			b.x1 = crtc->x;
1985428d7b3dSmrg			b.y1 = crtc->y;
1986428d7b3dSmrg			b.x2 = crtc->x + crtc->mode.HDisplay;
1987428d7b3dSmrg			b.y2 = crtc->y + crtc->mode.VDisplay;
1988428d7b3dSmrg
1989428d7b3dSmrg			DBG(("%s: copying onto shadow CRTC: (%d, %d)x(%d, %d), handle=%d\n",
1990428d7b3dSmrg			     __FUNCTION__,
1991428d7b3dSmrg			     b.x1, b.y1,
1992428d7b3dSmrg			     b.x2, b.y2,
1993428d7b3dSmrg			     bo->handle));
1994428d7b3dSmrg
1995428d7b3dSmrg			tmp.width = crtc->mode.HDisplay;
1996428d7b3dSmrg			tmp.height = crtc->mode.VDisplay;
1997428d7b3dSmrg			tmp.depth = sna->front->drawable.depth;
1998428d7b3dSmrg			tmp.bitsPerPixel = sna->front->drawable.bitsPerPixel;
1999428d7b3dSmrg
2000428d7b3dSmrg			(void)sna->render.copy_boxes(sna, GXcopy,
2001428d7b3dSmrg						     &sna->front->drawable, __sna_pixmap_get_bo(sna->front), 0, 0,
2002428d7b3dSmrg						     &tmp, bo, -b.x1, -b.y1,
2003428d7b3dSmrg						     &b, 1, 0);
2004428d7b3dSmrg		}
2005428d7b3dSmrg
2006428d7b3dSmrg		sna_crtc->shadow_bo_width = crtc->mode.HDisplay;
2007428d7b3dSmrg		sna_crtc->shadow_bo_height = crtc->mode.VDisplay;
2008428d7b3dSmrg		sna_crtc->shadow_bo = bo;
2009428d7b3dSmrgout_shadow:
2010428d7b3dSmrg		sna_crtc->transform = true;
2011428d7b3dSmrg		return kgem_bo_reference(bo);
2012428d7b3dSmrg	} else {
2013428d7b3dSmrg		if (sna_crtc->shadow_bo) {
2014428d7b3dSmrg			kgem_bo_destroy(&sna->kgem, sna_crtc->shadow_bo);
2015428d7b3dSmrg			sna_crtc->shadow_bo = NULL;
2016428d7b3dSmrg		}
2017428d7b3dSmrg
2018428d7b3dSmrg		if (sna_crtc->slave_pixmap) {
2019428d7b3dSmrg			DBG(("%s: attaching to scanout pixmap\n", __FUNCTION__));
2020428d7b3dSmrg			bo = get_scanout_bo(sna, sna_crtc->slave_pixmap);
2021428d7b3dSmrg			if (bo == NULL) {
2022428d7b3dSmrg				DBG(("%s: failed to pin crtc scanout\n", __FUNCTION__));
2023428d7b3dSmrg				sna_crtc->fallback_shadow = true;
2024428d7b3dSmrg				goto force_shadow;
2025428d7b3dSmrg			}
2026428d7b3dSmrg
2027428d7b3dSmrg			if (!get_fb(sna, bo,
2028428d7b3dSmrg				    sna_crtc->slave_pixmap->drawable.width,
2029428d7b3dSmrg				    sna_crtc->slave_pixmap->drawable.height)) {
2030428d7b3dSmrg				DBG(("%s: failed to bind fb for crtc scanout\n", __FUNCTION__));
2031428d7b3dSmrg				sna_crtc->fallback_shadow = true;
2032428d7b3dSmrg				goto force_shadow;
2033428d7b3dSmrg			}
2034428d7b3dSmrg		} else {
2035428d7b3dSmrg			DBG(("%s: attaching to framebuffer\n", __FUNCTION__));
2036428d7b3dSmrg			bo = get_scanout_bo(sna, sna->front);
2037428d7b3dSmrg			if (bo == NULL) {
2038428d7b3dSmrg				DBG(("%s: failed to pin framebuffer\n", __FUNCTION__));
2039428d7b3dSmrg				sna_crtc->fallback_shadow = true;
2040428d7b3dSmrg				goto force_shadow;
2041428d7b3dSmrg			}
2042428d7b3dSmrg
2043428d7b3dSmrg			if (!get_fb(sna, bo, scrn->virtualX, scrn->virtualY)) {
2044428d7b3dSmrg				DBG(("%s: failed to bind fb for crtc scanout\n", __FUNCTION__));
2045428d7b3dSmrg				sna_crtc->fallback_shadow = true;
2046428d7b3dSmrg				goto force_shadow;
2047428d7b3dSmrg			}
2048428d7b3dSmrg		}
2049428d7b3dSmrg
2050428d7b3dSmrg		if (sna->flags & SNA_TEAR_FREE) {
2051428d7b3dSmrg			assert(sna_crtc->slave_pixmap == NULL);
2052428d7b3dSmrg
2053428d7b3dSmrg			DBG(("%s: enabling TearFree shadow\n", __FUNCTION__));
2054428d7b3dSmrg			if (!sna_crtc_enable_shadow(sna, sna_crtc)) {
2055428d7b3dSmrg				DBG(("%s: failed to enable crtc shadow\n", __FUNCTION__));
2056428d7b3dSmrg				return NULL;
2057428d7b3dSmrg			}
2058428d7b3dSmrg
2059428d7b3dSmrg			if (sna->mode.shadow == NULL && !wedged(sna)) {
2060428d7b3dSmrg				RegionRec region;
2061428d7b3dSmrg				struct kgem_bo *shadow;
2062428d7b3dSmrg
2063428d7b3dSmrg				DBG(("%s: creating TearFree shadow bo\n", __FUNCTION__));
2064428d7b3dSmrg
2065428d7b3dSmrg				region.extents.x1 = 0;
2066428d7b3dSmrg				region.extents.y1 = 0;
2067428d7b3dSmrg				region.extents.x2 = sna->scrn->virtualX;
2068428d7b3dSmrg				region.extents.y2 = sna->scrn->virtualY;
2069428d7b3dSmrg				region.data = NULL;
2070428d7b3dSmrg
2071428d7b3dSmrg				shadow = kgem_create_2d(&sna->kgem,
2072428d7b3dSmrg							region.extents.x2,
2073428d7b3dSmrg							region.extents.y2,
2074428d7b3dSmrg							scrn->bitsPerPixel,
2075428d7b3dSmrg							kgem_choose_tiling(&sna->kgem,
2076428d7b3dSmrg									   I915_TILING_X,
2077428d7b3dSmrg									   region.extents.x2,
2078428d7b3dSmrg									   region.extents.y2,
2079428d7b3dSmrg									   sna->scrn->bitsPerPixel),
2080428d7b3dSmrg							CREATE_SCANOUT);
2081428d7b3dSmrg				if (shadow == NULL) {
2082428d7b3dSmrg					DBG(("%s: failed to allocate TearFree shadow bo\n", __FUNCTION__));
2083428d7b3dSmrg					sna_crtc->fallback_shadow = true;
2084428d7b3dSmrg					goto force_shadow;
2085428d7b3dSmrg				}
2086428d7b3dSmrg
2087428d7b3dSmrg				if (!get_fb(sna, shadow,
2088428d7b3dSmrg					    region.extents.x2,
2089428d7b3dSmrg					    region.extents.y2)) {
2090428d7b3dSmrg					DBG(("%s: failed to bind fb for TearFeee shadow\n", __FUNCTION__));
2091428d7b3dSmrg					kgem_bo_destroy(&sna->kgem, shadow);
2092428d7b3dSmrg					sna_crtc->fallback_shadow = true;
2093428d7b3dSmrg					goto force_shadow;
2094428d7b3dSmrg				}
2095428d7b3dSmrg
2096428d7b3dSmrg				sna->mode.shadow = shadow;
2097428d7b3dSmrg				set_shadow(sna, &region);
2098428d7b3dSmrg			}
2099428d7b3dSmrg
2100428d7b3dSmrg			sna_crtc_disable_override(sna, sna_crtc);
2101428d7b3dSmrg		} else
2102428d7b3dSmrg			sna_crtc_disable_shadow(sna, sna_crtc);
2103428d7b3dSmrg
2104428d7b3dSmrg		sna_crtc->rotation = rotation_reduce(&sna_crtc->primary, crtc->rotation);
2105428d7b3dSmrg		assert(sna_crtc->primary.rotation.supported & sna_crtc->rotation);
2106428d7b3dSmrg		return kgem_bo_reference(bo);
2107428d7b3dSmrg	}
2108428d7b3dSmrg}
2109428d7b3dSmrg
2110428d7b3dSmrgstatic void sna_crtc_randr(xf86CrtcPtr crtc)
2111428d7b3dSmrg{
2112428d7b3dSmrg	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
2113428d7b3dSmrg	struct pict_f_transform f_crtc_to_fb, f_fb_to_crtc;
2114428d7b3dSmrg	PictTransform crtc_to_fb;
2115428d7b3dSmrg	PictFilterPtr filter;
2116428d7b3dSmrg	xFixed *params;
2117428d7b3dSmrg	int nparams;
2118428d7b3dSmrg	RRTransformPtr transform;
2119428d7b3dSmrg	int needs_transform;
2120428d7b3dSmrg
2121428d7b3dSmrg	transform = NULL;
2122428d7b3dSmrg	if (crtc->transformPresent)
2123428d7b3dSmrg		transform = &crtc->transform;
2124428d7b3dSmrg
2125428d7b3dSmrg	needs_transform =
2126428d7b3dSmrg		RRTransformCompute(crtc->x, crtc->y,
2127428d7b3dSmrg				   crtc->mode.HDisplay, crtc->mode.VDisplay,
2128428d7b3dSmrg				   crtc->rotation, transform,
2129428d7b3dSmrg				   &crtc_to_fb,
2130428d7b3dSmrg				   &f_crtc_to_fb,
2131428d7b3dSmrg				   &f_fb_to_crtc);
2132428d7b3dSmrg
2133428d7b3dSmrg	filter = NULL;
2134428d7b3dSmrg	params = NULL;
2135428d7b3dSmrg	nparams = 0;
2136428d7b3dSmrg	if (sna_crtc->transform) {
2137428d7b3dSmrg#ifdef RANDR_12_INTERFACE
2138428d7b3dSmrg		if (transform) {
2139428d7b3dSmrg			if (transform->nparams) {
2140428d7b3dSmrg				params = malloc(transform->nparams * sizeof(xFixed));
2141428d7b3dSmrg				if (params) {
2142428d7b3dSmrg					memcpy(params, transform->params,
2143428d7b3dSmrg					       transform->nparams * sizeof(xFixed));
2144428d7b3dSmrg					nparams = transform->nparams;
2145428d7b3dSmrg					filter = transform->filter;
2146428d7b3dSmrg				}
2147428d7b3dSmrg			} else
2148428d7b3dSmrg				filter = transform->filter;
2149428d7b3dSmrg		}
2150428d7b3dSmrg#endif
2151428d7b3dSmrg		crtc->transform_in_use = needs_transform;
2152428d7b3dSmrg	} else
2153428d7b3dSmrg		crtc->transform_in_use = sna_crtc->rotation != RR_Rotate_0;
2154428d7b3dSmrg
2155428d7b3dSmrg	crtc->crtc_to_framebuffer = crtc_to_fb;
2156428d7b3dSmrg	crtc->f_crtc_to_framebuffer = f_crtc_to_fb;
2157428d7b3dSmrg	crtc->f_framebuffer_to_crtc = f_fb_to_crtc;
2158428d7b3dSmrg
2159428d7b3dSmrg	free(crtc->params);
2160428d7b3dSmrg	crtc->params  = params;
2161428d7b3dSmrg	crtc->nparams = nparams;
2162428d7b3dSmrg
2163428d7b3dSmrg	crtc->filter = filter;
2164428d7b3dSmrg	if (filter) {
2165428d7b3dSmrg		crtc->filter_width  = filter->width;
2166428d7b3dSmrg		crtc->filter_height = filter->height;
2167428d7b3dSmrg	} else {
2168428d7b3dSmrg		crtc->filter_width  = 0;
2169428d7b3dSmrg		crtc->filter_height = 0;
2170428d7b3dSmrg	}
2171428d7b3dSmrg
2172428d7b3dSmrg	crtc->bounds.x1 = 0;
2173428d7b3dSmrg	crtc->bounds.x2 = crtc->mode.HDisplay;
2174428d7b3dSmrg	crtc->bounds.y1 = 0;
2175428d7b3dSmrg	crtc->bounds.y2 = crtc->mode.VDisplay;
2176428d7b3dSmrg	pixman_f_transform_bounds(&f_crtc_to_fb, &crtc->bounds);
2177428d7b3dSmrg
2178428d7b3dSmrg	DBG(("%s: transform? %d, bounds (%d, %d), (%d, %d)\n",
2179428d7b3dSmrg	     __FUNCTION__, crtc->transform_in_use,
2180428d7b3dSmrg	     crtc->bounds.x1, crtc->bounds.y1,
2181428d7b3dSmrg	     crtc->bounds.x2, crtc->bounds.y2));
2182428d7b3dSmrg}
2183428d7b3dSmrg
2184428d7b3dSmrgstatic void
2185428d7b3dSmrgsna_crtc_damage(xf86CrtcPtr crtc)
2186428d7b3dSmrg{
2187428d7b3dSmrg	ScreenPtr screen = crtc->scrn->pScreen;
2188428d7b3dSmrg	struct sna *sna = to_sna(crtc->scrn);
2189428d7b3dSmrg	RegionRec region, *damage;
2190428d7b3dSmrg
2191428d7b3dSmrg	region.extents = crtc->bounds;
2192428d7b3dSmrg	region.data = NULL;
2193428d7b3dSmrg
2194428d7b3dSmrg	if (region.extents.x1 < 0)
2195428d7b3dSmrg		region.extents.x1 = 0;
2196428d7b3dSmrg	if (region.extents.y1 < 0)
2197428d7b3dSmrg		region.extents.y1 = 0;
2198428d7b3dSmrg	if (region.extents.x2 > screen->width)
2199428d7b3dSmrg		region.extents.x2 = screen->width;
2200428d7b3dSmrg	if (region.extents.y2 > screen->height)
2201428d7b3dSmrg		region.extents.y2 = screen->height;
2202428d7b3dSmrg
2203428d7b3dSmrg	DBG(("%s: marking crtc %d as completely damaged (%d, %d), (%d, %d)\n",
2204428d7b3dSmrg	     __FUNCTION__, to_sna_crtc(crtc)->id,
2205428d7b3dSmrg	     region.extents.x1, region.extents.y1,
2206428d7b3dSmrg	     region.extents.x2, region.extents.y2));
2207428d7b3dSmrg	to_sna_crtc(crtc)->client_damage = region;
2208428d7b3dSmrg
2209428d7b3dSmrg	assert(sna->mode.shadow_damage && sna->mode.shadow_active);
2210428d7b3dSmrg	damage = DamageRegion(sna->mode.shadow_damage);
2211428d7b3dSmrg	RegionUnion(damage, damage, &region);
2212428d7b3dSmrg
2213428d7b3dSmrg	DBG(("%s: damage now %dx[(%d, %d), (%d, %d)]\n",
2214428d7b3dSmrg	     __FUNCTION__,
2215428d7b3dSmrg	     region_num_rects(damage),
2216428d7b3dSmrg	     damage->extents.x1, damage->extents.y1,
2217428d7b3dSmrg	     damage->extents.x2, damage->extents.y2));
2218428d7b3dSmrg}
2219428d7b3dSmrg
2220428d7b3dSmrgstatic char *outputs_for_crtc(xf86CrtcPtr crtc, char *outputs, int max)
2221428d7b3dSmrg{
2222428d7b3dSmrg	struct sna *sna = to_sna(crtc->scrn);
2223428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
2224428d7b3dSmrg	int len, i;
2225428d7b3dSmrg
2226428d7b3dSmrg	for (i = len = 0; i < sna->mode.num_real_output; i++) {
2227428d7b3dSmrg		xf86OutputPtr output = config->output[i];
2228428d7b3dSmrg
2229428d7b3dSmrg		if (output->crtc != crtc)
2230428d7b3dSmrg			continue;
2231428d7b3dSmrg
2232428d7b3dSmrg		len += snprintf(outputs+len, max-len, "%s, ", output->name);
2233428d7b3dSmrg	}
2234428d7b3dSmrg	assert(len >= 2);
2235428d7b3dSmrg	outputs[len-2] = '\0';
2236428d7b3dSmrg
2237428d7b3dSmrg	return outputs;
2238428d7b3dSmrg}
2239428d7b3dSmrg
2240428d7b3dSmrgstatic const char *rotation_to_str(Rotation rotation)
2241428d7b3dSmrg{
2242428d7b3dSmrg	switch (rotation & RR_Rotate_All) {
2243428d7b3dSmrg	case 0:
2244428d7b3dSmrg	case RR_Rotate_0: return "normal";
2245428d7b3dSmrg	case RR_Rotate_90: return "left";
2246428d7b3dSmrg	case RR_Rotate_180: return "inverted";
2247428d7b3dSmrg	case RR_Rotate_270: return "right";
2248428d7b3dSmrg	default: return "unknown";
2249428d7b3dSmrg	}
2250428d7b3dSmrg}
2251428d7b3dSmrg
2252428d7b3dSmrgstatic const char *reflection_to_str(Rotation rotation)
2253428d7b3dSmrg{
2254428d7b3dSmrg	switch (rotation & RR_Reflect_All) {
2255428d7b3dSmrg	case 0: return "none";
2256428d7b3dSmrg	case RR_Reflect_X: return "X axis";
2257428d7b3dSmrg	case RR_Reflect_Y: return "Y axis";
2258428d7b3dSmrg	case RR_Reflect_X | RR_Reflect_Y: return "X and Y axes";
2259428d7b3dSmrg	default: return "invalid";
2260428d7b3dSmrg	}
2261428d7b3dSmrg}
2262428d7b3dSmrg
2263428d7b3dSmrgstatic Bool
2264428d7b3dSmrg__sna_crtc_set_mode(xf86CrtcPtr crtc)
2265428d7b3dSmrg{
2266428d7b3dSmrg	struct sna *sna = to_sna(crtc->scrn);
2267428d7b3dSmrg	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
2268428d7b3dSmrg	struct kgem_bo *saved_bo, *bo;
2269428d7b3dSmrg	uint32_t saved_offset;
2270428d7b3dSmrg	bool saved_transform;
2271428d7b3dSmrg
2272428d7b3dSmrg	DBG(("%s\n", __FUNCTION__));
2273428d7b3dSmrg
2274428d7b3dSmrg	saved_bo = sna_crtc->bo;
2275428d7b3dSmrg	saved_transform = sna_crtc->transform;
2276428d7b3dSmrg	saved_offset = sna_crtc->offset;
2277428d7b3dSmrg
2278428d7b3dSmrg	sna_crtc->fallback_shadow = false;
2279428d7b3dSmrgretry: /* Attach per-crtc pixmap or direct */
2280428d7b3dSmrg	bo = sna_crtc_attach(crtc);
2281428d7b3dSmrg	if (bo == NULL) {
2282428d7b3dSmrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
2283428d7b3dSmrg			   "unable to attach scanout\n");
2284428d7b3dSmrg		goto error;
2285428d7b3dSmrg	}
2286428d7b3dSmrg
2287428d7b3dSmrg	/* Prevent recursion when enabling outputs during execbuffer */
2288428d7b3dSmrg	if (bo->exec && RQ(bo->rq)->bo == NULL)
2289428d7b3dSmrg		_kgem_submit(&sna->kgem);
2290428d7b3dSmrg
2291428d7b3dSmrg	sna_crtc->bo = bo;
2292428d7b3dSmrg	if (!sna_crtc_apply(crtc)) {
2293428d7b3dSmrg		int err = errno;
2294428d7b3dSmrg
2295428d7b3dSmrg		kgem_bo_destroy(&sna->kgem, bo);
2296428d7b3dSmrg
2297428d7b3dSmrg		if (!sna_crtc->shadow) {
2298428d7b3dSmrg			sna_crtc->fallback_shadow = true;
2299428d7b3dSmrg			goto retry;
2300428d7b3dSmrg		}
2301428d7b3dSmrg
2302428d7b3dSmrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
2303428d7b3dSmrg			   "failed to set mode: %s [%d]\n", strerror(err), err);
2304428d7b3dSmrg		goto error;
2305428d7b3dSmrg	}
2306428d7b3dSmrg
2307428d7b3dSmrg	bo->active_scanout++;
2308428d7b3dSmrg	if (saved_bo) {
2309428d7b3dSmrg		assert(saved_bo->active_scanout);
2310428d7b3dSmrg		assert(saved_bo->refcnt >= saved_bo->active_scanout);
2311428d7b3dSmrg		saved_bo->active_scanout--;
2312428d7b3dSmrg		kgem_bo_destroy(&sna->kgem, saved_bo);
2313428d7b3dSmrg	}
2314428d7b3dSmrg
2315428d7b3dSmrg	sna_crtc_randr(crtc);
2316428d7b3dSmrg	if (sna_crtc->transform)
2317428d7b3dSmrg		sna_crtc_damage(crtc);
2318428d7b3dSmrg	sna->mode.front_active += saved_bo == NULL;
2319428d7b3dSmrg	sna->mode.dirty = true;
2320428d7b3dSmrg	DBG(("%s: front_active=%d\n", __FUNCTION__, sna->mode.front_active));
2321428d7b3dSmrg
2322428d7b3dSmrg	return TRUE;
2323428d7b3dSmrg
2324428d7b3dSmrgerror:
2325428d7b3dSmrg	sna_crtc->offset = saved_offset;
2326428d7b3dSmrg	sna_crtc->transform = saved_transform;
2327428d7b3dSmrg	sna_crtc->bo = saved_bo;
2328428d7b3dSmrg	sna_mode_discover(sna);
2329428d7b3dSmrg	return FALSE;
2330428d7b3dSmrg}
2331428d7b3dSmrg
2332428d7b3dSmrgstatic Bool
2333428d7b3dSmrgsna_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
2334428d7b3dSmrg			Rotation rotation, int x, int y)
2335428d7b3dSmrg{
2336428d7b3dSmrg	struct sna *sna = to_sna(crtc->scrn);
2337428d7b3dSmrg	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
2338428d7b3dSmrg	struct drm_mode_modeinfo saved_kmode;
2339428d7b3dSmrg	char outputs[256];
2340428d7b3dSmrg
2341428d7b3dSmrg	if (mode->HDisplay == 0 || mode->VDisplay == 0)
2342428d7b3dSmrg		return FALSE;
2343428d7b3dSmrg
2344428d7b3dSmrg	assert(sna_crtc);
2345428d7b3dSmrg
2346428d7b3dSmrg	xf86DrvMsg(crtc->scrn->scrnIndex, X_INFO,
2347428d7b3dSmrg		   "switch to mode %dx%d@%.1f on %s using pipe %d, position (%d, %d), rotation %s, reflection %s\n",
2348428d7b3dSmrg		   mode->HDisplay, mode->VDisplay, xf86ModeVRefresh(mode),
2349428d7b3dSmrg		   outputs_for_crtc(crtc, outputs, sizeof(outputs)), sna_crtc->pipe,
2350428d7b3dSmrg		   x, y, rotation_to_str(rotation), reflection_to_str(rotation));
2351428d7b3dSmrg
2352428d7b3dSmrg	assert(mode->HDisplay <= sna->mode.max_crtc_width &&
2353428d7b3dSmrg	       mode->VDisplay <= sna->mode.max_crtc_height);
2354428d7b3dSmrg
2355428d7b3dSmrg#if HAS_GAMMA
2356428d7b3dSmrg	drmModeCrtcSetGamma(sna->kgem.fd, sna_crtc->id,
2357428d7b3dSmrg			    crtc->gamma_size,
2358428d7b3dSmrg			    crtc->gamma_red,
2359428d7b3dSmrg			    crtc->gamma_green,
2360428d7b3dSmrg			    crtc->gamma_blue);
2361428d7b3dSmrg#endif
2362428d7b3dSmrg
2363428d7b3dSmrg	saved_kmode = sna_crtc->kmode;
2364428d7b3dSmrg	mode_to_kmode(&sna_crtc->kmode, mode);
2365428d7b3dSmrg	if (__sna_crtc_set_mode(crtc))
2366428d7b3dSmrg		return TRUE;
2367428d7b3dSmrg
2368428d7b3dSmrg	sna_crtc->kmode = saved_kmode;
2369428d7b3dSmrg	return FALSE;
2370428d7b3dSmrg}
2371428d7b3dSmrg
2372428d7b3dSmrgstatic void
2373428d7b3dSmrgsna_crtc_dpms(xf86CrtcPtr crtc, int mode)
2374428d7b3dSmrg{
2375428d7b3dSmrg	struct sna_crtc *priv = to_sna_crtc(crtc);
2376428d7b3dSmrg
2377428d7b3dSmrg	DBG(("%s(pipe %d, dpms mode -> %d):= active=%d\n",
2378428d7b3dSmrg	     __FUNCTION__, priv->pipe, mode, mode == DPMSModeOn));
2379428d7b3dSmrg	if (priv->dpms_mode == mode)
2380428d7b3dSmrg		return;
2381428d7b3dSmrg
2382428d7b3dSmrg	assert(priv);
2383428d7b3dSmrg	priv->dpms_mode = mode;
2384428d7b3dSmrg
2385428d7b3dSmrg	if (mode == DPMSModeOn && crtc->enabled && priv->bo == NULL) {
2386428d7b3dSmrg		if (__sna_crtc_set_mode(crtc))
2387428d7b3dSmrg			update_flush_interval(to_sna(crtc->scrn));
2388428d7b3dSmrg		else
2389428d7b3dSmrg			mode = DPMSModeOff;
2390428d7b3dSmrg	}
2391428d7b3dSmrg
2392428d7b3dSmrg	if (mode != DPMSModeOn)
2393428d7b3dSmrg		sna_crtc_disable(crtc);
2394428d7b3dSmrg}
2395428d7b3dSmrg
2396428d7b3dSmrgvoid sna_mode_adjust_frame(struct sna *sna, int x, int y)
2397428d7b3dSmrg{
2398428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
2399428d7b3dSmrg	xf86CrtcPtr crtc;
2400428d7b3dSmrg	int saved_x, saved_y;
2401428d7b3dSmrg
2402428d7b3dSmrg	if ((unsigned)config->compat_output >= config->num_output)
2403428d7b3dSmrg		return;
2404428d7b3dSmrg
2405428d7b3dSmrg	crtc = config->output[config->compat_output]->crtc;
2406428d7b3dSmrg	if (crtc == NULL || !crtc->enabled)
2407428d7b3dSmrg		return;
2408428d7b3dSmrg
2409428d7b3dSmrg	if (crtc->x == x && crtc->y == y)
2410428d7b3dSmrg		return;
2411428d7b3dSmrg
2412428d7b3dSmrg	saved_x = crtc->x;
2413428d7b3dSmrg	saved_y = crtc->y;
2414428d7b3dSmrg
2415428d7b3dSmrg	crtc->x = x;
2416428d7b3dSmrg	crtc->y = y;
2417428d7b3dSmrg	if (to_sna_crtc(crtc) && !__sna_crtc_set_mode(crtc)) {
2418428d7b3dSmrg		crtc->x = saved_x;
2419428d7b3dSmrg		crtc->y = saved_y;
2420428d7b3dSmrg	}
2421428d7b3dSmrg}
2422428d7b3dSmrg
2423428d7b3dSmrgstatic void
2424428d7b3dSmrgsna_crtc_gamma_set(xf86CrtcPtr crtc,
2425428d7b3dSmrg		       CARD16 *red, CARD16 *green, CARD16 *blue, int size)
2426428d7b3dSmrg{
2427428d7b3dSmrg	assert(to_sna_crtc(crtc));
2428428d7b3dSmrg	drmModeCrtcSetGamma(to_sna(crtc->scrn)->kgem.fd,
2429428d7b3dSmrg			    to_sna_crtc(crtc)->id,
2430428d7b3dSmrg			    size, red, green, blue);
2431428d7b3dSmrg}
2432428d7b3dSmrg
2433428d7b3dSmrgstatic void
2434428d7b3dSmrgsna_crtc_destroy(xf86CrtcPtr crtc)
2435428d7b3dSmrg{
2436428d7b3dSmrg	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
2437428d7b3dSmrg
2438428d7b3dSmrg	if (sna_crtc == NULL)
2439428d7b3dSmrg		return;
2440428d7b3dSmrg
2441428d7b3dSmrg	free(sna_crtc);
2442428d7b3dSmrg	crtc->driver_private = NULL;
2443428d7b3dSmrg}
2444428d7b3dSmrg
2445428d7b3dSmrg#if HAS_PIXMAP_SHARING
2446428d7b3dSmrgstatic Bool
2447428d7b3dSmrgsna_crtc_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr pixmap)
2448428d7b3dSmrg{
2449428d7b3dSmrg	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
2450428d7b3dSmrg
2451428d7b3dSmrg	if (sna_crtc == NULL)
2452428d7b3dSmrg		return FALSE;
2453428d7b3dSmrg
2454428d7b3dSmrg	if (pixmap == sna_crtc->slave_pixmap)
2455428d7b3dSmrg		return TRUE;
2456428d7b3dSmrg
2457428d7b3dSmrg	DBG(("%s: CRTC:%d, pipe=%d setting scanout pixmap=%ld\n",
2458428d7b3dSmrg	     __FUNCTION__, sna_crtc->id,  sna_crtc->pipe,
2459428d7b3dSmrg	     pixmap ? pixmap->drawable.serialNumber : 0));
2460428d7b3dSmrg
2461428d7b3dSmrg	/* Disable first so that we can unregister the damage tracking */
2462428d7b3dSmrg	sna_crtc_disable_shadow(to_sna(crtc->scrn), sna_crtc);
2463428d7b3dSmrg
2464428d7b3dSmrg	sna_crtc->slave_pixmap = pixmap;
2465428d7b3dSmrg
2466428d7b3dSmrg	return TRUE;
2467428d7b3dSmrg}
2468428d7b3dSmrg#endif
2469428d7b3dSmrg
2470428d7b3dSmrgstatic const xf86CrtcFuncsRec sna_crtc_funcs = {
2471428d7b3dSmrg#if XF86_CRTC_VERSION >= 1
2472428d7b3dSmrg	.dpms = sna_crtc_dpms,
2473428d7b3dSmrg#endif
2474428d7b3dSmrg	.set_mode_major = sna_crtc_set_mode_major,
2475428d7b3dSmrg	.gamma_set = sna_crtc_gamma_set,
2476428d7b3dSmrg	.destroy = sna_crtc_destroy,
2477428d7b3dSmrg#if HAS_PIXMAP_SHARING
2478428d7b3dSmrg	.set_scanout_pixmap = sna_crtc_set_scanout_pixmap,
2479428d7b3dSmrg#endif
2480428d7b3dSmrg};
2481428d7b3dSmrg
2482428d7b3dSmrginline static bool prop_is_rotation(struct drm_mode_get_property *prop)
2483428d7b3dSmrg{
2484428d7b3dSmrg	if ((prop->flags & (1 << 5)) == 0)
2485428d7b3dSmrg		return false;
2486428d7b3dSmrg
2487428d7b3dSmrg	if (strcmp(prop->name, "rotation"))
2488428d7b3dSmrg		return false;
2489428d7b3dSmrg
2490428d7b3dSmrg	return true;
2491428d7b3dSmrg}
2492428d7b3dSmrg
2493428d7b3dSmrgstatic int plane_details(struct sna *sna, struct plane *p)
2494428d7b3dSmrg{
2495428d7b3dSmrg	struct local_mode_obj_get_properties arg;
2496428d7b3dSmrg	uint64_t stack_props[24];
2497428d7b3dSmrg	uint32_t *props = (uint32_t *)stack_props;
2498428d7b3dSmrg	uint64_t *values = stack_props + 8;
2499428d7b3dSmrg	int i, type = DRM_PLANE_TYPE_OVERLAY;
2500428d7b3dSmrg
2501428d7b3dSmrg	memset(&arg, 0, sizeof(struct local_mode_obj_get_properties));
2502428d7b3dSmrg	arg.obj_id = p->id;
2503428d7b3dSmrg	arg.obj_type = LOCAL_MODE_OBJECT_PLANE;
2504428d7b3dSmrg
2505428d7b3dSmrg	arg.props_ptr = (uintptr_t)props;
2506428d7b3dSmrg	arg.prop_values_ptr = (uintptr_t)values;
2507428d7b3dSmrg	arg.count_props = 16;
2508428d7b3dSmrg
2509428d7b3dSmrg	if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_OBJ_GETPROPERTIES, &arg))
2510428d7b3dSmrg		return -1;
2511428d7b3dSmrg
2512428d7b3dSmrg	DBG(("%s: object %d (type %x) has %d props\n", __FUNCTION__,
2513428d7b3dSmrg	     p->id, LOCAL_MODE_OBJECT_PLANE, arg.count_props));
2514428d7b3dSmrg
2515428d7b3dSmrg	if (arg.count_props > 16) {
2516428d7b3dSmrg		props = malloc(2*sizeof(uint64_t)*arg.count_props);
2517428d7b3dSmrg		if (props == NULL)
2518428d7b3dSmrg			return -1;
2519428d7b3dSmrg
2520428d7b3dSmrg		values = (uint64_t *)props + arg.count_props;
2521428d7b3dSmrg
2522428d7b3dSmrg		arg.props_ptr = (uintptr_t)props;
2523428d7b3dSmrg		arg.prop_values_ptr = (uintptr_t)values;
2524428d7b3dSmrg
2525428d7b3dSmrg		if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_OBJ_GETPROPERTIES, &arg))
2526428d7b3dSmrg			arg.count_props = 0;
2527428d7b3dSmrg	}
2528428d7b3dSmrg	VG(VALGRIND_MAKE_MEM_DEFINED(arg.props_ptr, sizeof(uint32_t)*arg.count_props));
2529428d7b3dSmrg	VG(VALGRIND_MAKE_MEM_DEFINED(arg.prop_values_ptr, sizeof(uint64_t)*arg.count_props));
2530428d7b3dSmrg
2531428d7b3dSmrg	for (i = 0; i < arg.count_props; i++) {
2532428d7b3dSmrg		struct drm_mode_get_property prop;
2533428d7b3dSmrg
2534428d7b3dSmrg		memset(&prop, 0, sizeof(prop));
2535428d7b3dSmrg		prop.prop_id = props[i];
2536428d7b3dSmrg		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPERTY, &prop)) {
2537428d7b3dSmrg			ERR(("%s: prop[%d].id=%d GETPROPERTY failed with errno=%d\n",
2538428d7b3dSmrg			     __FUNCTION__, i, props[i], errno));
2539428d7b3dSmrg			continue;
2540428d7b3dSmrg		}
2541428d7b3dSmrg
2542428d7b3dSmrg		DBG(("%s: prop[%d] .id=%ld, .name=%s, .flags=%x, .value=%ld\n", __FUNCTION__, i,
2543428d7b3dSmrg		     (long)props[i], prop.name, (unsigned)prop.flags, (long)values[i]));
2544428d7b3dSmrg
2545428d7b3dSmrg		if (strcmp(prop.name, "type") == 0) {
2546428d7b3dSmrg			type = values[i];
2547428d7b3dSmrg		} else if (prop_is_rotation(&prop)) {
2548428d7b3dSmrg			struct drm_mode_property_enum *enums;
2549428d7b3dSmrg
2550428d7b3dSmrg			p->rotation.prop = props[i];
2551428d7b3dSmrg			p->rotation.current = values[i];
2552428d7b3dSmrg
2553428d7b3dSmrg			DBG(("%s: found rotation property .id=%d, value=%ld, num_enums=%d\n",
2554428d7b3dSmrg			     __FUNCTION__, prop.prop_id, (long)values[i], prop.count_enum_blobs));
2555428d7b3dSmrg			enums = malloc(prop.count_enum_blobs * sizeof(struct drm_mode_property_enum));
2556428d7b3dSmrg			if (enums != NULL) {
2557428d7b3dSmrg				prop.count_values = 0;
2558428d7b3dSmrg				prop.enum_blob_ptr = (uintptr_t)enums;
2559428d7b3dSmrg
2560428d7b3dSmrg				if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPERTY, &prop) == 0) {
2561428d7b3dSmrg					int j;
2562428d7b3dSmrg
2563428d7b3dSmrg					/* XXX we assume that the mapping between kernel enum and
2564428d7b3dSmrg					 * RandR remains fixed for our lifetimes.
2565428d7b3dSmrg					 */
2566428d7b3dSmrg					VG(VALGRIND_MAKE_MEM_DEFINED(enums, sizeof(*enums)*prop.count_enum_blobs));
2567428d7b3dSmrg					for (j = 0; j < prop.count_enum_blobs; j++) {
2568428d7b3dSmrg						DBG(("%s: rotation[%d] = %s [%lx]\n", __FUNCTION__,
2569428d7b3dSmrg						     j, enums[j].name, (long)enums[j].value));
2570428d7b3dSmrg						p->rotation.supported |= 1 << enums[j].value;
2571428d7b3dSmrg					}
2572428d7b3dSmrg				}
2573428d7b3dSmrg
2574428d7b3dSmrg				free(enums);
2575428d7b3dSmrg			}
2576428d7b3dSmrg		}
2577428d7b3dSmrg	}
2578428d7b3dSmrg
2579428d7b3dSmrg	if (props != (uint32_t *)stack_props)
2580428d7b3dSmrg		free(props);
2581428d7b3dSmrg
2582428d7b3dSmrg	DBG(("%s: plane=%d type=%d\n", __FUNCTION__, p->id, type));
2583428d7b3dSmrg	return type;
2584428d7b3dSmrg}
2585428d7b3dSmrg
2586428d7b3dSmrgstatic void
2587428d7b3dSmrgsna_crtc_find_planes(struct sna *sna, struct sna_crtc *crtc)
2588428d7b3dSmrg{
2589428d7b3dSmrg#define LOCAL_IOCTL_SET_CAP	DRM_IOWR(0x0d, struct local_set_cap)
2590428d7b3dSmrg#define LOCAL_IOCTL_MODE_GETPLANERESOURCES DRM_IOWR(0xb5, struct local_mode_get_plane_res)
2591428d7b3dSmrg#define LOCAL_IOCTL_MODE_GETPLANE DRM_IOWR(0xb6, struct local_mode_get_plane)
2592428d7b3dSmrg	struct local_set_cap {
2593428d7b3dSmrg		uint64_t name;
2594428d7b3dSmrg		uint64_t value;
2595428d7b3dSmrg	} cap;
2596428d7b3dSmrg	struct local_mode_get_plane_res {
2597428d7b3dSmrg		uint64_t plane_id_ptr;
2598428d7b3dSmrg		uint64_t count_planes;
2599428d7b3dSmrg	} r;
2600428d7b3dSmrg	uint32_t stack_planes[32];
2601428d7b3dSmrg	uint32_t *planes = stack_planes;
2602428d7b3dSmrg	int i;
2603428d7b3dSmrg
2604428d7b3dSmrg	VG_CLEAR(cap);
2605428d7b3dSmrg	cap.name = DRM_CLIENT_CAP_UNIVERSAL_PLANES;
2606428d7b3dSmrg	cap.value = 1;
2607428d7b3dSmrg	(void)drmIoctl(sna->kgem.fd, LOCAL_IOCTL_SET_CAP, &cap);
2608428d7b3dSmrg
2609428d7b3dSmrg	VG_CLEAR(r);
2610428d7b3dSmrg	r.plane_id_ptr = (uintptr_t)planes;
2611428d7b3dSmrg	r.count_planes = ARRAY_SIZE(stack_planes);
2612428d7b3dSmrg	if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_GETPLANERESOURCES, &r)) {
2613428d7b3dSmrg		ERR(("%s: GETPLANERESOURCES failed with errno=%d\n", __FUNCTION__, errno));
2614428d7b3dSmrg		return;
2615428d7b3dSmrg	}
2616428d7b3dSmrg
2617428d7b3dSmrg	DBG(("%s: %d planes\n", __FUNCTION__, (int)r.count_planes));
2618428d7b3dSmrg
2619428d7b3dSmrg	if (r.count_planes > ARRAY_SIZE(stack_planes)) {
2620428d7b3dSmrg		planes = malloc(sizeof(uint32_t)*r.count_planes);
2621428d7b3dSmrg		if (planes == NULL)
2622428d7b3dSmrg			return;
2623428d7b3dSmrg
2624428d7b3dSmrg		r.plane_id_ptr = (uintptr_t)planes;
2625428d7b3dSmrg		if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_GETPLANERESOURCES, &r))
2626428d7b3dSmrg			r.count_planes = 0;
2627428d7b3dSmrg	}
2628428d7b3dSmrg
2629428d7b3dSmrg	VG(VALGRIND_MAKE_MEM_DEFINED(planes, sizeof(uint32_t)*r.count_planes));
2630428d7b3dSmrg
2631428d7b3dSmrg	for (i = 0; i < r.count_planes; i++) {
2632428d7b3dSmrg		struct local_mode_get_plane {
2633428d7b3dSmrg			uint32_t plane_id;
2634428d7b3dSmrg
2635428d7b3dSmrg			uint32_t crtc_id;
2636428d7b3dSmrg			uint32_t fb_id;
2637428d7b3dSmrg
2638428d7b3dSmrg			uint32_t possible_crtcs;
2639428d7b3dSmrg			uint32_t gamma_size;
2640428d7b3dSmrg
2641428d7b3dSmrg			uint32_t count_format_types;
2642428d7b3dSmrg			uint64_t format_type_ptr;
2643428d7b3dSmrg		} p;
2644428d7b3dSmrg		struct plane details;
2645428d7b3dSmrg
2646428d7b3dSmrg		VG_CLEAR(p);
2647428d7b3dSmrg		p.plane_id = planes[i];
2648428d7b3dSmrg		p.count_format_types = 0;
2649428d7b3dSmrg		if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_GETPLANE, &p))
2650428d7b3dSmrg			continue;
2651428d7b3dSmrg
2652428d7b3dSmrg		if ((p.possible_crtcs & (1 << crtc->pipe)) == 0)
2653428d7b3dSmrg			continue;
2654428d7b3dSmrg
2655428d7b3dSmrg		DBG(("%s: plane %d is attached to our pipe=%d\n",
2656428d7b3dSmrg		     __FUNCTION__, planes[i], crtc->pipe));
2657428d7b3dSmrg
2658428d7b3dSmrg		details.id = p.plane_id;
2659428d7b3dSmrg		details.rotation.prop = 0;
2660428d7b3dSmrg		details.rotation.supported = RR_Rotate_0;
2661428d7b3dSmrg		details.rotation.current = RR_Rotate_0;
2662428d7b3dSmrg
2663428d7b3dSmrg		switch (plane_details(sna, &details)) {
2664428d7b3dSmrg		default:
2665428d7b3dSmrg			break;
2666428d7b3dSmrg
2667428d7b3dSmrg		case DRM_PLANE_TYPE_PRIMARY:
2668428d7b3dSmrg			crtc->primary = details;
2669428d7b3dSmrg			break;
2670428d7b3dSmrg
2671428d7b3dSmrg		case DRM_PLANE_TYPE_CURSOR:
2672428d7b3dSmrg			break;
2673428d7b3dSmrg
2674428d7b3dSmrg		case DRM_PLANE_TYPE_OVERLAY:
2675428d7b3dSmrg			if (crtc->sprite.id == 0)
2676428d7b3dSmrg				crtc->sprite = details;
2677428d7b3dSmrg			break;
2678428d7b3dSmrg		}
2679428d7b3dSmrg	}
2680428d7b3dSmrg
2681428d7b3dSmrg	if (planes != stack_planes)
2682428d7b3dSmrg		free(planes);
2683428d7b3dSmrg}
2684428d7b3dSmrg
2685428d7b3dSmrgstatic void
2686428d7b3dSmrgsna_crtc_init__rotation(struct sna *sna, struct sna_crtc *crtc)
2687428d7b3dSmrg{
2688428d7b3dSmrg	crtc->rotation = RR_Rotate_0;
2689428d7b3dSmrg	crtc->primary.rotation.supported = RR_Rotate_0;
2690428d7b3dSmrg	crtc->primary.rotation.current = RR_Rotate_0;
2691428d7b3dSmrg	crtc->sprite.rotation = crtc->primary.rotation;
2692428d7b3dSmrg}
2693428d7b3dSmrg
2694428d7b3dSmrgstatic void
2695428d7b3dSmrgsna_crtc_init__cursor(struct sna *sna, struct sna_crtc *crtc)
2696428d7b3dSmrg{
2697428d7b3dSmrg	struct drm_mode_cursor arg;
2698428d7b3dSmrg
2699428d7b3dSmrg	VG_CLEAR(arg);
2700428d7b3dSmrg	arg.flags = DRM_MODE_CURSOR_BO;
2701428d7b3dSmrg	arg.crtc_id = crtc->id;
2702428d7b3dSmrg	arg.width = arg.height = 0;
2703428d7b3dSmrg	arg.handle = 0;
2704428d7b3dSmrg
2705428d7b3dSmrg	(void)drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_CURSOR, &arg);
2706428d7b3dSmrg}
2707428d7b3dSmrg
2708428d7b3dSmrgstatic bool
2709428d7b3dSmrgsna_crtc_add(ScrnInfoPtr scrn, int id)
2710428d7b3dSmrg{
2711428d7b3dSmrg	struct sna *sna = to_sna(scrn);
2712428d7b3dSmrg	xf86CrtcPtr crtc;
2713428d7b3dSmrg	struct sna_crtc *sna_crtc;
2714428d7b3dSmrg	struct drm_i915_get_pipe_from_crtc_id get_pipe;
2715428d7b3dSmrg
2716428d7b3dSmrg	DBG(("%s(%d)\n", __FUNCTION__, id));
2717428d7b3dSmrg
2718428d7b3dSmrg	sna_crtc = calloc(sizeof(struct sna_crtc), 1);
2719428d7b3dSmrg	if (sna_crtc == NULL)
2720428d7b3dSmrg		return false;
2721428d7b3dSmrg
2722428d7b3dSmrg	sna_crtc->id = id;
2723428d7b3dSmrg	sna_crtc->dpms_mode = -1;
2724428d7b3dSmrg
2725428d7b3dSmrg	VG_CLEAR(get_pipe);
2726428d7b3dSmrg	get_pipe.pipe = 0;
2727428d7b3dSmrg	get_pipe.crtc_id = sna_crtc->id;
2728428d7b3dSmrg	if (drmIoctl(sna->kgem.fd,
2729428d7b3dSmrg		     DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID,
2730428d7b3dSmrg		     &get_pipe)) {
2731428d7b3dSmrg		free(sna_crtc);
2732428d7b3dSmrg		return false;
2733428d7b3dSmrg	}
2734428d7b3dSmrg	sna_crtc->pipe = get_pipe.pipe;
2735428d7b3dSmrg
2736428d7b3dSmrg	if (is_zaphod(scrn) &&
2737428d7b3dSmrg	    scrn->confScreen->device->screen != sna_crtc->pipe) {
2738428d7b3dSmrg		free(sna_crtc);
2739428d7b3dSmrg		return true;
2740428d7b3dSmrg	}
2741428d7b3dSmrg
2742428d7b3dSmrg	sna_crtc_init__rotation(sna, sna_crtc);
2743428d7b3dSmrg
2744428d7b3dSmrg	sna_crtc_find_planes(sna, sna_crtc);
2745428d7b3dSmrg
2746428d7b3dSmrg	DBG(("%s: CRTC:%d [pipe=%d], primary id=%x: supported-rotations=%x, current-rotation=%x, sprite id=%x: supported-rotations=%x, current-rotation=%x\n",
2747428d7b3dSmrg	     __FUNCTION__, sna_crtc->id, sna_crtc->pipe,
2748428d7b3dSmrg	     sna_crtc->primary.id, sna_crtc->primary.rotation.supported, sna_crtc->primary.rotation.current,
2749428d7b3dSmrg	     sna_crtc->sprite.id, sna_crtc->sprite.rotation.supported, sna_crtc->sprite.rotation.current));
2750428d7b3dSmrg
2751428d7b3dSmrg	list_init(&sna_crtc->shadow_link);
2752428d7b3dSmrg
2753428d7b3dSmrg	crtc = xf86CrtcCreate(scrn, &sna_crtc_funcs);
2754428d7b3dSmrg	if (crtc == NULL) {
2755428d7b3dSmrg		free(sna_crtc);
2756428d7b3dSmrg		return false;
2757428d7b3dSmrg	}
2758428d7b3dSmrg
2759428d7b3dSmrg	sna_crtc_init__cursor(sna, sna_crtc);
2760428d7b3dSmrg
2761428d7b3dSmrg	crtc->driver_private = sna_crtc;
2762428d7b3dSmrg	sna_crtc->base = crtc;
2763428d7b3dSmrg	DBG(("%s: attached crtc[%d] pipe=%d\n",
2764428d7b3dSmrg	     __FUNCTION__, id, sna_crtc->pipe));
2765428d7b3dSmrg
2766428d7b3dSmrg	return true;
2767428d7b3dSmrg}
2768428d7b3dSmrg
2769428d7b3dSmrgstatic bool
2770428d7b3dSmrgis_panel(int type)
2771428d7b3dSmrg{
2772428d7b3dSmrg#define DRM_MODE_CONNECTOR_LVDS 7
2773428d7b3dSmrg#define DRM_MODE_CONNECTOR_eDP 14
2774428d7b3dSmrg#define DRM_MODE_CONNECTOR_DSI 16
2775428d7b3dSmrg	return (type == DRM_MODE_CONNECTOR_LVDS ||
2776428d7b3dSmrg		type == DRM_MODE_CONNECTOR_eDP ||
2777428d7b3dSmrg		type == DRM_MODE_CONNECTOR_DSI);
2778428d7b3dSmrg}
2779428d7b3dSmrg
2780428d7b3dSmrgstatic int
2781428d7b3dSmrgfind_property(struct sna *sna, struct sna_output *output, const char *name)
2782428d7b3dSmrg{
2783428d7b3dSmrg	struct drm_mode_get_property prop;
2784428d7b3dSmrg	int i;
2785428d7b3dSmrg
2786428d7b3dSmrg	VG_CLEAR(prop);
2787428d7b3dSmrg	for (i = 0; i < output->num_props; i++) {
2788428d7b3dSmrg		prop.prop_id = output->prop_ids[i];
2789428d7b3dSmrg		prop.count_values = 0;
2790428d7b3dSmrg		prop.count_enum_blobs = 0;
2791428d7b3dSmrg		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPERTY, &prop))
2792428d7b3dSmrg			continue;
2793428d7b3dSmrg
2794428d7b3dSmrg		if (strcmp(prop.name, name) == 0)
2795428d7b3dSmrg			return i;
2796428d7b3dSmrg	}
2797428d7b3dSmrg
2798428d7b3dSmrg	return -1;
2799428d7b3dSmrg}
2800428d7b3dSmrg
2801428d7b3dSmrgstatic xf86OutputStatus
2802428d7b3dSmrgsna_output_detect(xf86OutputPtr output)
2803428d7b3dSmrg{
2804428d7b3dSmrg	struct sna *sna = to_sna(output->scrn);
2805428d7b3dSmrg	struct sna_output *sna_output = output->driver_private;
2806428d7b3dSmrg	union compat_mode_get_connector compat_conn;
2807428d7b3dSmrg
2808428d7b3dSmrg	DBG(("%s(%s:%d)\n", __FUNCTION__, output->name, sna_output->id));
2809428d7b3dSmrg
2810428d7b3dSmrg	if (!sna_output->id) {
2811428d7b3dSmrg		DBG(("%s(%s) hiding due to lost connection\n", __FUNCTION__, output->name));
2812428d7b3dSmrg		return XF86OutputStatusDisconnected;
2813428d7b3dSmrg	}
2814428d7b3dSmrg
2815428d7b3dSmrg	VG_CLEAR(compat_conn);
2816428d7b3dSmrg	compat_conn.conn.connector_id = sna_output->id;
2817428d7b3dSmrg	sna_output->num_modes = compat_conn.conn.count_modes = 0; /* reprobe */
2818428d7b3dSmrg	compat_conn.conn.count_encoders = 0;
2819428d7b3dSmrg	compat_conn.conn.count_props = sna_output->num_props;
2820428d7b3dSmrg	compat_conn.conn.props_ptr = (uintptr_t)sna_output->prop_ids;
2821428d7b3dSmrg	compat_conn.conn.prop_values_ptr = (uintptr_t)sna_output->prop_values;
2822428d7b3dSmrg
2823428d7b3dSmrg	if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCONNECTOR, &compat_conn.conn))
2824428d7b3dSmrg		return XF86OutputStatusUnknown;
2825428d7b3dSmrg	DBG(("%s(%s): num modes %d -> %d, num props %d -> %d\n",
2826428d7b3dSmrg	     __FUNCTION__, output->name,
2827428d7b3dSmrg	     sna_output->num_modes, compat_conn.conn.count_modes,
2828428d7b3dSmrg	     sna_output->num_props, compat_conn.conn.count_props));
2829428d7b3dSmrg
2830428d7b3dSmrg	assert(compat_conn.conn.count_props == sna_output->num_props);
2831428d7b3dSmrg
2832428d7b3dSmrg	while (compat_conn.conn.count_modes && compat_conn.conn.count_modes != sna_output->num_modes) {
2833428d7b3dSmrg		struct drm_mode_modeinfo *new_modes;
2834428d7b3dSmrg		int old_count;
2835428d7b3dSmrg
2836428d7b3dSmrg		old_count = sna_output->num_modes;
2837428d7b3dSmrg		new_modes = realloc(sna_output->modes,
2838428d7b3dSmrg				    sizeof(*sna_output->modes)*compat_conn.conn.count_modes);
2839428d7b3dSmrg		if (new_modes == NULL)
2840428d7b3dSmrg			break;
2841428d7b3dSmrg
2842428d7b3dSmrg		sna_output->modes = new_modes;
2843428d7b3dSmrg		sna_output->num_modes = compat_conn.conn.count_modes;
2844428d7b3dSmrg		compat_conn.conn.modes_ptr = (uintptr_t)sna_output->modes;
2845428d7b3dSmrg		compat_conn.conn.count_encoders = 0;
2846428d7b3dSmrg		compat_conn.conn.count_props = 0;
2847428d7b3dSmrg		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCONNECTOR, &compat_conn.conn)) {
2848428d7b3dSmrg			sna_output->num_modes = min(old_count, sna_output->num_modes);
2849428d7b3dSmrg			break;
2850428d7b3dSmrg		}
2851428d7b3dSmrg		VG(VALGRIND_MAKE_MEM_DEFINED(sna_output->modes, sizeof(*sna_output->modes)*sna_output->num_modes));
2852428d7b3dSmrg	}
2853428d7b3dSmrg
2854428d7b3dSmrg	DBG(("%s(%s): found %d modes, connection status=%d\n",
2855428d7b3dSmrg	     __FUNCTION__, output->name, sna_output->num_modes, compat_conn.conn.connection));
2856428d7b3dSmrg
2857428d7b3dSmrg	switch (compat_conn.conn.connection) {
2858428d7b3dSmrg	case DRM_MODE_CONNECTED:
2859428d7b3dSmrg		return XF86OutputStatusConnected;
2860428d7b3dSmrg	case DRM_MODE_DISCONNECTED:
2861428d7b3dSmrg		return XF86OutputStatusDisconnected;
2862428d7b3dSmrg	default:
2863428d7b3dSmrg	case DRM_MODE_UNKNOWNCONNECTION:
2864428d7b3dSmrg		return XF86OutputStatusUnknown;
2865428d7b3dSmrg	}
2866428d7b3dSmrg}
2867428d7b3dSmrg
2868428d7b3dSmrgstatic Bool
2869428d7b3dSmrgsna_output_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
2870428d7b3dSmrg{
2871428d7b3dSmrg	struct sna_output *sna_output = output->driver_private;
2872428d7b3dSmrg	struct sna *sna = to_sna(output->scrn);
2873428d7b3dSmrg
2874428d7b3dSmrg	if (mode->HDisplay > sna->mode.max_crtc_width)
2875428d7b3dSmrg		return MODE_VIRTUAL_X;
2876428d7b3dSmrg	if (mode->VDisplay > sna->mode.max_crtc_height)
2877428d7b3dSmrg		return MODE_VIRTUAL_Y;
2878428d7b3dSmrg
2879428d7b3dSmrg	/* Check that we can successfully pin this into the global GTT */
2880428d7b3dSmrg	if ((kgem_can_create_2d(&sna->kgem,
2881428d7b3dSmrg				mode->HDisplay, mode->VDisplay,
2882428d7b3dSmrg				sna->scrn->bitsPerPixel) & KGEM_CAN_CREATE_GTT) == 0)
2883428d7b3dSmrg		return MODE_MEM_VIRT;
2884428d7b3dSmrg
2885428d7b3dSmrg	/*
2886428d7b3dSmrg	 * If the connector type is a panel, we will use the panel limit to
2887428d7b3dSmrg	 * verfiy whether the mode is valid.
2888428d7b3dSmrg	 */
2889428d7b3dSmrg	if (sna_output->has_panel_limits) {
2890428d7b3dSmrg		if (mode->HDisplay > sna_output->panel_hdisplay ||
2891428d7b3dSmrg		    mode->VDisplay > sna_output->panel_vdisplay)
2892428d7b3dSmrg			return MODE_PANEL;
2893428d7b3dSmrg	}
2894428d7b3dSmrg
2895428d7b3dSmrg	return MODE_OK;
2896428d7b3dSmrg}
2897428d7b3dSmrg
2898428d7b3dSmrgstatic void
2899428d7b3dSmrgsna_output_attach_edid(xf86OutputPtr output)
2900428d7b3dSmrg{
2901428d7b3dSmrg	struct sna *sna = to_sna(output->scrn);
2902428d7b3dSmrg	struct sna_output *sna_output = output->driver_private;
2903428d7b3dSmrg	struct drm_mode_get_blob blob;
2904428d7b3dSmrg	void *old, *raw = NULL;
2905428d7b3dSmrg	xf86MonPtr mon = NULL;
2906428d7b3dSmrg
2907428d7b3dSmrg	if (sna_output->edid_idx == -1)
2908428d7b3dSmrg		return;
2909428d7b3dSmrg
2910428d7b3dSmrg	raw = sna_output->edid_raw;
2911428d7b3dSmrg	blob.length = sna_output->edid_len;
2912428d7b3dSmrg
2913428d7b3dSmrg	if (blob.length && output->MonInfo) {
2914428d7b3dSmrg		old = alloca(blob.length);
2915428d7b3dSmrg		memcpy(old, raw, blob.length);
2916428d7b3dSmrg	} else
2917428d7b3dSmrg		old = NULL;
2918428d7b3dSmrg
2919428d7b3dSmrg	blob.blob_id = sna_output->prop_values[sna_output->edid_idx];
2920428d7b3dSmrg	DBG(("%s: attaching EDID id=%d, current=%d\n",
2921428d7b3dSmrg	     __FUNCTION__, blob.blob_id, sna_output->edid_blob_id));
2922428d7b3dSmrg	if (blob.blob_id == sna_output->edid_blob_id && 0) { /* sigh */
2923428d7b3dSmrg		if (output->MonInfo) {
2924428d7b3dSmrg			/* XXX the property keeps on disappearing... */
2925428d7b3dSmrg			RRChangeOutputProperty(output->randr_output,
2926428d7b3dSmrg					       MakeAtom("EDID", strlen("EDID"), TRUE),
2927428d7b3dSmrg					       XA_INTEGER, 8, PropModeReplace,
2928428d7b3dSmrg					       sna_output->edid_len,
2929428d7b3dSmrg					       sna_output->edid_raw,
2930428d7b3dSmrg					       FALSE, FALSE);
2931428d7b3dSmrg
2932428d7b3dSmrg			return;
2933428d7b3dSmrg		}
2934428d7b3dSmrg
2935428d7b3dSmrg		goto skip_read;
2936428d7b3dSmrg	}
2937428d7b3dSmrg
2938428d7b3dSmrg	blob.data = (uintptr_t)raw;
2939428d7b3dSmrg	if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob))
2940428d7b3dSmrg		goto done;
2941428d7b3dSmrg
2942428d7b3dSmrg	DBG(("%s: retrieving blob id=%d, length=%d\n",
2943428d7b3dSmrg	     __FUNCTION__, blob.blob_id, blob.length));
2944428d7b3dSmrg
2945428d7b3dSmrg	if (blob.length > sna_output->edid_len) {
2946428d7b3dSmrg		raw = realloc(raw, blob.length);
2947428d7b3dSmrg		if (raw == NULL)
2948428d7b3dSmrg			goto done;
2949428d7b3dSmrg
2950428d7b3dSmrg		VG(memset(raw, 0, blob.length));
2951428d7b3dSmrg		blob.data = (uintptr_t)raw;
2952428d7b3dSmrg		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob))
2953428d7b3dSmrg			goto done;
2954428d7b3dSmrg	}
2955428d7b3dSmrg
2956428d7b3dSmrg	if (old &&
2957428d7b3dSmrg	    blob.length == sna_output->edid_len &&
2958428d7b3dSmrg	    memcmp(old, raw, blob.length) == 0) {
2959428d7b3dSmrg		assert(sna_output->edid_raw == raw);
2960428d7b3dSmrg		sna_output->edid_blob_id = blob.blob_id;
2961428d7b3dSmrg		RRChangeOutputProperty(output->randr_output,
2962428d7b3dSmrg				       MakeAtom("EDID", strlen("EDID"), TRUE),
2963428d7b3dSmrg				       XA_INTEGER, 8, PropModeReplace,
2964428d7b3dSmrg				       sna_output->edid_len,
2965428d7b3dSmrg				       sna_output->edid_raw,
2966428d7b3dSmrg				       FALSE, FALSE);
2967428d7b3dSmrg		return;
2968428d7b3dSmrg	}
2969428d7b3dSmrg
2970428d7b3dSmrgskip_read:
2971428d7b3dSmrg	if (raw) {
2972428d7b3dSmrg		mon = xf86InterpretEDID(output->scrn->scrnIndex, raw);
2973428d7b3dSmrg		if (mon && blob.length > 128)
2974428d7b3dSmrg			mon->flags |= MONITOR_EDID_COMPLETE_RAWDATA;
2975428d7b3dSmrg	}
2976428d7b3dSmrg
2977428d7b3dSmrgdone:
2978428d7b3dSmrg	xf86OutputSetEDID(output, mon);
2979428d7b3dSmrg	if (raw) {
2980428d7b3dSmrg		sna_output->edid_raw = raw;
2981428d7b3dSmrg		sna_output->edid_len = blob.length;
2982428d7b3dSmrg		sna_output->edid_blob_id = blob.blob_id;
2983428d7b3dSmrg	}
2984428d7b3dSmrg}
2985428d7b3dSmrg
2986428d7b3dSmrgstatic DisplayModePtr
2987428d7b3dSmrgdefault_modes(void)
2988428d7b3dSmrg{
2989428d7b3dSmrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,6,99,900,0)
2990428d7b3dSmrg	return xf86GetDefaultModes();
2991428d7b3dSmrg#else
2992428d7b3dSmrg	return xf86GetDefaultModes(0, 0);
2993428d7b3dSmrg#endif
2994428d7b3dSmrg}
2995428d7b3dSmrg
2996428d7b3dSmrgstatic DisplayModePtr
2997428d7b3dSmrgsna_output_panel_edid(xf86OutputPtr output, DisplayModePtr modes)
2998428d7b3dSmrg{
2999428d7b3dSmrg	xf86MonPtr mon = output->MonInfo;
3000428d7b3dSmrg	DisplayModePtr i, m, preferred = NULL;
3001428d7b3dSmrg	int max_x = 0, max_y = 0;
3002428d7b3dSmrg	float max_vrefresh = 0.0;
3003428d7b3dSmrg
3004428d7b3dSmrg	if (mon && GTF_SUPPORTED(mon->features.msc))
3005428d7b3dSmrg		return modes;
3006428d7b3dSmrg
3007428d7b3dSmrg	for (m = modes; m; m = m->next) {
3008428d7b3dSmrg		if (m->type & M_T_PREFERRED)
3009428d7b3dSmrg			preferred = m;
3010428d7b3dSmrg		max_x = max(max_x, m->HDisplay);
3011428d7b3dSmrg		max_y = max(max_y, m->VDisplay);
3012428d7b3dSmrg		max_vrefresh = max(max_vrefresh, xf86ModeVRefresh(m));
3013428d7b3dSmrg	}
3014428d7b3dSmrg
3015428d7b3dSmrg	max_vrefresh = max(max_vrefresh, 60.0);
3016428d7b3dSmrg	max_vrefresh *= (1 + SYNC_TOLERANCE);
3017428d7b3dSmrg
3018428d7b3dSmrg	m = default_modes();
3019428d7b3dSmrg	xf86ValidateModesSize(output->scrn, m, max_x, max_y, 0);
3020428d7b3dSmrg
3021428d7b3dSmrg	for (i = m; i; i = i->next) {
3022428d7b3dSmrg		if (xf86ModeVRefresh(i) > max_vrefresh)
3023428d7b3dSmrg			i->status = MODE_VSYNC;
3024428d7b3dSmrg		if (preferred &&
3025428d7b3dSmrg		    i->HDisplay >= preferred->HDisplay &&
3026428d7b3dSmrg		    i->VDisplay >= preferred->VDisplay &&
3027428d7b3dSmrg		    xf86ModeVRefresh(i) >= xf86ModeVRefresh(preferred))
3028428d7b3dSmrg			i->status = MODE_PANEL;
3029428d7b3dSmrg	}
3030428d7b3dSmrg
3031428d7b3dSmrg	xf86PruneInvalidModes(output->scrn, &m, FALSE);
3032428d7b3dSmrg
3033428d7b3dSmrg	return xf86ModesAdd(modes, m);
3034428d7b3dSmrg}
3035428d7b3dSmrg
3036428d7b3dSmrgstatic DisplayModePtr
3037428d7b3dSmrgsna_output_get_modes(xf86OutputPtr output)
3038428d7b3dSmrg{
3039428d7b3dSmrg	struct sna_output *sna_output = output->driver_private;
3040428d7b3dSmrg	DisplayModePtr Modes = NULL, current = NULL;
3041428d7b3dSmrg	int i;
3042428d7b3dSmrg
3043428d7b3dSmrg	DBG(("%s(%s:%d)\n", __FUNCTION__, output->name, sna_output->id));
3044428d7b3dSmrg	assert(sna_output->id);
3045428d7b3dSmrg
3046428d7b3dSmrg	sna_output_attach_edid(output);
3047428d7b3dSmrg
3048428d7b3dSmrg	if (output->crtc) {
3049428d7b3dSmrg		struct drm_mode_crtc mode;
3050428d7b3dSmrg
3051428d7b3dSmrg		VG_CLEAR(mode);
3052428d7b3dSmrg		assert(to_sna_crtc(output->crtc));
3053428d7b3dSmrg		mode.crtc_id = to_sna_crtc(output->crtc)->id;
3054428d7b3dSmrg
3055428d7b3dSmrg		if (drmIoctl(to_sna(output->scrn)->kgem.fd, DRM_IOCTL_MODE_GETCRTC, &mode) == 0) {
3056428d7b3dSmrg			DBG(("%s: CRTC:%d, pipe=%d: has mode?=%d\n", __FUNCTION__,
3057428d7b3dSmrg			     to_sna_crtc(output->crtc)->id,
3058428d7b3dSmrg			     to_sna_crtc(output->crtc)->pipe,
3059428d7b3dSmrg			     mode.mode_valid && mode.mode.clock));
3060428d7b3dSmrg
3061428d7b3dSmrg			if (mode.mode_valid && mode.mode.clock) {
3062428d7b3dSmrg				current = calloc(1, sizeof(DisplayModeRec));
3063428d7b3dSmrg				if (current) {
3064428d7b3dSmrg					mode_from_kmode(output->scrn, &mode.mode, current);
3065428d7b3dSmrg					current->type |= M_T_DRIVER | M_T_PREFERRED;
3066428d7b3dSmrg				}
3067428d7b3dSmrg			}
3068428d7b3dSmrg		}
3069428d7b3dSmrg	}
3070428d7b3dSmrg
3071428d7b3dSmrg	DBG(("%s: adding %d probed modes\n", __FUNCTION__, sna_output->num_modes));
3072428d7b3dSmrg
3073428d7b3dSmrg	for (i = 0; i < sna_output->num_modes; i++) {
3074428d7b3dSmrg		DisplayModePtr mode;
3075428d7b3dSmrg
3076428d7b3dSmrg		mode = calloc(1, sizeof(DisplayModeRec));
3077428d7b3dSmrg		if (mode == NULL)
3078428d7b3dSmrg			continue;
3079428d7b3dSmrg
3080428d7b3dSmrg		mode = mode_from_kmode(output->scrn,
3081428d7b3dSmrg				       &sna_output->modes[i],
3082428d7b3dSmrg				       mode);
3083428d7b3dSmrg		Modes = xf86ModesAdd(Modes, mode);
3084428d7b3dSmrg		if (current && xf86ModesEqual(mode, current)) {
3085428d7b3dSmrg			free((void*)current->name);
3086428d7b3dSmrg			free(current);
3087428d7b3dSmrg			current = NULL;
3088428d7b3dSmrg		}
3089428d7b3dSmrg		if (current && mode->type & M_T_PREFERRED)
3090428d7b3dSmrg			current->type &= ~M_T_PREFERRED;
3091428d7b3dSmrg	}
3092428d7b3dSmrg
3093428d7b3dSmrg	if (current)
3094428d7b3dSmrg		Modes = xf86ModesAdd(current, Modes);
3095428d7b3dSmrg
3096428d7b3dSmrg	/*
3097428d7b3dSmrg	 * If the connector type is a panel, we will traverse the kernel mode to
3098428d7b3dSmrg	 * get the panel limit. And then add all the standard modes to fake
3099428d7b3dSmrg	 * the fullscreen experience.
3100428d7b3dSmrg	 * If it is incorrect, please fix me.
3101428d7b3dSmrg	 */
3102428d7b3dSmrg	sna_output->has_panel_limits = false;
3103428d7b3dSmrg	if (sna_output->is_panel) {
3104428d7b3dSmrg		sna_output->panel_hdisplay = sna_output->panel_vdisplay = 0;
3105428d7b3dSmrg		for (i = 0; i < sna_output->num_modes; i++) {
3106428d7b3dSmrg			struct drm_mode_modeinfo *m;
3107428d7b3dSmrg
3108428d7b3dSmrg			m = &sna_output->modes[i];
3109428d7b3dSmrg			if (m->hdisplay > sna_output->panel_hdisplay)
3110428d7b3dSmrg				sna_output->panel_hdisplay = m->hdisplay;
3111428d7b3dSmrg			if (m->vdisplay > sna_output->panel_vdisplay)
3112428d7b3dSmrg				sna_output->panel_vdisplay = m->vdisplay;
3113428d7b3dSmrg		}
3114428d7b3dSmrg		sna_output->has_panel_limits =
3115428d7b3dSmrg			sna_output->panel_hdisplay &&
3116428d7b3dSmrg			sna_output->panel_vdisplay;
3117428d7b3dSmrg	}
3118428d7b3dSmrg
3119428d7b3dSmrg	if (sna_output->add_default_modes)
3120428d7b3dSmrg		Modes = sna_output_panel_edid(output, Modes);
3121428d7b3dSmrg
3122428d7b3dSmrg	return Modes;
3123428d7b3dSmrg}
3124428d7b3dSmrg
3125428d7b3dSmrgstatic void
3126428d7b3dSmrgsna_output_destroy(xf86OutputPtr output)
3127428d7b3dSmrg{
3128428d7b3dSmrg	struct sna_output *sna_output = output->driver_private;
3129428d7b3dSmrg	int i;
3130428d7b3dSmrg
3131428d7b3dSmrg	if (sna_output == NULL)
3132428d7b3dSmrg		return;
3133428d7b3dSmrg
3134428d7b3dSmrg	free(sna_output->edid_raw);
3135428d7b3dSmrg	for (i = 0; i < sna_output->num_props; i++) {
3136428d7b3dSmrg		if (sna_output->props[i].kprop == NULL)
3137428d7b3dSmrg			continue;
3138428d7b3dSmrg
3139428d7b3dSmrg		if (sna_output->props[i].atoms) {
3140428d7b3dSmrg			if (output->randr_output)
3141428d7b3dSmrg				RRDeleteOutputProperty(output->randr_output, sna_output->props[i].atoms[0]);
3142428d7b3dSmrg			free(sna_output->props[i].atoms);
3143428d7b3dSmrg		}
3144428d7b3dSmrg
3145428d7b3dSmrg		drmModeFreeProperty(sna_output->props[i].kprop);
3146428d7b3dSmrg	}
3147428d7b3dSmrg	free(sna_output->props);
3148428d7b3dSmrg	free(sna_output->prop_ids);
3149428d7b3dSmrg	free(sna_output->prop_values);
3150428d7b3dSmrg
3151428d7b3dSmrg	backlight_close(&sna_output->backlight);
3152428d7b3dSmrg
3153428d7b3dSmrg	free(sna_output);
3154428d7b3dSmrg	output->driver_private = NULL;
3155428d7b3dSmrg}
3156428d7b3dSmrg
3157428d7b3dSmrgstatic void
3158428d7b3dSmrgsna_output_dpms(xf86OutputPtr output, int dpms)
3159428d7b3dSmrg{
3160428d7b3dSmrg	struct sna *sna = to_sna(output->scrn);
3161428d7b3dSmrg	struct sna_output *sna_output = output->driver_private;
3162428d7b3dSmrg	int old_dpms = sna_output->dpms_mode;
3163428d7b3dSmrg
3164428d7b3dSmrg	DBG(("%s(%s:%d): dpms=%d (current: %d), active? %d\n",
3165428d7b3dSmrg	     __FUNCTION__, output->name, sna_output->id,
3166428d7b3dSmrg	     dpms, sna_output->dpms_mode,
3167428d7b3dSmrg	     output->crtc != NULL));
3168428d7b3dSmrg
3169428d7b3dSmrg	if (!sna_output->id)
3170428d7b3dSmrg		return;
3171428d7b3dSmrg
3172428d7b3dSmrg	if (old_dpms == dpms)
3173428d7b3dSmrg		return;
3174428d7b3dSmrg
3175428d7b3dSmrg	/* Record the value of the backlight before turning
3176428d7b3dSmrg	 * off the display, and reset if after turning it on.
3177428d7b3dSmrg	 * Order is important as the kernel may record and also
3178428d7b3dSmrg	 * reset the backlight across DPMS. Hence we need to
3179428d7b3dSmrg	 * record the value before the kernel modifies it
3180428d7b3dSmrg	 * and reapply it afterwards.
3181428d7b3dSmrg	 */
3182428d7b3dSmrg	if (sna_output->backlight.iface && dpms != DPMSModeOn) {
3183428d7b3dSmrg		if (old_dpms == DPMSModeOn) {
3184428d7b3dSmrg			sna_output->backlight_active_level = sna_output_backlight_get(output);
3185428d7b3dSmrg			DBG(("%s: saving current backlight %d\n",
3186428d7b3dSmrg			     __FUNCTION__, sna_output->backlight_active_level));
3187428d7b3dSmrg		}
3188428d7b3dSmrg		sna_output->dpms_mode = dpms;
3189428d7b3dSmrg		sna_output_backlight_off(sna_output);
3190428d7b3dSmrg	}
3191428d7b3dSmrg
3192428d7b3dSmrg	if (output->crtc &&
3193428d7b3dSmrg	    drmModeConnectorSetProperty(sna->kgem.fd,
3194428d7b3dSmrg					sna_output->id,
3195428d7b3dSmrg					sna_output->dpms_id,
3196428d7b3dSmrg					dpms))
3197428d7b3dSmrg		dpms = old_dpms;
3198428d7b3dSmrg
3199428d7b3dSmrg	if (sna_output->backlight.iface && dpms == DPMSModeOn) {
3200428d7b3dSmrg		DBG(("%s: restoring previous backlight %d\n",
3201428d7b3dSmrg		     __FUNCTION__, sna_output->backlight_active_level));
3202428d7b3dSmrg		sna_output_backlight_on(sna_output);
3203428d7b3dSmrg	}
3204428d7b3dSmrg
3205428d7b3dSmrg	sna_output->dpms_mode = dpms;
3206428d7b3dSmrg}
3207428d7b3dSmrg
3208428d7b3dSmrgstatic bool
3209428d7b3dSmrgsna_property_ignore(drmModePropertyPtr prop)
3210428d7b3dSmrg{
3211428d7b3dSmrg	if (!prop)
3212428d7b3dSmrg		return true;
3213428d7b3dSmrg
3214428d7b3dSmrg	/* ignore blob prop */
3215428d7b3dSmrg	if (prop->flags & DRM_MODE_PROP_BLOB)
3216428d7b3dSmrg		return true;
3217428d7b3dSmrg
3218428d7b3dSmrg	/* ignore standard property */
3219428d7b3dSmrg	if (!strcmp(prop->name, "EDID") ||
3220428d7b3dSmrg	    !strcmp(prop->name, "DPMS"))
3221428d7b3dSmrg		return true;
3222428d7b3dSmrg
3223428d7b3dSmrg	return false;
3224428d7b3dSmrg}
3225428d7b3dSmrg
3226428d7b3dSmrgstatic void
3227428d7b3dSmrgsna_output_create_ranged_atom(xf86OutputPtr output, Atom *atom,
3228428d7b3dSmrg			      const char *name, INT32 min, INT32 max,
3229428d7b3dSmrg			      uint64_t value, Bool immutable)
3230428d7b3dSmrg{
3231428d7b3dSmrg	int err;
3232428d7b3dSmrg	INT32 atom_range[2];
3233428d7b3dSmrg
3234428d7b3dSmrg	atom_range[0] = min;
3235428d7b3dSmrg	atom_range[1] = max;
3236428d7b3dSmrg
3237428d7b3dSmrg	*atom = MakeAtom(name, strlen(name), TRUE);
3238428d7b3dSmrg
3239428d7b3dSmrg	err = RRConfigureOutputProperty(output->randr_output, *atom, FALSE,
3240428d7b3dSmrg					TRUE, immutable, 2, atom_range);
3241428d7b3dSmrg	if (err != 0)
3242428d7b3dSmrg		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
3243428d7b3dSmrg			   "RRConfigureOutputProperty error, %d\n", err);
3244428d7b3dSmrg
3245428d7b3dSmrg	err = RRChangeOutputProperty(output->randr_output, *atom, XA_INTEGER,
3246428d7b3dSmrg				     32, PropModeReplace, 1, &value,
3247428d7b3dSmrg				     FALSE, FALSE);
3248428d7b3dSmrg	if (err != 0)
3249428d7b3dSmrg		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
3250428d7b3dSmrg			   "RRChangeOutputProperty error, %d\n", err);
3251428d7b3dSmrg}
3252428d7b3dSmrg
3253428d7b3dSmrgstatic void
3254428d7b3dSmrgsna_output_create_resources(xf86OutputPtr output)
3255428d7b3dSmrg{
3256428d7b3dSmrg	struct sna *sna = to_sna(output->scrn);
3257428d7b3dSmrg	struct sna_output *sna_output = output->driver_private;
3258428d7b3dSmrg	int i, j, err;
3259428d7b3dSmrg
3260428d7b3dSmrg	sna_output->props = calloc(sna_output->num_props,
3261428d7b3dSmrg				   sizeof(struct sna_property));
3262428d7b3dSmrg	if (!sna_output->props)
3263428d7b3dSmrg		return;
3264428d7b3dSmrg
3265428d7b3dSmrg	for (i = 0; i < sna_output->num_props; i++) {
3266428d7b3dSmrg		struct sna_property *p = &sna_output->props[i];
3267428d7b3dSmrg
3268428d7b3dSmrg		p->kprop = drmModeGetProperty(sna->kgem.fd,
3269428d7b3dSmrg					      sna_output->prop_ids[i]);
3270428d7b3dSmrg		if (sna_property_ignore(p->kprop)) {
3271428d7b3dSmrg			drmModeFreeProperty(p->kprop);
3272428d7b3dSmrg			p->kprop = NULL;
3273428d7b3dSmrg			continue;
3274428d7b3dSmrg		}
3275428d7b3dSmrg
3276428d7b3dSmrg		if (p->kprop->flags & DRM_MODE_PROP_RANGE) {
3277428d7b3dSmrg			p->num_atoms = 1;
3278428d7b3dSmrg			p->atoms = calloc(p->num_atoms, sizeof(Atom));
3279428d7b3dSmrg			if (!p->atoms)
3280428d7b3dSmrg				continue;
3281428d7b3dSmrg
3282428d7b3dSmrg			sna_output_create_ranged_atom(output, &p->atoms[0],
3283428d7b3dSmrg						      p->kprop->name,
3284428d7b3dSmrg						      p->kprop->values[0],
3285428d7b3dSmrg						      p->kprop->values[1],
3286428d7b3dSmrg						      sna_output->prop_values[i],
3287428d7b3dSmrg						      p->kprop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE);
3288428d7b3dSmrg
3289428d7b3dSmrg		} else if (p->kprop->flags & DRM_MODE_PROP_ENUM) {
3290428d7b3dSmrg			p->num_atoms = p->kprop->count_enums + 1;
3291428d7b3dSmrg			p->atoms = calloc(p->num_atoms, sizeof(Atom));
3292428d7b3dSmrg			if (!p->atoms)
3293428d7b3dSmrg				continue;
3294428d7b3dSmrg
3295428d7b3dSmrg			p->atoms[0] = MakeAtom(p->kprop->name, strlen(p->kprop->name), TRUE);
3296428d7b3dSmrg			for (j = 1; j <= p->kprop->count_enums; j++) {
3297428d7b3dSmrg				struct drm_mode_property_enum *e = &p->kprop->enums[j-1];
3298428d7b3dSmrg				p->atoms[j] = MakeAtom(e->name, strlen(e->name), TRUE);
3299428d7b3dSmrg			}
3300428d7b3dSmrg
3301428d7b3dSmrg			err = RRConfigureOutputProperty(output->randr_output, p->atoms[0],
3302428d7b3dSmrg							FALSE, FALSE,
3303428d7b3dSmrg							p->kprop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE,
3304428d7b3dSmrg							p->num_atoms - 1, (INT32 *)&p->atoms[1]);
3305428d7b3dSmrg			if (err != 0) {
3306428d7b3dSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
3307428d7b3dSmrg					   "RRConfigureOutputProperty error, %d\n", err);
3308428d7b3dSmrg			}
3309428d7b3dSmrg
3310428d7b3dSmrg			for (j = 0; j < p->kprop->count_enums; j++)
3311428d7b3dSmrg				if (p->kprop->enums[j].value == sna_output->prop_values[i])
3312428d7b3dSmrg					break;
3313428d7b3dSmrg			/* there's always a matching value */
3314428d7b3dSmrg			err = RRChangeOutputProperty(output->randr_output, p->atoms[0],
3315428d7b3dSmrg						     XA_ATOM, 32, PropModeReplace, 1, &p->atoms[j+1],
3316428d7b3dSmrg						     FALSE, FALSE);
3317428d7b3dSmrg			if (err != 0) {
3318428d7b3dSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
3319428d7b3dSmrg					   "RRChangeOutputProperty error, %d\n", err);
3320428d7b3dSmrg			}
3321428d7b3dSmrg		}
3322428d7b3dSmrg	}
3323428d7b3dSmrg
3324428d7b3dSmrg	if (sna_output->backlight.iface) {
3325428d7b3dSmrg		/* Set up the backlight property, which takes effect
3326428d7b3dSmrg		 * immediately and accepts values only within the
3327428d7b3dSmrg		 * backlight_range.
3328428d7b3dSmrg		 */
3329428d7b3dSmrg		sna_output_create_ranged_atom(output, &backlight_atom,
3330428d7b3dSmrg					      BACKLIGHT_NAME, 0,
3331428d7b3dSmrg					      sna_output->backlight.max,
3332428d7b3dSmrg					      sna_output->backlight_active_level,
3333428d7b3dSmrg					      FALSE);
3334428d7b3dSmrg		sna_output_create_ranged_atom(output,
3335428d7b3dSmrg					      &backlight_deprecated_atom,
3336428d7b3dSmrg					      BACKLIGHT_DEPRECATED_NAME, 0,
3337428d7b3dSmrg					      sna_output->backlight.max,
3338428d7b3dSmrg					      sna_output->backlight_active_level,
3339428d7b3dSmrg					      FALSE);
3340428d7b3dSmrg	}
3341428d7b3dSmrg}
3342428d7b3dSmrg
3343428d7b3dSmrgstatic Bool
3344428d7b3dSmrgsna_output_set_property(xf86OutputPtr output, Atom property,
3345428d7b3dSmrg			RRPropertyValuePtr value)
3346428d7b3dSmrg{
3347428d7b3dSmrg	struct sna *sna = to_sna(output->scrn);
3348428d7b3dSmrg	struct sna_output *sna_output = output->driver_private;
3349428d7b3dSmrg	int i;
3350428d7b3dSmrg
3351428d7b3dSmrg	if (property == backlight_atom || property == backlight_deprecated_atom) {
3352428d7b3dSmrg		INT32 val;
3353428d7b3dSmrg		int ret = 0;
3354428d7b3dSmrg
3355428d7b3dSmrg		if (value->type != XA_INTEGER || value->format != 32 ||
3356428d7b3dSmrg		    value->size != 1)
3357428d7b3dSmrg		{
3358428d7b3dSmrg			return FALSE;
3359428d7b3dSmrg		}
3360428d7b3dSmrg
3361428d7b3dSmrg		val = *(INT32 *)value->data;
3362428d7b3dSmrg		DBG(("%s: setting backlight to %d (max=%d)\n",
3363428d7b3dSmrg		     __FUNCTION__, (int)val, sna_output->backlight.max));
3364428d7b3dSmrg		if (val < 0 || val > sna_output->backlight.max)
3365428d7b3dSmrg			return FALSE;
3366428d7b3dSmrg
3367428d7b3dSmrg		sna_output->backlight_active_level = val;
3368428d7b3dSmrg		if (sna_output->dpms_mode == DPMSModeOn)
3369428d7b3dSmrg			ret = sna_output_backlight_set(sna_output, val);
3370428d7b3dSmrg		return ret == 0;
3371428d7b3dSmrg	}
3372428d7b3dSmrg
3373428d7b3dSmrg	if (!sna_output->id)
3374428d7b3dSmrg		return TRUE;
3375428d7b3dSmrg
3376428d7b3dSmrg	for (i = 0; i < sna_output->num_props; i++) {
3377428d7b3dSmrg		struct sna_property *p = &sna_output->props[i];
3378428d7b3dSmrg
3379428d7b3dSmrg		if (p->atoms == NULL || p->atoms[0] != property)
3380428d7b3dSmrg			continue;
3381428d7b3dSmrg
3382428d7b3dSmrg		if (p->kprop->flags & DRM_MODE_PROP_RANGE) {
3383428d7b3dSmrg			uint32_t val;
3384428d7b3dSmrg
3385428d7b3dSmrg			if (value->type != XA_INTEGER || value->format != 32 ||
3386428d7b3dSmrg			    value->size != 1)
3387428d7b3dSmrg				return FALSE;
3388428d7b3dSmrg			val = *(uint32_t *)value->data;
3389428d7b3dSmrg
3390428d7b3dSmrg			drmModeConnectorSetProperty(sna->kgem.fd, sna_output->id,
3391428d7b3dSmrg						    p->kprop->prop_id, (uint64_t)val);
3392428d7b3dSmrg			return TRUE;
3393428d7b3dSmrg		} else if (p->kprop->flags & DRM_MODE_PROP_ENUM) {
3394428d7b3dSmrg			Atom	atom;
3395428d7b3dSmrg			const char	*name;
3396428d7b3dSmrg			int		j;
3397428d7b3dSmrg
3398428d7b3dSmrg			if (value->type != XA_ATOM || value->format != 32 || value->size != 1)
3399428d7b3dSmrg				return FALSE;
3400428d7b3dSmrg			memcpy(&atom, value->data, 4);
3401428d7b3dSmrg			name = NameForAtom(atom);
3402428d7b3dSmrg			if (name == NULL)
3403428d7b3dSmrg				return FALSE;
3404428d7b3dSmrg
3405428d7b3dSmrg			/* search for matching name string, then set its value down */
3406428d7b3dSmrg			for (j = 0; j < p->kprop->count_enums; j++) {
3407428d7b3dSmrg				if (!strcmp(p->kprop->enums[j].name, name)) {
3408428d7b3dSmrg					drmModeConnectorSetProperty(sna->kgem.fd, sna_output->id,
3409428d7b3dSmrg								    p->kprop->prop_id, p->kprop->enums[j].value);
3410428d7b3dSmrg					return TRUE;
3411428d7b3dSmrg				}
3412428d7b3dSmrg			}
3413428d7b3dSmrg			return FALSE;
3414428d7b3dSmrg		}
3415428d7b3dSmrg	}
3416428d7b3dSmrg
3417428d7b3dSmrg	/* We didn't recognise this property, just report success in order
3418428d7b3dSmrg	 * to allow the set to continue, otherwise we break setting of
3419428d7b3dSmrg	 * common properties like EDID.
3420428d7b3dSmrg	 */
3421428d7b3dSmrg	return TRUE;
3422428d7b3dSmrg}
3423428d7b3dSmrg
3424428d7b3dSmrgstatic Bool
3425428d7b3dSmrgsna_output_get_property(xf86OutputPtr output, Atom property)
3426428d7b3dSmrg{
3427428d7b3dSmrg	struct sna_output *sna_output = output->driver_private;
3428428d7b3dSmrg	int err;
3429428d7b3dSmrg
3430428d7b3dSmrg	if (property == backlight_atom || property == backlight_deprecated_atom) {
3431428d7b3dSmrg		INT32 val;
3432428d7b3dSmrg
3433428d7b3dSmrg		if (!sna_output->backlight.iface)
3434428d7b3dSmrg			return FALSE;
3435428d7b3dSmrg
3436428d7b3dSmrg		if (sna_output->dpms_mode == DPMSModeOn) {
3437428d7b3dSmrg			val = sna_output_backlight_get(output);
3438428d7b3dSmrg			if (val < 0)
3439428d7b3dSmrg				return FALSE;
3440428d7b3dSmrg			DBG(("%s(%s): output on, reporting actual backlight value [%d]\n",
3441428d7b3dSmrg			     __FUNCTION__, output->name, val));
3442428d7b3dSmrg		} else {
3443428d7b3dSmrg			val = sna_output->backlight_active_level;
3444428d7b3dSmrg			DBG(("%s(%s): output off, reporting cached backlight value [%d]\n",
3445428d7b3dSmrg			     __FUNCTION__, output->name, val));
3446428d7b3dSmrg		}
3447428d7b3dSmrg
3448428d7b3dSmrg		err = RRChangeOutputProperty(output->randr_output, property,
3449428d7b3dSmrg					     XA_INTEGER, 32, PropModeReplace, 1, &val,
3450428d7b3dSmrg					     FALSE, FALSE);
3451428d7b3dSmrg		if (err != 0) {
3452428d7b3dSmrg			xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
3453428d7b3dSmrg				   "RRChangeOutputProperty error, %d\n", err);
3454428d7b3dSmrg			return FALSE;
3455428d7b3dSmrg		}
3456428d7b3dSmrg
3457428d7b3dSmrg		return TRUE;
3458428d7b3dSmrg	}
3459428d7b3dSmrg
3460428d7b3dSmrg	return FALSE;
3461428d7b3dSmrg}
3462428d7b3dSmrg
3463428d7b3dSmrgstatic const xf86OutputFuncsRec sna_output_funcs = {
3464428d7b3dSmrg	.create_resources = sna_output_create_resources,
3465428d7b3dSmrg#ifdef RANDR_12_INTERFACE
3466428d7b3dSmrg	.set_property = sna_output_set_property,
3467428d7b3dSmrg	.get_property = sna_output_get_property,
3468428d7b3dSmrg#endif
3469428d7b3dSmrg	.dpms = sna_output_dpms,
3470428d7b3dSmrg	.detect = sna_output_detect,
3471428d7b3dSmrg	.mode_valid = sna_output_mode_valid,
3472428d7b3dSmrg
3473428d7b3dSmrg	.get_modes = sna_output_get_modes,
3474428d7b3dSmrg	.destroy = sna_output_destroy
3475428d7b3dSmrg};
3476428d7b3dSmrg
3477428d7b3dSmrgstatic const int subpixel_conv_table[] = {
3478428d7b3dSmrg	SubPixelUnknown,
3479428d7b3dSmrg	SubPixelHorizontalRGB,
3480428d7b3dSmrg	SubPixelHorizontalBGR,
3481428d7b3dSmrg	SubPixelVerticalRGB,
3482428d7b3dSmrg	SubPixelVerticalBGR,
3483428d7b3dSmrg	SubPixelNone
3484428d7b3dSmrg};
3485428d7b3dSmrg
3486428d7b3dSmrgstatic const char * const output_names[] = {
3487428d7b3dSmrg	/* DRM_MODE_CONNECTOR_Unknown */	"None",
3488428d7b3dSmrg	/* DRM_MODE_CONNECTOR_VGA */		"VGA",
3489428d7b3dSmrg	/* DRM_MODE_CONNECTOR_DVII */		"DVI",
3490428d7b3dSmrg	/* DRM_MODE_CONNECTOR_DVID */		"DVI",
3491428d7b3dSmrg	/* DRM_MODE_CONNECTOR_DVIA */		"DVI",
3492428d7b3dSmrg	/* DRM_MODE_CONNECTOR_Composite */	"Composite",
3493428d7b3dSmrg	/* DRM_MODE_CONNECTOR_SVIDEO */		"TV",
3494428d7b3dSmrg	/* DRM_MODE_CONNECTOR_LVDS */		"LVDS",
3495428d7b3dSmrg	/* DRM_MODE_CONNECTOR_Component */	"CTV",
3496428d7b3dSmrg	/* DRM_MODE_CONNECTOR_9PinDIN */	"DIN",
3497428d7b3dSmrg	/* DRM_MODE_CONNECTOR_DisplayPort */	"DP",
3498428d7b3dSmrg	/* DRM_MODE_CONNECTOR_HDMIA */		"HDMI",
3499428d7b3dSmrg	/* DRM_MODE_CONNECTOR_HDMIB */		"HDMI",
3500428d7b3dSmrg	/* DRM_MODE_CONNECTOR_TV */		"TV",
3501428d7b3dSmrg	/* DRM_MODE_CONNECTOR_eDP */		"eDP",
3502428d7b3dSmrg	/* DRM_MODE_CONNECTOR_VIRTUAL */	"Virtual",
3503428d7b3dSmrg	/* DRM_MODE_CONNECTOR_DSI */		"DSI"
3504428d7b3dSmrg};
3505428d7b3dSmrg
3506428d7b3dSmrgstatic bool
3507428d7b3dSmrgsna_zaphod_match(const char *s, const char *output)
3508428d7b3dSmrg{
3509428d7b3dSmrg	char t[20];
3510428d7b3dSmrg	unsigned int i = 0;
3511428d7b3dSmrg
3512428d7b3dSmrg	do {
3513428d7b3dSmrg		/* match any outputs in a comma list, stopping at whitespace */
3514428d7b3dSmrg		switch (*s) {
3515428d7b3dSmrg		case '\0':
3516428d7b3dSmrg			t[i] = '\0';
3517428d7b3dSmrg			return strcmp(t, output) == 0;
3518428d7b3dSmrg
3519428d7b3dSmrg		case ',':
3520428d7b3dSmrg			t[i] ='\0';
3521428d7b3dSmrg			if (strcmp(t, output) == 0)
3522428d7b3dSmrg				return TRUE;
3523428d7b3dSmrg			i = 0;
3524428d7b3dSmrg			break;
3525428d7b3dSmrg
3526428d7b3dSmrg		case ' ':
3527428d7b3dSmrg		case '\t':
3528428d7b3dSmrg		case '\n':
3529428d7b3dSmrg		case '\r':
3530428d7b3dSmrg			break;
3531428d7b3dSmrg
3532428d7b3dSmrg		default:
3533428d7b3dSmrg			t[i++] = *s;
3534428d7b3dSmrg			break;
3535428d7b3dSmrg		}
3536428d7b3dSmrg
3537428d7b3dSmrg		s++;
3538428d7b3dSmrg	} while (i < sizeof(t));
3539428d7b3dSmrg
3540428d7b3dSmrg	return false;
3541428d7b3dSmrg}
3542428d7b3dSmrg
3543428d7b3dSmrgstatic bool
3544428d7b3dSmrgoutput_ignored(ScrnInfoPtr scrn, const char *name)
3545428d7b3dSmrg{
3546428d7b3dSmrg	char monitor_name[64];
3547428d7b3dSmrg	const char *monitor;
3548428d7b3dSmrg	XF86ConfMonitorPtr conf;
3549428d7b3dSmrg
3550428d7b3dSmrg	snprintf(monitor_name, sizeof(monitor_name), "monitor-%s", name);
3551428d7b3dSmrg	monitor = xf86findOptionValue(scrn->options, monitor_name);
3552428d7b3dSmrg	if (!monitor)
3553428d7b3dSmrg		monitor = name;
3554428d7b3dSmrg
3555428d7b3dSmrg	conf = xf86findMonitor(monitor,
3556428d7b3dSmrg			       xf86configptr->conf_monitor_lst);
3557428d7b3dSmrg	if (conf == NULL && XF86_CRTC_CONFIG_PTR(scrn)->num_output == 0)
3558428d7b3dSmrg		conf = xf86findMonitor(scrn->monitor->id,
3559428d7b3dSmrg				       xf86configptr->conf_monitor_lst);
3560428d7b3dSmrg	if (conf == NULL)
3561428d7b3dSmrg		return false;
3562428d7b3dSmrg
3563428d7b3dSmrg	return xf86CheckBoolOption(conf->mon_option_lst, "Ignore", 0);
3564428d7b3dSmrg}
3565428d7b3dSmrg
3566428d7b3dSmrgstatic bool
3567428d7b3dSmrggather_encoders(struct sna *sna, uint32_t id, int count,
3568428d7b3dSmrg		struct drm_mode_get_encoder *out)
3569428d7b3dSmrg{
3570428d7b3dSmrg	union compat_mode_get_connector compat_conn;
3571428d7b3dSmrg	struct drm_mode_modeinfo dummy;
3572428d7b3dSmrg	struct drm_mode_get_encoder enc;
3573428d7b3dSmrg	uint32_t *ids = NULL;
3574428d7b3dSmrg
3575428d7b3dSmrg	VG_CLEAR(compat_conn);
3576428d7b3dSmrg	memset(out, 0, sizeof(*out));
3577428d7b3dSmrg
3578428d7b3dSmrg	do {
3579428d7b3dSmrg		free(ids);
3580428d7b3dSmrg		ids = malloc(sizeof(*ids) * count);
3581428d7b3dSmrg		if (ids == 0)
3582428d7b3dSmrg			return false;
3583428d7b3dSmrg
3584428d7b3dSmrg		compat_conn.conn.connector_id = id;
3585428d7b3dSmrg		compat_conn.conn.count_props = 0;
3586428d7b3dSmrg		compat_conn.conn.count_modes = 1; /* skip detect */
3587428d7b3dSmrg		compat_conn.conn.modes_ptr = (uintptr_t)&dummy;
3588428d7b3dSmrg		compat_conn.conn.count_encoders = count;
3589428d7b3dSmrg		compat_conn.conn.encoders_ptr = (uintptr_t)ids;
3590428d7b3dSmrg
3591428d7b3dSmrg		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCONNECTOR, &compat_conn.conn)) {
3592428d7b3dSmrg			DBG(("%s: GETCONNECTOR[%d] failed, ret=%d\n", __FUNCTION__, id, errno));
3593428d7b3dSmrg			compat_conn.conn.count_encoders = count = 0;
3594428d7b3dSmrg		}
3595428d7b3dSmrg
3596428d7b3dSmrg		if (count == compat_conn.conn.count_encoders)
3597428d7b3dSmrg			break;
3598428d7b3dSmrg
3599428d7b3dSmrg		count = compat_conn.conn.count_encoders;
3600428d7b3dSmrg	} while (1);
3601428d7b3dSmrg
3602428d7b3dSmrg	for (count = 0; count < compat_conn.conn.count_encoders; count++) {
3603428d7b3dSmrg		enc.encoder_id = ids[count];
3604428d7b3dSmrg		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETENCODER, &enc)) {
3605428d7b3dSmrg			DBG(("%s: GETENCODER[%d] failed, ret=%d\n", __FUNCTION__, ids[count], errno));
3606428d7b3dSmrg			count = 0;
3607428d7b3dSmrg			break;
3608428d7b3dSmrg		}
3609428d7b3dSmrg		out->possible_crtcs |= enc.possible_crtcs;
3610428d7b3dSmrg		out->possible_clones |= enc.possible_clones;
3611428d7b3dSmrg
3612428d7b3dSmrg		for (id = 0; id < sna->mode.num_real_encoder; id++) {
3613428d7b3dSmrg			if (enc.encoder_id == sna->mode.encoders[id]) {
3614428d7b3dSmrg				out->crtc_id |= 1 << id;
3615428d7b3dSmrg				break;
3616428d7b3dSmrg			}
3617428d7b3dSmrg		}
3618428d7b3dSmrg	}
3619428d7b3dSmrg
3620428d7b3dSmrg	free(ids);
3621428d7b3dSmrg	return count > 0;
3622428d7b3dSmrg}
3623428d7b3dSmrg
3624428d7b3dSmrg/* We need to map from kms encoder based possible_clones mask to X output based
3625428d7b3dSmrg * possible clones masking. Note that for SDVO and on Haswell with DP/HDMI we
3626428d7b3dSmrg * can have more than one output hanging off the same encoder.
3627428d7b3dSmrg */
3628428d7b3dSmrgstatic void
3629428d7b3dSmrgsna_mode_compute_possible_outputs(struct sna *sna)
3630428d7b3dSmrg{
3631428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
3632428d7b3dSmrg	int encoder_mask[32];
3633428d7b3dSmrg	int i, j;
3634428d7b3dSmrg
3635428d7b3dSmrg	assert(sna->mode.num_real_output < 32);
3636428d7b3dSmrg	assert(sna->mode.num_real_crtc < 32);
3637428d7b3dSmrg
3638428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_output; i++) {
3639428d7b3dSmrg		xf86OutputPtr output = config->output[i];
3640428d7b3dSmrg		struct sna_output *sna_output = to_sna_output(output);
3641428d7b3dSmrg
3642428d7b3dSmrg		assert(sna_output);
3643428d7b3dSmrg
3644428d7b3dSmrg		if (sna_output->id) {
3645428d7b3dSmrg			output->possible_clones = sna_output->possible_encoders;
3646428d7b3dSmrg			encoder_mask[i] = sna_output->attached_encoders;
3647428d7b3dSmrg		} else {
3648428d7b3dSmrg			output->possible_clones = 0;
3649428d7b3dSmrg			encoder_mask[i] = 0;
3650428d7b3dSmrg		}
3651428d7b3dSmrg	}
3652428d7b3dSmrg
3653428d7b3dSmrg	/* Convert from encoder numbering to output numbering */
3654428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_output; i++) {
3655428d7b3dSmrg		xf86OutputPtr output = config->output[i];
3656428d7b3dSmrg		unsigned clones;
3657428d7b3dSmrg
3658428d7b3dSmrg		if (output->possible_clones == 0)
3659428d7b3dSmrg			continue;
3660428d7b3dSmrg
3661428d7b3dSmrg		clones = 0;
3662428d7b3dSmrg		for (j = 0; j < sna->mode.num_real_output; j++)
3663428d7b3dSmrg			if (i != j && output->possible_clones & encoder_mask[j])
3664428d7b3dSmrg				clones |= 1 << j;
3665428d7b3dSmrg		output->possible_clones = clones;
3666428d7b3dSmrg
3667428d7b3dSmrg		DBG(("%s: updated output '%s' %d [%d] (possible crtc:%x, possible clones:%x)\n",
3668428d7b3dSmrg		     __FUNCTION__, output->name, i, to_connector_id(output),
3669428d7b3dSmrg		     (uint32_t)output->possible_crtcs,
3670428d7b3dSmrg		     (uint32_t)output->possible_clones));
3671428d7b3dSmrg	}
3672428d7b3dSmrg}
3673428d7b3dSmrg
3674428d7b3dSmrgstatic int name_from_path(struct sna *sna,
3675428d7b3dSmrg			  struct sna_output *sna_output,
3676428d7b3dSmrg			  char *name)
3677428d7b3dSmrg{
3678428d7b3dSmrg	struct drm_mode_get_blob blob;
3679428d7b3dSmrg	char *path;
3680428d7b3dSmrg	int id;
3681428d7b3dSmrg
3682428d7b3dSmrg	id = find_property(sna, sna_output, "PATH");
3683428d7b3dSmrg	DBG(("%s: found? PATH=%d\n", __FUNCTION__, id));
3684428d7b3dSmrg	if (id == -1)
3685428d7b3dSmrg		return 0;
3686428d7b3dSmrg
3687428d7b3dSmrg	VG_CLEAR(blob);
3688428d7b3dSmrg	blob.blob_id = sna_output->prop_values[id];
3689428d7b3dSmrg	blob.length = 0;
3690428d7b3dSmrg	if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob))
3691428d7b3dSmrg		return 0;
3692428d7b3dSmrg
3693428d7b3dSmrg	do {
3694428d7b3dSmrg		id = blob.length;
3695428d7b3dSmrg		path = alloca(id + 1);
3696428d7b3dSmrg		blob.data = (uintptr_t)path;
3697428d7b3dSmrg		VG(memset(path, 0, id));
3698428d7b3dSmrg		DBG(("%s: reading %d bytes for path blob\n", __FUNCTION__, id));
3699428d7b3dSmrg		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob))
3700428d7b3dSmrg			return 0;
3701428d7b3dSmrg	} while (id != blob.length);
3702428d7b3dSmrg
3703428d7b3dSmrg	path[blob.length] = '\0'; /* paranoia */
3704428d7b3dSmrg	DBG(("%s: PATH='%s'\n", __FUNCTION__, path));
3705428d7b3dSmrg
3706428d7b3dSmrg	/* we only handle MST paths for now */
3707428d7b3dSmrg	if (strncmp(path, "mst:", 4) == 0) {
3708428d7b3dSmrg		xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
3709428d7b3dSmrg		char tmp[5], *c;
3710428d7b3dSmrg		int n;
3711428d7b3dSmrg
3712428d7b3dSmrg		c = strchr(path + 4, '-');
3713428d7b3dSmrg		if (c == NULL)
3714428d7b3dSmrg			return 0;
3715428d7b3dSmrg
3716428d7b3dSmrg		id = c - (path + 4);
3717428d7b3dSmrg		if (id + 1> 5)
3718428d7b3dSmrg			return 0;
3719428d7b3dSmrg
3720428d7b3dSmrg		memcpy(tmp, path + 4, id);
3721428d7b3dSmrg		tmp[id] = '\0';
3722428d7b3dSmrg		id = strtoul(tmp, NULL, 0);
3723428d7b3dSmrg
3724428d7b3dSmrg		for (n = 0; n < sna->mode.num_real_output; n++) {
3725428d7b3dSmrg			if (to_sna_output(config->output[n])->id == id)
3726428d7b3dSmrg				return snprintf(name, 32, "%s-%s",
3727428d7b3dSmrg						config->output[n]->name, c + 1);
3728428d7b3dSmrg		}
3729428d7b3dSmrg	}
3730428d7b3dSmrg
3731428d7b3dSmrg	return 0;
3732428d7b3dSmrg}
3733428d7b3dSmrg
3734428d7b3dSmrgstatic int
3735428d7b3dSmrgsna_output_add(struct sna *sna, unsigned id, unsigned serial)
3736428d7b3dSmrg{
3737428d7b3dSmrg	ScrnInfoPtr scrn = sna->scrn;
3738428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
3739428d7b3dSmrg	union compat_mode_get_connector compat_conn;
3740428d7b3dSmrg	struct drm_mode_get_encoder enc;
3741428d7b3dSmrg	struct drm_mode_modeinfo dummy;
3742428d7b3dSmrg	struct sna_output *sna_output;
3743428d7b3dSmrg	xf86OutputPtr *outputs, output;
3744428d7b3dSmrg	unsigned possible_encoders, attached_encoders, possible_crtcs;
3745428d7b3dSmrg	const char *output_name;
3746428d7b3dSmrg	char name[32];
3747428d7b3dSmrg	int path, len, i;
3748428d7b3dSmrg
3749428d7b3dSmrg	DBG(("%s(%d): serial=%d\n", __FUNCTION__, id, serial));
3750428d7b3dSmrg
3751428d7b3dSmrg	COMPILE_TIME_ASSERT(sizeof(struct drm_mode_get_connector) <= sizeof(compat_conn.pad));
3752428d7b3dSmrg
3753428d7b3dSmrg	VG_CLEAR(compat_conn);
3754428d7b3dSmrg	memset(&enc, 0, sizeof(enc));
3755428d7b3dSmrg
3756428d7b3dSmrg	compat_conn.conn.connector_id = id;
3757428d7b3dSmrg	compat_conn.conn.count_props = 0;
3758428d7b3dSmrg	compat_conn.conn.count_modes = 1; /* skip detect */
3759428d7b3dSmrg	compat_conn.conn.modes_ptr = (uintptr_t)&dummy;
3760428d7b3dSmrg	compat_conn.conn.count_encoders = 1;
3761428d7b3dSmrg	compat_conn.conn.encoders_ptr = (uintptr_t)&enc.encoder_id;
3762428d7b3dSmrg
3763428d7b3dSmrg	if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCONNECTOR, &compat_conn.conn)) {
3764428d7b3dSmrg		DBG(("%s: GETCONNECTOR[%d] failed, ret=%d\n", __FUNCTION__, id, errno));
3765428d7b3dSmrg		return -1;
3766428d7b3dSmrg	}
3767428d7b3dSmrg	assert(compat_conn.conn.connector_id == id);
3768428d7b3dSmrg
3769428d7b3dSmrg	if (compat_conn.conn.connector_type < ARRAY_SIZE(output_names))
3770428d7b3dSmrg		output_name = output_names[compat_conn.conn.connector_type];
3771428d7b3dSmrg	else
3772428d7b3dSmrg		output_name = "UNKNOWN";
3773428d7b3dSmrg	len = snprintf(name, 32, "%s%d", output_name, compat_conn.conn.connector_type_id);
3774428d7b3dSmrg	if (output_ignored(scrn, name))
3775428d7b3dSmrg		return 0;
3776428d7b3dSmrg
3777428d7b3dSmrg	if (enc.encoder_id) {
3778428d7b3dSmrg		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETENCODER, &enc)) {
3779428d7b3dSmrg			DBG(("%s: GETENCODER[%d] failed, ret=%d\n", __FUNCTION__, enc.encoder_id, errno));
3780428d7b3dSmrg			return 0;
3781428d7b3dSmrg		}
3782428d7b3dSmrg
3783428d7b3dSmrg		possible_encoders = enc.possible_clones;
3784428d7b3dSmrg		attached_encoders = 0;
3785428d7b3dSmrg		for (i = 0; i < sna->mode.num_real_encoder; i++) {
3786428d7b3dSmrg			if (enc.encoder_id == sna->mode.encoders[i]) {
3787428d7b3dSmrg				attached_encoders = 1 << i;
3788428d7b3dSmrg				break;
3789428d7b3dSmrg			}
3790428d7b3dSmrg		}
3791428d7b3dSmrg
3792428d7b3dSmrg		if (attached_encoders == 0) {
3793428d7b3dSmrg			DBG(("%s: failed to find attached encoder\n", __FUNCTION__));
3794428d7b3dSmrg			return 0;
3795428d7b3dSmrg		}
3796428d7b3dSmrg
3797428d7b3dSmrg		possible_crtcs = enc.possible_crtcs;
3798428d7b3dSmrg		assert(enc.encoder_id == compat_conn.conn.encoder_id || compat_conn.conn.encoder_id == 0);
3799428d7b3dSmrg	} else {
3800428d7b3dSmrg		DBG(("%s: unexpected number [%d] of encoders attached\n",
3801428d7b3dSmrg		     __FUNCTION__, compat_conn.conn.count_encoders));
3802428d7b3dSmrg		if (!gather_encoders(sna, id, compat_conn.conn.count_encoders, &enc)) {
3803428d7b3dSmrg			DBG(("%s: gather encoders failed\n", __FUNCTION__));
3804428d7b3dSmrg			return 0;
3805428d7b3dSmrg		}
3806428d7b3dSmrg		possible_encoders = enc.possible_clones;
3807428d7b3dSmrg		attached_encoders = enc.crtc_id;
3808428d7b3dSmrg		possible_crtcs = enc.possible_crtcs;
3809428d7b3dSmrg
3810428d7b3dSmrg		memset(&enc, 0, sizeof(enc));
3811428d7b3dSmrg		enc.encoder_id = compat_conn.conn.encoder_id;
3812428d7b3dSmrg		(void)drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETENCODER, &enc);
3813428d7b3dSmrg	}
3814428d7b3dSmrg
3815428d7b3dSmrg	if (is_zaphod(scrn)) {
3816428d7b3dSmrg		const char *str;
3817428d7b3dSmrg
3818428d7b3dSmrg		str = xf86GetOptValString(sna->Options, OPTION_ZAPHOD);
3819428d7b3dSmrg		if (str && !sna_zaphod_match(str, name)) {
3820428d7b3dSmrg			DBG(("%s: zaphod mismatch, want %s, have %s\n", __FUNCTION__, str, name));
3821428d7b3dSmrg			return 0;
3822428d7b3dSmrg		}
3823428d7b3dSmrg
3824428d7b3dSmrg		if ((possible_crtcs & (1 << scrn->confScreen->device->screen)) == 0) {
3825428d7b3dSmrg			if (str) {
3826428d7b3dSmrg				xf86DrvMsg(scrn->scrnIndex, X_ERROR,
3827428d7b3dSmrg					   "%s is an invalid output for screen (pipe) %d\n",
3828428d7b3dSmrg					   name, scrn->confScreen->device->screen);
3829428d7b3dSmrg				return -1;
3830428d7b3dSmrg			} else
3831428d7b3dSmrg				return 0;
3832428d7b3dSmrg		}
3833428d7b3dSmrg
3834428d7b3dSmrg		possible_crtcs = 1;
3835428d7b3dSmrg	}
3836428d7b3dSmrg
3837428d7b3dSmrg	sna_output = calloc(sizeof(struct sna_output), 1);
3838428d7b3dSmrg	if (!sna_output)
3839428d7b3dSmrg		return -1;
3840428d7b3dSmrg
3841428d7b3dSmrg	sna_output->num_props = compat_conn.conn.count_props;
3842428d7b3dSmrg	sna_output->prop_ids = malloc(sizeof(uint32_t)*compat_conn.conn.count_props);
3843428d7b3dSmrg	sna_output->prop_values = malloc(sizeof(uint64_t)*compat_conn.conn.count_props);
3844428d7b3dSmrg
3845428d7b3dSmrg	compat_conn.conn.count_encoders = 0;
3846428d7b3dSmrg
3847428d7b3dSmrg	compat_conn.conn.count_modes = 1;
3848428d7b3dSmrg	compat_conn.conn.modes_ptr = (uintptr_t)&dummy;
3849428d7b3dSmrg
3850428d7b3dSmrg	compat_conn.conn.count_props = sna_output->num_props;
3851428d7b3dSmrg	compat_conn.conn.props_ptr = (uintptr_t)sna_output->prop_ids;
3852428d7b3dSmrg	compat_conn.conn.prop_values_ptr = (uintptr_t)sna_output->prop_values;
3853428d7b3dSmrg
3854428d7b3dSmrg	if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCONNECTOR, &compat_conn.conn)) {
3855428d7b3dSmrg		DBG(("%s: second! GETCONNECTOR failed, ret=%d\n", __FUNCTION__, errno));
3856428d7b3dSmrg		goto cleanup;
3857428d7b3dSmrg	}
3858428d7b3dSmrg	assert(compat_conn.conn.connector_id == id);
3859428d7b3dSmrg
3860428d7b3dSmrg	/* statically constructed property list */
3861428d7b3dSmrg	assert(sna_output->num_props == compat_conn.conn.count_props);
3862428d7b3dSmrg	VG(VALGRIND_MAKE_MEM_DEFINED(sna_output->prop_ids, sizeof(uint32_t)*sna_output->num_props));
3863428d7b3dSmrg	VG(VALGRIND_MAKE_MEM_DEFINED(sna_output->prop_values, sizeof(uint64_t)*sna_output->num_props));
3864428d7b3dSmrg
3865428d7b3dSmrg	/* Construct name from topology, and recheck if output is acceptable */
3866428d7b3dSmrg	path = name_from_path(sna, sna_output, name);
3867428d7b3dSmrg	if (path) {
3868428d7b3dSmrg		const char *str;
3869428d7b3dSmrg
3870428d7b3dSmrg		if (output_ignored(scrn, name)) {
3871428d7b3dSmrg			len = 0;
3872428d7b3dSmrg			goto skip;
3873428d7b3dSmrg		}
3874428d7b3dSmrg
3875428d7b3dSmrg		str = xf86GetOptValString(sna->Options, OPTION_ZAPHOD);
3876428d7b3dSmrg		if (str && !sna_zaphod_match(str, name)) {
3877428d7b3dSmrg			DBG(("%s: zaphod mismatch, want %s, have %s\n", __FUNCTION__, str, name));
3878428d7b3dSmrg			len = 0;
3879428d7b3dSmrg			goto skip;
3880428d7b3dSmrg		}
3881428d7b3dSmrg
3882428d7b3dSmrg		len = path;
3883428d7b3dSmrg	}
3884428d7b3dSmrg
3885428d7b3dSmrg	/* Check if we are dynamically reattaching an old connector */
3886428d7b3dSmrg	if (serial) {
3887428d7b3dSmrg		for (i = 0; i < sna->mode.num_real_output; i++) {
3888428d7b3dSmrg			output = config->output[i];
3889428d7b3dSmrg			if (strcmp(output->name, name) == 0) {
3890428d7b3dSmrg				assert(output->scrn == scrn);
3891428d7b3dSmrg				assert(output->funcs == &sna_output_funcs);
3892428d7b3dSmrg				assert(to_sna_output(output)->id == 0);
3893428d7b3dSmrg				sna_output_destroy(output);
3894428d7b3dSmrg				goto reset;
3895428d7b3dSmrg			}
3896428d7b3dSmrg		}
3897428d7b3dSmrg	}
3898428d7b3dSmrg
3899428d7b3dSmrg	output = calloc(1, sizeof(*output) + len + 1);
3900428d7b3dSmrg	if (!output)
3901428d7b3dSmrg		goto cleanup;
3902428d7b3dSmrg
3903428d7b3dSmrg	outputs = realloc(config->output, (config->num_output + 1) * sizeof(output));
3904428d7b3dSmrg	if (outputs == NULL) {
3905428d7b3dSmrg		free(output);
3906428d7b3dSmrg		goto cleanup;
3907428d7b3dSmrg	}
3908428d7b3dSmrg
3909428d7b3dSmrg	output->scrn = scrn;
3910428d7b3dSmrg	output->funcs = &sna_output_funcs;
3911428d7b3dSmrg	output->name = (char *)(output + 1);
3912428d7b3dSmrg	memcpy(output->name, name, len + 1);
3913428d7b3dSmrg
3914428d7b3dSmrg	output->use_screen_monitor = config->num_output != 0;
3915428d7b3dSmrg	xf86OutputUseScreenMonitor(output, !output->use_screen_monitor);
3916428d7b3dSmrg	assert(output->options);
3917428d7b3dSmrg
3918428d7b3dSmrg	DBG(("%s: inserting output #%d of %d\n", __FUNCTION__, sna->mode.num_real_output, config->num_output));
3919428d7b3dSmrg	for (i = config->num_output; i > sna->mode.num_real_output; i--) {
3920428d7b3dSmrg		outputs[i] = outputs[i-1];
3921428d7b3dSmrg		assert(outputs[i]->driver_private == NULL);
3922428d7b3dSmrg		outputs[i]->possible_clones <<= 1;
3923428d7b3dSmrg	}
3924428d7b3dSmrg
3925428d7b3dSmrg	if (xf86ReturnOptValBool(output->options, OPTION_PRIMARY, FALSE)) {
3926428d7b3dSmrg		memmove(outputs + 1, outputs, sizeof(output)*config->num_output);
3927428d7b3dSmrg		outputs[0] = output;
3928428d7b3dSmrg	} else
3929428d7b3dSmrg		outputs[i] = output;
3930428d7b3dSmrg	sna->mode.num_real_output++;
3931428d7b3dSmrg	config->num_output++;
3932428d7b3dSmrg	config->output = outputs;
3933428d7b3dSmrg
3934428d7b3dSmrgreset:
3935428d7b3dSmrg	sna_output->id = compat_conn.conn.connector_id;
3936428d7b3dSmrg	sna_output->is_panel = is_panel(compat_conn.conn.connector_type);
3937428d7b3dSmrg	sna_output->edid_idx = find_property(sna, sna_output, "EDID");
3938428d7b3dSmrg	if (find_property(sna, sna_output, "scaling mode") != -1)
3939428d7b3dSmrg		sna_output->add_default_modes =
3940428d7b3dSmrg			xf86ReturnOptValBool(output->options, OPTION_DEFAULT_MODES, TRUE);
3941428d7b3dSmrg
3942428d7b3dSmrg	i = find_property(sna, sna_output, "DPMS");
3943428d7b3dSmrg	if (i != -1) {
3944428d7b3dSmrg		sna_output->dpms_id = sna_output->prop_ids[i];
3945428d7b3dSmrg		sna_output->dpms_mode = sna_output->prop_values[i];
3946428d7b3dSmrg		DBG(("%s: found 'DPMS' (idx=%d, id=%d), initial value=%d\n",
3947428d7b3dSmrg		     __FUNCTION__, i, sna_output->dpms_id, sna_output->dpms_mode));
3948428d7b3dSmrg	} else {
3949428d7b3dSmrg		sna_output->dpms_id = -1;
3950428d7b3dSmrg		sna_output->dpms_mode = DPMSModeOff;
3951428d7b3dSmrg	}
3952428d7b3dSmrg
3953428d7b3dSmrg	sna_output->possible_encoders = possible_encoders;
3954428d7b3dSmrg	sna_output->attached_encoders = attached_encoders;
3955428d7b3dSmrg
3956428d7b3dSmrg	output->mm_width = compat_conn.conn.mm_width;
3957428d7b3dSmrg	output->mm_height = compat_conn.conn.mm_height;
3958428d7b3dSmrg
3959428d7b3dSmrg	if (compat_conn.conn.subpixel >= ARRAY_SIZE(subpixel_conv_table))
3960428d7b3dSmrg		compat_conn.conn.subpixel = 0;
3961428d7b3dSmrg	output->subpixel_order = subpixel_conv_table[compat_conn.conn.subpixel];
3962428d7b3dSmrg	output->driver_private = sna_output;
3963428d7b3dSmrg	sna_output->base = output;
3964428d7b3dSmrg
3965428d7b3dSmrg	backlight_init(&sna_output->backlight);
3966428d7b3dSmrg	if (sna_output->is_panel)
3967428d7b3dSmrg		sna_output_backlight_init(output);
3968428d7b3dSmrg
3969428d7b3dSmrg	output->possible_crtcs = possible_crtcs & count_to_mask(sna->mode.num_real_crtc);
3970428d7b3dSmrg	output->interlaceAllowed = TRUE;
3971428d7b3dSmrg
3972428d7b3dSmrg	if (serial) {
3973428d7b3dSmrg		if (output->randr_output == NULL) {
3974428d7b3dSmrg			output->randr_output = RROutputCreate(xf86ScrnToScreen(scrn), name, len, output);
3975428d7b3dSmrg			if (output->randr_output == NULL)
3976428d7b3dSmrg				goto cleanup;
3977428d7b3dSmrg		}
3978428d7b3dSmrg
3979428d7b3dSmrg		sna_output_create_resources(output);
3980428d7b3dSmrg		RRPostPendingProperties(output->randr_output);
3981428d7b3dSmrg
3982428d7b3dSmrg		sna_output->serial = serial;
3983428d7b3dSmrg	} else {
3984428d7b3dSmrg		/* stash the active CRTC id for our probe function */
3985428d7b3dSmrg		if (compat_conn.conn.connection != DRM_MODE_DISCONNECTED)
3986428d7b3dSmrg			output->crtc = (void *)(uintptr_t)enc.crtc_id;
3987428d7b3dSmrg	}
3988428d7b3dSmrg
3989428d7b3dSmrg	DBG(("%s: created output '%s' %d, encoder=%d (possible crtc:%x, attached encoders:%x, possible clones:%x), serial=%d, edid=%d, dpms=%d, crtc=%lu\n",
3990428d7b3dSmrg	     __FUNCTION__, name, id, enc.encoder_id,
3991428d7b3dSmrg	     (uint32_t)output->possible_crtcs,
3992428d7b3dSmrg	     sna_output->attached_encoders,
3993428d7b3dSmrg	     sna_output->possible_encoders,
3994428d7b3dSmrg	     serial, sna_output->edid_idx, sna_output->dpms_id,
3995428d7b3dSmrg	     (unsigned long)(uintptr_t)output->crtc));
3996428d7b3dSmrg	assert(sna_output->id == id);
3997428d7b3dSmrg
3998428d7b3dSmrg	xf86DrvMsg(scrn->scrnIndex, X_INFO,
3999428d7b3dSmrg		   "Enabled output %s\n",
4000428d7b3dSmrg		   output->name);
4001428d7b3dSmrg	return 1;
4002428d7b3dSmrg
4003428d7b3dSmrgcleanup:
4004428d7b3dSmrg	len = -1;
4005428d7b3dSmrgskip:
4006428d7b3dSmrg	free(sna_output->prop_ids);
4007428d7b3dSmrg	free(sna_output->prop_values);
4008428d7b3dSmrg	free(sna_output);
4009428d7b3dSmrg	return len;
4010428d7b3dSmrg}
4011428d7b3dSmrg
4012428d7b3dSmrgstatic void sna_output_del(xf86OutputPtr output)
4013428d7b3dSmrg{
4014428d7b3dSmrg	ScrnInfoPtr scrn = output->scrn;
4015428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
4016428d7b3dSmrg	int i;
4017428d7b3dSmrg
4018428d7b3dSmrg	DBG(("%s(%s)\n", __FUNCTION__, output->name));
4019428d7b3dSmrg	assert(to_sna_output(output));
4020428d7b3dSmrg
4021428d7b3dSmrg	RROutputDestroy(output->randr_output);
4022428d7b3dSmrg	sna_output_destroy(output);
4023428d7b3dSmrg
4024428d7b3dSmrg	while (output->probed_modes)
4025428d7b3dSmrg		xf86DeleteMode(&output->probed_modes, output->probed_modes);
4026428d7b3dSmrg
4027428d7b3dSmrg	free(output);
4028428d7b3dSmrg
4029428d7b3dSmrg	for (i = 0; i < config->num_output; i++)
4030428d7b3dSmrg		if (config->output[i] == output)
4031428d7b3dSmrg			break;
4032428d7b3dSmrg	assert(i < to_sna(scrn)->mode.num_real_output);
4033428d7b3dSmrg	DBG(("%s: removing output #%d of %d\n",
4034428d7b3dSmrg	     __FUNCTION__, i, to_sna(scrn)->mode.num_real_output));
4035428d7b3dSmrg
4036428d7b3dSmrg	for (; i < config->num_output; i++) {
4037428d7b3dSmrg		config->output[i] = config->output[i+1];
4038428d7b3dSmrg		config->output[i]->possible_clones >>= 1;
4039428d7b3dSmrg	}
4040428d7b3dSmrg	config->num_output--;
4041428d7b3dSmrg	to_sna(scrn)->mode.num_real_output--;
4042428d7b3dSmrg}
4043428d7b3dSmrg
4044428d7b3dSmrgstatic int output_rank(const void *A, const void *B)
4045428d7b3dSmrg{
4046428d7b3dSmrg	const xf86OutputPtr *a = A;
4047428d7b3dSmrg	const xf86OutputPtr *b = B;
4048428d7b3dSmrg	struct sna_output *sa = to_sna_output(*a);
4049428d7b3dSmrg	struct sna_output *sb = to_sna_output(*b);
4050428d7b3dSmrg
4051428d7b3dSmrg	if (sa->is_panel != sb->is_panel)
4052428d7b3dSmrg		return sb->is_panel - sa->is_panel;
4053428d7b3dSmrg
4054428d7b3dSmrg	return strcmp((*a)->name, (*b)->name);
4055428d7b3dSmrg}
4056428d7b3dSmrg
4057428d7b3dSmrgstatic void sort_config_outputs(struct sna *sna)
4058428d7b3dSmrg{
4059428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
4060428d7b3dSmrg	qsort(config->output, sna->mode.num_real_output, sizeof(*config->output), output_rank);
4061428d7b3dSmrg	sna_mode_compute_possible_outputs(sna);
4062428d7b3dSmrg}
4063428d7b3dSmrg
4064428d7b3dSmrgstatic void sort_randr_outputs(struct sna *sna, ScreenPtr screen)
4065428d7b3dSmrg{
4066428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
4067428d7b3dSmrg	rrScrPriv(screen);
4068428d7b3dSmrg	int i;
4069428d7b3dSmrg
4070428d7b3dSmrg	assert(pScrPriv->numOutputs == config->num_output);
4071428d7b3dSmrg	for (i = 0; i < config->num_output; i++) {
4072428d7b3dSmrg		assert(config->output[i]->randr_output);
4073428d7b3dSmrg		pScrPriv->outputs[i] = config->output[i]->randr_output;
4074428d7b3dSmrg	}
4075428d7b3dSmrg}
4076428d7b3dSmrg
4077428d7b3dSmrgstatic bool disable_unused_crtc(struct sna *sna)
4078428d7b3dSmrg{
4079428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
4080428d7b3dSmrg	bool update = false;
4081428d7b3dSmrg	int o, c;
4082428d7b3dSmrg
4083428d7b3dSmrg	for (c = 0; c < sna->mode.num_real_crtc; c++) {
4084428d7b3dSmrg		xf86CrtcPtr crtc = config->crtc[c];
4085428d7b3dSmrg
4086428d7b3dSmrg		if (!crtc->enabled)
4087428d7b3dSmrg			continue;
4088428d7b3dSmrg
4089428d7b3dSmrg		for (o = 0; o < sna->mode.num_real_output; o++) {
4090428d7b3dSmrg			xf86OutputPtr output = config->output[o];
4091428d7b3dSmrg			if (output->crtc == crtc)
4092428d7b3dSmrg				break;
4093428d7b3dSmrg		}
4094428d7b3dSmrg
4095428d7b3dSmrg		if (o == sna->mode.num_real_output) {
4096428d7b3dSmrg			DBG(("%s: CRTC:%d was enabled with no outputs\n",
4097428d7b3dSmrg			     __FUNCTION__, to_sna_crtc(crtc)->id));
4098428d7b3dSmrg			crtc->enabled = false;
4099428d7b3dSmrg			update = true;
4100428d7b3dSmrg		}
4101428d7b3dSmrg	}
4102428d7b3dSmrg
4103428d7b3dSmrg	if (update) {
4104428d7b3dSmrg		DBG(("%s: disabling unused functions\n", __FUNCTION__));
4105428d7b3dSmrg		xf86DisableUnusedFunctions(sna->scrn);
4106428d7b3dSmrg	}
4107428d7b3dSmrg
4108428d7b3dSmrg	return update;
4109428d7b3dSmrg}
4110428d7b3dSmrg
4111428d7b3dSmrgvoid sna_mode_discover(struct sna *sna)
4112428d7b3dSmrg{
4113428d7b3dSmrg	ScreenPtr screen = xf86ScrnToScreen(sna->scrn);
4114428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
4115428d7b3dSmrg	struct drm_mode_card_res res;
4116428d7b3dSmrg	uint32_t connectors[32];
4117428d7b3dSmrg	unsigned changed = 0;
4118428d7b3dSmrg	unsigned serial;
4119428d7b3dSmrg	int i, j;
4120428d7b3dSmrg
4121428d7b3dSmrg	DBG(("%s()\n", __FUNCTION__));
4122428d7b3dSmrg	VG_CLEAR(connectors);
4123428d7b3dSmrg
4124428d7b3dSmrg	memset(&res, 0, sizeof(res));
4125428d7b3dSmrg	res.count_connectors = 32;
4126428d7b3dSmrg	res.connector_id_ptr = (uintptr_t)connectors;
4127428d7b3dSmrg
4128428d7b3dSmrg	if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
4129428d7b3dSmrg		return;
4130428d7b3dSmrg
4131428d7b3dSmrg	DBG(("%s: now %d (was %d) connectors\n", __FUNCTION__,
4132428d7b3dSmrg	     res.count_connectors, sna->mode.num_real_output));
4133428d7b3dSmrg	if (res.count_connectors > 32)
4134428d7b3dSmrg		return;
4135428d7b3dSmrg
4136428d7b3dSmrg	assert(sna->mode.num_real_crtc == res.count_crtcs || is_zaphod(sna->scrn));
4137428d7b3dSmrg	assert(sna->mode.max_crtc_width  == res.max_width);
4138428d7b3dSmrg	assert(sna->mode.max_crtc_height == res.max_height);
4139428d7b3dSmrg	assert(sna->mode.num_real_encoder == res.count_encoders);
4140428d7b3dSmrg
4141428d7b3dSmrg	serial = ++sna->mode.serial;
4142428d7b3dSmrg	if (serial == 0)
4143428d7b3dSmrg		serial = ++sna->mode.serial;
4144428d7b3dSmrg
4145428d7b3dSmrg	for (i = 0; i < res.count_connectors; i++) {
4146428d7b3dSmrg		DBG(("%s: connector[%d] = %d\n", __FUNCTION__, i, connectors[i]));
4147428d7b3dSmrg		for (j = 0; j < sna->mode.num_real_output; j++) {
4148428d7b3dSmrg			xf86OutputPtr output = config->output[j];
4149428d7b3dSmrg			if (to_sna_output(output)->id == connectors[i]) {
4150428d7b3dSmrg				DBG(("%s: found %s (id=%d)\n", __FUNCTION__, output->name, connectors[i]));
4151428d7b3dSmrg				assert(to_sna_output(output)->id);
4152428d7b3dSmrg				to_sna_output(output)->serial = serial;
4153428d7b3dSmrg				break;
4154428d7b3dSmrg			}
4155428d7b3dSmrg		}
4156428d7b3dSmrg		if (j == sna->mode.num_real_output) {
4157428d7b3dSmrg			DBG(("%s: adding id=%d\n", __FUNCTION__, connectors[i]));
4158428d7b3dSmrg			changed |= sna_output_add(sna, connectors[i], serial) > 0;
4159428d7b3dSmrg		}
4160428d7b3dSmrg	}
4161428d7b3dSmrg
4162428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_output; i++) {
4163428d7b3dSmrg		xf86OutputPtr output = config->output[i];
4164428d7b3dSmrg
4165428d7b3dSmrg		if (to_sna_output(output)->id == 0)
4166428d7b3dSmrg			continue;
4167428d7b3dSmrg
4168428d7b3dSmrg		if (to_sna_output(output)->serial == serial)
4169428d7b3dSmrg			continue;
4170428d7b3dSmrg
4171428d7b3dSmrg		DBG(("%s: removing output %s (id=%d), serial=%u [now %u]\n",
4172428d7b3dSmrg		     __FUNCTION__, output->name, to_sna_output(output)->id,
4173428d7b3dSmrg		    to_sna_output(output)->serial, serial));
4174428d7b3dSmrg
4175428d7b3dSmrg		xf86DrvMsg(sna->scrn->scrnIndex, X_INFO,
4176428d7b3dSmrg			   "%s output %s\n",
4177428d7b3dSmrg			   sna->flags & SNA_REMOVE_OUTPUTS ? "Removed" : "Disabled",
4178428d7b3dSmrg			   output->name);
4179428d7b3dSmrg		if (sna->flags & SNA_REMOVE_OUTPUTS) {
4180428d7b3dSmrg			sna_output_del(output);
4181428d7b3dSmrg			i--;
4182428d7b3dSmrg		} else {
4183428d7b3dSmrg			to_sna_output(output)->id = 0;
4184428d7b3dSmrg			output->crtc = NULL;
4185428d7b3dSmrg		}
4186428d7b3dSmrg		changed |= 2;
4187428d7b3dSmrg	}
4188428d7b3dSmrg
4189428d7b3dSmrg	if (changed) {
4190428d7b3dSmrg		DBG(("%s: outputs changed, broadcasting\n", __FUNCTION__));
4191428d7b3dSmrg
4192428d7b3dSmrg		sna_mode_set_primary(sna);
4193428d7b3dSmrg
4194428d7b3dSmrg		/* Reorder user visible listing */
4195428d7b3dSmrg		sort_config_outputs(sna);
4196428d7b3dSmrg		sort_randr_outputs(sna, screen);
4197428d7b3dSmrg
4198428d7b3dSmrg		if (changed & 2)
4199428d7b3dSmrg			disable_unused_crtc(sna);
4200428d7b3dSmrg
4201428d7b3dSmrg		xf86RandR12TellChanged(screen);
4202428d7b3dSmrg	}
4203428d7b3dSmrg}
4204428d7b3dSmrg
4205428d7b3dSmrgstatic void copy_front(struct sna *sna, PixmapPtr old, PixmapPtr new)
4206428d7b3dSmrg{
4207428d7b3dSmrg	struct sna_pixmap *old_priv, *new_priv;
4208428d7b3dSmrg
4209428d7b3dSmrg	DBG(("%s\n", __FUNCTION__));
4210428d7b3dSmrg
4211428d7b3dSmrg	if (wedged(sna))
4212428d7b3dSmrg		return;
4213428d7b3dSmrg
4214428d7b3dSmrg	old_priv = sna_pixmap_force_to_gpu(old, MOVE_READ);
4215428d7b3dSmrg	if (!old_priv)
4216428d7b3dSmrg		return;
4217428d7b3dSmrg
4218428d7b3dSmrg	new_priv = sna_pixmap_force_to_gpu(new, MOVE_WRITE | __MOVE_SCANOUT);
4219428d7b3dSmrg	if (!new_priv)
4220428d7b3dSmrg		return;
4221428d7b3dSmrg
4222428d7b3dSmrg	if (old_priv->clear) {
4223428d7b3dSmrg		(void)sna->render.fill_one(sna, new, new_priv->gpu_bo,
4224428d7b3dSmrg					   old_priv->clear_color,
4225428d7b3dSmrg					   0, 0,
4226428d7b3dSmrg					   new->drawable.width,
4227428d7b3dSmrg					   new->drawable.height,
4228428d7b3dSmrg					   GXcopy);
4229428d7b3dSmrg		new_priv->clear = true;
4230428d7b3dSmrg		new_priv->clear_color = old_priv->clear_color;
4231428d7b3dSmrg	} else {
4232428d7b3dSmrg		BoxRec box;
4233428d7b3dSmrg		int16_t sx, sy, dx, dy;
4234428d7b3dSmrg
4235428d7b3dSmrg		if (new->drawable.width >= old->drawable.width &&
4236428d7b3dSmrg		    new->drawable.height >= old->drawable.height)
4237428d7b3dSmrg		{
4238428d7b3dSmrg			int nx = (new->drawable.width + old->drawable.width - 1) / old->drawable.width;
4239428d7b3dSmrg			int ny = (new->drawable.height + old->drawable.height - 1) / old->drawable.height;
4240428d7b3dSmrg
4241428d7b3dSmrg			box.x1 = box.y1 = 0;
4242428d7b3dSmrg
4243428d7b3dSmrg			dy = 0;
4244428d7b3dSmrg			for (sy = 0; sy < ny; sy++) {
4245428d7b3dSmrg				box.y2 = old->drawable.height;
4246428d7b3dSmrg				if (box.y2 + dy > new->drawable.height)
4247428d7b3dSmrg					box.y2 = new->drawable.height - dy;
4248428d7b3dSmrg
4249428d7b3dSmrg				dx = 0;
4250428d7b3dSmrg				for (sx = 0; sx < nx; sx++) {
4251428d7b3dSmrg					box.x2 = old->drawable.width;
4252428d7b3dSmrg					if (box.x2 + dx > new->drawable.width)
4253428d7b3dSmrg						box.x2 = new->drawable.width - dx;
4254428d7b3dSmrg
4255428d7b3dSmrg					(void)sna->render.copy_boxes(sna, GXcopy,
4256428d7b3dSmrg								     &old->drawable, old_priv->gpu_bo, 0, 0,
4257428d7b3dSmrg								     &new->drawable, new_priv->gpu_bo, dx, dy,
4258428d7b3dSmrg								     &box, 1, 0);
4259428d7b3dSmrg					dx += old->drawable.width;
4260428d7b3dSmrg				}
4261428d7b3dSmrg				dy += old->drawable.height;
4262428d7b3dSmrg			}
4263428d7b3dSmrg		} else {
4264428d7b3dSmrg			box.x1 = box.y1 = 0;
4265428d7b3dSmrg			box.x2 = min(old->drawable.width, new->drawable.width);
4266428d7b3dSmrg			box.y2 = min(old->drawable.height, new->drawable.height);
4267428d7b3dSmrg
4268428d7b3dSmrg			sx = dx = 0;
4269428d7b3dSmrg			if (box.x2 < old->drawable.width)
4270428d7b3dSmrg				sx = (old->drawable.width - box.x2) / 2;
4271428d7b3dSmrg			if (box.x2 < new->drawable.width)
4272428d7b3dSmrg				dx = (new->drawable.width - box.x2) / 2;
4273428d7b3dSmrg
4274428d7b3dSmrg			sy = dy = 0;
4275428d7b3dSmrg			if (box.y2 < old->drawable.height)
4276428d7b3dSmrg				sy = (old->drawable.height - box.y2) / 2;
4277428d7b3dSmrg			if (box.y2 < new->drawable.height)
4278428d7b3dSmrg				dy = (new->drawable.height - box.y2) / 2;
4279428d7b3dSmrg
4280428d7b3dSmrg			DBG(("%s: copying box (%dx%d) from (%d, %d) to (%d, %d)\n",
4281428d7b3dSmrg			     __FUNCTION__, box.x2, box.y2, sx, sy, dx, dy));
4282428d7b3dSmrg
4283428d7b3dSmrg			if (box.x2 != new->drawable.width || box.y2 != new->drawable.height) {
4284428d7b3dSmrg				(void)sna->render.fill_one(sna, new, new_priv->gpu_bo, 0,
4285428d7b3dSmrg							   0, 0,
4286428d7b3dSmrg							   new->drawable.width,
4287428d7b3dSmrg							   new->drawable.height,
4288428d7b3dSmrg							   GXclear);
4289428d7b3dSmrg			}
4290428d7b3dSmrg			(void)sna->render.copy_boxes(sna, GXcopy,
4291428d7b3dSmrg						     &old->drawable, old_priv->gpu_bo, sx, sy,
4292428d7b3dSmrg						     &new->drawable, new_priv->gpu_bo, dx, dy,
4293428d7b3dSmrg						     &box, 1, 0);
4294428d7b3dSmrg		}
4295428d7b3dSmrg	}
4296428d7b3dSmrg
4297428d7b3dSmrg	sna_damage_all(&new_priv->gpu_damage, new);
4298428d7b3dSmrg}
4299428d7b3dSmrg
4300428d7b3dSmrgstatic Bool
4301428d7b3dSmrgsna_mode_resize(ScrnInfoPtr scrn, int width, int height)
4302428d7b3dSmrg{
4303428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
4304428d7b3dSmrg	struct sna *sna = to_sna(scrn);
4305428d7b3dSmrg	ScreenPtr screen = scrn->pScreen;
4306428d7b3dSmrg	PixmapPtr new_front;
4307428d7b3dSmrg	int i;
4308428d7b3dSmrg
4309428d7b3dSmrg	DBG(("%s (%d, %d) -> (%d, %d)\n", __FUNCTION__,
4310428d7b3dSmrg	     scrn->virtualX, scrn->virtualY,
4311428d7b3dSmrg	     width, height));
4312428d7b3dSmrg	assert((sna->flags & SNA_IS_HOSTED) == 0);
4313428d7b3dSmrg
4314428d7b3dSmrg	if (scrn->virtualX == width && scrn->virtualY == height)
4315428d7b3dSmrg		return TRUE;
4316428d7b3dSmrg
4317428d7b3dSmrg	/* Paranoid defense against rogue internal calls by Xorg */
4318428d7b3dSmrg	if (width == 0 || height == 0)
4319428d7b3dSmrg		return FALSE;
4320428d7b3dSmrg
4321428d7b3dSmrg	assert(sna->front);
4322428d7b3dSmrg	assert(screen->GetScreenPixmap(screen) == sna->front);
4323428d7b3dSmrg
4324428d7b3dSmrg	DBG(("%s: creating new framebuffer %dx%d\n",
4325428d7b3dSmrg	     __FUNCTION__, width, height));
4326428d7b3dSmrg
4327428d7b3dSmrg	new_front = screen->CreatePixmap(screen,
4328428d7b3dSmrg					 width, height, scrn->depth,
4329428d7b3dSmrg					 SNA_CREATE_FB);
4330428d7b3dSmrg	if (!new_front)
4331428d7b3dSmrg		return FALSE;
4332428d7b3dSmrg
4333428d7b3dSmrg	xf86DrvMsg(scrn->scrnIndex, X_INFO,
4334428d7b3dSmrg		   "resizing framebuffer to %dx%d\n",
4335428d7b3dSmrg		   width, height);
4336428d7b3dSmrg
4337428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_crtc; i++)
4338428d7b3dSmrg		sna_crtc_disable_shadow(sna, to_sna_crtc(config->crtc[i]));
4339428d7b3dSmrg	assert(sna->mode.shadow_active == 0);
4340428d7b3dSmrg	assert(sna->mode.shadow_damage == NULL);
4341428d7b3dSmrg	assert(sna->mode.shadow == NULL);
4342428d7b3dSmrg
4343428d7b3dSmrg	copy_front(sna, sna->front, new_front);
4344428d7b3dSmrg
4345428d7b3dSmrg	screen->SetScreenPixmap(new_front);
4346428d7b3dSmrg	assert(screen->GetScreenPixmap(screen) == new_front);
4347428d7b3dSmrg	assert(sna->front == new_front);
4348428d7b3dSmrg	screen->DestroyPixmap(new_front); /* owned by screen now */
4349428d7b3dSmrg
4350428d7b3dSmrg	scrn->virtualX = width;
4351428d7b3dSmrg	scrn->virtualY = height;
4352428d7b3dSmrg	scrn->displayWidth = width;
4353428d7b3dSmrg
4354428d7b3dSmrg	/* Flush pending shadow updates */
4355428d7b3dSmrg	if (sna->mode.flip_active) {
4356428d7b3dSmrg		DBG(("%s: waiting for %d outstanding TearFree flips\n",
4357428d7b3dSmrg		     __FUNCTION__, sna->mode.flip_active));
4358428d7b3dSmrg		while (sna->mode.flip_active && sna_mode_wait_for_event(sna))
4359428d7b3dSmrg			sna_mode_wakeup(sna);
4360428d7b3dSmrg	}
4361428d7b3dSmrg
4362428d7b3dSmrg	/* Only update the CRTCs if we are in control */
4363428d7b3dSmrg	if (!scrn->vtSema)
4364428d7b3dSmrg		return TRUE;
4365428d7b3dSmrg
4366428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_crtc; i++) {
4367428d7b3dSmrg		xf86CrtcPtr crtc = config->crtc[i];
4368428d7b3dSmrg
4369428d7b3dSmrg		assert(to_sna_crtc(crtc) != NULL);
4370428d7b3dSmrg		if (to_sna_crtc(crtc)->bo == NULL)
4371428d7b3dSmrg			continue;
4372428d7b3dSmrg
4373428d7b3dSmrg		if (!__sna_crtc_set_mode(crtc))
4374428d7b3dSmrg			sna_crtc_disable(crtc);
4375428d7b3dSmrg	}
4376428d7b3dSmrg
4377428d7b3dSmrg	sna_mode_wakeup(sna);
4378428d7b3dSmrg	kgem_clean_scanout_cache(&sna->kgem);
4379428d7b3dSmrg
4380428d7b3dSmrg	return TRUE;
4381428d7b3dSmrg}
4382428d7b3dSmrg
4383428d7b3dSmrg/* cursor handling */
4384428d7b3dSmrgstruct sna_cursor {
4385428d7b3dSmrg	struct sna_cursor *next;
4386428d7b3dSmrg	uint32_t *image;
4387428d7b3dSmrg	Rotation rotation;
4388428d7b3dSmrg	int ref;
4389428d7b3dSmrg	int size;
4390428d7b3dSmrg	int last_width;
4391428d7b3dSmrg	int last_height;
4392428d7b3dSmrg	unsigned handle;
4393428d7b3dSmrg	unsigned serial;
4394428d7b3dSmrg	unsigned alloc;
4395428d7b3dSmrg};
4396428d7b3dSmrg
4397428d7b3dSmrgstatic void
4398428d7b3dSmrgrotate_coord(Rotation rotation, int size,
4399428d7b3dSmrg	     int x_dst, int y_dst,
4400428d7b3dSmrg	     int *x_src, int *y_src)
4401428d7b3dSmrg{
4402428d7b3dSmrg	int t;
4403428d7b3dSmrg
4404428d7b3dSmrg	switch (rotation & 0xf) {
4405428d7b3dSmrg	case RR_Rotate_0:
4406428d7b3dSmrg		break;
4407428d7b3dSmrg	case RR_Rotate_90:
4408428d7b3dSmrg		t = x_dst;
4409428d7b3dSmrg		x_dst = size - y_dst - 1;
4410428d7b3dSmrg		y_dst = t;
4411428d7b3dSmrg		break;
4412428d7b3dSmrg	case RR_Rotate_180:
4413428d7b3dSmrg		x_dst = size - x_dst - 1;
4414428d7b3dSmrg		y_dst = size - y_dst - 1;
4415428d7b3dSmrg		break;
4416428d7b3dSmrg	case RR_Rotate_270:
4417428d7b3dSmrg		t = x_dst;
4418428d7b3dSmrg		x_dst = y_dst;
4419428d7b3dSmrg		y_dst = size - t - 1;
4420428d7b3dSmrg		break;
4421428d7b3dSmrg	}
4422428d7b3dSmrg
4423428d7b3dSmrg	if (rotation & RR_Reflect_X)
4424428d7b3dSmrg		x_dst = size - x_dst - 1;
4425428d7b3dSmrg	if (rotation & RR_Reflect_Y)
4426428d7b3dSmrg		y_dst = size - y_dst - 1;
4427428d7b3dSmrg
4428428d7b3dSmrg	*x_src = x_dst;
4429428d7b3dSmrg	*y_src = y_dst;
4430428d7b3dSmrg}
4431428d7b3dSmrg
4432428d7b3dSmrgstatic void
4433428d7b3dSmrgrotate_coord_back(Rotation rotation, int size, int *x, int *y)
4434428d7b3dSmrg{
4435428d7b3dSmrg	int t;
4436428d7b3dSmrg
4437428d7b3dSmrg	if (rotation & RR_Reflect_X)
4438428d7b3dSmrg		*x = size - *x - 1;
4439428d7b3dSmrg	if (rotation & RR_Reflect_Y)
4440428d7b3dSmrg		*y = size - *y - 1;
4441428d7b3dSmrg
4442428d7b3dSmrg	switch (rotation & 0xf) {
4443428d7b3dSmrg	case RR_Rotate_0:
4444428d7b3dSmrg		break;
4445428d7b3dSmrg	case RR_Rotate_90:
4446428d7b3dSmrg		t = *x;
4447428d7b3dSmrg		*x = *y;
4448428d7b3dSmrg		*y = size - t - 1;
4449428d7b3dSmrg		break;
4450428d7b3dSmrg	case RR_Rotate_180:
4451428d7b3dSmrg		*x = size - *x - 1;
4452428d7b3dSmrg		*y = size - *y - 1;
4453428d7b3dSmrg		break;
4454428d7b3dSmrg	case RR_Rotate_270:
4455428d7b3dSmrg		t = *x;
4456428d7b3dSmrg		*x = size - *y - 1;
4457428d7b3dSmrg		*y = t;
4458428d7b3dSmrg		break;
4459428d7b3dSmrg	}
4460428d7b3dSmrg}
4461428d7b3dSmrg
4462428d7b3dSmrgstatic struct sna_cursor *__sna_create_cursor(struct sna *sna, int size)
4463428d7b3dSmrg{
4464428d7b3dSmrg	struct sna_cursor *c;
4465428d7b3dSmrg
4466428d7b3dSmrg	for (c = sna->cursor.cursors; c; c = c->next) {
4467428d7b3dSmrg		if (c->ref == 0 && c->alloc >= size) {
4468428d7b3dSmrg			__DBG(("%s: stealing handle=%d, serial=%d, rotation=%d, alloc=%d\n",
4469428d7b3dSmrg			       __FUNCTION__, c->handle, c->serial, c->rotation, c->alloc));
4470428d7b3dSmrg			return c;
4471428d7b3dSmrg		}
4472428d7b3dSmrg	}
4473428d7b3dSmrg
4474428d7b3dSmrg	__DBG(("%s(size=%d, num_stash=%d)\n", __FUNCTION__, size, sna->cursor.num_stash));
4475428d7b3dSmrg
4476428d7b3dSmrg	c = sna->cursor.stash;
4477428d7b3dSmrg	assert(c);
4478428d7b3dSmrg
4479428d7b3dSmrg	c->alloc = ALIGN(size, 4096);
4480428d7b3dSmrg	c->handle = gem_create(sna->kgem.fd, c->alloc);
4481428d7b3dSmrg	if (c->handle == 0)
4482428d7b3dSmrg		return NULL;
4483428d7b3dSmrg
4484428d7b3dSmrg	/* Old hardware uses physical addresses, which the kernel
4485428d7b3dSmrg	 * implements in an incoherent fashion requiring a pwrite.
4486428d7b3dSmrg	 */
4487428d7b3dSmrg	if (sna->cursor.use_gtt) {
4488428d7b3dSmrg		c->image = gem_mmap(sna->kgem.fd, c->handle, c->alloc);
4489428d7b3dSmrg		if (c->image == NULL) {
4490428d7b3dSmrg			gem_close(sna->kgem.fd, c->handle);
4491428d7b3dSmrg			return NULL;
4492428d7b3dSmrg		}
4493428d7b3dSmrg	} else
4494428d7b3dSmrg		c->image = NULL;
4495428d7b3dSmrg
4496428d7b3dSmrg	__DBG(("%s: handle=%d, allocated %d\n", __FUNCTION__, c->handle, size));
4497428d7b3dSmrg
4498428d7b3dSmrg	c->ref = 0;
4499428d7b3dSmrg	c->serial = 0;
4500428d7b3dSmrg	c->rotation = 0;
4501428d7b3dSmrg	c->last_width = c->last_height = 0; /* all clear */
4502428d7b3dSmrg	c->size = size;
4503428d7b3dSmrg
4504428d7b3dSmrg	sna->cursor.num_stash--;
4505428d7b3dSmrg	sna->cursor.stash = c->next;
4506428d7b3dSmrg
4507428d7b3dSmrg	c->next = sna->cursor.cursors;
4508428d7b3dSmrg	sna->cursor.cursors = c;
4509428d7b3dSmrg
4510428d7b3dSmrg	return c;
4511428d7b3dSmrg}
4512428d7b3dSmrg
4513428d7b3dSmrgstatic uint32_t *get_cursor_argb(CursorPtr c)
4514428d7b3dSmrg{
4515428d7b3dSmrg#ifdef ARGB_CURSOR
4516428d7b3dSmrg	return (uint32_t *)c->bits->argb;
4517428d7b3dSmrg#else
4518428d7b3dSmrg	return NULL;
4519428d7b3dSmrg#endif
4520428d7b3dSmrg}
4521428d7b3dSmrg
4522428d7b3dSmrgstatic struct sna_cursor *__sna_get_cursor(struct sna *sna, xf86CrtcPtr crtc)
4523428d7b3dSmrg{
4524428d7b3dSmrg	struct sna_cursor *cursor;
4525428d7b3dSmrg	const uint8_t *source, *mask;
4526428d7b3dSmrg	const uint32_t *argb;
4527428d7b3dSmrg	uint32_t *image;
4528428d7b3dSmrg	int width, height, pitch, size, x, y;
4529428d7b3dSmrg	Rotation rotation;
4530428d7b3dSmrg
4531428d7b3dSmrg	assert(sna->cursor.ref);
4532428d7b3dSmrg
4533428d7b3dSmrg	cursor = to_sna_crtc(crtc)->cursor;
4534428d7b3dSmrg	__DBG(("%s: current cursor handle=%d, serial=%d [expected %d]\n",
4535428d7b3dSmrg	       __FUNCTION__,
4536428d7b3dSmrg	       cursor ? cursor->handle : 0,
4537428d7b3dSmrg	       cursor ? cursor->serial : 0,
4538428d7b3dSmrg	       sna->cursor.serial));
4539428d7b3dSmrg	if (cursor && cursor->serial == sna->cursor.serial) {
4540428d7b3dSmrg		assert(cursor->size == sna->cursor.size);
4541428d7b3dSmrg		assert(cursor->rotation == crtc->transform_in_use ? crtc->rotation : RR_Rotate_0);
4542428d7b3dSmrg		assert(cursor->ref);
4543428d7b3dSmrg		return cursor;
4544428d7b3dSmrg	}
4545428d7b3dSmrg
4546428d7b3dSmrg	__DBG(("%s: cursor=%dx%d, pitch=%d, serial=%d, argb?=%d\n", __FUNCTION__,
4547428d7b3dSmrg	       sna->cursor.ref->bits->width,
4548428d7b3dSmrg	       sna->cursor.ref->bits->height,
4549428d7b3dSmrg	       get_cursor_argb(sna->cursor.ref) ? 4*sna->cursor.ref->bits->width : BitmapBytePad(sna->cursor.ref->bits->width),
4550428d7b3dSmrg	       sna->cursor.serial,
4551428d7b3dSmrg	       get_cursor_argb(sna->cursor.ref) != NULL));
4552428d7b3dSmrg
4553428d7b3dSmrg	rotation = crtc->transform_in_use ? crtc->rotation : RR_Rotate_0;
4554428d7b3dSmrg
4555428d7b3dSmrg	if (sna->cursor.use_gtt) { /* Don't allow phys cursor sharing */
4556428d7b3dSmrg		for (cursor = sna->cursor.cursors; cursor; cursor = cursor->next) {
4557428d7b3dSmrg			if (cursor->serial == sna->cursor.serial && cursor->rotation == rotation) {
4558428d7b3dSmrg				__DBG(("%s: reusing handle=%d, serial=%d, rotation=%d, size=%d\n",
4559428d7b3dSmrg				       __FUNCTION__, cursor->handle, cursor->serial, cursor->rotation, cursor->size));
4560428d7b3dSmrg				assert(cursor->size == sna->cursor.size);
4561428d7b3dSmrg				return cursor;
4562428d7b3dSmrg			}
4563428d7b3dSmrg		}
4564428d7b3dSmrg
4565428d7b3dSmrg		cursor = to_sna_crtc(crtc)->cursor;
4566428d7b3dSmrg	}
4567428d7b3dSmrg
4568428d7b3dSmrg	size = sna->cursor.size;
4569428d7b3dSmrg	if (cursor && cursor->alloc < 4*size*size)
4570428d7b3dSmrg		cursor = NULL;
4571428d7b3dSmrg
4572428d7b3dSmrg	if (cursor == NULL) {
4573428d7b3dSmrg		cursor = __sna_create_cursor(sna, 4*size*size);
4574428d7b3dSmrg		if (cursor == NULL) {
4575428d7b3dSmrg			DBG(("%s: failed to allocate cursor\n", __FUNCTION__));
4576428d7b3dSmrg			return NULL;
4577428d7b3dSmrg		}
4578428d7b3dSmrg	}
4579428d7b3dSmrg
4580428d7b3dSmrg	width = sna->cursor.ref->bits->width;
4581428d7b3dSmrg	height = sna->cursor.ref->bits->height;
4582428d7b3dSmrg	source = sna->cursor.ref->bits->source;
4583428d7b3dSmrg	mask = sna->cursor.ref->bits->mask;
4584428d7b3dSmrg	argb = get_cursor_argb(sna->cursor.ref);
4585428d7b3dSmrg	pitch = BitmapBytePad(width);
4586428d7b3dSmrg
4587428d7b3dSmrg	image = cursor->image;
4588428d7b3dSmrg	if (image == NULL) {
4589428d7b3dSmrg		image = sna->cursor.scratch;
4590428d7b3dSmrg		cursor->last_width = cursor->last_height = size;
4591428d7b3dSmrg	}
4592428d7b3dSmrg	if (size > cursor->size ||
4593428d7b3dSmrg	    width < cursor->last_width ||
4594428d7b3dSmrg	    height < cursor->last_height ||
4595428d7b3dSmrg	    rotation != cursor->rotation)
4596428d7b3dSmrg		memset(image, 0, 4*size*size);
4597428d7b3dSmrg	if (rotation == RR_Rotate_0) {
4598428d7b3dSmrg		if (argb == NULL) {
4599428d7b3dSmrg			for (y = 0; y < height; y++) {
4600428d7b3dSmrg				uint32_t *p = image + y*size;
4601428d7b3dSmrg				for (x = 0; x < width; x++) {
4602428d7b3dSmrg					int byte = x / 8;
4603428d7b3dSmrg					uint8_t bit = 1 << (x & 7);
4604428d7b3dSmrg					uint32_t pixel;
4605428d7b3dSmrg
4606428d7b3dSmrg					if (mask[byte] & bit) {
4607428d7b3dSmrg						if (source[byte] & bit)
4608428d7b3dSmrg							pixel = sna->cursor.fg;
4609428d7b3dSmrg						else
4610428d7b3dSmrg							pixel = sna->cursor.bg;
4611428d7b3dSmrg					} else
4612428d7b3dSmrg						pixel = 0;
4613428d7b3dSmrg
4614428d7b3dSmrg					*p++ = pixel;
4615428d7b3dSmrg				}
4616428d7b3dSmrg				mask += pitch;
4617428d7b3dSmrg				source += pitch;
4618428d7b3dSmrg			}
4619428d7b3dSmrg		} else
4620428d7b3dSmrg			memcpy_blt(argb, image, 32,
4621428d7b3dSmrg				   width * 4, size * 4,
4622428d7b3dSmrg				   0, 0,
4623428d7b3dSmrg				   0, 0,
4624428d7b3dSmrg				   width, height);
4625428d7b3dSmrg	} else {
4626428d7b3dSmrg		for (y = 0; y < size; y++)
4627428d7b3dSmrg			for (x = 0; x < size; x++) {
4628428d7b3dSmrg				uint32_t pixel;
4629428d7b3dSmrg				int xin, yin;
4630428d7b3dSmrg
4631428d7b3dSmrg				rotate_coord(rotation, size, x, y, &xin, &yin);
4632428d7b3dSmrg				if (xin < width && yin < height)
4633428d7b3dSmrg					if (argb == NULL) {
4634428d7b3dSmrg						int byte = xin / 8;
4635428d7b3dSmrg						int bit = xin & 7;
4636428d7b3dSmrg						if (mask[yin*pitch + byte] & (1 << bit)) {
4637428d7b3dSmrg							if (source[yin*pitch + byte] & (1 << bit))
4638428d7b3dSmrg								pixel = sna->cursor.fg;
4639428d7b3dSmrg							else
4640428d7b3dSmrg								pixel = sna->cursor.bg;
4641428d7b3dSmrg						} else
4642428d7b3dSmrg							pixel = 0;
4643428d7b3dSmrg					} else
4644428d7b3dSmrg						pixel = argb[yin * width + xin];
4645428d7b3dSmrg				else
4646428d7b3dSmrg					pixel = 0;
4647428d7b3dSmrg				image[y * size + x] = pixel;
4648428d7b3dSmrg			}
4649428d7b3dSmrg	}
4650428d7b3dSmrg
4651428d7b3dSmrg	if (image != cursor->image) {
4652428d7b3dSmrg		struct drm_i915_gem_pwrite pwrite;
4653428d7b3dSmrg
4654428d7b3dSmrg		VG_CLEAR(pwrite);
4655428d7b3dSmrg		pwrite.handle = cursor->handle;
4656428d7b3dSmrg		pwrite.offset = 0;
4657428d7b3dSmrg		pwrite.size = 4*size*size;
4658428d7b3dSmrg		pwrite.data_ptr = (uintptr_t)image;
4659428d7b3dSmrg		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GEM_PWRITE, &pwrite))
4660428d7b3dSmrg			__DBG(("%s: cursor update (pwrite) failed: %d\n", __FUNCTION__, errno));
4661428d7b3dSmrg	}
4662428d7b3dSmrg
4663428d7b3dSmrg	cursor->size = size;
4664428d7b3dSmrg	cursor->rotation = rotation;
4665428d7b3dSmrg	cursor->serial = sna->cursor.serial;
4666428d7b3dSmrg	cursor->last_width = width;
4667428d7b3dSmrg	cursor->last_height = height;
4668428d7b3dSmrg	return cursor;
4669428d7b3dSmrg}
4670428d7b3dSmrg
4671428d7b3dSmrgstatic unsigned char *
4672428d7b3dSmrgsna_realize_cursor(xf86CursorInfoPtr info, CursorPtr cursor)
4673428d7b3dSmrg{
4674428d7b3dSmrg	return NULL;
4675428d7b3dSmrg}
4676428d7b3dSmrg
4677428d7b3dSmrg/* XXXMRG OsBlockSIGIO() is gone gone, old one remains before porting */
4678428d7b3dSmrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,12,99,901,0) && 0
4679428d7b3dSmrgstatic inline int sigio_block(void)
4680428d7b3dSmrg{
4681428d7b3dSmrg	OsBlockSIGIO();
4682428d7b3dSmrg	return 0;
4683428d7b3dSmrg}
4684428d7b3dSmrgstatic inline void sigio_unblock(int was_blocked)
4685428d7b3dSmrg{
4686428d7b3dSmrg	OsReleaseSIGIO();
4687428d7b3dSmrg	(void)was_blocked;
4688428d7b3dSmrg}
4689428d7b3dSmrg#else
4690428d7b3dSmrg#include <xf86_OSproc.h>
4691428d7b3dSmrgstatic inline int sigio_block(void)
4692428d7b3dSmrg{
4693428d7b3dSmrg	return xf86BlockSIGIO();
4694428d7b3dSmrg}
4695428d7b3dSmrgstatic inline void sigio_unblock(int was_blocked)
4696428d7b3dSmrg{
4697428d7b3dSmrg	xf86UnblockSIGIO(was_blocked);
4698428d7b3dSmrg}
4699428d7b3dSmrg#endif
4700428d7b3dSmrg
4701428d7b3dSmrgstatic void
4702428d7b3dSmrgsna_show_cursors(ScrnInfoPtr scrn)
4703428d7b3dSmrg{
4704428d7b3dSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
4705428d7b3dSmrg	struct sna *sna = to_sna(scrn);
4706428d7b3dSmrg	int sigio, c;
4707428d7b3dSmrg
4708428d7b3dSmrg	DBG(("%s: cursor?=%d\n", __FUNCTION__, sna->cursor.ref != NULL));
4709428d7b3dSmrg	if (sna->cursor.ref == NULL)
4710428d7b3dSmrg		return;
4711428d7b3dSmrg
4712428d7b3dSmrg	sigio = sigio_block();
4713428d7b3dSmrg	for (c = 0; c < sna->mode.num_real_crtc; c++) {
4714428d7b3dSmrg		xf86CrtcPtr crtc = xf86_config->crtc[c];
4715428d7b3dSmrg		struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
4716428d7b3dSmrg		struct drm_mode_cursor arg;
4717428d7b3dSmrg		struct sna_cursor *cursor;
4718428d7b3dSmrg
4719428d7b3dSmrg		assert(sna_crtc != NULL);
4720428d7b3dSmrg		if (sna_crtc->bo == NULL)
4721428d7b3dSmrg			continue;
4722428d7b3dSmrg
4723428d7b3dSmrg		if (!crtc->cursor_in_range) {
4724428d7b3dSmrg			DBG(("%s: skipping cursor outside CRTC (pipe=%d)\n",
4725428d7b3dSmrg			     __FUNCTION__, sna_crtc->pipe));
4726428d7b3dSmrg			continue;
4727428d7b3dSmrg		}
4728428d7b3dSmrg
4729428d7b3dSmrg		cursor = __sna_get_cursor(sna, crtc);
4730428d7b3dSmrg		if (cursor == NULL ||
4731428d7b3dSmrg		    (sna_crtc->cursor == cursor && sna_crtc->last_cursor_size == cursor->size)) {
4732428d7b3dSmrg			DBG(("%s: skipping cursor already show on CRTC (pipe=%d)\n",
4733428d7b3dSmrg			     __FUNCTION__, sna_crtc->pipe));
4734428d7b3dSmrg			continue;
4735428d7b3dSmrg		}
4736428d7b3dSmrg
4737428d7b3dSmrg		DBG(("%s: CRTC pipe=%d, handle->%d\n", __FUNCTION__,
4738428d7b3dSmrg		     sna_crtc->pipe, cursor->handle));
4739428d7b3dSmrg
4740428d7b3dSmrg		VG_CLEAR(arg);
4741428d7b3dSmrg		arg.flags = DRM_MODE_CURSOR_BO;
4742428d7b3dSmrg		arg.crtc_id = sna_crtc->id;
4743428d7b3dSmrg		arg.width = arg.height = cursor->size;
4744428d7b3dSmrg		arg.handle = cursor->handle;
4745428d7b3dSmrg
4746428d7b3dSmrg		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_CURSOR, &arg) == 0) {
4747428d7b3dSmrg			if (sna_crtc->cursor) {
4748428d7b3dSmrg				assert(sna_crtc->cursor->ref > 0);
4749428d7b3dSmrg				sna_crtc->cursor->ref--;
4750428d7b3dSmrg			}
4751428d7b3dSmrg			cursor->ref++;
4752428d7b3dSmrg			sna_crtc->cursor = cursor;
4753428d7b3dSmrg			sna_crtc->last_cursor_size = cursor->size;
4754428d7b3dSmrg		}
4755428d7b3dSmrg	}
4756428d7b3dSmrg	sigio_unblock(sigio);
4757428d7b3dSmrg	sna->cursor.active = true;
4758428d7b3dSmrg}
4759428d7b3dSmrg
4760428d7b3dSmrgstatic void
4761428d7b3dSmrgsna_set_cursor_colors(ScrnInfoPtr scrn, int _bg, int _fg)
4762428d7b3dSmrg{
4763428d7b3dSmrg	struct sna *sna = to_sna(scrn);
4764428d7b3dSmrg	uint32_t fg = _fg, bg = _bg;
4765428d7b3dSmrg
4766428d7b3dSmrg	__DBG(("%s(bg=%08x, fg=%08x)\n", __FUNCTION__, bg, fg));
4767428d7b3dSmrg
4768428d7b3dSmrg	/* Save ARGB versions of these colors */
4769428d7b3dSmrg	fg |= 0xff000000;
4770428d7b3dSmrg	bg |= 0xff000000;
4771428d7b3dSmrg	if (fg == sna->cursor.fg && bg == sna->cursor.bg)
4772428d7b3dSmrg		return;
4773428d7b3dSmrg
4774428d7b3dSmrg	sna->cursor.fg = fg;
4775428d7b3dSmrg	sna->cursor.bg = bg;
4776428d7b3dSmrg
4777428d7b3dSmrg	if (sna->cursor.ref == NULL)
4778428d7b3dSmrg		return;
4779428d7b3dSmrg
4780428d7b3dSmrg	if (get_cursor_argb(sna->cursor.ref))
4781428d7b3dSmrg		return;
4782428d7b3dSmrg
4783428d7b3dSmrg	sna->cursor.serial++;
4784428d7b3dSmrg	__DBG(("%s: serial->%d\n", __FUNCTION__, sna->cursor.serial));
4785428d7b3dSmrg
4786428d7b3dSmrg	sna_show_cursors(scrn);
4787428d7b3dSmrg}
4788428d7b3dSmrg
4789428d7b3dSmrgstatic void
4790428d7b3dSmrgsna_crtc_disable_cursor(struct sna *sna, struct sna_crtc *crtc)
4791428d7b3dSmrg{
4792428d7b3dSmrg	struct drm_mode_cursor arg;
4793428d7b3dSmrg
4794428d7b3dSmrg	if (!crtc->cursor)
4795428d7b3dSmrg		return;
4796428d7b3dSmrg
4797428d7b3dSmrg	DBG(("%s: CRTC:%d, handle=%d\n", __FUNCTION__, crtc->id, crtc->cursor->handle));
4798428d7b3dSmrg	assert(crtc->cursor->ref);
4799428d7b3dSmrg
4800428d7b3dSmrg	VG_CLEAR(arg);
4801428d7b3dSmrg	arg.flags = DRM_MODE_CURSOR_BO;
4802428d7b3dSmrg	arg.crtc_id = crtc->id;
4803428d7b3dSmrg	arg.width = arg.height = 0;
4804428d7b3dSmrg	arg.handle = 0;
4805428d7b3dSmrg
4806428d7b3dSmrg	(void)drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_CURSOR, &arg);
4807428d7b3dSmrg	assert(crtc->cursor->ref > 0);
4808428d7b3dSmrg	crtc->cursor->ref--;
4809428d7b3dSmrg	crtc->cursor = NULL;
4810428d7b3dSmrg	crtc->last_cursor_size = 0;
4811428d7b3dSmrg}
4812428d7b3dSmrg
4813428d7b3dSmrgstatic void
4814428d7b3dSmrgsna_hide_cursors(ScrnInfoPtr scrn)
4815428d7b3dSmrg{
4816428d7b3dSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
4817428d7b3dSmrg	struct sna *sna = to_sna(scrn);
4818428d7b3dSmrg	struct sna_cursor *cursor, **prev;
4819428d7b3dSmrg	int sigio, c;
4820428d7b3dSmrg
4821428d7b3dSmrg	DBG(("%s\n", __FUNCTION__));
4822428d7b3dSmrg	sna->cursor.active = false;
4823428d7b3dSmrg
4824428d7b3dSmrg	sigio = sigio_block();
4825428d7b3dSmrg	for (c = 0; c < sna->mode.num_real_crtc; c++) {
4826428d7b3dSmrg		assert(to_sna_crtc(xf86_config->crtc[c]));
4827428d7b3dSmrg		sna_crtc_disable_cursor(sna, to_sna_crtc(xf86_config->crtc[c]));
4828428d7b3dSmrg	}
4829428d7b3dSmrg
4830428d7b3dSmrg	for (prev = &sna->cursor.cursors; (cursor = *prev) != NULL; ) {
4831428d7b3dSmrg		assert(cursor->ref == 0);
4832428d7b3dSmrg
4833428d7b3dSmrg		if (cursor->serial == sna->cursor.serial) {
4834428d7b3dSmrg			prev = &cursor->next;
4835428d7b3dSmrg			continue;
4836428d7b3dSmrg		}
4837428d7b3dSmrg
4838428d7b3dSmrg		*prev = cursor->next;
4839428d7b3dSmrg		if (cursor->image)
4840428d7b3dSmrg			munmap(cursor->image, cursor->alloc);
4841428d7b3dSmrg		gem_close(sna->kgem.fd, cursor->handle);
4842428d7b3dSmrg
4843428d7b3dSmrg		cursor->next = sna->cursor.stash;
4844428d7b3dSmrg		sna->cursor.stash = cursor;
4845428d7b3dSmrg		sna->cursor.num_stash++;
4846428d7b3dSmrg	}
4847428d7b3dSmrg
4848428d7b3dSmrg	sigio_unblock(sigio);
4849428d7b3dSmrg}
4850428d7b3dSmrg
4851428d7b3dSmrgstatic void
4852428d7b3dSmrgsna_set_cursor_position(ScrnInfoPtr scrn, int x, int y)
4853428d7b3dSmrg{
4854428d7b3dSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
4855428d7b3dSmrg	struct sna *sna = to_sna(scrn);
4856428d7b3dSmrg	int sigio, c;
4857428d7b3dSmrg
4858428d7b3dSmrg	__DBG(("%s(%d, %d), cursor? %d\n", __FUNCTION__,
4859428d7b3dSmrg	       x, y, sna->cursor.ref!=NULL));
4860428d7b3dSmrg	if (sna->cursor.ref == NULL)
4861428d7b3dSmrg		return;
4862428d7b3dSmrg
4863428d7b3dSmrg	sigio = sigio_block();
4864428d7b3dSmrg	sna->cursor.last_x = x;
4865428d7b3dSmrg	sna->cursor.last_y = y;
4866428d7b3dSmrg
4867428d7b3dSmrg	/* undo what xf86HWCurs did to the coordinates */
4868428d7b3dSmrg	x += scrn->frameX0;
4869428d7b3dSmrg	y += scrn->frameY0;
4870428d7b3dSmrg	for (c = 0; c < sna->mode.num_real_crtc; c++) {
4871428d7b3dSmrg		xf86CrtcPtr crtc = xf86_config->crtc[c];
4872428d7b3dSmrg		struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
4873428d7b3dSmrg		struct sna_cursor *cursor = NULL;
4874428d7b3dSmrg		struct drm_mode_cursor arg;
4875428d7b3dSmrg
4876428d7b3dSmrg		assert(sna_crtc != NULL);
4877428d7b3dSmrg
4878428d7b3dSmrg		VG_CLEAR(arg);
4879428d7b3dSmrg		arg.flags = 0;
4880428d7b3dSmrg		arg.crtc_id = sna_crtc->id;
4881428d7b3dSmrg		arg.handle = 0;
4882428d7b3dSmrg
4883428d7b3dSmrg		if (sna_crtc->bo == NULL)
4884428d7b3dSmrg			goto disable;
4885428d7b3dSmrg
4886428d7b3dSmrg		if (crtc->transform_in_use) {
4887428d7b3dSmrg			int xhot = sna->cursor.ref->bits->xhot;
4888428d7b3dSmrg			int yhot = sna->cursor.ref->bits->yhot;
4889428d7b3dSmrg			struct pict_f_vector v;
4890428d7b3dSmrg
4891428d7b3dSmrg			v.v[0] = (x + xhot) + 0.5;
4892428d7b3dSmrg			v.v[1] = (y + yhot) + 0.5;
4893428d7b3dSmrg			v.v[2] = 1;
4894428d7b3dSmrg			pixman_f_transform_point(&crtc->f_framebuffer_to_crtc, &v);
4895428d7b3dSmrg
4896428d7b3dSmrg			rotate_coord_back(crtc->rotation, sna->cursor.size, &xhot, &yhot);
4897428d7b3dSmrg
4898428d7b3dSmrg			/* cursor will have 0.5 added to it already so floor is sufficent */
4899428d7b3dSmrg			arg.x = floor(v.v[0]) - xhot;
4900428d7b3dSmrg			arg.y = floor(v.v[1]) - yhot;
4901428d7b3dSmrg		} else {
4902428d7b3dSmrg			arg.x = x - crtc->x;
4903428d7b3dSmrg			arg.y = y - crtc->y;
4904428d7b3dSmrg		}
4905428d7b3dSmrg
4906428d7b3dSmrg		if (arg.x < crtc->mode.HDisplay && arg.x > -sna->cursor.size &&
4907428d7b3dSmrg		    arg.y < crtc->mode.VDisplay && arg.y > -sna->cursor.size) {
4908428d7b3dSmrg			cursor = __sna_get_cursor(sna, crtc);
4909428d7b3dSmrg			if (cursor == NULL)
4910428d7b3dSmrg				cursor = sna_crtc->cursor;
4911428d7b3dSmrg			if (cursor == NULL) {
4912428d7b3dSmrg				__DBG(("%s: failed to grab cursor, disabling\n",
4913428d7b3dSmrg				       __FUNCTION__));
4914428d7b3dSmrg				goto disable;
4915428d7b3dSmrg			}
4916428d7b3dSmrg
4917428d7b3dSmrg			if (sna_crtc->cursor != cursor || sna_crtc->last_cursor_size != cursor->size) {
4918428d7b3dSmrg				arg.flags |= DRM_MODE_CURSOR_BO;
4919428d7b3dSmrg				arg.handle = cursor->handle;
4920428d7b3dSmrg			}
4921428d7b3dSmrg
4922428d7b3dSmrg			arg.width = arg.height = cursor->size;
4923428d7b3dSmrg			arg.flags |= DRM_MODE_CURSOR_MOVE;
4924428d7b3dSmrg			crtc->cursor_in_range = true;
4925428d7b3dSmrg		} else {
4926428d7b3dSmrg			crtc->cursor_in_range = false;
4927428d7b3dSmrgdisable:
4928428d7b3dSmrg			if (sna_crtc->cursor) {
4929428d7b3dSmrg				arg.flags = DRM_MODE_CURSOR_BO;
4930428d7b3dSmrg				arg.width = arg.height = 0;
4931428d7b3dSmrg			}
4932428d7b3dSmrg			cursor = NULL;
4933428d7b3dSmrg		}
4934428d7b3dSmrg
4935428d7b3dSmrg		__DBG(("%s: CRTC:%d (%d, %d), handle=%d, flags=%x (old cursor handle=%d), move? %d, update handle? %d\n",
4936428d7b3dSmrg		       __FUNCTION__, sna_crtc->id, arg.x, arg.y, arg.handle, arg.flags, sna_crtc->cursor ? sna_crtc->cursor->handle : 0,
4937428d7b3dSmrg		       arg.flags & DRM_MODE_CURSOR_MOVE, arg.flags & DRM_MODE_CURSOR_BO));
4938428d7b3dSmrg
4939428d7b3dSmrg		if (arg.flags &&
4940428d7b3dSmrg		    drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_CURSOR, &arg) == 0) {
4941428d7b3dSmrg			if (arg.flags & DRM_MODE_CURSOR_BO) {
4942428d7b3dSmrg				if (sna_crtc->cursor) {
4943428d7b3dSmrg					assert(sna_crtc->cursor->ref > 0);
4944428d7b3dSmrg					sna_crtc->cursor->ref--;
4945428d7b3dSmrg				}
4946428d7b3dSmrg				sna_crtc->cursor = cursor;
4947428d7b3dSmrg				if (cursor) {
4948428d7b3dSmrg					sna_crtc->last_cursor_size = cursor->size;
4949428d7b3dSmrg					cursor->ref++;
4950428d7b3dSmrg				} else
4951428d7b3dSmrg					sna_crtc->last_cursor_size = 0;
4952428d7b3dSmrg			}
4953428d7b3dSmrg		}
4954428d7b3dSmrg	}
4955428d7b3dSmrg	sigio_unblock(sigio);
4956428d7b3dSmrg}
4957428d7b3dSmrg
4958428d7b3dSmrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,15,99,902,2)
4959428d7b3dSmrgstatic Bool
4960428d7b3dSmrgsna_load_cursor_argb2(ScrnInfoPtr scrn, CursorPtr cursor)
4961428d7b3dSmrg{
4962428d7b3dSmrg	return TRUE;
4963428d7b3dSmrg}
4964428d7b3dSmrg
4965428d7b3dSmrgstatic Bool
4966428d7b3dSmrgsna_load_cursor_image2(ScrnInfoPtr scrn, unsigned char *src)
4967428d7b3dSmrg{
4968428d7b3dSmrg	return TRUE;
4969428d7b3dSmrg}
4970428d7b3dSmrg#endif
4971428d7b3dSmrg
4972428d7b3dSmrgstatic void
4973428d7b3dSmrgsna_load_cursor_argb(ScrnInfoPtr scrn, CursorPtr cursor)
4974428d7b3dSmrg{
4975428d7b3dSmrg}
4976428d7b3dSmrg
4977428d7b3dSmrgstatic void
4978428d7b3dSmrgsna_load_cursor_image(ScrnInfoPtr scrn, unsigned char *src)
4979428d7b3dSmrg{
4980428d7b3dSmrg}
4981428d7b3dSmrg
4982428d7b3dSmrgstatic int __cursor_size(CursorPtr cursor)
4983428d7b3dSmrg{
4984428d7b3dSmrg	int i, size;
4985428d7b3dSmrg
4986428d7b3dSmrg	i = MAX(cursor->bits->width, cursor->bits->height);
4987428d7b3dSmrg	for (size = 64; size < i; size <<= 1)
4988428d7b3dSmrg		;
4989428d7b3dSmrg
4990428d7b3dSmrg	return size;
4991428d7b3dSmrg}
4992428d7b3dSmrg
4993428d7b3dSmrgstatic bool
4994428d7b3dSmrgsna_cursor_preallocate(struct sna *sna)
4995428d7b3dSmrg{
4996428d7b3dSmrg	while (sna->cursor.num_stash < 0) {
4997428d7b3dSmrg		struct sna_cursor *cursor = malloc(sizeof(*cursor));
4998428d7b3dSmrg		if (!cursor)
4999428d7b3dSmrg			return false;
5000428d7b3dSmrg
5001428d7b3dSmrg		cursor->next = sna->cursor.stash;
5002428d7b3dSmrg		sna->cursor.stash = cursor;
5003428d7b3dSmrg
5004428d7b3dSmrg		sna->cursor.num_stash++;
5005428d7b3dSmrg	}
5006428d7b3dSmrg
5007428d7b3dSmrg	return true;
5008428d7b3dSmrg}
5009428d7b3dSmrg
5010428d7b3dSmrgstatic Bool
5011428d7b3dSmrgsna_use_hw_cursor(ScreenPtr screen, CursorPtr cursor)
5012428d7b3dSmrg{
5013428d7b3dSmrg	struct sna *sna = to_sna_from_screen(screen);
5014428d7b3dSmrg
5015428d7b3dSmrg	DBG(("%s (%dx%d)?\n", __FUNCTION__,
5016428d7b3dSmrg	     cursor->bits->width, cursor->bits->height));
5017428d7b3dSmrg
5018428d7b3dSmrg	/* cursors are invariant */
5019428d7b3dSmrg	if (cursor == sna->cursor.ref)
5020428d7b3dSmrg		return TRUE;
5021428d7b3dSmrg
5022428d7b3dSmrg	if (sna->cursor.ref) {
5023428d7b3dSmrg		FreeCursor(sna->cursor.ref, None);
5024428d7b3dSmrg		sna->cursor.ref = NULL;
5025428d7b3dSmrg	}
5026428d7b3dSmrg
5027428d7b3dSmrg	sna->cursor.size = __cursor_size(cursor);
5028428d7b3dSmrg	if (sna->cursor.size > sna->cursor.max_size)
5029428d7b3dSmrg		return FALSE;
5030428d7b3dSmrg
5031428d7b3dSmrg	if (!sna_cursor_preallocate(sna))
5032428d7b3dSmrg		return FALSE;
5033428d7b3dSmrg
5034428d7b3dSmrg	sna->cursor.ref = cursor;
5035428d7b3dSmrg	cursor->refcnt++;
5036428d7b3dSmrg	sna->cursor.serial++;
5037428d7b3dSmrg
5038428d7b3dSmrg	DBG(("%s(%dx%d): ARGB?=%d, serial->%d, size->%d\n", __FUNCTION__,
5039428d7b3dSmrg	       cursor->bits->width,
5040428d7b3dSmrg	       cursor->bits->height,
5041428d7b3dSmrg	       get_cursor_argb(cursor) != NULL,
5042428d7b3dSmrg	       sna->cursor.serial,
5043428d7b3dSmrg	       sna->cursor.size));
5044428d7b3dSmrg	return TRUE;
5045428d7b3dSmrg}
5046428d7b3dSmrg
5047428d7b3dSmrgstatic void
5048428d7b3dSmrgsna_cursor_pre_init(struct sna *sna)
5049428d7b3dSmrg{
5050428d7b3dSmrg	struct local_get_cap {
5051428d7b3dSmrg		uint64_t name;
5052428d7b3dSmrg		uint64_t value;
5053428d7b3dSmrg	} cap;
5054428d7b3dSmrg	int v;
5055428d7b3dSmrg
5056428d7b3dSmrg	if (sna->mode.num_real_crtc == 0)
5057428d7b3dSmrg		return;
5058428d7b3dSmrg
5059428d7b3dSmrg#define LOCAL_IOCTL_GET_CAP	DRM_IOWR(0x0c, struct local_get_cap)
5060428d7b3dSmrg#define DRM_CAP_CURSOR_WIDTH	0x8
5061428d7b3dSmrg#define DRM_CAP_CURSOR_HEIGHT	0x9
5062428d7b3dSmrg
5063428d7b3dSmrg#define I915_PARAM_HAS_COHERENT_PHYS_GTT 29
5064428d7b3dSmrg
5065428d7b3dSmrg	sna->cursor.max_size = 64;
5066428d7b3dSmrg
5067428d7b3dSmrg	cap.value = 0;
5068428d7b3dSmrg	cap.name = DRM_CAP_CURSOR_WIDTH;
5069428d7b3dSmrg	if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_GET_CAP, &cap) == 0)
5070428d7b3dSmrg		sna->cursor.max_size = cap.value;
5071428d7b3dSmrg
5072428d7b3dSmrg	cap.name = DRM_CAP_CURSOR_HEIGHT;
5073428d7b3dSmrg	if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_GET_CAP, &cap) == 0 &&
5074428d7b3dSmrg	    cap.value < sna->cursor.max_size)
5075428d7b3dSmrg		sna->cursor.max_size = cap.value;
5076428d7b3dSmrg
5077428d7b3dSmrg	v = -1; /* No param uses the sign bit, reserve it for errors */
5078428d7b3dSmrg	if (sna->kgem.gen >= 033) {
5079428d7b3dSmrg		v = 1;
5080428d7b3dSmrg	} else {
5081428d7b3dSmrg		drm_i915_getparam_t gp = {
5082428d7b3dSmrg			I915_PARAM_HAS_COHERENT_PHYS_GTT,
5083428d7b3dSmrg			&v,
5084428d7b3dSmrg		};
5085428d7b3dSmrg		(void)drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GETPARAM, &gp);
5086428d7b3dSmrg	}
5087428d7b3dSmrg	sna->cursor.use_gtt = v > 0;
5088428d7b3dSmrg	DBG(("%s: cursor updates use_gtt?=%d\n",
5089428d7b3dSmrg	     __FUNCTION__, sna->cursor.use_gtt));
5090428d7b3dSmrg
5091428d7b3dSmrg	if (!sna->cursor.use_gtt) {
5092428d7b3dSmrg		sna->cursor.scratch = malloc(sna->cursor.max_size * sna->cursor.max_size * 4);
5093428d7b3dSmrg		if (!sna->cursor.scratch)
5094428d7b3dSmrg			sna->cursor.max_size = 0;
5095428d7b3dSmrg	}
5096428d7b3dSmrg
5097428d7b3dSmrg	sna->cursor.num_stash = -sna->mode.num_real_crtc;
5098428d7b3dSmrg
5099428d7b3dSmrg	xf86DrvMsg(sna->scrn->scrnIndex, X_PROBED,
5100428d7b3dSmrg		   "Using a maximum size of %dx%d for hardware cursors\n",
5101428d7b3dSmrg		   sna->cursor.max_size, sna->cursor.max_size);
5102428d7b3dSmrg}
5103428d7b3dSmrg
5104428d7b3dSmrgstatic void
5105428d7b3dSmrgsna_cursor_close(struct sna *sna)
5106428d7b3dSmrg{
5107428d7b3dSmrg	sna->cursor.serial = 0;
5108428d7b3dSmrg	sna_hide_cursors(sna->scrn);
5109428d7b3dSmrg
5110428d7b3dSmrg	while (sna->cursor.stash) {
5111428d7b3dSmrg		struct sna_cursor *cursor = sna->cursor.stash;
5112428d7b3dSmrg		sna->cursor.stash = cursor->next;
5113428d7b3dSmrg		free(cursor);
5114428d7b3dSmrg	}
5115428d7b3dSmrg
5116428d7b3dSmrg	sna->cursor.num_stash = -sna->mode.num_real_crtc;
5117428d7b3dSmrg}
5118428d7b3dSmrg
5119428d7b3dSmrgbool
5120428d7b3dSmrgsna_cursors_init(ScreenPtr screen, struct sna *sna)
5121428d7b3dSmrg{
5122428d7b3dSmrg	xf86CursorInfoPtr cursor_info;
5123428d7b3dSmrg
5124428d7b3dSmrg	if (sna->cursor.max_size == 0)
5125428d7b3dSmrg		return false;
5126428d7b3dSmrg
5127428d7b3dSmrg	cursor_info = xf86CreateCursorInfoRec();
5128428d7b3dSmrg	if (cursor_info == NULL)
5129428d7b3dSmrg		return false;
5130428d7b3dSmrg
5131428d7b3dSmrg	cursor_info->MaxWidth = sna->cursor.max_size;
5132428d7b3dSmrg	cursor_info->MaxHeight = sna->cursor.max_size;
5133428d7b3dSmrg	cursor_info->Flags = (HARDWARE_CURSOR_TRUECOLOR_AT_8BPP |
5134428d7b3dSmrg			      HARDWARE_CURSOR_UPDATE_UNHIDDEN |
5135428d7b3dSmrg			      HARDWARE_CURSOR_ARGB);
5136428d7b3dSmrg
5137428d7b3dSmrg	cursor_info->RealizeCursor = sna_realize_cursor;
5138428d7b3dSmrg	cursor_info->SetCursorColors = sna_set_cursor_colors;
5139428d7b3dSmrg	cursor_info->SetCursorPosition = sna_set_cursor_position;
5140428d7b3dSmrg	cursor_info->LoadCursorImage = sna_load_cursor_image;
5141428d7b3dSmrg	cursor_info->HideCursor = sna_hide_cursors;
5142428d7b3dSmrg	cursor_info->ShowCursor = sna_show_cursors;
5143428d7b3dSmrg	cursor_info->UseHWCursor = sna_use_hw_cursor;
5144428d7b3dSmrg#ifdef ARGB_CURSOR
5145428d7b3dSmrg	cursor_info->UseHWCursorARGB = sna_use_hw_cursor;
5146428d7b3dSmrg	cursor_info->LoadCursorARGB = sna_load_cursor_argb;
5147428d7b3dSmrg#endif
5148428d7b3dSmrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,15,99,902,3)
5149428d7b3dSmrg	cursor_info->LoadCursorImageCheck = sna_load_cursor_image2;
5150428d7b3dSmrg#ifdef ARGB_CURSOR
5151428d7b3dSmrg	cursor_info->LoadCursorARGBCheck = sna_load_cursor_argb2;
5152428d7b3dSmrg#endif
5153428d7b3dSmrg#endif
5154428d7b3dSmrg
5155428d7b3dSmrg	if (!xf86InitCursor(screen, cursor_info)) {
5156428d7b3dSmrg		xf86DestroyCursorInfoRec(cursor_info);
5157428d7b3dSmrg		return false;
5158428d7b3dSmrg	}
5159428d7b3dSmrg
5160428d7b3dSmrg	sna->cursor.info = cursor_info;
5161428d7b3dSmrg	return true;
5162428d7b3dSmrg}
5163428d7b3dSmrg
5164428d7b3dSmrgstatic void
5165428d7b3dSmrgsna_cursors_reload(struct sna *sna)
5166428d7b3dSmrg{
5167428d7b3dSmrg	DBG(("%s: active?=%d\n", __FUNCTION__, sna->cursor.active));
5168428d7b3dSmrg	if (sna->cursor.active)
5169428d7b3dSmrg		sna_set_cursor_position(sna->scrn,
5170428d7b3dSmrg					sna->cursor.last_x,
5171428d7b3dSmrg					sna->cursor.last_y);
5172428d7b3dSmrg}
5173428d7b3dSmrg
5174428d7b3dSmrgstatic void
5175428d7b3dSmrgsna_cursors_fini(struct sna *sna)
5176428d7b3dSmrg{
5177428d7b3dSmrg	if (sna->cursor.info) {
5178428d7b3dSmrg		xf86DestroyCursorInfoRec(sna->cursor.info);
5179428d7b3dSmrg		sna->cursor.info = NULL;
5180428d7b3dSmrg	}
5181428d7b3dSmrg
5182428d7b3dSmrg	if (sna->cursor.ref) {
5183428d7b3dSmrg		FreeCursor(sna->cursor.ref, None);
5184428d7b3dSmrg		sna->cursor.ref = NULL;
5185428d7b3dSmrg	}
5186428d7b3dSmrg}
5187428d7b3dSmrg
5188428d7b3dSmrgstatic bool
5189428d7b3dSmrgsna_crtc_flip(struct sna *sna, struct sna_crtc *crtc, struct kgem_bo *bo, int x, int y)
5190428d7b3dSmrg{
5191428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
5192428d7b3dSmrg	struct drm_mode_crtc arg;
5193428d7b3dSmrg	uint32_t output_ids[32];
5194428d7b3dSmrg	int output_count = 0;
5195428d7b3dSmrg	int i;
5196428d7b3dSmrg
5197428d7b3dSmrg	DBG(("%s CRTC:%d [pipe=%d], handle=%d\n", __FUNCTION__, crtc->id, crtc->pipe, bo->handle));
5198428d7b3dSmrg
5199428d7b3dSmrg	assert(sna->mode.num_real_output < ARRAY_SIZE(output_ids));
5200428d7b3dSmrg	assert(crtc->bo);
5201428d7b3dSmrg	assert(crtc->kmode.clock);
5202428d7b3dSmrg
5203428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_output; i++) {
5204428d7b3dSmrg		xf86OutputPtr output = config->output[i];
5205428d7b3dSmrg
5206428d7b3dSmrg		if (output->crtc != crtc->base)
5207428d7b3dSmrg			continue;
5208428d7b3dSmrg
5209428d7b3dSmrg		DBG(("%s: attaching output '%s' %d [%d] to crtc:%d (pipe %d) (possible crtc:%x, possible clones:%x)\n",
5210428d7b3dSmrg		     __FUNCTION__, output->name, i, to_connector_id(output),
5211428d7b3dSmrg		     crtc->id, crtc->pipe,
5212428d7b3dSmrg		     (uint32_t)output->possible_crtcs,
5213428d7b3dSmrg		     (uint32_t)output->possible_clones));
5214428d7b3dSmrg
5215428d7b3dSmrg		assert(output->possible_crtcs & (1 << crtc->pipe) ||
5216428d7b3dSmrg		       is_zaphod(sna->scrn));
5217428d7b3dSmrg
5218428d7b3dSmrg		output_ids[output_count] = to_connector_id(output);
5219428d7b3dSmrg		if (++output_count == ARRAY_SIZE(output_ids))
5220428d7b3dSmrg			return false;
5221428d7b3dSmrg	}
5222428d7b3dSmrg	assert(output_count);
5223428d7b3dSmrg
5224428d7b3dSmrg	VG_CLEAR(arg);
5225428d7b3dSmrg	arg.crtc_id = crtc->id;
5226428d7b3dSmrg	arg.fb_id = fb_id(bo);
5227428d7b3dSmrg	assert(arg.fb_id);
5228428d7b3dSmrg	arg.x = x;
5229428d7b3dSmrg	arg.y = y;
5230428d7b3dSmrg	arg.set_connectors_ptr = (uintptr_t)output_ids;
5231428d7b3dSmrg	arg.count_connectors = output_count;
5232428d7b3dSmrg	arg.mode = crtc->kmode;
5233428d7b3dSmrg	arg.mode_valid = 1;
5234428d7b3dSmrg
5235428d7b3dSmrg	DBG(("%s: applying crtc [%d, pipe=%d] mode=%dx%d+%d+%d@%d, fb=%d across %d outputs [%d...]\n",
5236428d7b3dSmrg	     __FUNCTION__, crtc->id, crtc->pipe,
5237428d7b3dSmrg	     arg.mode.hdisplay,
5238428d7b3dSmrg	     arg.mode.vdisplay,
5239428d7b3dSmrg	     arg.x, arg.y,
5240428d7b3dSmrg	     arg.mode.clock,
5241428d7b3dSmrg	     arg.fb_id,
5242428d7b3dSmrg	     output_count, output_count ? output_ids[0] : 0));
5243428d7b3dSmrg
5244428d7b3dSmrg	if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_SETCRTC, &arg))
5245428d7b3dSmrg		return false;
5246428d7b3dSmrg
5247428d7b3dSmrg	crtc->offset = y << 16 | x;
5248428d7b3dSmrg	return true;
5249428d7b3dSmrg}
5250428d7b3dSmrg
5251428d7b3dSmrgint
5252428d7b3dSmrgsna_page_flip(struct sna *sna,
5253428d7b3dSmrg	      struct kgem_bo *bo,
5254428d7b3dSmrg	      sna_flip_handler_t handler,
5255428d7b3dSmrg	      void *data)
5256428d7b3dSmrg{
5257428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
5258428d7b3dSmrg	const int width = sna->scrn->virtualX;
5259428d7b3dSmrg	const int height = sna->scrn->virtualY;
5260428d7b3dSmrg	int count = 0;
5261428d7b3dSmrg	int i;
5262428d7b3dSmrg
5263428d7b3dSmrg	DBG(("%s: handle %d attached\n", __FUNCTION__, bo->handle));
5264428d7b3dSmrg	assert(bo->refcnt);
5265428d7b3dSmrg
5266428d7b3dSmrg	assert((sna->flags & SNA_IS_HOSTED) == 0);
5267428d7b3dSmrg	assert((sna->flags & SNA_TEAR_FREE) == 0);
5268428d7b3dSmrg	assert(sna->mode.flip_active == 0);
5269428d7b3dSmrg	assert(sna->mode.front_active);
5270428d7b3dSmrg	assert(sna->scrn->vtSema);
5271428d7b3dSmrg
5272428d7b3dSmrg	if ((sna->flags & (data ? SNA_HAS_FLIP : SNA_HAS_ASYNC_FLIP)) == 0)
5273428d7b3dSmrg		return 0;
5274428d7b3dSmrg
5275428d7b3dSmrg	kgem_bo_submit(&sna->kgem, bo);
5276428d7b3dSmrg
5277428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_crtc; i++) {
5278428d7b3dSmrg		struct sna_crtc *crtc = config->crtc[i]->driver_private;
5279428d7b3dSmrg		struct drm_mode_crtc_page_flip arg;
5280428d7b3dSmrg		uint32_t crtc_offset;
5281428d7b3dSmrg
5282428d7b3dSmrg		DBG(("%s: crtc %d id=%d, pipe=%d active? %d\n",
5283428d7b3dSmrg		     __FUNCTION__, i, crtc->id, crtc->pipe, crtc->bo != NULL));
5284428d7b3dSmrg		if (crtc->bo == NULL)
5285428d7b3dSmrg			continue;
5286428d7b3dSmrg		assert(!crtc->transform);
5287428d7b3dSmrg		assert(!crtc->slave_pixmap);
5288428d7b3dSmrg		assert(crtc->bo->active_scanout);
5289428d7b3dSmrg		assert(crtc->bo->refcnt >= crtc->bo->active_scanout);
5290428d7b3dSmrg		assert(crtc->flip_bo == NULL);
5291428d7b3dSmrg
5292428d7b3dSmrg		arg.crtc_id = crtc->id;
5293428d7b3dSmrg		arg.fb_id = get_fb(sna, bo, width, height);
5294428d7b3dSmrg		if (arg.fb_id == 0) {
5295428d7b3dSmrg			assert(count == 0);
5296428d7b3dSmrg			return 0;
5297428d7b3dSmrg		}
5298428d7b3dSmrg
5299428d7b3dSmrg		crtc_offset = crtc->base->y << 16 | crtc->base->x;
5300428d7b3dSmrg
5301428d7b3dSmrg		if (bo->pitch != crtc->bo->pitch || crtc_offset != crtc->offset) {
5302428d7b3dSmrg			DBG(("%s: changing pitch (%d == %d) or offset (%x == %x)\n",
5303428d7b3dSmrg			     __FUNCTION__,
5304428d7b3dSmrg			     bo->pitch, crtc->bo->pitch,
5305428d7b3dSmrg			     crtc_offset, crtc->offset));
5306428d7b3dSmrgfixup_flip:
5307428d7b3dSmrg			if (crtc->bo != bo && sna_crtc_flip(sna, crtc, bo, crtc->base->x, crtc->base->y)) {
5308428d7b3dSmrg				assert(crtc->bo->active_scanout);
5309428d7b3dSmrg				assert(crtc->bo->refcnt >= crtc->bo->active_scanout);
5310428d7b3dSmrg				crtc->bo->active_scanout--;
5311428d7b3dSmrg				kgem_bo_destroy(&sna->kgem, crtc->bo);
5312428d7b3dSmrg
5313428d7b3dSmrg				if (crtc->shadow_bo) {
5314428d7b3dSmrg					kgem_bo_destroy(&sna->kgem, crtc->shadow_bo);
5315428d7b3dSmrg					crtc->shadow_bo = NULL;
5316428d7b3dSmrg				}
5317428d7b3dSmrg
5318428d7b3dSmrg				crtc->bo = kgem_bo_reference(bo);
5319428d7b3dSmrg				crtc->bo->active_scanout++;
5320428d7b3dSmrg
5321428d7b3dSmrg				if (data == NULL)
5322428d7b3dSmrg					goto next_crtc;
5323428d7b3dSmrg
5324428d7b3dSmrg				/* queue a flip in order to send the event */
5325428d7b3dSmrg			} else {
5326428d7b3dSmrg				if (count && !xf86SetDesiredModes(sna->scrn)) {
5327428d7b3dSmrg					xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR,
5328428d7b3dSmrg						   "failed to restore display configuration\n");
5329428d7b3dSmrg					for (; i < sna->mode.num_real_crtc; i++)
5330428d7b3dSmrg						sna_crtc_disable(config->crtc[i]);
5331428d7b3dSmrg				}
5332428d7b3dSmrg				return 0;
5333428d7b3dSmrg			}
5334428d7b3dSmrg		}
5335428d7b3dSmrg
5336428d7b3dSmrg		/* Only the reference crtc will finally deliver its page flip
5337428d7b3dSmrg		 * completion event. All other crtc's events will be discarded.
5338428d7b3dSmrg		 */
5339428d7b3dSmrg		if (data) {
5340428d7b3dSmrg			arg.user_data = (uintptr_t)crtc;
5341428d7b3dSmrg			arg.flags = DRM_MODE_PAGE_FLIP_EVENT;
5342428d7b3dSmrg		} else {
5343428d7b3dSmrg			arg.user_data = 0;
5344428d7b3dSmrg			arg.flags = DRM_MODE_PAGE_FLIP_ASYNC;
5345428d7b3dSmrg		}
5346428d7b3dSmrg		arg.reserved = 0;
5347428d7b3dSmrg
5348428d7b3dSmrgretry_flip:
5349428d7b3dSmrg		DBG(("%s: crtc %d id=%d, pipe=%d  --> fb %d\n",
5350428d7b3dSmrg		     __FUNCTION__, i, crtc->id, crtc->pipe, arg.fb_id));
5351428d7b3dSmrg		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) {
5352428d7b3dSmrg			ERR(("%s: pageflip failed with err=%d\n", __FUNCTION__, errno));
5353428d7b3dSmrg
5354428d7b3dSmrg			if (errno == EBUSY) {
5355428d7b3dSmrg				struct drm_mode_crtc mode;
5356428d7b3dSmrg
5357428d7b3dSmrg				memset(&mode, 0, sizeof(mode));
5358428d7b3dSmrg				mode.crtc_id = crtc->id;
5359428d7b3dSmrg				drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCRTC, &mode);
5360428d7b3dSmrg
5361428d7b3dSmrg				DBG(("%s: crtc=%d, valid?=%d, fb attached?=%d, expected=%d\n",
5362428d7b3dSmrg				     __FUNCTION__,
5363428d7b3dSmrg				     mode.crtc_id, mode.mode_valid,
5364428d7b3dSmrg				     mode.fb_id, fb_id(crtc->bo)));
5365428d7b3dSmrg
5366428d7b3dSmrg				if (mode.fb_id != fb_id(crtc->bo))
5367428d7b3dSmrg					goto fixup_flip;
5368428d7b3dSmrg
5369428d7b3dSmrg				if (count == 0)
5370428d7b3dSmrg					return 0;
5371428d7b3dSmrg
5372428d7b3dSmrg				DBG(("%s: throttling on busy flip / waiting for kernel to catch up\n", __FUNCTION__));
5373428d7b3dSmrg				drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GEM_THROTTLE, 0);
5374428d7b3dSmrg				sna->kgem.need_throttle = false;
5375428d7b3dSmrg
5376428d7b3dSmrg				goto retry_flip;
5377428d7b3dSmrg			}
5378428d7b3dSmrg
5379428d7b3dSmrg			xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR,
5380428d7b3dSmrg				   "page flipping failed, on CRTC:%d (pipe=%d), disabling %s page flips\n",
5381428d7b3dSmrg				   crtc->id, crtc->pipe, data ? "synchronous": "asynchronous");
5382428d7b3dSmrg			sna->flags &= ~(data ? SNA_HAS_FLIP : SNA_HAS_ASYNC_FLIP);
5383428d7b3dSmrg			goto fixup_flip;
5384428d7b3dSmrg		}
5385428d7b3dSmrg
5386428d7b3dSmrg		if (data) {
5387428d7b3dSmrg			assert(crtc->flip_bo == NULL);
5388428d7b3dSmrg			crtc->flip_handler = handler;
5389428d7b3dSmrg			crtc->flip_data = data;
5390428d7b3dSmrg			crtc->flip_bo = kgem_bo_reference(bo);
5391428d7b3dSmrg			crtc->flip_bo->active_scanout++;
5392428d7b3dSmrg			crtc->flip_serial = crtc->mode_serial;
5393428d7b3dSmrg			crtc->flip_pending = true;
5394428d7b3dSmrg			sna->mode.flip_active++;
5395428d7b3dSmrg		}
5396428d7b3dSmrg
5397428d7b3dSmrgnext_crtc:
5398428d7b3dSmrg		count++;
5399428d7b3dSmrg	}
5400428d7b3dSmrg
5401428d7b3dSmrg	DBG(("%s: page flipped %d crtcs\n", __FUNCTION__, count));
5402428d7b3dSmrg	return count;
5403428d7b3dSmrg}
5404428d7b3dSmrg
5405428d7b3dSmrgstatic const xf86CrtcConfigFuncsRec sna_mode_funcs = {
5406428d7b3dSmrg	sna_mode_resize
5407428d7b3dSmrg};
5408428d7b3dSmrg
5409428d7b3dSmrgstatic void set_size_range(struct sna *sna)
5410428d7b3dSmrg{
5411428d7b3dSmrg	/* We lie slightly as we expect no single monitor to exceed the
5412428d7b3dSmrg	 * crtc limits, so if the mode exceeds the scanout restrictions,
5413428d7b3dSmrg	 * we will quietly convert that to per-crtc pixmaps.
5414428d7b3dSmrg	 */
5415428d7b3dSmrg	xf86CrtcSetSizeRange(sna->scrn, 8, 8, INT16_MAX, INT16_MAX);
5416428d7b3dSmrg}
5417428d7b3dSmrg
5418428d7b3dSmrg#if HAS_GAMMA
5419428d7b3dSmrgstatic void set_gamma(uint16_t *curve, int size, double value)
5420428d7b3dSmrg{
5421428d7b3dSmrg	int i;
5422428d7b3dSmrg
5423428d7b3dSmrg	value = 1/value;
5424428d7b3dSmrg	for (i = 0; i < size; i++)
5425428d7b3dSmrg		curve[i] = 256*(size-1)*pow(i/(double)(size-1), value);
5426428d7b3dSmrg}
5427428d7b3dSmrg
5428428d7b3dSmrgstatic void output_set_gamma(xf86OutputPtr output, xf86CrtcPtr crtc)
5429428d7b3dSmrg{
5430428d7b3dSmrg	XF86ConfMonitorPtr mon = output->conf_monitor;
5431428d7b3dSmrg
5432428d7b3dSmrg	if (!mon)
5433428d7b3dSmrg		return;
5434428d7b3dSmrg
5435428d7b3dSmrg	DBG(("%s: red=%f\n", __FUNCTION__, mon->mon_gamma_red));
5436428d7b3dSmrg	if (mon->mon_gamma_red >= GAMMA_MIN &&
5437428d7b3dSmrg	    mon->mon_gamma_red <= GAMMA_MAX &&
5438428d7b3dSmrg	    mon->mon_gamma_red != 1.0)
5439428d7b3dSmrg		set_gamma(crtc->gamma_red, crtc->gamma_size,
5440428d7b3dSmrg			  mon->mon_gamma_red);
5441428d7b3dSmrg
5442428d7b3dSmrg	DBG(("%s: green=%f\n", __FUNCTION__, mon->mon_gamma_green));
5443428d7b3dSmrg	if (mon->mon_gamma_green >= GAMMA_MIN &&
5444428d7b3dSmrg	    mon->mon_gamma_green <= GAMMA_MAX &&
5445428d7b3dSmrg	    mon->mon_gamma_green != 1.0)
5446428d7b3dSmrg		set_gamma(crtc->gamma_green, crtc->gamma_size,
5447428d7b3dSmrg			  mon->mon_gamma_green);
5448428d7b3dSmrg
5449428d7b3dSmrg	DBG(("%s: blue=%f\n", __FUNCTION__, mon->mon_gamma_blue));
5450428d7b3dSmrg	if (mon->mon_gamma_blue >= GAMMA_MIN &&
5451428d7b3dSmrg	    mon->mon_gamma_blue <= GAMMA_MAX &&
5452428d7b3dSmrg	    mon->mon_gamma_blue != 1.0)
5453428d7b3dSmrg		set_gamma(crtc->gamma_blue, crtc->gamma_size,
5454428d7b3dSmrg			  mon->mon_gamma_blue);
5455428d7b3dSmrg}
5456428d7b3dSmrg
5457428d7b3dSmrgstatic void crtc_init_gamma(xf86CrtcPtr crtc)
5458428d7b3dSmrg{
5459428d7b3dSmrg	uint16_t *gamma;
5460428d7b3dSmrg
5461428d7b3dSmrg	/* Initialize the gamma ramps */
5462428d7b3dSmrg	gamma = NULL;
5463428d7b3dSmrg	if (crtc->gamma_size == 256)
5464428d7b3dSmrg		gamma = crtc->gamma_red;
5465428d7b3dSmrg	if (gamma == NULL)
5466428d7b3dSmrg		gamma = malloc(3 * 256 * sizeof(uint16_t));
5467428d7b3dSmrg	if (gamma) {
5468428d7b3dSmrg		struct sna *sna = to_sna(crtc->scrn);
5469428d7b3dSmrg		struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
5470428d7b3dSmrg		struct drm_mode_crtc_lut lut;
5471428d7b3dSmrg		bool gamma_set = false;
5472428d7b3dSmrg
5473428d7b3dSmrg		assert(sna_crtc);
5474428d7b3dSmrg
5475428d7b3dSmrg		lut.crtc_id = sna_crtc->id;
5476428d7b3dSmrg		lut.gamma_size = 256;
5477428d7b3dSmrg		lut.red = (uintptr_t)(gamma);
5478428d7b3dSmrg		lut.green = (uintptr_t)(gamma + 256);
5479428d7b3dSmrg		lut.blue = (uintptr_t)(gamma + 2 * 256);
5480428d7b3dSmrg		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETGAMMA, &lut) == 0) {
5481428d7b3dSmrg			VG(VALGRIND_MAKE_MEM_DEFINED(gamma, 3*256*sizeof(gamma[0])));
5482428d7b3dSmrg			gamma_set =
5483428d7b3dSmrg				gamma[256 - 1] &&
5484428d7b3dSmrg				gamma[2*256 - 1] &&
5485428d7b3dSmrg				gamma[3*256 - 1];
5486428d7b3dSmrg		}
5487428d7b3dSmrg
5488428d7b3dSmrg		DBG(("%s: CRTC:%d, pipe=%d: gamma set?=%d\n",
5489428d7b3dSmrg		     __FUNCTION__, sna_crtc->id, sna_crtc->pipe,
5490428d7b3dSmrg		     gamma_set));
5491428d7b3dSmrg		if (!gamma_set) {
5492428d7b3dSmrg			int i;
5493428d7b3dSmrg
5494428d7b3dSmrg			for (i = 0; i < 256; i++) {
5495428d7b3dSmrg				gamma[i] = i << 8;
5496428d7b3dSmrg				gamma[256 + i] = i << 8;
5497428d7b3dSmrg				gamma[2*256 + i] = i << 8;
5498428d7b3dSmrg			}
5499428d7b3dSmrg		}
5500428d7b3dSmrg
5501428d7b3dSmrg		if (gamma != crtc->gamma_red) {
5502428d7b3dSmrg			free(crtc->gamma_red);
5503428d7b3dSmrg			crtc->gamma_red = gamma;
5504428d7b3dSmrg			crtc->gamma_green = gamma + 256;
5505428d7b3dSmrg			crtc->gamma_blue = gamma + 2*256;
5506428d7b3dSmrg		}
5507428d7b3dSmrg	}
5508428d7b3dSmrg}
5509428d7b3dSmrg#else
5510428d7b3dSmrgstatic void output_set_gamma(xf86OutputPtr output, xf86CrtcPtr crtc) { }
5511428d7b3dSmrgstatic void crtc_init_gamma(xf86CrtcPtr crtc) { }
5512428d7b3dSmrg#endif
5513428d7b3dSmrg
5514428d7b3dSmrgstatic const char *preferred_mode(xf86OutputPtr output)
5515428d7b3dSmrg{
5516428d7b3dSmrg	const char *mode;
5517428d7b3dSmrg
5518428d7b3dSmrg	mode = xf86GetOptValString(output->options, OPTION_PREFERRED_MODE);
5519428d7b3dSmrg	if (mode)
5520428d7b3dSmrg		return mode;
5521428d7b3dSmrg
5522428d7b3dSmrg	if (output->scrn->display->modes && *output->scrn->display->modes)
5523428d7b3dSmrg		return *output->scrn->display->modes;
5524428d7b3dSmrg
5525428d7b3dSmrg	return NULL;
5526428d7b3dSmrg}
5527428d7b3dSmrg
5528428d7b3dSmrgstatic bool sna_probe_initial_configuration(struct sna *sna)
5529428d7b3dSmrg{
5530428d7b3dSmrg	ScrnInfoPtr scrn = sna->scrn;
5531428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
5532428d7b3dSmrg	int width, height;
5533428d7b3dSmrg	int i, j;
5534428d7b3dSmrg
5535428d7b3dSmrg	assert((sna->flags & SNA_IS_HOSTED) == 0);
5536428d7b3dSmrg
5537428d7b3dSmrg	if ((sna->flags & SNA_IS_SLAVED) == 0) {
5538428d7b3dSmrg		const int user_overrides[] = {
5539428d7b3dSmrg			OPTION_POSITION,
5540428d7b3dSmrg			OPTION_BELOW,
5541428d7b3dSmrg			OPTION_RIGHT_OF,
5542428d7b3dSmrg			OPTION_ABOVE,
5543428d7b3dSmrg			OPTION_LEFT_OF,
5544428d7b3dSmrg			OPTION_ROTATE,
5545428d7b3dSmrg			OPTION_PANNING,
5546428d7b3dSmrg		};
5547428d7b3dSmrg		if (xf86ReturnOptValBool(sna->Options, OPTION_REPROBE, FALSE)) {
5548428d7b3dSmrg			DBG(("%s: user requests reprobing\n", __FUNCTION__));
5549428d7b3dSmrg			return false;
5550428d7b3dSmrg		}
5551428d7b3dSmrg
5552428d7b3dSmrg		/* First scan through all outputs and look for user overrides */
5553428d7b3dSmrg		for (i = 0; i < sna->mode.num_real_output; i++) {
5554428d7b3dSmrg			xf86OutputPtr output = config->output[i];
5555428d7b3dSmrg
5556428d7b3dSmrg			for (j = 0; j < ARRAY_SIZE(user_overrides); j++) {
5557428d7b3dSmrg				if (xf86GetOptValString(output->options, user_overrides[j])) {
5558428d7b3dSmrg					DBG(("%s: user placement [%d] for %s\n",
5559428d7b3dSmrg					     __FUNCTION__,
5560428d7b3dSmrg					     user_overrides[j],
5561428d7b3dSmrg					     output->name));
5562428d7b3dSmrg					return false;
5563428d7b3dSmrg				}
5564428d7b3dSmrg			}
5565428d7b3dSmrg		}
5566428d7b3dSmrg	}
5567428d7b3dSmrg
5568428d7b3dSmrg	/* Copy the existing modes on each CRTCs */
5569428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_crtc; i++) {
5570428d7b3dSmrg		xf86CrtcPtr crtc = config->crtc[i];
5571428d7b3dSmrg		struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
5572428d7b3dSmrg		struct drm_mode_crtc mode;
5573428d7b3dSmrg
5574428d7b3dSmrg		crtc->enabled = FALSE;
5575428d7b3dSmrg		crtc->desiredMode.status = MODE_NOMODE;
5576428d7b3dSmrg
5577428d7b3dSmrg		crtc_init_gamma(crtc);
5578428d7b3dSmrg
5579428d7b3dSmrg		/* Retrieve the current mode */
5580428d7b3dSmrg		VG_CLEAR(mode);
5581428d7b3dSmrg		mode.crtc_id = sna_crtc->id;
5582428d7b3dSmrg		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCRTC, &mode))
5583428d7b3dSmrg			continue;
5584428d7b3dSmrg
5585428d7b3dSmrg		DBG(("%s: CRTC:%d, pipe=%d: has mode?=%d\n", __FUNCTION__,
5586428d7b3dSmrg		     sna_crtc->id, sna_crtc->pipe,
5587428d7b3dSmrg		     mode.mode_valid && mode.mode.clock));
5588428d7b3dSmrg
5589428d7b3dSmrg		if (!mode.mode_valid || mode.mode.clock == 0)
5590428d7b3dSmrg			continue;
5591428d7b3dSmrg
5592428d7b3dSmrg		mode_from_kmode(scrn, &mode.mode, &crtc->desiredMode);
5593428d7b3dSmrg		crtc->desiredRotation = sna_crtc->primary.rotation.current;
5594428d7b3dSmrg		crtc->desiredX = mode.x;
5595428d7b3dSmrg		crtc->desiredY = mode.y;
5596428d7b3dSmrg		crtc->desiredTransformPresent = FALSE;
5597428d7b3dSmrg	}
5598428d7b3dSmrg
5599428d7b3dSmrg	/* Reconstruct outputs pointing to active CRTC */
5600428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_output; i++) {
5601428d7b3dSmrg		xf86OutputPtr output = config->output[i];
5602428d7b3dSmrg		uint32_t crtc_id;
5603428d7b3dSmrg
5604428d7b3dSmrg		assert(to_sna_output(output));
5605428d7b3dSmrg
5606428d7b3dSmrg		crtc_id = (uintptr_t)output->crtc;
5607428d7b3dSmrg		output->crtc = NULL;
5608428d7b3dSmrg		if (sna->flags & SNA_IS_SLAVED)
5609428d7b3dSmrg			continue;
5610428d7b3dSmrg
5611428d7b3dSmrg		if (crtc_id == 0) {
5612428d7b3dSmrg			DBG(("%s: not using output %s, disconnected\n",
5613428d7b3dSmrg			     __FUNCTION__, output->name));
5614428d7b3dSmrg			continue;
5615428d7b3dSmrg		}
5616428d7b3dSmrg
5617428d7b3dSmrg		if (xf86ReturnOptValBool(output->options, OPTION_DISABLE, 0)) {
5618428d7b3dSmrg			DBG(("%s: not using output %s, manually disabled\n",
5619428d7b3dSmrg			     __FUNCTION__, output->name));
5620428d7b3dSmrg			continue;
5621428d7b3dSmrg		}
5622428d7b3dSmrg
5623428d7b3dSmrg		for (j = 0; j < sna->mode.num_real_crtc; j++) {
5624428d7b3dSmrg			xf86CrtcPtr crtc = config->crtc[j];
5625428d7b3dSmrg
5626428d7b3dSmrg			assert(to_sna_crtc(crtc));
5627428d7b3dSmrg			if (to_sna_crtc(crtc)->id != crtc_id)
5628428d7b3dSmrg				continue;
5629428d7b3dSmrg
5630428d7b3dSmrg			if (crtc->desiredMode.status == MODE_OK) {
5631428d7b3dSmrg				DisplayModePtr M;
5632428d7b3dSmrg				const char *pref;
5633428d7b3dSmrg
5634428d7b3dSmrg				pref = preferred_mode(output);
5635428d7b3dSmrg				if (pref && strcmp(pref, crtc->desiredMode.name)) {
5636428d7b3dSmrg					DBG(("%s: output %s user requests a different preferred mode %s, found %s\n",
5637428d7b3dSmrg					     __FUNCTION__, output->name, pref, crtc->desiredMode.name));
5638428d7b3dSmrg					return false;
5639428d7b3dSmrg				}
5640428d7b3dSmrg
5641428d7b3dSmrg				xf86DrvMsg(scrn->scrnIndex, X_PROBED,
5642428d7b3dSmrg					   "Output %s using initial mode %s on pipe %d\n",
5643428d7b3dSmrg					   output->name,
5644428d7b3dSmrg					   crtc->desiredMode.name,
5645428d7b3dSmrg					   to_sna_crtc(crtc)->pipe);
5646428d7b3dSmrg
5647428d7b3dSmrg				output->crtc = crtc;
5648428d7b3dSmrg				crtc->enabled = TRUE;
5649428d7b3dSmrg
5650428d7b3dSmrg				if (output->mm_width == 0 || output->mm_height == 0) {
5651428d7b3dSmrg					output->mm_height = (crtc->desiredMode.VDisplay * 254) / (10*DEFAULT_DPI);
5652428d7b3dSmrg					output->mm_width = (crtc->desiredMode.HDisplay * 254) / (10*DEFAULT_DPI);
5653428d7b3dSmrg				}
5654428d7b3dSmrg
5655428d7b3dSmrg				output_set_gamma(output, crtc);
5656428d7b3dSmrg
5657428d7b3dSmrg				M = calloc(1, sizeof(DisplayModeRec));
5658428d7b3dSmrg				if (M) {
5659428d7b3dSmrg					*M = crtc->desiredMode;
5660428d7b3dSmrg					M->name = strdup(M->name);
5661428d7b3dSmrg					output->probed_modes =
5662428d7b3dSmrg						xf86ModesAdd(output->probed_modes, M);
5663428d7b3dSmrg				}
5664428d7b3dSmrg			}
5665428d7b3dSmrg
5666428d7b3dSmrg			break;
5667428d7b3dSmrg		}
5668428d7b3dSmrg
5669428d7b3dSmrg		if (j == sna->mode.num_real_crtc) {
5670428d7b3dSmrg			/* Can not find the earlier associated CRTC, bail */
5671428d7b3dSmrg			DBG(("%s: existing setup conflicts with output assignment (Zaphod), reprobing\n",
5672428d7b3dSmrg			     __FUNCTION__));
5673428d7b3dSmrg			return false;
5674428d7b3dSmrg		}
5675428d7b3dSmrg	}
5676428d7b3dSmrg
5677428d7b3dSmrg	width = height = 0;
5678428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_crtc; i++) {
5679428d7b3dSmrg		xf86CrtcPtr crtc = config->crtc[i];
5680428d7b3dSmrg		int w, h;
5681428d7b3dSmrg
5682428d7b3dSmrg		if (!crtc->enabled)
5683428d7b3dSmrg			continue;
5684428d7b3dSmrg
5685428d7b3dSmrg		w = crtc->desiredX + crtc->desiredMode.HDisplay;
5686428d7b3dSmrg		if (w > width)
5687428d7b3dSmrg			width = w;
5688428d7b3dSmrg		h = crtc->desiredY + crtc->desiredMode.VDisplay;
5689428d7b3dSmrg		if (h > height)
5690428d7b3dSmrg			height = h;
5691428d7b3dSmrg	}
5692428d7b3dSmrg
5693428d7b3dSmrg	/* Prefer the native panel size if any */
5694428d7b3dSmrg	if (!width || !height) {
5695428d7b3dSmrg		for (i = 0; i < sna->mode.num_real_output; i++) {
5696428d7b3dSmrg			xf86OutputPtr output = config->output[i];
5697428d7b3dSmrg			struct sna_output *sna_output = to_sna_output(output);
5698428d7b3dSmrg
5699428d7b3dSmrg			if (!sna_output->is_panel)
5700428d7b3dSmrg				continue;
5701428d7b3dSmrg
5702428d7b3dSmrg			DBG(("%s: querying panel '%s' for preferred unattached size\n",
5703428d7b3dSmrg			     __FUNCTION__, output->name));
5704428d7b3dSmrg
5705428d7b3dSmrg			if (sna_output_detect(output) != XF86OutputStatusConnected)
5706428d7b3dSmrg				continue;
5707428d7b3dSmrg
5708428d7b3dSmrg			if (sna_output->num_modes == 0)
5709428d7b3dSmrg				continue;
5710428d7b3dSmrg
5711428d7b3dSmrg			width = sna_output->modes[0].hdisplay;
5712428d7b3dSmrg			height= sna_output->modes[0].vdisplay;
5713428d7b3dSmrg
5714428d7b3dSmrg			DBG(("%s: panel '%s' is %dx%d\n",
5715428d7b3dSmrg			     __FUNCTION__, output->name, width, height));
5716428d7b3dSmrg			break;
5717428d7b3dSmrg		}
5718428d7b3dSmrg	}
5719428d7b3dSmrg
5720428d7b3dSmrg	if (!width || !height) {
5721428d7b3dSmrg		width = 1024;
5722428d7b3dSmrg		height = 768;
5723428d7b3dSmrg	}
5724428d7b3dSmrg
5725428d7b3dSmrg	scrn->display->frameX0 = 0;
5726428d7b3dSmrg	scrn->display->frameY0 = 0;
5727428d7b3dSmrg	scrn->display->virtualX = width;
5728428d7b3dSmrg	scrn->display->virtualY = height;
5729428d7b3dSmrg
5730428d7b3dSmrg	scrn->virtualX = width;
5731428d7b3dSmrg	scrn->virtualY = height;
5732428d7b3dSmrg
5733428d7b3dSmrg	xf86SetScrnInfoModes(sna->scrn);
5734428d7b3dSmrg	DBG(("%s: SetScrnInfoModes = %p\n", __FUNCTION__, scrn->modes));
5735428d7b3dSmrg	return scrn->modes != NULL;
5736428d7b3dSmrg}
5737428d7b3dSmrg
5738428d7b3dSmrgstatic void
5739428d7b3dSmrgsanitize_outputs(struct sna *sna)
5740428d7b3dSmrg{
5741428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
5742428d7b3dSmrg	int i;
5743428d7b3dSmrg
5744428d7b3dSmrg	for (i = 0; i < config->num_output; i++)
5745428d7b3dSmrg		config->output[i]->crtc = NULL;
5746428d7b3dSmrg}
5747428d7b3dSmrg
5748428d7b3dSmrgstatic bool has_flip(struct sna *sna)
5749428d7b3dSmrg{
5750428d7b3dSmrg	drm_i915_getparam_t gp;
5751428d7b3dSmrg	int v;
5752428d7b3dSmrg
5753428d7b3dSmrg	if (sna->flags & SNA_NO_FLIP)
5754428d7b3dSmrg		return false;
5755428d7b3dSmrg
5756428d7b3dSmrg	v = 0;
5757428d7b3dSmrg
5758428d7b3dSmrg	VG_CLEAR(gp);
5759428d7b3dSmrg	gp.param = I915_PARAM_HAS_PAGEFLIPPING;
5760428d7b3dSmrg	gp.value = &v;
5761428d7b3dSmrg
5762428d7b3dSmrg	if (drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GETPARAM, &gp))
5763428d7b3dSmrg		return false;
5764428d7b3dSmrg
5765428d7b3dSmrg	VG(VALGRIND_MAKE_MEM_DEFINED(&v, sizeof(v)));
5766428d7b3dSmrg	return v > 0;
5767428d7b3dSmrg}
5768428d7b3dSmrg
5769428d7b3dSmrgstatic bool has_flip__async(struct sna *sna)
5770428d7b3dSmrg{
5771428d7b3dSmrg#define DRM_CAP_ASYNC_PAGE_FLIP 0x7
5772428d7b3dSmrg	struct local_get_cap {
5773428d7b3dSmrg		uint64_t name;
5774428d7b3dSmrg		uint64_t value;
5775428d7b3dSmrg	} cap = { DRM_CAP_ASYNC_PAGE_FLIP };
5776428d7b3dSmrg
5777428d7b3dSmrg	if (sna->flags & SNA_NO_FLIP)
5778428d7b3dSmrg		return false;
5779428d7b3dSmrg
5780428d7b3dSmrg	if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_GET_CAP, &cap) == 0)
5781428d7b3dSmrg		return cap.value > 0;
5782428d7b3dSmrg
5783428d7b3dSmrg	return false;
5784428d7b3dSmrg}
5785428d7b3dSmrg
5786428d7b3dSmrgstatic void
5787428d7b3dSmrgprobe_capabilities(struct sna *sna)
5788428d7b3dSmrg{
5789428d7b3dSmrg	sna->flags &= ~(SNA_HAS_FLIP | SNA_HAS_ASYNC_FLIP);
5790428d7b3dSmrg	if (has_flip(sna))
5791428d7b3dSmrg		sna->flags |= SNA_HAS_FLIP;
5792428d7b3dSmrg	if (has_flip__async(sna))
5793428d7b3dSmrg		sna->flags |= SNA_HAS_ASYNC_FLIP;
5794428d7b3dSmrg	DBG(("%s: page flips? %s, async? %s\n", __FUNCTION__,
5795428d7b3dSmrg	     sna->flags & SNA_HAS_FLIP ? "enabled" : "disabled",
5796428d7b3dSmrg	     sna->flags & SNA_HAS_ASYNC_FLIP ? "enabled" : "disabled"));
5797428d7b3dSmrg}
5798428d7b3dSmrg
5799428d7b3dSmrgvoid
5800428d7b3dSmrgsna_crtc_config_notify(ScreenPtr screen)
5801428d7b3dSmrg{
5802428d7b3dSmrg	struct sna *sna = to_sna_from_screen(screen);
5803428d7b3dSmrg
5804428d7b3dSmrg	DBG(("%s(dirty?=%d)\n", __FUNCTION__, sna->mode.dirty));
5805428d7b3dSmrg	if (!sna->mode.dirty)
5806428d7b3dSmrg		return;
5807428d7b3dSmrg
5808428d7b3dSmrg	if (disable_unused_crtc(sna)) {
5809428d7b3dSmrg		/* This will have recursed, so simply bail at this point */
5810428d7b3dSmrg		assert(sna->mode.dirty == false);
5811428d7b3dSmrg#ifdef RANDR_12_INTERFACE
5812428d7b3dSmrg		xf86RandR12TellChanged(screen);
5813428d7b3dSmrg#endif
5814428d7b3dSmrg		return;
5815428d7b3dSmrg	}
5816428d7b3dSmrg
5817428d7b3dSmrg	update_flush_interval(sna);
5818428d7b3dSmrg	sna_cursors_reload(sna);
5819428d7b3dSmrg
5820428d7b3dSmrg	probe_capabilities(sna);
5821428d7b3dSmrg	sna_present_update(sna);
5822428d7b3dSmrg
5823428d7b3dSmrg	sna->mode.dirty = false;
5824428d7b3dSmrg}
5825428d7b3dSmrg
5826428d7b3dSmrg#if HAS_PIXMAP_SHARING
5827428d7b3dSmrg#define sna_setup_provider(scrn) xf86ProviderSetup(scrn, NULL, "Intel")
5828428d7b3dSmrg#else
5829428d7b3dSmrg#define sna_setup_provider(scrn)
5830428d7b3dSmrg#endif
5831428d7b3dSmrg
5832428d7b3dSmrgbool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna)
5833428d7b3dSmrg{
5834428d7b3dSmrg	drmModeResPtr res;
5835428d7b3dSmrg	int num_fake = 0;
5836428d7b3dSmrg	int i;
5837428d7b3dSmrg
5838428d7b3dSmrg	if (sna->flags & SNA_IS_HOSTED) {
5839428d7b3dSmrg		sna_setup_provider(scrn);
5840428d7b3dSmrg		return true;
5841428d7b3dSmrg	}
5842428d7b3dSmrg
5843428d7b3dSmrg	probe_capabilities(sna);
5844428d7b3dSmrg
5845428d7b3dSmrg	if (!xf86GetOptValInteger(sna->Options, OPTION_VIRTUAL, &num_fake))
5846428d7b3dSmrg		num_fake = 1;
5847428d7b3dSmrg
5848428d7b3dSmrg	res = drmModeGetResources(sna->kgem.fd);
5849428d7b3dSmrg	if (res &&
5850428d7b3dSmrg	    (res->count_crtcs == 0 ||
5851428d7b3dSmrg	     res->count_encoders == 0 ||
5852428d7b3dSmrg	     res->count_connectors == 0)) {
5853428d7b3dSmrg		drmModeFreeResources(res);
5854428d7b3dSmrg		res = NULL;
5855428d7b3dSmrg	}
5856428d7b3dSmrg	if (res) {
5857428d7b3dSmrg		xf86CrtcConfigPtr xf86_config;
5858428d7b3dSmrg
5859428d7b3dSmrg		assert(res->count_crtcs);
5860428d7b3dSmrg		assert(res->count_connectors);
5861428d7b3dSmrg
5862428d7b3dSmrg		xf86CrtcConfigInit(scrn, &sna_mode_funcs);
5863428d7b3dSmrg
5864428d7b3dSmrg		xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
5865428d7b3dSmrg		xf86_config->xf86_crtc_notify = sna_crtc_config_notify;
5866428d7b3dSmrg
5867428d7b3dSmrg		for (i = 0; i < res->count_crtcs; i++)
5868428d7b3dSmrg			if (!sna_crtc_add(scrn, res->crtcs[i]))
5869428d7b3dSmrg				return false;
5870428d7b3dSmrg
5871428d7b3dSmrg		sna->mode.num_real_crtc = xf86_config->num_crtc;
5872428d7b3dSmrg
5873428d7b3dSmrg		sna->mode.num_real_encoder = res->count_encoders;
5874428d7b3dSmrg		sna->mode.encoders = res->encoders;
5875428d7b3dSmrg		res->encoders = NULL;
5876428d7b3dSmrg
5877428d7b3dSmrg		for (i = 0; i < res->count_connectors; i++)
5878428d7b3dSmrg			if (sna_output_add(sna, res->connectors[i], 0) < 0)
5879428d7b3dSmrg				return false;
5880428d7b3dSmrg
5881428d7b3dSmrg		sna->mode.num_real_output = xf86_config->num_output;
5882428d7b3dSmrg
5883428d7b3dSmrg		sna->mode.max_crtc_width  = res->max_width;
5884428d7b3dSmrg		sna->mode.max_crtc_height = res->max_height;
5885428d7b3dSmrg
5886428d7b3dSmrg		RegionEmpty(&sna->mode.shadow_region);
5887428d7b3dSmrg		RegionEmpty(&sna->mode.shadow_cancel);
5888428d7b3dSmrg		list_init(&sna->mode.shadow_crtc);
5889428d7b3dSmrg
5890428d7b3dSmrg		drmModeFreeResources(res);
5891428d7b3dSmrg
5892428d7b3dSmrg		sna_cursor_pre_init(sna);
5893428d7b3dSmrg		sna_backlight_pre_init(sna);
5894428d7b3dSmrg
5895428d7b3dSmrg		set_size_range(sna);
5896428d7b3dSmrg	} else {
5897428d7b3dSmrg		if (num_fake == 0)
5898428d7b3dSmrg			num_fake = 1;
5899428d7b3dSmrg	}
5900428d7b3dSmrg
5901428d7b3dSmrg	if (!sna_mode_fake_init(sna, num_fake))
5902428d7b3dSmrg		return false;
5903428d7b3dSmrg
5904428d7b3dSmrg	if (!sna_probe_initial_configuration(sna)) {
5905428d7b3dSmrg		xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
5906428d7b3dSmrg
5907428d7b3dSmrg		sanitize_outputs(sna);
5908428d7b3dSmrg		if (config->num_crtc && config->num_output) {
5909428d7b3dSmrg			if (!xf86ReturnOptValBool(config->output[0]->options,
5910428d7b3dSmrg						  OPTION_PRIMARY, FALSE))
5911428d7b3dSmrg				sort_config_outputs(sna);
5912428d7b3dSmrg			xf86InitialConfiguration(scrn, TRUE);
5913428d7b3dSmrg		}
5914428d7b3dSmrg	}
5915428d7b3dSmrg	sort_config_outputs(sna);
5916428d7b3dSmrg
5917428d7b3dSmrg	sna_setup_provider(scrn);
5918428d7b3dSmrg	return scrn->modes != NULL;
5919428d7b3dSmrg}
5920428d7b3dSmrg
5921428d7b3dSmrgbool
5922428d7b3dSmrgsna_mode_wants_tear_free(struct sna *sna)
5923428d7b3dSmrg{
5924428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
5925428d7b3dSmrg	int i;
5926428d7b3dSmrg
5927428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_output; i++) {
5928428d7b3dSmrg		struct sna_output *output = to_sna_output(config->output[i]);
5929428d7b3dSmrg		int id = find_property(sna, output, "Panel Self-Refresh");
5930428d7b3dSmrg		if (id !=-1 && output->prop_values[id] != -1) {
5931428d7b3dSmrg			DBG(("%s: Panel Self-Refresh detected on %s\n",
5932428d7b3dSmrg			     __FUNCTION__, config->output[i]->name));
5933428d7b3dSmrg			return true;
5934428d7b3dSmrg		}
5935428d7b3dSmrg	}
5936428d7b3dSmrg
5937428d7b3dSmrg	return false;
5938428d7b3dSmrg}
5939428d7b3dSmrg
5940428d7b3dSmrgvoid
5941428d7b3dSmrgsna_mode_set_primary(struct sna *sna)
5942428d7b3dSmrg{
5943428d7b3dSmrg#ifdef RANDR_12_INTERFACE
5944428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
5945428d7b3dSmrg	rrScrPrivPtr rr = rrGetScrPriv(xf86ScrnToScreen(sna->scrn));
5946428d7b3dSmrg	int i;
5947428d7b3dSmrg
5948428d7b3dSmrg	if (rr == NULL || rr->primaryOutput)
5949428d7b3dSmrg		return;
5950428d7b3dSmrg
5951428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_output; i++) {
5952428d7b3dSmrg		xf86OutputPtr output = config->output[i];
5953428d7b3dSmrg
5954428d7b3dSmrg		if (!xf86ReturnOptValBool(output->options, OPTION_PRIMARY, FALSE))
5955428d7b3dSmrg			continue;
5956428d7b3dSmrg
5957428d7b3dSmrg		DBG(("%s: setting PrimaryOutput %s\n", __FUNCTION__, output->name));
5958428d7b3dSmrg		rr->primaryOutput = output->randr_output;
5959428d7b3dSmrg		RROutputChanged(rr->primaryOutput, 0);
5960428d7b3dSmrg		rr->layoutChanged = TRUE;
5961428d7b3dSmrg		break;
5962428d7b3dSmrg	}
5963428d7b3dSmrg#endif
5964428d7b3dSmrg}
5965428d7b3dSmrg
5966428d7b3dSmrgbool
5967428d7b3dSmrgsna_mode_disable(struct sna *sna)
5968428d7b3dSmrg{
5969428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
5970428d7b3dSmrg	int i;
5971428d7b3dSmrg
5972428d7b3dSmrg	if (sna->flags & SNA_IS_HOSTED)
5973428d7b3dSmrg		return false;
5974428d7b3dSmrg
5975428d7b3dSmrg	if (!sna->scrn->vtSema)
5976428d7b3dSmrg		return false;
5977428d7b3dSmrg
5978428d7b3dSmrg	/* XXX we will cause previously hidden cursors to be reshown, but
5979428d7b3dSmrg	 * this should be a rare fixup case for severe fragmentation.
5980428d7b3dSmrg	 */
5981428d7b3dSmrg	sna_hide_cursors(sna->scrn);
5982428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_crtc; i++)
5983428d7b3dSmrg		sna_crtc_disable(config->crtc[i]);
5984428d7b3dSmrg	assert(sna->mode.front_active == 0);
5985428d7b3dSmrg
5986428d7b3dSmrg	sna_mode_wakeup(sna);
5987428d7b3dSmrg	kgem_clean_scanout_cache(&sna->kgem);
5988428d7b3dSmrg	return true;
5989428d7b3dSmrg}
5990428d7b3dSmrg
5991428d7b3dSmrgvoid
5992428d7b3dSmrgsna_mode_enable(struct sna *sna)
5993428d7b3dSmrg{
5994428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
5995428d7b3dSmrg	int i;
5996428d7b3dSmrg
5997428d7b3dSmrg	DBG(("%s\n", __FUNCTION__));
5998428d7b3dSmrg
5999428d7b3dSmrg	if (sna->flags & SNA_IS_HOSTED)
6000428d7b3dSmrg		return;
6001428d7b3dSmrg
6002428d7b3dSmrg	if (!sna->scrn->vtSema)
6003428d7b3dSmrg		return;
6004428d7b3dSmrg
6005428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_crtc; i++) {
6006428d7b3dSmrg		xf86CrtcPtr crtc = config->crtc[i];
6007428d7b3dSmrg
6008428d7b3dSmrg		DBG(("%s: crtc[%d].enabled?=%d\n", __FUNCTION__, i, crtc->enabled));
6009428d7b3dSmrg		assert(to_sna_crtc(crtc) != NULL);
6010428d7b3dSmrg		if (!crtc->enabled)
6011428d7b3dSmrg			continue;
6012428d7b3dSmrg
6013428d7b3dSmrg		if (crtc->mode.Clock == 0)
6014428d7b3dSmrg			continue;
6015428d7b3dSmrg
6016428d7b3dSmrg		__sna_crtc_set_mode(crtc);
6017428d7b3dSmrg	}
6018428d7b3dSmrg
6019428d7b3dSmrg	update_flush_interval(sna);
6020428d7b3dSmrg	sna_show_cursors(sna->scrn);
6021428d7b3dSmrg	sna->mode.dirty = false;
6022428d7b3dSmrg}
6023428d7b3dSmrg
6024428d7b3dSmrgvoid
6025428d7b3dSmrgsna_mode_close(struct sna *sna)
6026428d7b3dSmrg{
6027428d7b3dSmrg	sna_mode_wakeup(sna);
6028428d7b3dSmrg
6029428d7b3dSmrg	if (sna->flags & SNA_IS_HOSTED)
6030428d7b3dSmrg		return;
6031428d7b3dSmrg
6032428d7b3dSmrg	sna_mode_reset(sna);
6033428d7b3dSmrg
6034428d7b3dSmrg	sna_cursor_close(sna);
6035428d7b3dSmrg	sna_cursors_fini(sna);
6036428d7b3dSmrg
6037428d7b3dSmrg	sna_backlight_close(sna);
6038428d7b3dSmrg	sna->mode.dirty = false;
6039428d7b3dSmrg}
6040428d7b3dSmrg
6041428d7b3dSmrgvoid
6042428d7b3dSmrgsna_mode_fini(struct sna *sna)
6043428d7b3dSmrg{
6044428d7b3dSmrg	free(sna->mode.encoders);
6045428d7b3dSmrg}
6046428d7b3dSmrg
6047428d7b3dSmrgstatic bool sna_box_intersect(BoxPtr r, const BoxRec *a, const BoxRec *b)
6048428d7b3dSmrg{
6049428d7b3dSmrg	r->x1 = a->x1 > b->x1 ? a->x1 : b->x1;
6050428d7b3dSmrg	r->x2 = a->x2 < b->x2 ? a->x2 : b->x2;
6051428d7b3dSmrg	if (r->x1 >= r->x2)
6052428d7b3dSmrg		return false;
6053428d7b3dSmrg
6054428d7b3dSmrg	r->y1 = a->y1 > b->y1 ? a->y1 : b->y1;
6055428d7b3dSmrg	r->y2 = a->y2 < b->y2 ? a->y2 : b->y2;
6056428d7b3dSmrg	DBG(("%s: (%d, %d), (%d, %d) intersect (%d, %d), (%d, %d) = (%d, %d), (%d, %d)\n",
6057428d7b3dSmrg	     __FUNCTION__,
6058428d7b3dSmrg	     a->x1, a->y1, a->x2, a->y2,
6059428d7b3dSmrg	     b->x1, b->y1, b->x2, b->y2,
6060428d7b3dSmrg	     r->x1, r->y1, r->x2, r->y2));
6061428d7b3dSmrg	if (r->y1 >= r->y2)
6062428d7b3dSmrg		return false;
6063428d7b3dSmrg
6064428d7b3dSmrg	return true;
6065428d7b3dSmrg}
6066428d7b3dSmrg
6067428d7b3dSmrgstatic int sna_box_area(const BoxRec *box)
6068428d7b3dSmrg{
6069428d7b3dSmrg	return (int)(box->x2 - box->x1) * (int)(box->y2 - box->y1);
6070428d7b3dSmrg}
6071428d7b3dSmrg
6072428d7b3dSmrg/*
6073428d7b3dSmrg * Return the crtc covering 'box'. If two crtcs cover a portion of
6074428d7b3dSmrg * 'box', then prefer 'desired'. If 'desired' is NULL, then prefer the crtc
6075428d7b3dSmrg * with greater coverage
6076428d7b3dSmrg */
6077428d7b3dSmrgxf86CrtcPtr
6078428d7b3dSmrgsna_covering_crtc(struct sna *sna, const BoxRec *box, xf86CrtcPtr desired)
6079428d7b3dSmrg{
6080428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
6081428d7b3dSmrg	xf86CrtcPtr best_crtc;
6082428d7b3dSmrg	int best_coverage, c;
6083428d7b3dSmrg
6084428d7b3dSmrg	if (sna->flags & SNA_IS_HOSTED)
6085428d7b3dSmrg		return NULL;
6086428d7b3dSmrg
6087428d7b3dSmrg	/* If we do not own the VT, we do not own the CRTC either */
6088428d7b3dSmrg	if (!sna->scrn->vtSema)
6089428d7b3dSmrg		return NULL;
6090428d7b3dSmrg
6091428d7b3dSmrg	DBG(("%s for box=(%d, %d), (%d, %d)\n",
6092428d7b3dSmrg	     __FUNCTION__, box->x1, box->y1, box->x2, box->y2));
6093428d7b3dSmrg
6094428d7b3dSmrg	if (desired == NULL) {
6095428d7b3dSmrg		rrScrPrivPtr rr = rrGetScrPriv(xf86ScrnToScreen(sna->scrn));
6096428d7b3dSmrg		if (rr && rr->primaryOutput) {
6097428d7b3dSmrg			xf86OutputPtr output = rr->primaryOutput->devPrivate;
6098428d7b3dSmrg			DBG(("%s: have PrimaryOutput? %d marking as desired\n", __FUNCTION__, output->crtc != NULL));
6099428d7b3dSmrg			desired = output->crtc;
6100428d7b3dSmrg		}
6101428d7b3dSmrg	}
6102428d7b3dSmrg	if (desired && to_sna_crtc(desired) && to_sna_crtc(desired)->bo) {
6103428d7b3dSmrg		BoxRec cover_box;
6104428d7b3dSmrg		if (sna_box_intersect(&cover_box, &desired->bounds, box)) {
6105428d7b3dSmrg			DBG(("%s: box overlaps desired crtc: (%d, %d), (%d, %d)\n",
6106428d7b3dSmrg			     __FUNCTION__,
6107428d7b3dSmrg			     cover_box.x1, cover_box.y1,
6108428d7b3dSmrg			     cover_box.x2, cover_box.y2));
6109428d7b3dSmrg			return desired;
6110428d7b3dSmrg		}
6111428d7b3dSmrg	}
6112428d7b3dSmrg
6113428d7b3dSmrg	best_crtc = NULL;
6114428d7b3dSmrg	best_coverage = 0;
6115428d7b3dSmrg	for (c = 0; c < sna->mode.num_real_crtc; c++) {
6116428d7b3dSmrg		xf86CrtcPtr crtc = config->crtc[c];
6117428d7b3dSmrg		BoxRec cover_box;
6118428d7b3dSmrg		int coverage;
6119428d7b3dSmrg
6120428d7b3dSmrg		assert(to_sna_crtc(crtc));
6121428d7b3dSmrg
6122428d7b3dSmrg		/* If the CRTC is off, treat it as not covering */
6123428d7b3dSmrg		if (to_sna_crtc(crtc)->bo == NULL) {
6124428d7b3dSmrg			DBG(("%s: crtc %d off, skipping\n", __FUNCTION__, c));
6125428d7b3dSmrg			continue;
6126428d7b3dSmrg		}
6127428d7b3dSmrg
6128428d7b3dSmrg		DBG(("%s: crtc %d: (%d, %d), (%d, %d)\n",
6129428d7b3dSmrg		     __FUNCTION__, c,
6130428d7b3dSmrg		     crtc->bounds.x1, crtc->bounds.y1,
6131428d7b3dSmrg		     crtc->bounds.x2, crtc->bounds.y2));
6132428d7b3dSmrg		if (*(const uint64_t *)box == *(uint64_t *)&crtc->bounds) {
6133428d7b3dSmrg			DBG(("%s: box exactly matches crtc [%d]\n",
6134428d7b3dSmrg			     __FUNCTION__, c));
6135428d7b3dSmrg			return crtc;
6136428d7b3dSmrg		}
6137428d7b3dSmrg
6138428d7b3dSmrg		if (!sna_box_intersect(&cover_box, &crtc->bounds, box))
6139428d7b3dSmrg			continue;
6140428d7b3dSmrg
6141428d7b3dSmrg		DBG(("%s: box instersects (%d, %d), (%d, %d) of crtc %d\n",
6142428d7b3dSmrg		     __FUNCTION__,
6143428d7b3dSmrg		     cover_box.x1, cover_box.y1,
6144428d7b3dSmrg		     cover_box.x2, cover_box.y2,
6145428d7b3dSmrg		     c));
6146428d7b3dSmrg
6147428d7b3dSmrg		coverage = sna_box_area(&cover_box);
6148428d7b3dSmrg		DBG(("%s: box covers %d of crtc %d\n",
6149428d7b3dSmrg		     __FUNCTION__, coverage, c));
6150428d7b3dSmrg		if (coverage > best_coverage) {
6151428d7b3dSmrg			best_crtc = crtc;
6152428d7b3dSmrg			best_coverage = coverage;
6153428d7b3dSmrg		}
6154428d7b3dSmrg	}
6155428d7b3dSmrg	DBG(("%s: best crtc = %p, coverage = %d\n",
6156428d7b3dSmrg	     __FUNCTION__, best_crtc, best_coverage));
6157428d7b3dSmrg	return best_crtc;
6158428d7b3dSmrg}
6159428d7b3dSmrg
6160428d7b3dSmrg#define MI_LOAD_REGISTER_IMM			(0x22<<23)
6161428d7b3dSmrg
6162428d7b3dSmrgstatic bool sna_emit_wait_for_scanline_hsw(struct sna *sna,
6163428d7b3dSmrg					   xf86CrtcPtr crtc,
6164428d7b3dSmrg					   int pipe, int y1, int y2,
6165428d7b3dSmrg					   bool full_height)
6166428d7b3dSmrg{
6167428d7b3dSmrg	uint32_t event;
6168428d7b3dSmrg	uint32_t *b;
6169428d7b3dSmrg
6170428d7b3dSmrg	if (!sna->kgem.has_secure_batches)
6171428d7b3dSmrg		return false;
6172428d7b3dSmrg
6173428d7b3dSmrg	b = kgem_get_batch(&sna->kgem);
6174428d7b3dSmrg	sna->kgem.nbatch += 17;
6175428d7b3dSmrg
6176428d7b3dSmrg	switch (pipe) {
6177428d7b3dSmrg	default: assert(0);
6178428d7b3dSmrg	case 0: event = 1 << 0; break;
6179428d7b3dSmrg	case 1: event = 1 << 8; break;
6180428d7b3dSmrg	case 2: event = 1 << 14; break;
6181428d7b3dSmrg	}
6182428d7b3dSmrg
6183428d7b3dSmrg	b[0] = MI_LOAD_REGISTER_IMM | 1;
6184428d7b3dSmrg	b[1] = 0x44050; /* DERRMR */
6185428d7b3dSmrg	b[2] = ~event;
6186428d7b3dSmrg	b[3] = MI_LOAD_REGISTER_IMM | 1;
6187428d7b3dSmrg	b[4] = 0xa188; /* FORCEWAKE_MT */
6188428d7b3dSmrg	b[5] = 2 << 16 | 2;
6189428d7b3dSmrg
6190428d7b3dSmrg	/* The documentation says that the LOAD_SCAN_LINES command
6191428d7b3dSmrg	 * always comes in pairs. Don't ask me why. */
6192428d7b3dSmrg	switch (pipe) {
6193428d7b3dSmrg	default: assert(0);
6194428d7b3dSmrg	case 0: event = 0 << 19; break;
6195428d7b3dSmrg	case 1: event = 1 << 19; break;
6196428d7b3dSmrg	case 2: event = 4 << 19; break;
6197428d7b3dSmrg	}
6198428d7b3dSmrg	b[8] = b[6] = MI_LOAD_SCAN_LINES_INCL | event;
6199428d7b3dSmrg	b[9] = b[7] = (y1 << 16) | (y2-1);
6200428d7b3dSmrg
6201428d7b3dSmrg	switch (pipe) {
6202428d7b3dSmrg	default: assert(0);
6203428d7b3dSmrg	case 0: event = 1 << 0; break;
6204428d7b3dSmrg	case 1: event = 1 << 8; break;
6205428d7b3dSmrg	case 2: event = 1 << 14; break;
6206428d7b3dSmrg	}
6207428d7b3dSmrg	b[10] = MI_WAIT_FOR_EVENT | event;
6208428d7b3dSmrg
6209428d7b3dSmrg	b[11] = MI_LOAD_REGISTER_IMM | 1;
6210428d7b3dSmrg	b[12] = 0xa188; /* FORCEWAKE_MT */
6211428d7b3dSmrg	b[13] = 2 << 16;
6212428d7b3dSmrg	b[14] = MI_LOAD_REGISTER_IMM | 1;
6213428d7b3dSmrg	b[15] = 0x44050; /* DERRMR */
6214428d7b3dSmrg	b[16] = ~0;
6215428d7b3dSmrg
6216428d7b3dSmrg	sna->kgem.batch_flags |= I915_EXEC_SECURE;
6217428d7b3dSmrg	return true;
6218428d7b3dSmrg}
6219428d7b3dSmrg
6220428d7b3dSmrgstatic bool sna_emit_wait_for_scanline_ivb(struct sna *sna,
6221428d7b3dSmrg					   xf86CrtcPtr crtc,
6222428d7b3dSmrg					   int pipe, int y1, int y2,
6223428d7b3dSmrg					   bool full_height)
6224428d7b3dSmrg{
6225428d7b3dSmrg	uint32_t event, *b;
6226428d7b3dSmrg
6227428d7b3dSmrg	if (!sna->kgem.has_secure_batches)
6228428d7b3dSmrg		return false;
6229428d7b3dSmrg
6230428d7b3dSmrg	assert(y1 >= 0);
6231428d7b3dSmrg	assert(y2 > y1);
6232428d7b3dSmrg	assert(sna->kgem.mode);
6233428d7b3dSmrg
6234428d7b3dSmrg	/* Always program one less than the desired value */
6235428d7b3dSmrg	if (--y1 < 0)
6236428d7b3dSmrg		y1 = crtc->bounds.y2;
6237428d7b3dSmrg	y2--;
6238428d7b3dSmrg
6239428d7b3dSmrg	switch (pipe) {
6240428d7b3dSmrg	default:
6241428d7b3dSmrg		assert(0);
6242428d7b3dSmrg	case 0:
6243428d7b3dSmrg		event = 1 << (full_height ? 3 : 0);
6244428d7b3dSmrg		break;
6245428d7b3dSmrg	case 1:
6246428d7b3dSmrg		event = 1 << (full_height ? 11 : 8);
6247428d7b3dSmrg		break;
6248428d7b3dSmrg	case 2:
6249428d7b3dSmrg		event = 1 << (full_height ? 21 : 14);
6250428d7b3dSmrg		break;
6251428d7b3dSmrg	}
6252428d7b3dSmrg
6253428d7b3dSmrg	b = kgem_get_batch(&sna->kgem);
6254428d7b3dSmrg
6255428d7b3dSmrg	/* Both the LRI and WAIT_FOR_EVENT must be in the same cacheline */
6256428d7b3dSmrg	if (((sna->kgem.nbatch + 6) >> 4) != (sna->kgem.nbatch + 10) >> 4) {
6257428d7b3dSmrg		int dw = sna->kgem.nbatch + 6;
6258428d7b3dSmrg		dw = ALIGN(dw, 16) - dw;
6259428d7b3dSmrg		while (dw--)
6260428d7b3dSmrg			*b++ = MI_NOOP;
6261428d7b3dSmrg	}
6262428d7b3dSmrg
6263428d7b3dSmrg	b[0] = MI_LOAD_REGISTER_IMM | 1;
6264428d7b3dSmrg	b[1] = 0x44050; /* DERRMR */
6265428d7b3dSmrg	b[2] = ~event;
6266428d7b3dSmrg	b[3] = MI_LOAD_REGISTER_IMM | 1;
6267428d7b3dSmrg	b[4] = 0xa188; /* FORCEWAKE_MT */
6268428d7b3dSmrg	b[5] = 2 << 16 | 2;
6269428d7b3dSmrg	b[6] = MI_LOAD_REGISTER_IMM | 1;
6270428d7b3dSmrg	b[7] = 0x70068 + 0x1000 * pipe;
6271428d7b3dSmrg	b[8] = (1 << 31) | (1 << 30) | (y1 << 16) | y2;
6272428d7b3dSmrg	b[9] = MI_WAIT_FOR_EVENT | event;
6273428d7b3dSmrg	b[10] = MI_LOAD_REGISTER_IMM | 1;
6274428d7b3dSmrg	b[11] = 0xa188; /* FORCEWAKE_MT */
6275428d7b3dSmrg	b[12] = 2 << 16;
6276428d7b3dSmrg	b[13] = MI_LOAD_REGISTER_IMM | 1;
6277428d7b3dSmrg	b[14] = 0x44050; /* DERRMR */
6278428d7b3dSmrg	b[15] = ~0;
6279428d7b3dSmrg
6280428d7b3dSmrg	sna->kgem.nbatch = b - sna->kgem.batch + 16;
6281428d7b3dSmrg
6282428d7b3dSmrg	sna->kgem.batch_flags |= I915_EXEC_SECURE;
6283428d7b3dSmrg	return true;
6284428d7b3dSmrg}
6285428d7b3dSmrg
6286428d7b3dSmrgstatic bool sna_emit_wait_for_scanline_gen6(struct sna *sna,
6287428d7b3dSmrg					    xf86CrtcPtr crtc,
6288428d7b3dSmrg					    int pipe, int y1, int y2,
6289428d7b3dSmrg					    bool full_height)
6290428d7b3dSmrg{
6291428d7b3dSmrg	uint32_t *b;
6292428d7b3dSmrg	uint32_t event;
6293428d7b3dSmrg
6294428d7b3dSmrg	if (!sna->kgem.has_secure_batches)
6295428d7b3dSmrg		return false;
6296428d7b3dSmrg
6297428d7b3dSmrg	assert(y1 >= 0);
6298428d7b3dSmrg	assert(y2 > y1);
6299428d7b3dSmrg	assert(sna->kgem.mode == KGEM_RENDER);
6300428d7b3dSmrg
6301428d7b3dSmrg	/* Always program one less than the desired value */
6302428d7b3dSmrg	if (--y1 < 0)
6303428d7b3dSmrg		y1 = crtc->bounds.y2;
6304428d7b3dSmrg	y2--;
6305428d7b3dSmrg
6306428d7b3dSmrg	/* The scanline granularity is 3 bits */
6307428d7b3dSmrg	y1 &= ~7;
6308428d7b3dSmrg	y2 &= ~7;
6309428d7b3dSmrg	if (y2 == y1)
6310428d7b3dSmrg		return false;
6311428d7b3dSmrg
6312428d7b3dSmrg	event = 1 << (3*full_height + pipe*8);
6313428d7b3dSmrg
6314428d7b3dSmrg	b = kgem_get_batch(&sna->kgem);
6315428d7b3dSmrg	sna->kgem.nbatch += 16;
6316428d7b3dSmrg
6317428d7b3dSmrg	b[0] = MI_LOAD_REGISTER_IMM | 1;
6318428d7b3dSmrg	b[1] = 0x44050; /* DERRMR */
6319428d7b3dSmrg	b[2] = ~event;
6320428d7b3dSmrg	b[3] = MI_LOAD_REGISTER_IMM | 1;
6321428d7b3dSmrg	b[4] = 0x4f100; /* magic */
6322428d7b3dSmrg	b[5] = (1 << 31) | (1 << 30) | pipe << 29 | (y1 << 16) | y2;
6323428d7b3dSmrg	b[6] = MI_LOAD_REGISTER_IMM | 1;
6324428d7b3dSmrg	b[7] = 0x2050; /* PSMI_CTL(rcs) */
6325428d7b3dSmrg	b[8] = 1 << 16 | 1;
6326428d7b3dSmrg	b[9] = MI_WAIT_FOR_EVENT | event;
6327428d7b3dSmrg	b[10] = MI_LOAD_REGISTER_IMM | 1;
6328428d7b3dSmrg	b[11] = 0x2050; /* PSMI_CTL(rcs) */
6329428d7b3dSmrg	b[12] = 1 << 16;
6330428d7b3dSmrg	b[13] = MI_LOAD_REGISTER_IMM | 1;
6331428d7b3dSmrg	b[14] = 0x44050; /* DERRMR */
6332428d7b3dSmrg	b[15] = ~0;
6333428d7b3dSmrg
6334428d7b3dSmrg	sna->kgem.batch_flags |= I915_EXEC_SECURE;
6335428d7b3dSmrg	return true;
6336428d7b3dSmrg}
6337428d7b3dSmrg
6338428d7b3dSmrgstatic bool sna_emit_wait_for_scanline_gen4(struct sna *sna,
6339428d7b3dSmrg					    xf86CrtcPtr crtc,
6340428d7b3dSmrg					    int pipe, int y1, int y2,
6341428d7b3dSmrg					    bool full_height)
6342428d7b3dSmrg{
6343428d7b3dSmrg	uint32_t event;
6344428d7b3dSmrg	uint32_t *b;
6345428d7b3dSmrg
6346428d7b3dSmrg	if (pipe == 0) {
6347428d7b3dSmrg		if (full_height)
6348428d7b3dSmrg			event = MI_WAIT_FOR_PIPEA_SVBLANK;
6349428d7b3dSmrg		else
6350428d7b3dSmrg			event = MI_WAIT_FOR_PIPEA_SCAN_LINE_WINDOW;
6351428d7b3dSmrg	} else {
6352428d7b3dSmrg		if (full_height)
6353428d7b3dSmrg			event = MI_WAIT_FOR_PIPEB_SVBLANK;
6354428d7b3dSmrg		else
6355428d7b3dSmrg			event = MI_WAIT_FOR_PIPEB_SCAN_LINE_WINDOW;
6356428d7b3dSmrg	}
6357428d7b3dSmrg
6358428d7b3dSmrg	b = kgem_get_batch(&sna->kgem);
6359428d7b3dSmrg	sna->kgem.nbatch += 5;
6360428d7b3dSmrg
6361428d7b3dSmrg	/* The documentation says that the LOAD_SCAN_LINES command
6362428d7b3dSmrg	 * always comes in pairs. Don't ask me why. */
6363428d7b3dSmrg	b[2] = b[0] = MI_LOAD_SCAN_LINES_INCL | pipe << 20;
6364428d7b3dSmrg	b[3] = b[1] = (y1 << 16) | (y2-1);
6365428d7b3dSmrg	b[4] = MI_WAIT_FOR_EVENT | event;
6366428d7b3dSmrg
6367428d7b3dSmrg	return true;
6368428d7b3dSmrg}
6369428d7b3dSmrg
6370428d7b3dSmrgstatic bool sna_emit_wait_for_scanline_gen2(struct sna *sna,
6371428d7b3dSmrg					    xf86CrtcPtr crtc,
6372428d7b3dSmrg					    int pipe, int y1, int y2,
6373428d7b3dSmrg					    bool full_height)
6374428d7b3dSmrg{
6375428d7b3dSmrg	uint32_t *b;
6376428d7b3dSmrg
6377428d7b3dSmrg	/*
6378428d7b3dSmrg	 * Pre-965 doesn't have SVBLANK, so we need a bit
6379428d7b3dSmrg	 * of extra time for the blitter to start up and
6380428d7b3dSmrg	 * do its job for a full height blit
6381428d7b3dSmrg	 */
6382428d7b3dSmrg	if (full_height)
6383428d7b3dSmrg		y2 -= 2;
6384428d7b3dSmrg
6385428d7b3dSmrg	b = kgem_get_batch(&sna->kgem);
6386428d7b3dSmrg	sna->kgem.nbatch += 5;
6387428d7b3dSmrg
6388428d7b3dSmrg	/* The documentation says that the LOAD_SCAN_LINES command
6389428d7b3dSmrg	 * always comes in pairs. Don't ask me why. */
6390428d7b3dSmrg	b[2] = b[0] = MI_LOAD_SCAN_LINES_INCL | pipe << 20;
6391428d7b3dSmrg	b[3] = b[1] = (y1 << 16) | (y2-1);
6392428d7b3dSmrg	b[4] = MI_WAIT_FOR_EVENT | 1 << (1 + 4*pipe);
6393428d7b3dSmrg
6394428d7b3dSmrg	return true;
6395428d7b3dSmrg}
6396428d7b3dSmrg
6397428d7b3dSmrgbool
6398428d7b3dSmrgsna_wait_for_scanline(struct sna *sna,
6399428d7b3dSmrg		      PixmapPtr pixmap,
6400428d7b3dSmrg		      xf86CrtcPtr crtc,
6401428d7b3dSmrg		      const BoxRec *clip)
6402428d7b3dSmrg{
6403428d7b3dSmrg	bool full_height;
6404428d7b3dSmrg	int y1, y2, pipe;
6405428d7b3dSmrg	bool ret;
6406428d7b3dSmrg
6407428d7b3dSmrg	assert(crtc != NULL);
6408428d7b3dSmrg	assert(to_sna_crtc(crtc) != NULL);
6409428d7b3dSmrg	assert(to_sna_crtc(crtc)->bo != NULL);
6410428d7b3dSmrg	assert(pixmap == sna->front);
6411428d7b3dSmrg
6412428d7b3dSmrg	if (sna->flags & SNA_NO_VSYNC)
6413428d7b3dSmrg		return false;
6414428d7b3dSmrg
6415428d7b3dSmrg	/*
6416428d7b3dSmrg	 * Make sure we don't wait for a scanline that will
6417428d7b3dSmrg	 * never occur
6418428d7b3dSmrg	 */
6419428d7b3dSmrg	y1 = clip->y1 - crtc->bounds.y1;
6420428d7b3dSmrg	if (y1 < 0)
6421428d7b3dSmrg		y1 = 0;
6422428d7b3dSmrg	y2 = clip->y2 - crtc->bounds.y1;
6423428d7b3dSmrg	if (y2 > crtc->bounds.y2 - crtc->bounds.y1)
6424428d7b3dSmrg		y2 = crtc->bounds.y2 - crtc->bounds.y1;
6425428d7b3dSmrg	DBG(("%s: clipped range = %d, %d\n", __FUNCTION__, y1, y2));
6426428d7b3dSmrg	if (y2 <= y1 + 4)
6427428d7b3dSmrg		return false;
6428428d7b3dSmrg
6429428d7b3dSmrg	full_height = y1 == 0 && y2 == crtc->bounds.y2 - crtc->bounds.y1;
6430428d7b3dSmrg
6431428d7b3dSmrg	if (crtc->mode.Flags & V_INTERLACE) {
6432428d7b3dSmrg		/* DSL count field lines */
6433428d7b3dSmrg		y1 /= 2;
6434428d7b3dSmrg		y2 /= 2;
6435428d7b3dSmrg	}
6436428d7b3dSmrg
6437428d7b3dSmrg	pipe = sna_crtc_to_pipe(crtc);
6438428d7b3dSmrg	DBG(("%s: pipe=%d, y1=%d, y2=%d, full_height?=%d\n",
6439428d7b3dSmrg	     __FUNCTION__, pipe, y1, y2, full_height));
6440428d7b3dSmrg
6441428d7b3dSmrg	if (sna->kgem.gen >= 0110)
6442428d7b3dSmrg		ret = false;
6443428d7b3dSmrg	else if (sna->kgem.gen == 0101)
6444428d7b3dSmrg		ret = false; /* chv, vsync method unknown */
6445428d7b3dSmrg	else if (sna->kgem.gen >= 075)
6446428d7b3dSmrg		ret = sna_emit_wait_for_scanline_hsw(sna, crtc, pipe, y1, y2, full_height);
6447428d7b3dSmrg	else if (sna->kgem.gen == 071)
6448428d7b3dSmrg		ret = false; /* vlv, vsync method unknown */
6449428d7b3dSmrg	else if (sna->kgem.gen >= 070)
6450428d7b3dSmrg		ret = sna_emit_wait_for_scanline_ivb(sna, crtc, pipe, y1, y2, full_height);
6451428d7b3dSmrg	else if (sna->kgem.gen >= 060)
6452428d7b3dSmrg		ret =sna_emit_wait_for_scanline_gen6(sna, crtc, pipe, y1, y2, full_height);
6453428d7b3dSmrg	else if (sna->kgem.gen >= 040)
6454428d7b3dSmrg		ret = sna_emit_wait_for_scanline_gen4(sna, crtc, pipe, y1, y2, full_height);
6455428d7b3dSmrg	else
6456428d7b3dSmrg		ret = sna_emit_wait_for_scanline_gen2(sna, crtc, pipe, y1, y2, full_height);
6457428d7b3dSmrg
6458428d7b3dSmrg	return ret;
6459428d7b3dSmrg}
6460428d7b3dSmrg
6461428d7b3dSmrgvoid sna_mode_check(struct sna *sna)
6462428d7b3dSmrg{
6463428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
6464428d7b3dSmrg	int i;
6465428d7b3dSmrg
6466428d7b3dSmrg	if (sna->flags & SNA_IS_HOSTED)
6467428d7b3dSmrg		return;
6468428d7b3dSmrg
6469428d7b3dSmrg	DBG(("%s\n", __FUNCTION__));
6470428d7b3dSmrg
6471428d7b3dSmrg	/* Validate CRTC attachments and force consistency upon the kernel */
6472428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_crtc; i++) {
6473428d7b3dSmrg		xf86CrtcPtr crtc = config->crtc[i];
6474428d7b3dSmrg		struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
6475428d7b3dSmrg		struct drm_mode_crtc mode;
6476428d7b3dSmrg		uint32_t expected[2];
6477428d7b3dSmrg
6478428d7b3dSmrg		assert(sna_crtc);
6479428d7b3dSmrg
6480428d7b3dSmrg#if XF86_CRTC_VERSION >= 3
6481428d7b3dSmrg		assert(sna_crtc->bo == NULL || crtc->active);
6482428d7b3dSmrg#endif
6483428d7b3dSmrg		expected[0] = sna_crtc->bo ? fb_id(sna_crtc->bo) : 0;
6484428d7b3dSmrg		expected[1] = sna_crtc->flip_bo ? fb_id(sna_crtc->flip_bo) : -1;
6485428d7b3dSmrg
6486428d7b3dSmrg		VG_CLEAR(mode);
6487428d7b3dSmrg		mode.crtc_id = sna_crtc->id;
6488428d7b3dSmrg		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCRTC, &mode))
6489428d7b3dSmrg			continue;
6490428d7b3dSmrg
6491428d7b3dSmrg		DBG(("%s: crtc=%d, valid?=%d, fb attached?=%d, expected=(%d or %d)\n",
6492428d7b3dSmrg		     __FUNCTION__,
6493428d7b3dSmrg		     mode.crtc_id, mode.mode_valid,
6494428d7b3dSmrg		     mode.fb_id, expected[0], expected[1]));
6495428d7b3dSmrg
6496428d7b3dSmrg		if (mode.fb_id != expected[0] && mode.fb_id != expected[1]) {
6497428d7b3dSmrg			xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
6498428d7b3dSmrg				   "%s: invalid state found on pipe %d, disabling CRTC:%d\n",
6499428d7b3dSmrg				   __FUNCTION__, sna_crtc->pipe, sna_crtc->id);
6500428d7b3dSmrg			sna_crtc_disable(crtc);
6501428d7b3dSmrg		}
6502428d7b3dSmrg	}
6503428d7b3dSmrg
6504428d7b3dSmrg	for (i = 0; i < config->num_output; i++) {
6505428d7b3dSmrg		xf86OutputPtr output = config->output[i];
6506428d7b3dSmrg		struct sna_output *sna_output;
6507428d7b3dSmrg
6508428d7b3dSmrg		if (output->crtc)
6509428d7b3dSmrg			continue;
6510428d7b3dSmrg
6511428d7b3dSmrg		sna_output = to_sna_output(output);
6512428d7b3dSmrg		if (sna_output == NULL)
6513428d7b3dSmrg			continue;
6514428d7b3dSmrg
6515428d7b3dSmrg		sna_output->dpms_mode = DPMSModeOff;
6516428d7b3dSmrg	}
6517428d7b3dSmrg
6518428d7b3dSmrg	update_flush_interval(sna);
6519428d7b3dSmrg}
6520428d7b3dSmrg
6521428d7b3dSmrgstatic bool
6522428d7b3dSmrgsna_crtc_hide_planes(struct sna *sna, struct sna_crtc *crtc)
6523428d7b3dSmrg{
6524428d7b3dSmrg#define LOCAL_IOCTL_MODE_SETPLANE DRM_IOWR(0xB7, struct local_mode_set_plane)
6525428d7b3dSmrg	struct local_mode_set_plane {
6526428d7b3dSmrg		uint32_t plane_id;
6527428d7b3dSmrg		uint32_t crtc_id;
6528428d7b3dSmrg		uint32_t fb_id; /* fb object contains surface format type */
6529428d7b3dSmrg		uint32_t flags;
6530428d7b3dSmrg
6531428d7b3dSmrg		/* Signed dest location allows it to be partially off screen */
6532428d7b3dSmrg		int32_t crtc_x, crtc_y;
6533428d7b3dSmrg		uint32_t crtc_w, crtc_h;
6534428d7b3dSmrg
6535428d7b3dSmrg		/* Source values are 16.16 fixed point */
6536428d7b3dSmrg		uint32_t src_x, src_y;
6537428d7b3dSmrg		uint32_t src_h, src_w;
6538428d7b3dSmrg	} s;
6539428d7b3dSmrg
6540428d7b3dSmrg	if (crtc->primary.id == 0)
6541428d7b3dSmrg		return false;
6542428d7b3dSmrg
6543428d7b3dSmrg	memset(&s, 0, sizeof(s));
6544428d7b3dSmrg	s.plane_id = crtc->primary.id;
6545428d7b3dSmrg	if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_SETPLANE, &s))
6546428d7b3dSmrg		return false;
6547428d7b3dSmrg
6548428d7b3dSmrg	s.plane_id = crtc->sprite.id;
6549428d7b3dSmrg	(void)drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_SETPLANE, &s);
6550428d7b3dSmrg
6551428d7b3dSmrg	__sna_crtc_disable(sna, crtc);
6552428d7b3dSmrg	return true;
6553428d7b3dSmrg}
6554428d7b3dSmrg
6555428d7b3dSmrgvoid sna_mode_reset(struct sna *sna)
6556428d7b3dSmrg{
6557428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
6558428d7b3dSmrg	int i;
6559428d7b3dSmrg
6560428d7b3dSmrg	if (sna->flags & SNA_IS_HOSTED)
6561428d7b3dSmrg		return;
6562428d7b3dSmrg
6563428d7b3dSmrg	DBG(("%s\n", __FUNCTION__));
6564428d7b3dSmrg
6565428d7b3dSmrg	sna_hide_cursors(sna->scrn);
6566428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_crtc; i++)
6567428d7b3dSmrg		if (!sna_crtc_hide_planes(sna, to_sna_crtc(config->crtc[i])))
6568428d7b3dSmrg			sna_crtc_disable(config->crtc[i]);
6569428d7b3dSmrg	assert(sna->mode.front_active == 0);
6570428d7b3dSmrg
6571428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_crtc; i++) {
6572428d7b3dSmrg		struct sna_crtc *sna_crtc = to_sna_crtc(config->crtc[i]);
6573428d7b3dSmrg
6574428d7b3dSmrg		assert(sna_crtc != NULL);
6575428d7b3dSmrg		sna_crtc->dpms_mode = -1;
6576428d7b3dSmrg
6577428d7b3dSmrg		/* Force the rotation property to be reset on next use */
6578428d7b3dSmrg		rotation_reset(&sna_crtc->primary);
6579428d7b3dSmrg		rotation_reset(&sna_crtc->sprite);
6580428d7b3dSmrg	}
6581428d7b3dSmrg
6582428d7b3dSmrg	/* VT switching, likely to be fbcon so make the backlight usable */
6583428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_output; i++) {
6584428d7b3dSmrg		struct sna_output *sna_output = to_sna_output(config->output[i]);
6585428d7b3dSmrg
6586428d7b3dSmrg		assert(sna_output != NULL);
6587428d7b3dSmrg		if (sna_output->dpms_mode != DPMSModeOff)
6588428d7b3dSmrg			continue;
6589428d7b3dSmrg
6590428d7b3dSmrg		if (!sna_output->backlight.iface)
6591428d7b3dSmrg			continue;
6592428d7b3dSmrg
6593428d7b3dSmrg		sna_output_backlight_set(sna_output,
6594428d7b3dSmrg					 sna_output->backlight_active_level);
6595428d7b3dSmrg	}
6596428d7b3dSmrg
6597428d7b3dSmrg	/* drain the event queue */
6598428d7b3dSmrg	sna_mode_wakeup(sna);
6599428d7b3dSmrg}
6600428d7b3dSmrg
6601428d7b3dSmrgstatic void transformed_box(BoxRec *box, xf86CrtcPtr crtc)
6602428d7b3dSmrg{
6603428d7b3dSmrg	box->x1 -= crtc->filter_width >> 1;
6604428d7b3dSmrg	box->x2 += crtc->filter_width >> 1;
6605428d7b3dSmrg	box->y1 -= crtc->filter_height >> 1;
6606428d7b3dSmrg	box->y2 += crtc->filter_height >> 1;
6607428d7b3dSmrg
6608428d7b3dSmrg	pixman_f_transform_bounds(&crtc->f_framebuffer_to_crtc, box);
6609428d7b3dSmrg
6610428d7b3dSmrg	if (box->x1 < 0)
6611428d7b3dSmrg		box->x1 = 0;
6612428d7b3dSmrg	if (box->y1 < 0)
6613428d7b3dSmrg		box->y1 = 0;
6614428d7b3dSmrg	if (box->x2 > crtc->mode.HDisplay)
6615428d7b3dSmrg		box->x2 = crtc->mode.HDisplay;
6616428d7b3dSmrg	if (box->y2 > crtc->mode.VDisplay)
6617428d7b3dSmrg		box->y2 = crtc->mode.VDisplay;
6618428d7b3dSmrg}
6619428d7b3dSmrg
6620428d7b3dSmrginline static DrawablePtr crtc_source(xf86CrtcPtr crtc, int16_t *sx, int16_t *sy)
6621428d7b3dSmrg{
6622428d7b3dSmrg	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
6623428d7b3dSmrg	if (sna_crtc->slave_pixmap) {
6624428d7b3dSmrg		DBG(("%s: using slave pixmap=%ld, offset (%d, %d)\n",
6625428d7b3dSmrg		     __FUNCTION__,
6626428d7b3dSmrg		     sna_crtc->slave_pixmap->drawable.serialNumber,
6627428d7b3dSmrg		 -crtc->x, -crtc->y));
6628428d7b3dSmrg		*sx = -crtc->x;
6629428d7b3dSmrg		*sy = -crtc->y;
6630428d7b3dSmrg		return &sna_crtc->slave_pixmap->drawable;
6631428d7b3dSmrg	} else {
6632428d7b3dSmrg		DBG(("%s: using Screen pixmap=%ld\n",
6633428d7b3dSmrg		     __FUNCTION__,
6634428d7b3dSmrg		     to_sna(crtc->scrn)->front->drawable.serialNumber));
6635428d7b3dSmrg		*sx = *sy = 0;
6636428d7b3dSmrg		return &to_sna(crtc->scrn)->front->drawable;
6637428d7b3dSmrg	}
6638428d7b3dSmrg}
6639428d7b3dSmrg
6640428d7b3dSmrgstatic void
6641428d7b3dSmrgsna_crtc_redisplay__fallback(xf86CrtcPtr crtc, RegionPtr region, struct kgem_bo *bo)
6642428d7b3dSmrg{
6643428d7b3dSmrg	int16_t sx, sy;
6644428d7b3dSmrg	struct sna *sna = to_sna(crtc->scrn);
6645428d7b3dSmrg	ScreenPtr screen = sna->scrn->pScreen;
6646428d7b3dSmrg	DrawablePtr draw = crtc_source(crtc, &sx, &sy);
6647428d7b3dSmrg	PictFormatPtr format;
6648428d7b3dSmrg	PicturePtr src, dst;
6649428d7b3dSmrg	PixmapPtr pixmap;
6650428d7b3dSmrg	int depth, error;
6651428d7b3dSmrg	void *ptr;
6652428d7b3dSmrg
6653428d7b3dSmrg	DBG(("%s: compositing transformed damage boxes, target handle=%d\n", __FUNCTION__, bo->handle));
6654428d7b3dSmrg
6655428d7b3dSmrg	error = sna_render_format_for_depth(draw->depth);
6656428d7b3dSmrg	depth = PIXMAN_FORMAT_DEPTH(error);
6657428d7b3dSmrg	format = PictureMatchFormat(screen, depth, error);
6658428d7b3dSmrg	if (format == NULL) {
6659428d7b3dSmrg		DBG(("%s: can't find format for depth=%d [%08x]\n",
6660428d7b3dSmrg		     __FUNCTION__, depth, error));
6661428d7b3dSmrg		return;
6662428d7b3dSmrg	}
6663428d7b3dSmrg
6664428d7b3dSmrg	DBG(("%s: dst format=%08x, depth=%d, bpp=%d, pitch=%d, size=%dx%d\n",
6665428d7b3dSmrg	     __FUNCTION__, format->format, depth, draw->bitsPerPixel,
6666428d7b3dSmrg	     bo->pitch, crtc->mode.HDisplay, crtc->mode.VDisplay));
6667428d7b3dSmrg
6668428d7b3dSmrg	ptr = kgem_bo_map__gtt(&sna->kgem, bo);
6669428d7b3dSmrg	if (ptr == NULL)
6670428d7b3dSmrg		return;
6671428d7b3dSmrg
6672428d7b3dSmrg	pixmap = sna_pixmap_create_unattached(screen, 0, 0, depth);
6673428d7b3dSmrg	if (pixmap == NullPixmap)
6674428d7b3dSmrg		return;
6675428d7b3dSmrg
6676428d7b3dSmrg	if (!screen->ModifyPixmapHeader(pixmap,
6677428d7b3dSmrg					crtc->mode.HDisplay, crtc->mode.VDisplay,
6678428d7b3dSmrg					depth, draw->bitsPerPixel,
6679428d7b3dSmrg					bo->pitch, ptr))
6680428d7b3dSmrg		goto free_pixmap;
6681428d7b3dSmrg
6682428d7b3dSmrg	src = CreatePicture(None, draw, format,
6683428d7b3dSmrg			    0, NULL, serverClient, &error);
6684428d7b3dSmrg	if (!src)
6685428d7b3dSmrg		goto free_pixmap;
6686428d7b3dSmrg
6687428d7b3dSmrg	error = SetPictureTransform(src, &crtc->crtc_to_framebuffer);
6688428d7b3dSmrg	if (error)
6689428d7b3dSmrg		goto free_src;
6690428d7b3dSmrg
6691428d7b3dSmrg	if (crtc->filter && crtc->transform_in_use)
6692428d7b3dSmrg		SetPicturePictFilter(src, crtc->filter,
6693428d7b3dSmrg				     crtc->params, crtc->nparams);
6694428d7b3dSmrg
6695428d7b3dSmrg	dst = CreatePicture(None, &pixmap->drawable, format,
6696428d7b3dSmrg			    0, NULL, serverClient, &error);
6697428d7b3dSmrg	if (!dst)
6698428d7b3dSmrg		goto free_src;
6699428d7b3dSmrg
6700428d7b3dSmrg	kgem_bo_sync__gtt(&sna->kgem, bo);
6701428d7b3dSmrg
6702428d7b3dSmrg	if (sigtrap_get() == 0) { /* paranoia */
6703428d7b3dSmrg		const BoxRec *b = region_rects(region);
6704428d7b3dSmrg		int n = region_num_rects(region);
6705428d7b3dSmrg		do {
6706428d7b3dSmrg			BoxRec box;
6707428d7b3dSmrg
6708428d7b3dSmrg			box = *b++;
6709428d7b3dSmrg			transformed_box(&box, crtc);
6710428d7b3dSmrg
6711428d7b3dSmrg			DBG(("%s: (%d, %d)x(%d, %d) -> (%d, %d), (%d, %d)\n",
6712428d7b3dSmrg			     __FUNCTION__,
6713428d7b3dSmrg			     b[-1].x1, b[-1].y1, b[-1].x2-b[-1].x1, b[-1].y2-b[-1].y1,
6714428d7b3dSmrg			     box.x1, box.y1, box.x2, box.y2));
6715428d7b3dSmrg
6716428d7b3dSmrg			fbComposite(PictOpSrc, src, NULL, dst,
6717428d7b3dSmrg				    box.x1 + sx, box.y1 + sy,
6718428d7b3dSmrg				    0, 0,
6719428d7b3dSmrg				    box.x1, box.y1,
6720428d7b3dSmrg				    box.x2 - box.x1, box.y2 - box.y1);
6721428d7b3dSmrg		} while (--n);
6722428d7b3dSmrg		sigtrap_put();
6723428d7b3dSmrg	}
6724428d7b3dSmrg
6725428d7b3dSmrg	FreePicture(dst, None);
6726428d7b3dSmrgfree_src:
6727428d7b3dSmrg	FreePicture(src, None);
6728428d7b3dSmrgfree_pixmap:
6729428d7b3dSmrg	screen->DestroyPixmap(pixmap);
6730428d7b3dSmrg}
6731428d7b3dSmrg
6732428d7b3dSmrgstatic void
6733428d7b3dSmrgsna_crtc_redisplay__composite(xf86CrtcPtr crtc, RegionPtr region, struct kgem_bo *bo)
6734428d7b3dSmrg{
6735428d7b3dSmrg	int16_t sx, sy;
6736428d7b3dSmrg	struct sna *sna = to_sna(crtc->scrn);
6737428d7b3dSmrg	ScreenPtr screen = crtc->scrn->pScreen;
6738428d7b3dSmrg	DrawablePtr draw = crtc_source(crtc, &sx, &sy);
6739428d7b3dSmrg	struct sna_composite_op tmp;
6740428d7b3dSmrg	PictFormatPtr format;
6741428d7b3dSmrg	PicturePtr src, dst;
6742428d7b3dSmrg	PixmapPtr pixmap;
6743428d7b3dSmrg	const BoxRec *b;
6744428d7b3dSmrg	int n, depth, error;
6745428d7b3dSmrg
6746428d7b3dSmrg	DBG(("%s: compositing transformed damage boxes\n", __FUNCTION__));
6747428d7b3dSmrg
6748428d7b3dSmrg	error = sna_render_format_for_depth(draw->depth);
6749428d7b3dSmrg	depth = PIXMAN_FORMAT_DEPTH(error);
6750428d7b3dSmrg	format = PictureMatchFormat(screen, depth, error);
6751428d7b3dSmrg	if (format == NULL) {
6752428d7b3dSmrg		DBG(("%s: can't find format for depth=%d [%08x]\n",
6753428d7b3dSmrg		     __FUNCTION__, depth, error));
6754428d7b3dSmrg		return;
6755428d7b3dSmrg	}
6756428d7b3dSmrg
6757428d7b3dSmrg	DBG(("%s: dst format=%08x, depth=%d, bpp=%d, pitch=%d, size=%dx%d\n",
6758428d7b3dSmrg	     __FUNCTION__, format->format, depth, draw->bitsPerPixel,
6759428d7b3dSmrg	     bo->pitch, crtc->mode.HDisplay, crtc->mode.VDisplay));
6760428d7b3dSmrg
6761428d7b3dSmrg	pixmap = sna_pixmap_create_unattached(screen, 0, 0, depth);
6762428d7b3dSmrg	if (pixmap == NullPixmap)
6763428d7b3dSmrg		return;
6764428d7b3dSmrg
6765428d7b3dSmrg	if (!screen->ModifyPixmapHeader(pixmap,
6766428d7b3dSmrg					crtc->mode.HDisplay, crtc->mode.VDisplay,
6767428d7b3dSmrg					depth, draw->bitsPerPixel,
6768428d7b3dSmrg					bo->pitch, NULL))
6769428d7b3dSmrg		goto free_pixmap;
6770428d7b3dSmrg
6771428d7b3dSmrg	if (!sna_pixmap_attach_to_bo(pixmap, kgem_bo_reference(bo))) {
6772428d7b3dSmrg		kgem_bo_destroy(&sna->kgem, bo);
6773428d7b3dSmrg		goto free_pixmap;
6774428d7b3dSmrg	}
6775428d7b3dSmrg
6776428d7b3dSmrg	src = CreatePicture(None, draw, format,
6777428d7b3dSmrg			    0, NULL, serverClient, &error);
6778428d7b3dSmrg	if (!src)
6779428d7b3dSmrg		goto free_pixmap;
6780428d7b3dSmrg
6781428d7b3dSmrg	error = SetPictureTransform(src, &crtc->crtc_to_framebuffer);
6782428d7b3dSmrg	if (error)
6783428d7b3dSmrg		goto free_src;
6784428d7b3dSmrg
6785428d7b3dSmrg	if (crtc->filter && crtc->transform_in_use)
6786428d7b3dSmrg		SetPicturePictFilter(src, crtc->filter,
6787428d7b3dSmrg				     crtc->params, crtc->nparams);
6788428d7b3dSmrg
6789428d7b3dSmrg	dst = CreatePicture(None, &pixmap->drawable, format,
6790428d7b3dSmrg			    0, NULL, serverClient, &error);
6791428d7b3dSmrg	if (!dst)
6792428d7b3dSmrg		goto free_src;
6793428d7b3dSmrg
6794428d7b3dSmrg	ValidatePicture(src);
6795428d7b3dSmrg	ValidatePicture(dst);
6796428d7b3dSmrg
6797428d7b3dSmrg	if (!sna->render.composite(sna,
6798428d7b3dSmrg				   PictOpSrc, src, NULL, dst,
6799428d7b3dSmrg				   sx, sy,
6800428d7b3dSmrg				   0, 0,
6801428d7b3dSmrg				   0, 0,
6802428d7b3dSmrg				   crtc->mode.HDisplay, crtc->mode.VDisplay,
6803428d7b3dSmrg				   COMPOSITE_PARTIAL, memset(&tmp, 0, sizeof(tmp)))) {
6804428d7b3dSmrg		DBG(("%s: unsupported operation!\n", __FUNCTION__));
6805428d7b3dSmrg		sna_crtc_redisplay__fallback(crtc, region, bo);
6806428d7b3dSmrg		goto free_dst;
6807428d7b3dSmrg	}
6808428d7b3dSmrg
6809428d7b3dSmrg	n = region_num_rects(region);
6810428d7b3dSmrg	b = region_rects(region);
6811428d7b3dSmrg	do {
6812428d7b3dSmrg		BoxRec box;
6813428d7b3dSmrg
6814428d7b3dSmrg		box = *b++;
6815428d7b3dSmrg		transformed_box(&box, crtc);
6816428d7b3dSmrg
6817428d7b3dSmrg		DBG(("%s: (%d, %d)x(%d, %d) -> (%d, %d), (%d, %d)\n",
6818428d7b3dSmrg		     __FUNCTION__,
6819428d7b3dSmrg		     b[-1].x1, b[-1].y1, b[-1].x2-b[-1].x1, b[-1].y2-b[-1].y1,
6820428d7b3dSmrg		     box.x1, box.y1, box.x2, box.y2));
6821428d7b3dSmrg
6822428d7b3dSmrg		tmp.box(sna, &tmp, &box);
6823428d7b3dSmrg	} while (--n);
6824428d7b3dSmrg	tmp.done(sna, &tmp);
6825428d7b3dSmrg
6826428d7b3dSmrgfree_dst:
6827428d7b3dSmrg	FreePicture(dst, None);
6828428d7b3dSmrgfree_src:
6829428d7b3dSmrg	FreePicture(src, None);
6830428d7b3dSmrgfree_pixmap:
6831428d7b3dSmrg	screen->DestroyPixmap(pixmap);
6832428d7b3dSmrg}
6833428d7b3dSmrg
6834428d7b3dSmrgstatic void
6835428d7b3dSmrgsna_crtc_redisplay(xf86CrtcPtr crtc, RegionPtr region, struct kgem_bo *bo)
6836428d7b3dSmrg{
6837428d7b3dSmrg	int16_t tx, ty, sx, sy;
6838428d7b3dSmrg	struct sna *sna = to_sna(crtc->scrn);
6839428d7b3dSmrg	DrawablePtr draw = crtc_source(crtc, &sx, &sy);
6840428d7b3dSmrg	struct sna_pixmap *priv = sna_pixmap((PixmapPtr)draw);
6841428d7b3dSmrg
6842428d7b3dSmrg	DBG(("%s: crtc %d [pipe=%d], damage (%d, %d), (%d, %d) x %d\n",
6843428d7b3dSmrg	     __FUNCTION__, to_sna_crtc(crtc)->id, to_sna_crtc(crtc)->pipe,
6844428d7b3dSmrg	     region->extents.x1, region->extents.y1,
6845428d7b3dSmrg	     region->extents.x2, region->extents.y2,
6846428d7b3dSmrg	     region_num_rects(region)));
6847428d7b3dSmrg
6848428d7b3dSmrg	assert(!wedged(sna));
6849428d7b3dSmrg
6850428d7b3dSmrg	if (priv->clear) {
6851428d7b3dSmrg		RegionRec whole;
6852428d7b3dSmrg
6853428d7b3dSmrg		DBG(("%s: clear damage boxes\n", __FUNCTION__));
6854428d7b3dSmrg
6855428d7b3dSmrg		if (sna_transform_is_integer_translation(&crtc->crtc_to_framebuffer,
6856428d7b3dSmrg							 &tx, &ty)) {
6857428d7b3dSmrg			RegionTranslate(region, -tx, -ty);
6858428d7b3dSmrg		} else {
6859428d7b3dSmrg			whole.extents = region->extents;
6860428d7b3dSmrg			whole.data = NULL;
6861428d7b3dSmrg			transformed_box(&whole.extents, crtc);
6862428d7b3dSmrg			region = &whole;
6863428d7b3dSmrg		}
6864428d7b3dSmrg		if (sna_blt_fill_boxes(sna, GXcopy,
6865428d7b3dSmrg				       bo, draw->bitsPerPixel,
6866428d7b3dSmrg				       priv->clear_color,
6867428d7b3dSmrg				       region_rects(region),
6868428d7b3dSmrg				       region_num_rects(region)))
6869428d7b3dSmrg			return;
6870428d7b3dSmrg	}
6871428d7b3dSmrg
6872428d7b3dSmrg	if (crtc->filter == NULL &&
6873428d7b3dSmrg	    priv->gpu_bo &&
6874428d7b3dSmrg	    priv->cpu_damage == NULL &&
6875428d7b3dSmrg	    sna_transform_is_integer_translation(&crtc->crtc_to_framebuffer,
6876428d7b3dSmrg						 &tx, &ty)) {
6877428d7b3dSmrg		DrawableRec tmp;
6878428d7b3dSmrg
6879428d7b3dSmrg		DBG(("%s: copy damage boxes\n", __FUNCTION__));
6880428d7b3dSmrg
6881428d7b3dSmrg		tmp.width = crtc->mode.HDisplay;
6882428d7b3dSmrg		tmp.height = crtc->mode.VDisplay;
6883428d7b3dSmrg		tmp.depth = sna->front->drawable.depth;
6884428d7b3dSmrg		tmp.bitsPerPixel = sna->front->drawable.bitsPerPixel;
6885428d7b3dSmrg
6886428d7b3dSmrg		if (sna->render.copy_boxes(sna, GXcopy,
6887428d7b3dSmrg					   draw, priv->gpu_bo, sx, sy,
6888428d7b3dSmrg					   &tmp, bo, -tx, -ty,
6889428d7b3dSmrg					   region_rects(region), region_num_rects(region), 0))
6890428d7b3dSmrg			return;
6891428d7b3dSmrg	}
6892428d7b3dSmrg
6893428d7b3dSmrg	if (can_render(sna))
6894428d7b3dSmrg		sna_crtc_redisplay__composite(crtc, region, bo);
6895428d7b3dSmrg	else
6896428d7b3dSmrg		sna_crtc_redisplay__fallback(crtc, region, bo);
6897428d7b3dSmrg}
6898428d7b3dSmrg
6899428d7b3dSmrgstatic void shadow_flip_handler(struct drm_event_vblank *e,
6900428d7b3dSmrg				void *data)
6901428d7b3dSmrg{
6902428d7b3dSmrg	sna_mode_redisplay(data);
6903428d7b3dSmrg}
6904428d7b3dSmrg
6905428d7b3dSmrgvoid sna_shadow_set_crtc(struct sna *sna,
6906428d7b3dSmrg			 xf86CrtcPtr crtc,
6907428d7b3dSmrg			 struct kgem_bo *bo)
6908428d7b3dSmrg{
6909428d7b3dSmrg	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
6910428d7b3dSmrg	struct sna_pixmap *priv;
6911428d7b3dSmrg
6912428d7b3dSmrg	DBG(("%s: setting shadow override for CRTC:%d to handle=%d\n",
6913428d7b3dSmrg	     __FUNCTION__, sna_crtc->id, bo->handle));
6914428d7b3dSmrg
6915428d7b3dSmrg	assert(sna->flags & SNA_TEAR_FREE);
6916428d7b3dSmrg	assert(sna_crtc);
6917428d7b3dSmrg	assert(!sna_crtc->transform);
6918428d7b3dSmrg
6919428d7b3dSmrg	if (sna_crtc->client_bo != bo) {
6920428d7b3dSmrg		if (sna_crtc->client_bo)
6921428d7b3dSmrg			kgem_bo_destroy(&sna->kgem, sna_crtc->client_bo);
6922428d7b3dSmrg
6923428d7b3dSmrg		sna_crtc->client_bo = kgem_bo_reference(bo);
6924428d7b3dSmrg		sna_crtc_damage(crtc);
6925428d7b3dSmrg	}
6926428d7b3dSmrg
6927428d7b3dSmrg	list_move(&sna_crtc->shadow_link, &sna->mode.shadow_crtc);
6928428d7b3dSmrg	sna->mode.shadow_dirty = true;
6929428d7b3dSmrg
6930428d7b3dSmrg	priv = sna_pixmap(sna->front);
6931428d7b3dSmrg	assert(priv->gpu_bo);
6932428d7b3dSmrg	priv->move_to_gpu = wait_for_shadow;
6933428d7b3dSmrg	priv->move_to_gpu_data = sna;
6934428d7b3dSmrg}
6935428d7b3dSmrg
6936428d7b3dSmrgvoid sna_shadow_steal_crtcs(struct sna *sna, struct list *list)
6937428d7b3dSmrg{
6938428d7b3dSmrg	list_init(list);
6939428d7b3dSmrg	while (!list_is_empty(&sna->mode.shadow_crtc)) {
6940428d7b3dSmrg		RegionRec sub, *damage;
6941428d7b3dSmrg		struct sna_crtc *crtc =
6942428d7b3dSmrg			list_first_entry(&sna->mode.shadow_crtc,
6943428d7b3dSmrg					 struct sna_crtc,
6944428d7b3dSmrg					 shadow_link);
6945428d7b3dSmrg
6946428d7b3dSmrg		damage = DamageRegion(sna->mode.shadow_damage);
6947428d7b3dSmrg		sub.extents = crtc->base->bounds;
6948428d7b3dSmrg		sub.data = NULL;
6949428d7b3dSmrg		RegionSubtract(damage, damage, &sub);
6950428d7b3dSmrg
6951428d7b3dSmrg		list_move(&crtc->shadow_link, list);
6952428d7b3dSmrg	}
6953428d7b3dSmrg}
6954428d7b3dSmrg
6955428d7b3dSmrgvoid sna_shadow_unsteal_crtcs(struct sna *sna, struct list *list)
6956428d7b3dSmrg{
6957428d7b3dSmrg	while (!list_is_empty(list)) {
6958428d7b3dSmrg		struct sna_crtc *crtc =
6959428d7b3dSmrg			list_first_entry(list,
6960428d7b3dSmrg					 struct sna_crtc,
6961428d7b3dSmrg					 shadow_link);
6962428d7b3dSmrg		assert(crtc->client_bo);
6963428d7b3dSmrg		sna_shadow_set_crtc(sna, crtc->base, crtc->client_bo);
6964428d7b3dSmrg	}
6965428d7b3dSmrg}
6966428d7b3dSmrg
6967428d7b3dSmrgvoid sna_shadow_unset_crtc(struct sna *sna,
6968428d7b3dSmrg			   xf86CrtcPtr crtc)
6969428d7b3dSmrg{
6970428d7b3dSmrg	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
6971428d7b3dSmrg
6972428d7b3dSmrg	DBG(("%s: clearin shadow override for CRTC:%d\n",
6973428d7b3dSmrg	     __FUNCTION__, sna_crtc->id));
6974428d7b3dSmrg
6975428d7b3dSmrg	if (sna_crtc->client_bo == NULL)
6976428d7b3dSmrg		return;
6977428d7b3dSmrg
6978428d7b3dSmrg	kgem_bo_destroy(&sna->kgem, sna_crtc->client_bo);
6979428d7b3dSmrg	sna_crtc->client_bo = NULL;
6980428d7b3dSmrg	list_del(&sna_crtc->shadow_link);
6981428d7b3dSmrg	sna->mode.shadow_dirty = true;
6982428d7b3dSmrg
6983428d7b3dSmrg	sna_crtc_damage(crtc);
6984428d7b3dSmrg}
6985428d7b3dSmrg
6986428d7b3dSmrgvoid sna_mode_redisplay(struct sna *sna)
6987428d7b3dSmrg{
6988428d7b3dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
6989428d7b3dSmrg	RegionPtr region;
6990428d7b3dSmrg	int i;
6991428d7b3dSmrg
6992428d7b3dSmrg	if (!sna->mode.shadow_damage)
6993428d7b3dSmrg		return;
6994428d7b3dSmrg
6995428d7b3dSmrg	DBG(("%s: posting shadow damage? %d (flips pending? %d, mode reconfiguration pending? %d)\n",
6996428d7b3dSmrg	     __FUNCTION__,
6997428d7b3dSmrg	     !RegionNil(DamageRegion(sna->mode.shadow_damage)),
6998428d7b3dSmrg	     sna->mode.flip_active,
6999428d7b3dSmrg	     sna->mode.dirty));
7000428d7b3dSmrg	assert((sna->flags & SNA_IS_HOSTED) == 0);
7001428d7b3dSmrg	assert(sna->mode.shadow_active);
7002428d7b3dSmrg
7003428d7b3dSmrg	if (sna->mode.dirty)
7004428d7b3dSmrg		return;
7005428d7b3dSmrg
7006428d7b3dSmrg	region = DamageRegion(sna->mode.shadow_damage);
7007428d7b3dSmrg	if (RegionNil(region))
7008428d7b3dSmrg		return;
7009428d7b3dSmrg
7010428d7b3dSmrg	DBG(("%s: damage: %dx(%d, %d), (%d, %d)\n",
7011428d7b3dSmrg	     __FUNCTION__, region_num_rects(region),
7012428d7b3dSmrg	     region->extents.x1, region->extents.y1,
7013428d7b3dSmrg	     region->extents.x2, region->extents.y2));
7014428d7b3dSmrg
7015428d7b3dSmrg	if (sna->mode.flip_active) {
7016428d7b3dSmrg		DamagePtr damage;
7017428d7b3dSmrg
7018428d7b3dSmrg		damage = sna->mode.shadow_damage;
7019428d7b3dSmrg		sna->mode.shadow_damage = NULL;
7020428d7b3dSmrg
7021428d7b3dSmrg		while (sna->mode.flip_active && sna_mode_wakeup(sna))
7022428d7b3dSmrg			;
7023428d7b3dSmrg
7024428d7b3dSmrg		sna->mode.shadow_damage = damage;
7025428d7b3dSmrg	}
7026428d7b3dSmrg
7027428d7b3dSmrg	if (sna->mode.flip_active)
7028428d7b3dSmrg		return;
7029428d7b3dSmrg
7030428d7b3dSmrg	if (wedged(sna) || !sna_pixmap_move_to_gpu(sna->front, MOVE_READ | MOVE_ASYNC_HINT | __MOVE_SCANOUT)) {
7031428d7b3dSmrg		DBG(("%s: forcing scanout update using the CPU\n", __FUNCTION__));
7032428d7b3dSmrg		if (!sna_pixmap_move_to_cpu(sna->front, MOVE_READ))
7033428d7b3dSmrg			return;
7034428d7b3dSmrg
7035428d7b3dSmrg		for (i = 0; i < sna->mode.num_real_crtc; i++) {
7036428d7b3dSmrg			xf86CrtcPtr crtc = config->crtc[i];
7037428d7b3dSmrg			struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
7038428d7b3dSmrg			RegionRec damage;
7039428d7b3dSmrg
7040428d7b3dSmrg			assert(sna_crtc != NULL);
7041428d7b3dSmrg			if (!sna_crtc->shadow)
7042428d7b3dSmrg				continue;
7043428d7b3dSmrg
7044428d7b3dSmrg			assert(crtc->enabled);
7045428d7b3dSmrg			assert(sna_crtc->transform || sna->flags & SNA_TEAR_FREE);
7046428d7b3dSmrg
7047428d7b3dSmrg			damage.extents = crtc->bounds;
7048428d7b3dSmrg			damage.data = NULL;
7049428d7b3dSmrg			RegionIntersect(&damage, &damage, region);
7050428d7b3dSmrg			if (!box_empty(&damage.extents)) {
7051428d7b3dSmrg				struct kgem_bo *bo = NULL;
7052428d7b3dSmrg
7053428d7b3dSmrg				DBG(("%s: fallback intersects pipe=%d [(%d, %d), (%d, %d)]\n",
7054428d7b3dSmrg				     __FUNCTION__, sna_crtc->pipe,
7055428d7b3dSmrg				     damage.extents.x1, damage.extents.y1,
7056428d7b3dSmrg				     damage.extents.x2, damage.extents.y2));
7057428d7b3dSmrg
7058428d7b3dSmrg				if (sna->flags & SNA_TEAR_FREE) {
7059428d7b3dSmrg					RegionRec new_damage;
7060428d7b3dSmrg
7061428d7b3dSmrg					RegionNull(&new_damage);
7062428d7b3dSmrg					RegionCopy(&new_damage, &damage);
7063428d7b3dSmrg
7064428d7b3dSmrg					bo = sna_crtc->client_bo;
7065428d7b3dSmrg					if (bo == NULL) {
7066428d7b3dSmrg						damage.extents = crtc->bounds;
7067428d7b3dSmrg						damage.data = NULL;
7068428d7b3dSmrg						bo = kgem_create_2d(&sna->kgem,
7069428d7b3dSmrg								crtc->mode.HDisplay,
7070428d7b3dSmrg								crtc->mode.VDisplay,
7071428d7b3dSmrg								crtc->scrn->bitsPerPixel,
7072428d7b3dSmrg								sna_crtc->bo->tiling,
7073428d7b3dSmrg								CREATE_SCANOUT);
7074428d7b3dSmrg					} else
7075428d7b3dSmrg						RegionUnion(&damage, &damage, &sna_crtc->client_damage);
7076428d7b3dSmrg
7077428d7b3dSmrg					DBG(("%s: TearFree fallback, shadow handle=%d, crtc handle=%d\n", __FUNCTION__, bo->handle, sna_crtc->bo->handle));
7078428d7b3dSmrg
7079428d7b3dSmrg					sna_crtc->client_damage = new_damage;
7080428d7b3dSmrg				}
7081428d7b3dSmrg
7082428d7b3dSmrg				if (bo == NULL)
7083428d7b3dSmrg					bo = sna_crtc->bo;
7084428d7b3dSmrg				sna_crtc_redisplay__fallback(crtc, &damage, bo);
7085428d7b3dSmrg
7086428d7b3dSmrg				if (bo != sna_crtc->bo) {
7087428d7b3dSmrg					struct drm_mode_crtc_page_flip arg;
7088428d7b3dSmrg
7089428d7b3dSmrg					arg.crtc_id = sna_crtc->id;
7090428d7b3dSmrg					arg.fb_id = get_fb(sna, bo,
7091428d7b3dSmrg							   crtc->mode.HDisplay,
7092428d7b3dSmrg							   crtc->mode.VDisplay);
7093428d7b3dSmrg
7094428d7b3dSmrg					arg.user_data = (uintptr_t)sna_crtc;
7095428d7b3dSmrg					arg.flags = DRM_MODE_PAGE_FLIP_EVENT;
7096428d7b3dSmrg					arg.reserved = 0;
7097428d7b3dSmrg
7098428d7b3dSmrg					if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) {
7099428d7b3dSmrg						if (sna_crtc_flip(sna, sna_crtc, bo, 0, 0)) {
7100428d7b3dSmrg							assert(sna_crtc->bo->active_scanout);
7101428d7b3dSmrg							assert(sna_crtc->bo->refcnt >= sna_crtc->bo->active_scanout);
7102428d7b3dSmrg							sna_crtc->bo->active_scanout--;
7103428d7b3dSmrg							kgem_bo_destroy(&sna->kgem, sna_crtc->bo);
7104428d7b3dSmrg
7105428d7b3dSmrg							sna_crtc->bo = bo;
7106428d7b3dSmrg							sna_crtc->bo->active_scanout++;
7107428d7b3dSmrg							sna_crtc->client_bo = NULL;
7108428d7b3dSmrg						} else {
7109428d7b3dSmrg							DBG(("%s: flip [fb=%d] on crtc %d [%d, pipe=%d] failed - %d\n",
7110428d7b3dSmrg							     __FUNCTION__, arg.fb_id, i, sna_crtc->id, sna_crtc->pipe, errno));
7111428d7b3dSmrg							xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR,
7112428d7b3dSmrg								   "Page flipping failed, disabling TearFree\n");
7113428d7b3dSmrg							sna->flags &= ~SNA_TEAR_FREE;
7114428d7b3dSmrg
7115428d7b3dSmrg							damage.extents = crtc->bounds;
7116428d7b3dSmrg							damage.data = NULL;
7117428d7b3dSmrg							sna_crtc_redisplay__fallback(crtc, &damage, sna_crtc->bo);
7118428d7b3dSmrg
7119428d7b3dSmrg							kgem_bo_destroy(&sna->kgem, bo);
7120428d7b3dSmrg							sna_crtc->client_bo = NULL;
7121428d7b3dSmrg						}
7122428d7b3dSmrg					} else {
7123428d7b3dSmrg						sna->mode.flip_active++;
7124428d7b3dSmrg
7125428d7b3dSmrg						assert(sna_crtc->flip_bo == NULL);
7126428d7b3dSmrg						sna_crtc->flip_handler = shadow_flip_handler;
7127428d7b3dSmrg						sna_crtc->flip_data = sna;
7128428d7b3dSmrg						sna_crtc->flip_bo = bo;
7129428d7b3dSmrg						sna_crtc->flip_bo->active_scanout++;
7130428d7b3dSmrg						sna_crtc->flip_serial = sna_crtc->mode_serial;
7131428d7b3dSmrg
7132428d7b3dSmrg						sna_crtc->client_bo = kgem_bo_reference(sna_crtc->bo);
7133428d7b3dSmrg					}
7134428d7b3dSmrg				}
7135428d7b3dSmrg			}
7136428d7b3dSmrg			RegionUninit(&damage);
7137428d7b3dSmrg
7138428d7b3dSmrg			if (sna_crtc->slave_damage)
7139428d7b3dSmrg				DamageEmpty(sna_crtc->slave_damage);
7140428d7b3dSmrg		}
7141428d7b3dSmrg
7142428d7b3dSmrg		RegionEmpty(region);
7143428d7b3dSmrg		return;
7144428d7b3dSmrg	}
7145428d7b3dSmrg
7146428d7b3dSmrg	{
7147428d7b3dSmrg		struct sna_pixmap *priv;
7148428d7b3dSmrg
7149428d7b3dSmrg		priv = sna_pixmap(sna->front);
7150428d7b3dSmrg		assert(priv != NULL);
7151428d7b3dSmrg
7152428d7b3dSmrg		if (priv->move_to_gpu) {
7153428d7b3dSmrg			if (priv->move_to_gpu == wait_for_shadow &&
7154428d7b3dSmrg			    !sna->mode.shadow_dirty) {
7155428d7b3dSmrg				/* No damage written to new scanout
7156428d7b3dSmrg				 * (backbuffer), ignore redisplay request
7157428d7b3dSmrg				 * and continue with the current intact
7158428d7b3dSmrg				 * scanout (frontbuffer).
7159428d7b3dSmrg				 */
7160428d7b3dSmrg				DBG(("%s: shadow idle, skipping update\n", __FUNCTION__));
7161428d7b3dSmrg				RegionEmpty(region);
7162428d7b3dSmrg				return;
7163428d7b3dSmrg			}
7164428d7b3dSmrg
7165428d7b3dSmrg			(void)priv->move_to_gpu(sna, priv, 0);
7166428d7b3dSmrg		}
7167428d7b3dSmrg
7168428d7b3dSmrg		assert(priv->move_to_gpu == NULL);
7169428d7b3dSmrg	}
7170428d7b3dSmrg
7171428d7b3dSmrg	for (i = 0; i < sna->mode.num_real_crtc; i++) {
7172428d7b3dSmrg		xf86CrtcPtr crtc = config->crtc[i];
7173428d7b3dSmrg		struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
7174428d7b3dSmrg		RegionRec damage;
7175428d7b3dSmrg
7176428d7b3dSmrg		assert(sna_crtc != NULL);
7177428d7b3dSmrg		DBG(("%s: crtc[%d] transformed? %d\n",
7178428d7b3dSmrg		     __FUNCTION__, i, sna_crtc->transform));
7179428d7b3dSmrg
7180428d7b3dSmrg		if (!sna_crtc->transform)
7181428d7b3dSmrg			continue;
7182428d7b3dSmrg
7183428d7b3dSmrg		assert(crtc->enabled);
7184428d7b3dSmrg		assert(sna_crtc->bo);
7185428d7b3dSmrg
7186428d7b3dSmrg		damage.extents = crtc->bounds;
7187428d7b3dSmrg		damage.data = NULL;
7188428d7b3dSmrg
7189428d7b3dSmrg		RegionIntersect(&damage, &damage, region);
7190428d7b3dSmrg		DBG(("%s: crtc[%d] damage? %d[%d]: %dx[(%d, %d), (%d, %d)]\n",
7191428d7b3dSmrg		     __FUNCTION__, i,
7192428d7b3dSmrg		     !box_empty(&damage.extents), RegionNotEmpty(&damage),
7193428d7b3dSmrg		     region_num_rects(&damage),
7194428d7b3dSmrg		     damage.extents.x1, damage.extents.y1,
7195428d7b3dSmrg		     damage.extents.x2, damage.extents.y2));
7196428d7b3dSmrg		if (!box_empty(&damage.extents)) {
7197428d7b3dSmrg			if (sna->flags & SNA_TEAR_FREE) {
7198428d7b3dSmrg				struct drm_mode_crtc_page_flip arg;
7199428d7b3dSmrg				struct kgem_bo *bo;
7200428d7b3dSmrg
7201428d7b3dSmrg				RegionUninit(&damage);
7202428d7b3dSmrg				damage.extents = crtc->bounds;
7203428d7b3dSmrg				damage.data = NULL;
7204428d7b3dSmrg
7205428d7b3dSmrg				bo = sna_crtc->client_bo;
7206428d7b3dSmrg				if (bo == NULL)
7207428d7b3dSmrg					bo = kgem_create_2d(&sna->kgem,
7208428d7b3dSmrg							    crtc->mode.HDisplay,
7209428d7b3dSmrg							    crtc->mode.VDisplay,
7210428d7b3dSmrg							    crtc->scrn->bitsPerPixel,
7211428d7b3dSmrg							    sna_crtc->bo->tiling,
7212428d7b3dSmrg							    CREATE_SCANOUT);
7213428d7b3dSmrg				if (bo == NULL)
7214428d7b3dSmrg					goto disable1;
7215428d7b3dSmrg
7216428d7b3dSmrg				sna_crtc_redisplay(crtc, &damage, bo);
7217428d7b3dSmrg				kgem_bo_submit(&sna->kgem, bo);
7218428d7b3dSmrg
7219428d7b3dSmrg				arg.crtc_id = sna_crtc->id;
7220428d7b3dSmrg				arg.fb_id = get_fb(sna, bo,
7221428d7b3dSmrg						   crtc->mode.HDisplay,
7222428d7b3dSmrg						   crtc->mode.VDisplay);
7223428d7b3dSmrg				if (arg.fb_id == 0)
7224428d7b3dSmrg					goto disable1;
7225428d7b3dSmrg
7226428d7b3dSmrg				arg.user_data = (uintptr_t)sna_crtc;
7227428d7b3dSmrg				arg.flags = DRM_MODE_PAGE_FLIP_EVENT;
7228428d7b3dSmrg				arg.reserved = 0;
7229428d7b3dSmrg
7230428d7b3dSmrg				if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) {
7231428d7b3dSmrg					if (sna_crtc_flip(sna, sna_crtc, bo, 0, 0)) {
7232428d7b3dSmrg						assert(sna_crtc->bo->active_scanout);
7233428d7b3dSmrg						assert(sna_crtc->bo->refcnt >= sna_crtc->bo->active_scanout);
7234428d7b3dSmrg						sna_crtc->bo->active_scanout--;
7235428d7b3dSmrg						kgem_bo_destroy(&sna->kgem, sna_crtc->bo);
7236428d7b3dSmrg
7237428d7b3dSmrg						sna_crtc->bo = kgem_bo_reference(bo);
7238428d7b3dSmrg						sna_crtc->bo->active_scanout++;
7239428d7b3dSmrg						sna_crtc->client_bo = kgem_bo_reference(bo);
7240428d7b3dSmrg					} else {
7241428d7b3dSmrg						BoxRec box;
7242428d7b3dSmrg						DrawableRec tmp;
7243428d7b3dSmrg
7244428d7b3dSmrg						DBG(("%s: flip [fb=%d] on crtc %d [%d, pipe=%d] failed - %d\n",
7245428d7b3dSmrg						     __FUNCTION__, arg.fb_id, i, sna_crtc->id, sna_crtc->pipe, errno));
7246428d7b3dSmrg						xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR,
7247428d7b3dSmrg							   "Page flipping failed, disabling TearFree\n");
7248428d7b3dSmrg						sna->flags &= ~SNA_TEAR_FREE;
7249428d7b3dSmrg
7250428d7b3dSmrgdisable1:
7251428d7b3dSmrg						box.x1 = 0;
7252428d7b3dSmrg						box.y1 = 0;
7253428d7b3dSmrg						tmp.width = box.x2 = crtc->mode.HDisplay;
7254428d7b3dSmrg						tmp.height = box.y2 = crtc->mode.VDisplay;
7255428d7b3dSmrg						tmp.depth = sna->front->drawable.depth;
7256428d7b3dSmrg						tmp.bitsPerPixel = sna->front->drawable.bitsPerPixel;
7257428d7b3dSmrg
7258428d7b3dSmrg						if (!sna->render.copy_boxes(sna, GXcopy,
7259428d7b3dSmrg									    &sna->front->drawable, bo, 0, 0,
7260428d7b3dSmrg									    &tmp, sna_crtc->bo, 0, 0,
7261428d7b3dSmrg									    &box, 1, COPY_LAST)) {
7262428d7b3dSmrg							xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
7263428d7b3dSmrg								   "%s: page flipping failed, disabling CRTC:%d (pipe=%d)\n",
7264428d7b3dSmrg								   __FUNCTION__, sna_crtc->id, sna_crtc->pipe);
7265428d7b3dSmrg							sna_crtc_disable(crtc);
7266428d7b3dSmrg						}
7267428d7b3dSmrg
7268428d7b3dSmrg						kgem_bo_destroy(&sna->kgem, bo);
7269428d7b3dSmrg						sna_crtc->client_bo = NULL;
7270428d7b3dSmrg					}
7271428d7b3dSmrg					continue;
7272428d7b3dSmrg				}
7273428d7b3dSmrg				sna->mode.flip_active++;
7274428d7b3dSmrg
7275428d7b3dSmrg				assert(sna_crtc->flip_bo == NULL);
7276428d7b3dSmrg				sna_crtc->flip_handler = shadow_flip_handler;
7277428d7b3dSmrg				sna_crtc->flip_data = sna;
7278428d7b3dSmrg				sna_crtc->flip_bo = bo;
7279428d7b3dSmrg				sna_crtc->flip_bo->active_scanout++;
7280428d7b3dSmrg				sna_crtc->flip_serial = sna_crtc->mode_serial;
7281428d7b3dSmrg				sna_crtc->flip_pending = true;
7282428d7b3dSmrg
7283428d7b3dSmrg				sna_crtc->client_bo = kgem_bo_reference(sna_crtc->bo);
7284428d7b3dSmrg			} else {
7285428d7b3dSmrg				sna_crtc_redisplay(crtc, &damage, sna_crtc->bo);
7286428d7b3dSmrg				kgem_scanout_flush(&sna->kgem, sna_crtc->bo);
7287428d7b3dSmrg			}
7288428d7b3dSmrg		}
7289428d7b3dSmrg		RegionUninit(&damage);
7290428d7b3dSmrg
7291428d7b3dSmrg		if (sna_crtc->slave_damage)
7292428d7b3dSmrg			DamageEmpty(sna_crtc->slave_damage);
7293428d7b3dSmrg	}
7294428d7b3dSmrg
7295428d7b3dSmrg	if (sna->mode.shadow) {
7296428d7b3dSmrg		struct kgem_bo *new = __sna_pixmap_get_bo(sna->front);
7297428d7b3dSmrg		struct kgem_bo *old = sna->mode.shadow;
7298428d7b3dSmrg		struct drm_mode_crtc_page_flip arg;
7299428d7b3dSmrg		uint32_t fb = 0;
7300428d7b3dSmrg
7301428d7b3dSmrg		DBG(("%s: flipping TearFree outputs, current scanout handle=%d [active?=%d], new handle=%d [active=%d]\n",
7302428d7b3dSmrg		     __FUNCTION__, old->handle, old->active_scanout, new->handle, new->active_scanout));
7303428d7b3dSmrg
7304428d7b3dSmrg		assert(new != old);
7305428d7b3dSmrg		assert(new->refcnt);
7306428d7b3dSmrg
7307428d7b3dSmrg		arg.flags = DRM_MODE_PAGE_FLIP_EVENT;
7308428d7b3dSmrg		arg.reserved = 0;
7309428d7b3dSmrg
7310428d7b3dSmrg		kgem_bo_submit(&sna->kgem, new);
7311428d7b3dSmrg
7312428d7b3dSmrg		for (i = 0; i < sna->mode.num_real_crtc; i++) {
7313428d7b3dSmrg			struct sna_crtc *crtc = config->crtc[i]->driver_private;
7314428d7b3dSmrg			struct kgem_bo *flip_bo;
7315428d7b3dSmrg			int x, y;
7316428d7b3dSmrg
7317428d7b3dSmrg			assert(crtc != NULL);
7318428d7b3dSmrg			DBG(("%s: crtc %d [%d, pipe=%d] active? %d, transformed? %d\n",
7319428d7b3dSmrg			     __FUNCTION__, i, crtc->id, crtc->pipe, crtc->bo ? crtc->bo->handle : 0, crtc->transform));
7320428d7b3dSmrg			if (crtc->bo == NULL || crtc->transform)
7321428d7b3dSmrg				continue;
7322428d7b3dSmrg
7323428d7b3dSmrg			assert(config->crtc[i]->enabled);
7324428d7b3dSmrg			assert(crtc->dpms_mode <= DPMSModeOn);
7325428d7b3dSmrg			assert(crtc->flip_bo == NULL);
7326428d7b3dSmrg
7327428d7b3dSmrg			arg.crtc_id = crtc->id;
7328428d7b3dSmrg			arg.user_data = (uintptr_t)crtc;
7329428d7b3dSmrg
7330428d7b3dSmrg			if (crtc->client_bo) {
7331428d7b3dSmrg				DBG(("%s: apply shadow override bo for CRTC:%d on pipe=%d, handle=%d\n",
7332428d7b3dSmrg				     __FUNCTION__, crtc->id, crtc->pipe, crtc->client_bo->handle));
7333428d7b3dSmrg				arg.fb_id = get_fb(sna, crtc->client_bo,
7334428d7b3dSmrg						   crtc->base->mode.HDisplay,
7335428d7b3dSmrg						   crtc->base->mode.VDisplay);
7336428d7b3dSmrg				assert(arg.fb_id != fb);
7337428d7b3dSmrg				flip_bo = crtc->client_bo;
7338428d7b3dSmrg				x = y = 0;
7339428d7b3dSmrg			} else {
7340428d7b3dSmrg				if (fb == 0)
7341428d7b3dSmrg					fb = get_fb(sna, new, sna->scrn->virtualX, sna->scrn->virtualY);
7342428d7b3dSmrg				if (fb == 0) {
7343428d7b3dSmrgfixup_shadow:
7344428d7b3dSmrg					if (sna_pixmap_move_to_gpu(sna->front, MOVE_READ | MOVE_ASYNC_HINT)) {
7345428d7b3dSmrg						BoxRec box;
7346428d7b3dSmrg
7347428d7b3dSmrg						box.x1 = 0;
7348428d7b3dSmrg						box.y1 = 0;
7349428d7b3dSmrg						box.x2 = sna->scrn->virtualX;
7350428d7b3dSmrg						box.y2 = sna->scrn->virtualY;
7351428d7b3dSmrg						if (sna->render.copy_boxes(sna, GXcopy,
7352428d7b3dSmrg									   &sna->front->drawable, __sna_pixmap_get_bo(sna->front), 0, 0,
7353428d7b3dSmrg									   &sna->front->drawable, old, 0, 0,
7354428d7b3dSmrg									   &box, 1, COPY_LAST)) {
7355428d7b3dSmrg							kgem_submit(&sna->kgem);
7356428d7b3dSmrg							RegionEmpty(region);
7357428d7b3dSmrg						}
7358428d7b3dSmrg					}
7359428d7b3dSmrg
7360428d7b3dSmrg					return;
7361428d7b3dSmrg				}
7362428d7b3dSmrg
7363428d7b3dSmrg				arg.fb_id = fb;
7364428d7b3dSmrg				flip_bo = new;
7365428d7b3dSmrg				x = crtc->base->x;
7366428d7b3dSmrg				y = crtc->base->y;
7367428d7b3dSmrg			}
7368428d7b3dSmrg
7369428d7b3dSmrg			if (crtc->bo == flip_bo)
7370428d7b3dSmrg				continue;
7371428d7b3dSmrg
7372428d7b3dSmrg			if (flip_bo->pitch != crtc->bo->pitch || (y << 16 | x)  != crtc->offset) {
7373428d7b3dSmrg				DBG(("%s: changing pitch (new %d =?= old %d) or offset (new %x =?= old %x)\n",
7374428d7b3dSmrg				     __FUNCTION__,
7375428d7b3dSmrg				     flip_bo->pitch, crtc->bo->pitch,
7376428d7b3dSmrg				     y << 16 | x, crtc->offset));
7377428d7b3dSmrgfixup_flip:
7378428d7b3dSmrg				if (sna_crtc_flip(sna, crtc, flip_bo, x, y)) {
7379428d7b3dSmrg					assert(flip_bo != crtc->bo);
7380428d7b3dSmrg					assert(crtc->bo->active_scanout);
7381428d7b3dSmrg					assert(crtc->bo->refcnt >= crtc->bo->active_scanout);
7382428d7b3dSmrg					crtc->bo->active_scanout--;
7383428d7b3dSmrg					kgem_bo_destroy(&sna->kgem, crtc->bo);
7384428d7b3dSmrg
7385428d7b3dSmrg					if (crtc->shadow_bo) {
7386428d7b3dSmrg						kgem_bo_destroy(&sna->kgem, crtc->shadow_bo);
7387428d7b3dSmrg						crtc->shadow_bo = NULL;
7388428d7b3dSmrg					}
7389428d7b3dSmrg
7390428d7b3dSmrg					crtc->bo = kgem_bo_reference(flip_bo);
7391428d7b3dSmrg					crtc->bo->active_scanout++;
7392428d7b3dSmrg				} else {
7393428d7b3dSmrg					xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR,
7394428d7b3dSmrg						   "Failed to prepare CRTC for page flipping, disabling TearFree\n");
7395428d7b3dSmrg					sna->flags &= ~SNA_TEAR_FREE;
7396428d7b3dSmrg
7397428d7b3dSmrg					if (sna->mode.flip_active == 0) {
7398428d7b3dSmrg						DBG(("%s: abandoning flip attempt\n", __FUNCTION__));
7399428d7b3dSmrg						goto fixup_shadow;
7400428d7b3dSmrg					}
7401428d7b3dSmrg
7402428d7b3dSmrg					xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR,
7403428d7b3dSmrg						   "%s: page flipping failed, disabling CRTC:%d (pipe=%d)\n",
7404428d7b3dSmrg						   __FUNCTION__, crtc->id, crtc->pipe);
7405428d7b3dSmrg					sna_crtc_disable(crtc->base);
7406428d7b3dSmrg				}
7407428d7b3dSmrg				continue;
7408428d7b3dSmrg			}
7409428d7b3dSmrg
7410428d7b3dSmrg			if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) {
7411428d7b3dSmrg				ERR(("%s: flip [fb=%d] on crtc %d [%d, pipe=%d] failed - %d\n",
7412428d7b3dSmrg				     __FUNCTION__, arg.fb_id, i, crtc->id, crtc->pipe, errno));
7413428d7b3dSmrg				goto fixup_flip;
7414428d7b3dSmrg			}
7415428d7b3dSmrg			sna->mode.flip_active++;
7416428d7b3dSmrg
7417428d7b3dSmrg			assert(crtc->flip_bo == NULL);
7418428d7b3dSmrg			crtc->flip_handler = shadow_flip_handler;
7419428d7b3dSmrg			crtc->flip_data = sna;
7420428d7b3dSmrg			crtc->flip_bo = kgem_bo_reference(flip_bo);
7421428d7b3dSmrg			crtc->flip_bo->active_scanout++;
7422428d7b3dSmrg			crtc->flip_serial = crtc->mode_serial;
7423428d7b3dSmrg			crtc->flip_pending = true;
7424428d7b3dSmrg
7425428d7b3dSmrg			{
7426428d7b3dSmrg				struct drm_i915_gem_busy busy = { flip_bo->handle };
7427428d7b3dSmrg				if (drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GEM_BUSY, &busy) == 0) {
7428428d7b3dSmrg					if (busy.busy) {
7429428d7b3dSmrg						int mode = KGEM_RENDER;
7430428d7b3dSmrg						if (busy.busy & (0xfffe << 16))
7431428d7b3dSmrg							mode = KGEM_BLT;
7432428d7b3dSmrg						DBG(("%s: marking flip bo as busy [%x -> mode=%d]\n", __FUNCTION__, busy.busy, mode));
7433428d7b3dSmrg						kgem_bo_mark_busy(&sna->kgem, flip_bo, mode);
7434428d7b3dSmrg					} else
7435428d7b3dSmrg						__kgem_bo_clear_busy(flip_bo);
7436428d7b3dSmrg				}
7437428d7b3dSmrg			}
7438428d7b3dSmrg		}
7439428d7b3dSmrg
7440428d7b3dSmrg		DBG(("%s: flipped %d outputs, shadow active? %d\n",
7441428d7b3dSmrg		     __FUNCTION__,
7442428d7b3dSmrg		     sna->mode.flip_active,
7443428d7b3dSmrg		     sna->mode.shadow ? sna->mode.shadow->handle : 0));
7444428d7b3dSmrg
7445428d7b3dSmrg		if (sna->mode.flip_active) {
7446428d7b3dSmrg			assert(old == sna->mode.shadow);
7447428d7b3dSmrg			assert(old->refcnt >= 1);
7448428d7b3dSmrg			set_shadow(sna, region);
7449428d7b3dSmrg		}
7450428d7b3dSmrg	} else
7451428d7b3dSmrg		kgem_submit(&sna->kgem);
7452428d7b3dSmrg
7453428d7b3dSmrg	RegionEmpty(region);
7454428d7b3dSmrg}
7455428d7b3dSmrg
7456428d7b3dSmrgint sna_mode_wakeup(struct sna *sna)
7457428d7b3dSmrg{
7458428d7b3dSmrg	char buffer[1024];
7459428d7b3dSmrg	int len, i;
7460428d7b3dSmrg	int ret = 0;
7461428d7b3dSmrg
7462428d7b3dSmrgagain:
7463428d7b3dSmrg	/* In order to workaround a kernel bug in not honouring O_NONBLOCK,
7464428d7b3dSmrg	 * check that the fd is readable before attempting to read the next
7465428d7b3dSmrg	 * event from drm.
7466428d7b3dSmrg	 */
7467428d7b3dSmrg	if (!event_pending(sna->kgem.fd))
7468428d7b3dSmrg		return ret;
7469428d7b3dSmrg
7470428d7b3dSmrg	/* The DRM read semantics guarantees that we always get only
7471428d7b3dSmrg	 * complete events.
7472428d7b3dSmrg	 */
7473428d7b3dSmrg	len = read(sna->kgem.fd, buffer, sizeof (buffer));
7474428d7b3dSmrg	if (len < (int)sizeof(struct drm_event))
7475428d7b3dSmrg		return ret;
7476428d7b3dSmrg
7477428d7b3dSmrg	/* Note that we cannot rely on the passed in struct sna matching
7478428d7b3dSmrg	 * the struct sna used for the vblank event (in case it was submitted
7479428d7b3dSmrg	 * by a different ZaphodHead). When processing the event, we must
7480428d7b3dSmrg	 * ensure that we only use the pointer passed along with the event.
7481428d7b3dSmrg	 */
7482428d7b3dSmrg
7483428d7b3dSmrg	DBG(("%s: len=%d\n", __FUNCTION__, len));
7484428d7b3dSmrg
7485428d7b3dSmrg	i = 0;
7486428d7b3dSmrg	while (i < len) {
7487428d7b3dSmrg		struct drm_event *e = (struct drm_event *)&buffer[i];
7488428d7b3dSmrg		switch (e->type) {
7489428d7b3dSmrg		case DRM_EVENT_VBLANK:
7490428d7b3dSmrg			if (((uintptr_t)((struct drm_event_vblank *)e)->user_data) & 2)
7491428d7b3dSmrg				sna_present_vblank_handler((struct drm_event_vblank *)e);
7492428d7b3dSmrg			else
7493428d7b3dSmrg				sna_dri2_vblank_handler((struct drm_event_vblank *)e);
7494428d7b3dSmrg			break;
7495428d7b3dSmrg		case DRM_EVENT_FLIP_COMPLETE:
7496428d7b3dSmrg			{
7497428d7b3dSmrg				struct drm_event_vblank *vbl = (struct drm_event_vblank *)e;
7498428d7b3dSmrg				struct sna_crtc *crtc = (void *)(uintptr_t)vbl->user_data;
7499428d7b3dSmrg
7500428d7b3dSmrg				/* Beware Zaphod! */
7501428d7b3dSmrg				sna = to_sna(crtc->base->scrn);
7502428d7b3dSmrg
7503428d7b3dSmrg				crtc->swap.tv_sec = vbl->tv_sec;
7504428d7b3dSmrg				crtc->swap.tv_usec = vbl->tv_usec;
7505428d7b3dSmrg				crtc->swap.msc = msc64(crtc, vbl->sequence);
7506428d7b3dSmrg				crtc->flip_pending = false;
7507428d7b3dSmrg
7508428d7b3dSmrg				assert(crtc->flip_bo);
7509428d7b3dSmrg				assert(crtc->flip_bo->active_scanout);
7510428d7b3dSmrg				assert(crtc->flip_bo->refcnt >= crtc->flip_bo->active_scanout);
7511428d7b3dSmrg
7512428d7b3dSmrg				if (crtc->flip_serial == crtc->mode_serial) {
7513428d7b3dSmrg					DBG(("%s: removing handle=%d from scanout, installing handle=%d\n",
7514428d7b3dSmrg					     __FUNCTION__, crtc->bo->handle, crtc->flip_bo->handle));
7515428d7b3dSmrg					assert(crtc->bo->active_scanout);
7516428d7b3dSmrg					assert(crtc->bo->refcnt >= crtc->bo->active_scanout);
7517428d7b3dSmrg					crtc->bo->active_scanout--;
7518428d7b3dSmrg					kgem_bo_destroy(&sna->kgem, crtc->bo);
7519428d7b3dSmrg
7520428d7b3dSmrg					if (crtc->shadow_bo) {
7521428d7b3dSmrg						kgem_bo_destroy(&sna->kgem, crtc->shadow_bo);
7522428d7b3dSmrg						crtc->shadow_bo = NULL;
7523428d7b3dSmrg					}
7524428d7b3dSmrg
7525428d7b3dSmrg					crtc->bo = crtc->flip_bo;
7526428d7b3dSmrg					crtc->flip_bo = NULL;
7527428d7b3dSmrg				} else {
7528428d7b3dSmrg					crtc->flip_bo->active_scanout--;
7529428d7b3dSmrg					kgem_bo_destroy(&sna->kgem, crtc->flip_bo);
7530428d7b3dSmrg					crtc->flip_bo = NULL;
7531428d7b3dSmrg				}
7532428d7b3dSmrg
7533428d7b3dSmrg				DBG(("%s: flip complete, pending? %d\n", __FUNCTION__, sna->mode.flip_active));
7534428d7b3dSmrg				assert(sna->mode.flip_active);
7535428d7b3dSmrg				if (--sna->mode.flip_active == 0)
7536428d7b3dSmrg					crtc->flip_handler(vbl, crtc->flip_data);
7537428d7b3dSmrg			}
7538428d7b3dSmrg			break;
7539428d7b3dSmrg		default:
7540428d7b3dSmrg			break;
7541428d7b3dSmrg		}
7542428d7b3dSmrg		i += e->length;
7543428d7b3dSmrg		ret++;
7544428d7b3dSmrg	}
7545428d7b3dSmrg
7546428d7b3dSmrg	goto again;
7547428d7b3dSmrg}
7548