drmmode_display.c revision 22d74663
1fda9279dSmrg/*
2fda9279dSmrg * Copyright © 2007 Red Hat, Inc.
3fda9279dSmrg * Copyright © 2008 Maarten Maathuis
4fda9279dSmrg *
5fda9279dSmrg *
6fda9279dSmrg * Permission is hereby granted, free of charge, to any person obtaining a
7fda9279dSmrg * copy of this software and associated documentation files (the "Software"),
8fda9279dSmrg * to deal in the Software without restriction, including without limitation
9fda9279dSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10fda9279dSmrg * and/or sell copies of the Software, and to permit persons to whom the
11fda9279dSmrg * Software is furnished to do so, subject to the following conditions:
12fda9279dSmrg *
13fda9279dSmrg * The above copyright notice and this permission notice (including the next
14fda9279dSmrg * paragraph) shall be included in all copies or substantial portions of the
15fda9279dSmrg * Software.
16fda9279dSmrg *
17fda9279dSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18fda9279dSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19fda9279dSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20fda9279dSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21fda9279dSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22fda9279dSmrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23fda9279dSmrg * SOFTWARE.
24fda9279dSmrg *
25fda9279dSmrg * Authors:
26fda9279dSmrg *    Dave Airlie <airlied@redhat.com>
27fda9279dSmrg *
28fda9279dSmrg */
29fda9279dSmrg
30fda9279dSmrg#ifdef HAVE_CONFIG_H
31fda9279dSmrg#include "config.h"
32fda9279dSmrg#endif
33fda9279dSmrg
34479f40c1Smrg#include "xorg-config.h"
35fda9279dSmrg#include "xorgVersion.h"
36479f40c1Smrg#include "Xdefs.h"
3746edf8f1Smrg#include "X11/Xdefs.h"
38fda9279dSmrg
39fda9279dSmrg#include "nv_include.h"
40fda9279dSmrg#include "xf86drmMode.h"
41fda9279dSmrg#include "X11/Xatom.h"
42fda9279dSmrg
43fda9279dSmrg#include <sys/ioctl.h>
44fda9279dSmrg#ifdef HAVE_LIBUDEV
45fda9279dSmrg#include "libudev.h"
46fda9279dSmrg#endif
47fda9279dSmrg
48fda9279dSmrgstatic Bool drmmode_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height);
49fda9279dSmrgtypedef struct {
50fda9279dSmrg    int fd;
51fda9279dSmrg    uint32_t fb_id;
52fda9279dSmrg    int cpp;
53fda9279dSmrg    drmEventContext event_context;
54fda9279dSmrg#ifdef HAVE_LIBUDEV
55fda9279dSmrg    struct udev_monitor *uevent_monitor;
56fda9279dSmrg#endif
57fda9279dSmrg} drmmode_rec, *drmmode_ptr;
58fda9279dSmrg
59fda9279dSmrgtypedef struct {
60fda9279dSmrg    drmmode_ptr drmmode;
61fda9279dSmrg    drmModeCrtcPtr mode_crtc;
62fda9279dSmrg    int hw_crtc_index;
63fda9279dSmrg    struct nouveau_bo *cursor;
64fda9279dSmrg    struct nouveau_bo *rotate_bo;
65fda9279dSmrg    int rotate_pitch;
66fda9279dSmrg    PixmapPtr rotate_pixmap;
67fda9279dSmrg    uint32_t rotate_fb_id;
68fda9279dSmrg    Bool cursor_visible;
69fda9279dSmrg    int scanout_pixmap_x;
701090d90aSmrg    int dpms_mode;
71fda9279dSmrg} drmmode_crtc_private_rec, *drmmode_crtc_private_ptr;
72fda9279dSmrg
73fda9279dSmrgtypedef struct {
74fda9279dSmrg	drmModePropertyPtr mode_prop;
75fda9279dSmrg	int index; /* Index within the kernel-side property arrays for
76fda9279dSmrg		    * this connector. */
77fda9279dSmrg	int num_atoms; /* if range prop, num_atoms == 1; if enum prop,
78fda9279dSmrg			* num_atoms == num_enums + 1 */
79fda9279dSmrg	Atom *atoms;
80fda9279dSmrg} drmmode_prop_rec, *drmmode_prop_ptr;
81fda9279dSmrg
82fda9279dSmrgtypedef struct {
83fda9279dSmrg    drmmode_ptr drmmode;
84fda9279dSmrg    int output_id;
85fda9279dSmrg    drmModeConnectorPtr mode_output;
86fda9279dSmrg    drmModeEncoderPtr mode_encoder;
87fda9279dSmrg    drmModePropertyBlobPtr edid_blob;
8822d74663Smrg    drmModePropertyBlobPtr tile_blob;
89fda9279dSmrg    int num_props;
90fda9279dSmrg    drmmode_prop_ptr props;
91fda9279dSmrg} drmmode_output_private_rec, *drmmode_output_private_ptr;
92fda9279dSmrg
93fda9279dSmrgstatic void drmmode_output_dpms(xf86OutputPtr output, int mode);
94fda9279dSmrg
95fda9279dSmrgstatic drmmode_ptr
96fda9279dSmrgdrmmode_from_scrn(ScrnInfoPtr scrn)
97fda9279dSmrg{
98fda9279dSmrg	if (scrn) {
99fda9279dSmrg		xf86CrtcConfigPtr conf = XF86_CRTC_CONFIG_PTR(scrn);
100fda9279dSmrg		drmmode_crtc_private_ptr crtc = conf->crtc[0]->driver_private;
101fda9279dSmrg
102fda9279dSmrg		return crtc->drmmode;
103fda9279dSmrg	}
104fda9279dSmrg
105fda9279dSmrg	return NULL;
106fda9279dSmrg}
107fda9279dSmrg
108fda9279dSmrgstatic inline struct nouveau_pixmap *
109fda9279dSmrgdrmmode_pixmap(PixmapPtr ppix)
110fda9279dSmrg{
111fda9279dSmrg	return nouveau_pixmap(ppix);
112fda9279dSmrg}
113fda9279dSmrg
114fda9279dSmrgint
115fda9279dSmrgdrmmode_crtc(xf86CrtcPtr crtc)
116fda9279dSmrg{
117fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
118fda9279dSmrg	return drmmode_crtc->mode_crtc->crtc_id;
119fda9279dSmrg}
120fda9279dSmrg
1211090d90aSmrgBool
1221090d90aSmrgdrmmode_crtc_on(xf86CrtcPtr crtc)
1231090d90aSmrg{
1241090d90aSmrg    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1251090d90aSmrg
1261090d90aSmrg    return crtc->enabled && drmmode_crtc->dpms_mode == DPMSModeOn;
1271090d90aSmrg}
1281090d90aSmrg
129fda9279dSmrgint
130fda9279dSmrgdrmmode_head(xf86CrtcPtr crtc)
131fda9279dSmrg{
132fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
133fda9279dSmrg	return drmmode_crtc->hw_crtc_index;
134fda9279dSmrg}
135fda9279dSmrg
136fda9279dSmrgvoid
137fda9279dSmrgdrmmode_swap(ScrnInfoPtr scrn, uint32_t next, uint32_t *prev)
138fda9279dSmrg{
139fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
140fda9279dSmrg	*prev = drmmode->fb_id;
141fda9279dSmrg	drmmode->fb_id = next;
142fda9279dSmrg}
143fda9279dSmrg
144fda9279dSmrg#if !HAVE_XORG_LIST
145fda9279dSmrg#define xorg_list                       list
146fda9279dSmrg#define xorg_list_for_each_entry        list_for_each_entry
147fda9279dSmrg#define xorg_list_for_each_entry_safe   list_for_each_entry_safe
148fda9279dSmrg#define xorg_list_append                list_append
149fda9279dSmrg#define xorg_list_del                   list_del
150fda9279dSmrg#endif
151fda9279dSmrg
152fda9279dSmrgstruct drmmode_event {
153fda9279dSmrg	struct xorg_list head;
154fda9279dSmrg	drmmode_ptr drmmode;
155fda9279dSmrg	uint64_t name;
156fda9279dSmrg	void (*func)(void *, uint64_t, uint64_t, uint32_t);
157fda9279dSmrg};
158fda9279dSmrg
159fda9279dSmrgstatic struct xorg_list
160fda9279dSmrgdrmmode_events = {
161fda9279dSmrg	.next = &drmmode_events,
162fda9279dSmrg	.prev = &drmmode_events,
163fda9279dSmrg};
164fda9279dSmrg
165fda9279dSmrgstatic void
166fda9279dSmrgdrmmode_event_handler(int fd, unsigned int frame, unsigned int tv_sec,
167fda9279dSmrg		      unsigned int tv_usec, void *event_data)
168fda9279dSmrg{
169fda9279dSmrg	const uint64_t ust = (uint64_t)tv_sec * 1000000 + tv_usec;
170fda9279dSmrg	struct drmmode_event *e = event_data;
171fda9279dSmrg
172fda9279dSmrg	xorg_list_for_each_entry(e, &drmmode_events, head) {
173fda9279dSmrg		if (e == event_data) {
174fda9279dSmrg			xorg_list_del(&e->head);
175fda9279dSmrg			e->func((void *)(e + 1), e->name, ust, frame);
176fda9279dSmrg			free(e);
177fda9279dSmrg			break;
178fda9279dSmrg		}
179fda9279dSmrg	}
180fda9279dSmrg}
181fda9279dSmrg
182fda9279dSmrgvoid
183fda9279dSmrgdrmmode_event_abort(ScrnInfoPtr scrn, uint64_t name, bool pending)
184fda9279dSmrg{
185fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
186fda9279dSmrg	struct drmmode_event *e, *t;
187fda9279dSmrg
188fda9279dSmrg	xorg_list_for_each_entry_safe(e, t, &drmmode_events, head) {
189fda9279dSmrg		if (e->drmmode == drmmode && e->name == name) {
190fda9279dSmrg			xorg_list_del(&e->head);
191fda9279dSmrg			if (!pending)
192fda9279dSmrg				free(e);
193fda9279dSmrg			break;
194fda9279dSmrg		}
195fda9279dSmrg	}
196fda9279dSmrg}
197fda9279dSmrg
198fda9279dSmrgvoid *
199fda9279dSmrgdrmmode_event_queue(ScrnInfoPtr scrn, uint64_t name, unsigned size,
200fda9279dSmrg		    void (*func)(void *, uint64_t, uint64_t, uint32_t),
201fda9279dSmrg		    void **event_data)
202fda9279dSmrg{
203fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
204fda9279dSmrg	struct drmmode_event *e;
205fda9279dSmrg
206fda9279dSmrg	e = *event_data = calloc(1, sizeof(*e) + size);
207fda9279dSmrg	if (e) {
208fda9279dSmrg		e->drmmode = drmmode;
209fda9279dSmrg		e->name = name;
210fda9279dSmrg		e->func = func;
211fda9279dSmrg		xorg_list_append(&e->head, &drmmode_events);
212fda9279dSmrg		return (void *)(e + 1);
213fda9279dSmrg	}
214fda9279dSmrg
215fda9279dSmrg	return NULL;
216fda9279dSmrg}
217fda9279dSmrg
218fda9279dSmrgint
219fda9279dSmrgdrmmode_event_flush(ScrnInfoPtr scrn)
220fda9279dSmrg{
221fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
222fda9279dSmrg	return drmHandleEvent(drmmode->fd, &drmmode->event_context);
223fda9279dSmrg}
224fda9279dSmrg
225fda9279dSmrgvoid
226fda9279dSmrgdrmmode_event_fini(ScrnInfoPtr scrn)
227fda9279dSmrg{
228fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
229fda9279dSmrg	struct drmmode_event *e, *t;
230fda9279dSmrg
231fda9279dSmrg	xorg_list_for_each_entry_safe(e, t, &drmmode_events, head) {
232fda9279dSmrg		if (e->drmmode == drmmode) {
233fda9279dSmrg			xorg_list_del(&e->head);
234fda9279dSmrg			free(e);
235fda9279dSmrg		}
236fda9279dSmrg	}
237fda9279dSmrg}
238fda9279dSmrg
239fda9279dSmrgvoid
240fda9279dSmrgdrmmode_event_init(ScrnInfoPtr scrn)
241fda9279dSmrg{
242fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
243fda9279dSmrg	drmmode->event_context.version = DRM_EVENT_CONTEXT_VERSION;
244fda9279dSmrg	drmmode->event_context.vblank_handler = drmmode_event_handler;
245fda9279dSmrg	drmmode->event_context.page_flip_handler = drmmode_event_handler;
246fda9279dSmrg}
247fda9279dSmrg
248fda9279dSmrgstatic PixmapPtr
249fda9279dSmrgdrmmode_pixmap_wrap(ScreenPtr pScreen, int width, int height, int depth,
250fda9279dSmrg		    int bpp, int pitch, struct nouveau_bo *bo, void *data)
251fda9279dSmrg{
252fda9279dSmrg	NVPtr pNv = NVPTR(xf86ScreenToScrn(pScreen));
253fda9279dSmrg	PixmapPtr ppix;
254fda9279dSmrg
255fda9279dSmrg	if (pNv->AccelMethod > NONE)
256fda9279dSmrg		data = NULL;
257fda9279dSmrg
258fda9279dSmrg	ppix = pScreen->CreatePixmap(pScreen, 0, 0, depth, 0);
259fda9279dSmrg	if (!ppix)
260fda9279dSmrg		return NULL;
261fda9279dSmrg
262fda9279dSmrg	pScreen->ModifyPixmapHeader(ppix, width, height, depth, bpp,
263fda9279dSmrg				    pitch, data);
264fda9279dSmrg	if (pNv->AccelMethod > NONE)
265fda9279dSmrg		nouveau_bo_ref(bo, &drmmode_pixmap(ppix)->bo);
266fda9279dSmrg
267fda9279dSmrg	return ppix;
268fda9279dSmrg}
269fda9279dSmrg
270fda9279dSmrgstatic void
271fda9279dSmrgdrmmode_ConvertFromKMode(ScrnInfoPtr scrn, drmModeModeInfo *kmode,
272fda9279dSmrg			 DisplayModePtr	mode)
273fda9279dSmrg{
274fda9279dSmrg	memset(mode, 0, sizeof(DisplayModeRec));
275fda9279dSmrg	mode->status = MODE_OK;
276fda9279dSmrg
277fda9279dSmrg	mode->Clock = kmode->clock;
278fda9279dSmrg
279fda9279dSmrg	mode->HDisplay = kmode->hdisplay;
280fda9279dSmrg	mode->HSyncStart = kmode->hsync_start;
281fda9279dSmrg	mode->HSyncEnd = kmode->hsync_end;
282fda9279dSmrg	mode->HTotal = kmode->htotal;
283fda9279dSmrg	mode->HSkew = kmode->hskew;
284fda9279dSmrg
285fda9279dSmrg	mode->VDisplay = kmode->vdisplay;
286fda9279dSmrg	mode->VSyncStart = kmode->vsync_start;
287fda9279dSmrg	mode->VSyncEnd = kmode->vsync_end;
288fda9279dSmrg	mode->VTotal = kmode->vtotal;
289fda9279dSmrg	mode->VScan = kmode->vscan;
290fda9279dSmrg
291fda9279dSmrg	mode->Flags = kmode->flags; //& FLAG_BITS;
292fda9279dSmrg	mode->name = strdup(kmode->name);
293fda9279dSmrg
294fda9279dSmrg	if (kmode->type & DRM_MODE_TYPE_DRIVER)
295fda9279dSmrg		mode->type = M_T_DRIVER;
296fda9279dSmrg	if (kmode->type & DRM_MODE_TYPE_PREFERRED)
297fda9279dSmrg		mode->type |= M_T_PREFERRED;
298fda9279dSmrg	xf86SetModeCrtc (mode, scrn->adjustFlags);
299fda9279dSmrg}
300fda9279dSmrg
301fda9279dSmrgstatic void
302fda9279dSmrgdrmmode_ConvertToKMode(ScrnInfoPtr scrn, drmModeModeInfo *kmode,
303fda9279dSmrg		       DisplayModePtr mode)
304fda9279dSmrg{
305fda9279dSmrg	memset(kmode, 0, sizeof(*kmode));
306fda9279dSmrg
307fda9279dSmrg	kmode->clock = mode->Clock;
308fda9279dSmrg	kmode->hdisplay = mode->HDisplay;
309fda9279dSmrg	kmode->hsync_start = mode->HSyncStart;
310fda9279dSmrg	kmode->hsync_end = mode->HSyncEnd;
311fda9279dSmrg	kmode->htotal = mode->HTotal;
312fda9279dSmrg	kmode->hskew = mode->HSkew;
313fda9279dSmrg
314fda9279dSmrg	kmode->vdisplay = mode->VDisplay;
315fda9279dSmrg	kmode->vsync_start = mode->VSyncStart;
316fda9279dSmrg	kmode->vsync_end = mode->VSyncEnd;
317fda9279dSmrg	kmode->vtotal = mode->VTotal;
318fda9279dSmrg	kmode->vscan = mode->VScan;
319fda9279dSmrg
320fda9279dSmrg	kmode->flags = mode->Flags; //& FLAG_BITS;
321fda9279dSmrg	if (mode->name)
322fda9279dSmrg		strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN);
323fda9279dSmrg	kmode->name[DRM_DISPLAY_MODE_LEN-1] = 0;
324fda9279dSmrg
325fda9279dSmrg}
326fda9279dSmrg
327fda9279dSmrgstatic void
3281090d90aSmrgdrmmode_crtc_dpms(xf86CrtcPtr crtc, int mode)
329fda9279dSmrg{
3301090d90aSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
3311090d90aSmrg	drmmode_crtc->dpms_mode = mode;
332fda9279dSmrg}
333fda9279dSmrg
334fda9279dSmrgvoid
335fda9279dSmrgdrmmode_fbcon_copy(ScreenPtr pScreen)
336fda9279dSmrg{
337fda9279dSmrg	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
338fda9279dSmrg	NVPtr pNv = NVPTR(pScrn);
339fda9279dSmrg#if XORG_VERSION_CURRENT >= 10999001
340fda9279dSmrg	ExaDriverPtr exa = pNv->EXADriverPtr;
341fda9279dSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
342fda9279dSmrg	struct nouveau_bo *bo = NULL;
343a33a703bSmrg	PixmapPtr pspix, pdpix = NULL;
344fda9279dSmrg	drmModeFBPtr fb;
345fda9279dSmrg	unsigned w = pScrn->virtualX, h = pScrn->virtualY;
346fda9279dSmrg	int i, ret, fbcon_id = 0;
347fda9279dSmrg
348fda9279dSmrg	if (pNv->AccelMethod != EXA)
349fda9279dSmrg		goto fallback;
350fda9279dSmrg
351a33a703bSmrg	pdpix = drmmode_pixmap_wrap(pScreen, pScrn->virtualX,
352a33a703bSmrg				    pScrn->virtualY, pScrn->depth,
353a33a703bSmrg				    pScrn->bitsPerPixel, pScrn->displayWidth *
354a33a703bSmrg				    pScrn->bitsPerPixel / 8, pNv->scanout,
355a33a703bSmrg				    NULL);
356a33a703bSmrg	if (!pdpix) {
357a33a703bSmrg		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
358a33a703bSmrg			   "Failed to init scanout pixmap for fbcon mirror\n");
359a33a703bSmrg		goto fallback;
360a33a703bSmrg	}
361a33a703bSmrg
362fda9279dSmrg	for (i = 0; i < xf86_config->num_crtc; i++) {
363fda9279dSmrg		drmmode_crtc_private_ptr drmmode_crtc =
364fda9279dSmrg			xf86_config->crtc[i]->driver_private;
365fda9279dSmrg
366fda9279dSmrg		if (drmmode_crtc->mode_crtc->buffer_id)
367fda9279dSmrg			fbcon_id = drmmode_crtc->mode_crtc->buffer_id;
368fda9279dSmrg	}
369fda9279dSmrg
370fda9279dSmrg	if (!fbcon_id)
371fda9279dSmrg		goto fallback;
372fda9279dSmrg
373fda9279dSmrg	fb = drmModeGetFB(pNv->dev->fd, fbcon_id);
374fda9279dSmrg	if (!fb) {
375fda9279dSmrg		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
376fda9279dSmrg			   "Failed to retrieve fbcon fb: id %d\n", fbcon_id);
377fda9279dSmrg		goto fallback;
378fda9279dSmrg	}
379fda9279dSmrg
380fda9279dSmrg	if (fb->depth != pScrn->depth || fb->width != w || fb->height != h) {
381fda9279dSmrg		drmFree(fb);
382fda9279dSmrg		goto fallback;
383fda9279dSmrg	}
384fda9279dSmrg
385fda9279dSmrg	ret = nouveau_bo_wrap(pNv->dev, fb->handle, &bo);
386fda9279dSmrg	if (ret) {
387fda9279dSmrg		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
388fda9279dSmrg			   "Failed to retrieve fbcon buffer: handle=0x%08x\n",
389fda9279dSmrg			   fb->handle);
390fda9279dSmrg		drmFree(fb);
391fda9279dSmrg		goto fallback;
392fda9279dSmrg	}
393fda9279dSmrg
394fda9279dSmrg	pspix = drmmode_pixmap_wrap(pScreen, fb->width, fb->height,
395fda9279dSmrg				    fb->depth, fb->bpp, fb->pitch, bo, NULL);
396fda9279dSmrg	nouveau_bo_ref(NULL, &bo);
397fda9279dSmrg	drmFree(fb);
398fda9279dSmrg	if (!pspix) {
399fda9279dSmrg		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
400fda9279dSmrg			   "Failed to create pixmap for fbcon contents\n");
401fda9279dSmrg		goto fallback;
402fda9279dSmrg	}
403fda9279dSmrg
404fda9279dSmrg	exa->PrepareCopy(pspix, pdpix, 0, 0, GXcopy, ~0);
405fda9279dSmrg	exa->Copy(pdpix, 0, 0, 0, 0, w, h);
406fda9279dSmrg	exa->DoneCopy(pdpix);
407fda9279dSmrg	PUSH_KICK(pNv->pushbuf);
408fda9279dSmrg
409fda9279dSmrg	/* wait for completion before continuing, avoids seeing a momentary
410fda9279dSmrg	 * flash of "corruption" on occasion
411fda9279dSmrg	 */
412fda9279dSmrg	nouveau_bo_wait(pNv->scanout, NOUVEAU_BO_RDWR, pNv->client);
413fda9279dSmrg
414fda9279dSmrg	pScreen->DestroyPixmap(pdpix);
415fda9279dSmrg	pScreen->DestroyPixmap(pspix);
416fda9279dSmrg	pScreen->canDoBGNoneRoot = TRUE;
417fda9279dSmrg	return;
418fda9279dSmrg
419fda9279dSmrgfallback:
420a33a703bSmrg	if (pdpix) {
421a33a703bSmrg		if (exa->PrepareSolid(pdpix, GXcopy, ~0, 0)) {
422a33a703bSmrg			exa->Solid(pdpix, 0, 0, w, h);
423a33a703bSmrg			exa->DoneSolid(pdpix);
424a33a703bSmrg			PUSH_KICK(pNv->pushbuf);
425a33a703bSmrg			nouveau_bo_wait(pNv->scanout, NOUVEAU_BO_RDWR, pNv->client);
426a33a703bSmrg			pScreen->DestroyPixmap(pdpix);
427a33a703bSmrg			return;
428a33a703bSmrg		}
429a33a703bSmrg		pScreen->DestroyPixmap(pdpix);
430a33a703bSmrg	}
431fda9279dSmrg#endif
432fda9279dSmrg	if (nouveau_bo_map(pNv->scanout, NOUVEAU_BO_WR, pNv->client))
433fda9279dSmrg		return;
434fda9279dSmrg	memset(pNv->scanout->map, 0x00, pNv->scanout->size);
435fda9279dSmrg}
436fda9279dSmrg
437fda9279dSmrgstatic Bool
438fda9279dSmrgdrmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
439fda9279dSmrg		       Rotation rotation, int x, int y)
440fda9279dSmrg{
441fda9279dSmrg	ScrnInfoPtr pScrn = crtc->scrn;
442fda9279dSmrg	NVPtr pNv = NVPTR(pScrn);
443fda9279dSmrg	xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
444fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
445fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
446fda9279dSmrg	uint32_t *output_ids;
447fda9279dSmrg	int output_count = 0;
448fda9279dSmrg	int ret = TRUE;
449fda9279dSmrg	int i;
450fda9279dSmrg	int fb_id;
451fda9279dSmrg	drmModeModeInfo kmode;
452fda9279dSmrg
453fda9279dSmrg	if (drmmode->fb_id == 0) {
454fda9279dSmrg		unsigned int pitch =
455fda9279dSmrg			pScrn->displayWidth * (pScrn->bitsPerPixel / 8);
456fda9279dSmrg
457fda9279dSmrg		ret = drmModeAddFB(drmmode->fd,
458fda9279dSmrg				   pScrn->virtualX, pScrn->virtualY,
459fda9279dSmrg				   pScrn->depth, pScrn->bitsPerPixel,
460fda9279dSmrg				   pitch, pNv->scanout->handle,
461fda9279dSmrg				   &drmmode->fb_id);
462fda9279dSmrg		if (ret < 0) {
463fda9279dSmrg			ErrorF("failed to add fb\n");
464fda9279dSmrg			return FALSE;
465fda9279dSmrg		}
466fda9279dSmrg	}
467fda9279dSmrg
468fda9279dSmrg	if (!xf86CrtcRotate(crtc))
469fda9279dSmrg		return FALSE;
470fda9279dSmrg
471fda9279dSmrg	output_ids = calloc(sizeof(uint32_t), xf86_config->num_output);
472fda9279dSmrg	if (!output_ids)
473fda9279dSmrg		return FALSE;
474fda9279dSmrg
475fda9279dSmrg	for (i = 0; i < xf86_config->num_output; i++) {
476fda9279dSmrg		xf86OutputPtr output = xf86_config->output[i];
477fda9279dSmrg		drmmode_output_private_ptr drmmode_output;
478fda9279dSmrg
479fda9279dSmrg		if (output->crtc != crtc)
480fda9279dSmrg			continue;
481fda9279dSmrg
482fda9279dSmrg		drmmode_output = output->driver_private;
48322d74663Smrg		if (drmmode_output->output_id == -1)
48422d74663Smrg			continue;
485fda9279dSmrg		output_ids[output_count] =
486fda9279dSmrg			drmmode_output->mode_output->connector_id;
487fda9279dSmrg		output_count++;
488fda9279dSmrg	}
489fda9279dSmrg
490fda9279dSmrg	drmmode_ConvertToKMode(crtc->scrn, &kmode, mode);
491fda9279dSmrg
492fda9279dSmrg	fb_id = drmmode->fb_id;
493fda9279dSmrg#ifdef NOUVEAU_PIXMAP_SHARING
494fda9279dSmrg	if (crtc->randr_crtc && crtc->randr_crtc->scanout_pixmap) {
495fda9279dSmrg		x = drmmode_crtc->scanout_pixmap_x;
496fda9279dSmrg		y = 0;
497fda9279dSmrg	} else
498fda9279dSmrg#endif
499fda9279dSmrg	if (drmmode_crtc->rotate_fb_id) {
500fda9279dSmrg		fb_id = drmmode_crtc->rotate_fb_id;
501fda9279dSmrg		x = 0;
502fda9279dSmrg		y = 0;
503fda9279dSmrg	}
504fda9279dSmrg
505fda9279dSmrg	ret = drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
506fda9279dSmrg			     fb_id, x, y, output_ids, output_count, &kmode);
507fda9279dSmrg	free(output_ids);
508fda9279dSmrg
509fda9279dSmrg	if (ret) {
510fda9279dSmrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
511fda9279dSmrg			   "failed to set mode: %s\n", strerror(-ret));
512fda9279dSmrg		return FALSE;
513fda9279dSmrg	}
514fda9279dSmrg
515fda9279dSmrg	/* Work around some xserver stupidity */
516fda9279dSmrg	for (i = 0; i < xf86_config->num_output; i++) {
517fda9279dSmrg		xf86OutputPtr output = xf86_config->output[i];
518fda9279dSmrg
519fda9279dSmrg		if (output->crtc != crtc)
520fda9279dSmrg			continue;
521fda9279dSmrg
522fda9279dSmrg		drmmode_output_dpms(output, DPMSModeOn);
523fda9279dSmrg	}
524fda9279dSmrg
525fda9279dSmrg	crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
526fda9279dSmrg			       crtc->gamma_blue, crtc->gamma_size);
527fda9279dSmrg
528861b9feeSmrg#ifdef HAVE_XF86_CURSOR_RESET_CURSOR
529861b9feeSmrg	xf86CursorResetCursor(crtc->scrn->pScreen);
53022d74663Smrg#else
531fda9279dSmrg	xf86_reload_cursors(crtc->scrn->pScreen);
532861b9feeSmrg#endif
533fda9279dSmrg
534fda9279dSmrg	return TRUE;
535fda9279dSmrg}
536fda9279dSmrg
537fda9279dSmrgstatic void
538fda9279dSmrgdrmmode_set_cursor_position (xf86CrtcPtr crtc, int x, int y)
539fda9279dSmrg{
540fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
541fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
542fda9279dSmrg
543fda9279dSmrg	drmModeMoveCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, x, y);
544fda9279dSmrg}
545fda9279dSmrg
546fda9279dSmrgstatic void
547fda9279dSmrgconvert_cursor(CARD32 *dst, CARD32 *src, int dw, int sw)
548fda9279dSmrg{
549fda9279dSmrg	int i, j;
550fda9279dSmrg
551fda9279dSmrg	for (j = 0;  j < sw; j++) {
552fda9279dSmrg		for (i = 0; i < sw; i++) {
553fda9279dSmrg			dst[j * dw + i] = src[j * sw + i];
554fda9279dSmrg		}
555fda9279dSmrg	}
556fda9279dSmrg}
557fda9279dSmrg
558fda9279dSmrgstatic void
559fda9279dSmrgdrmmode_load_cursor_argb (xf86CrtcPtr crtc, CARD32 *image)
560fda9279dSmrg{
561fda9279dSmrg	NVPtr pNv = NVPTR(crtc->scrn);
562fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
563fda9279dSmrg	struct nouveau_bo *cursor = drmmode_crtc->cursor;
564fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
565fda9279dSmrg
566fda9279dSmrg	nouveau_bo_map(cursor, NOUVEAU_BO_WR, pNv->client);
567fda9279dSmrg	convert_cursor(cursor->map, image, 64, nv_cursor_width(pNv));
568fda9279dSmrg
569fda9279dSmrg	if (drmmode_crtc->cursor_visible) {
570fda9279dSmrg		drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
571fda9279dSmrg				 cursor->handle, 64, 64);
572fda9279dSmrg	}
573fda9279dSmrg}
574fda9279dSmrg
575fda9279dSmrgstatic void
576fda9279dSmrgdrmmode_hide_cursor (xf86CrtcPtr crtc)
577fda9279dSmrg{
578fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
579fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
580fda9279dSmrg
581fda9279dSmrg	drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
582fda9279dSmrg			 0, 64, 64);
583fda9279dSmrg	drmmode_crtc->cursor_visible = FALSE;
584fda9279dSmrg}
585fda9279dSmrg
586fda9279dSmrgstatic void
587fda9279dSmrgdrmmode_show_cursor (xf86CrtcPtr crtc)
588fda9279dSmrg{
589fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
590fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
591fda9279dSmrg
592fda9279dSmrg	drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
593fda9279dSmrg			 drmmode_crtc->cursor->handle, 64, 64);
594fda9279dSmrg	drmmode_crtc->cursor_visible = TRUE;
595fda9279dSmrg}
596fda9279dSmrg
597fda9279dSmrgstatic void *
598fda9279dSmrgdrmmode_crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
599fda9279dSmrg{
600fda9279dSmrg	ScrnInfoPtr scrn = crtc->scrn;
601fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
602fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
603fda9279dSmrg	void *virtual;
604fda9279dSmrg	int ret;
605fda9279dSmrg
606fda9279dSmrg	ret = nouveau_allocate_surface(scrn, width, height,
607fda9279dSmrg				       scrn->bitsPerPixel,
608fda9279dSmrg				       NOUVEAU_CREATE_PIXMAP_SCANOUT,
609fda9279dSmrg				       &drmmode_crtc->rotate_pitch,
610fda9279dSmrg				       &drmmode_crtc->rotate_bo);
611fda9279dSmrg	if (!ret) {
612fda9279dSmrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
613fda9279dSmrg			   "Couldn't allocate shadow memory for rotated CRTC\n");
614fda9279dSmrg		return NULL;
615fda9279dSmrg	}
616fda9279dSmrg
617fda9279dSmrg	ret = nouveau_bo_map(drmmode_crtc->rotate_bo, NOUVEAU_BO_RDWR,
618fda9279dSmrg			     NVPTR(scrn)->client);
619fda9279dSmrg	if (ret) {
620fda9279dSmrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
621fda9279dSmrg			   "Couldn't get virtual address of shadow scanout\n");
622fda9279dSmrg		nouveau_bo_ref(NULL, &drmmode_crtc->rotate_bo);
623fda9279dSmrg		return NULL;
624fda9279dSmrg	}
625fda9279dSmrg	virtual = drmmode_crtc->rotate_bo->map;
626fda9279dSmrg
627fda9279dSmrg	ret = drmModeAddFB(drmmode->fd, width, height, crtc->scrn->depth,
628fda9279dSmrg			   crtc->scrn->bitsPerPixel, drmmode_crtc->rotate_pitch,
629fda9279dSmrg			   drmmode_crtc->rotate_bo->handle,
630fda9279dSmrg			   &drmmode_crtc->rotate_fb_id);
631fda9279dSmrg	if (ret) {
632fda9279dSmrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
633fda9279dSmrg			   "Error adding FB for shadow scanout: %s\n",
634fda9279dSmrg			   strerror(-ret));
635fda9279dSmrg		nouveau_bo_ref(NULL, &drmmode_crtc->rotate_bo);
636fda9279dSmrg		return NULL;
637fda9279dSmrg	}
638fda9279dSmrg
639fda9279dSmrg	return virtual;
640fda9279dSmrg}
641fda9279dSmrg
642fda9279dSmrgstatic PixmapPtr
643fda9279dSmrgdrmmode_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
644fda9279dSmrg{
645fda9279dSmrg	ScrnInfoPtr pScrn = crtc->scrn;
646fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
647fda9279dSmrg	PixmapPtr rotate_pixmap;
648fda9279dSmrg
649fda9279dSmrg	if (!data)
650fda9279dSmrg		data = drmmode_crtc_shadow_allocate (crtc, width, height);
651fda9279dSmrg
652fda9279dSmrg	rotate_pixmap = drmmode_pixmap_wrap(pScrn->pScreen, width, height,
653fda9279dSmrg					    pScrn->depth, pScrn->bitsPerPixel,
654fda9279dSmrg					    drmmode_crtc->rotate_pitch,
655fda9279dSmrg					    drmmode_crtc->rotate_bo, data);
656fda9279dSmrg
657fda9279dSmrg	drmmode_crtc->rotate_pixmap = rotate_pixmap;
658fda9279dSmrg	return drmmode_crtc->rotate_pixmap;
659fda9279dSmrg}
660fda9279dSmrg
661fda9279dSmrgstatic void
662fda9279dSmrgdrmmode_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *data)
663fda9279dSmrg{
664fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
665fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
666fda9279dSmrg
667fda9279dSmrg	if (rotate_pixmap)
668fda9279dSmrg		FreeScratchPixmapHeader(rotate_pixmap);
669fda9279dSmrg
670fda9279dSmrg	if (data) {
671fda9279dSmrg		drmModeRmFB(drmmode->fd, drmmode_crtc->rotate_fb_id);
672fda9279dSmrg		drmmode_crtc->rotate_fb_id = 0;
673fda9279dSmrg		nouveau_bo_ref(NULL, &drmmode_crtc->rotate_bo);
674fda9279dSmrg		drmmode_crtc->rotate_pixmap = NULL;
675fda9279dSmrg	}
676fda9279dSmrg}
677fda9279dSmrg
678fda9279dSmrgstatic void
679fda9279dSmrgdrmmode_gamma_set(xf86CrtcPtr crtc, CARD16 *red, CARD16 *green, CARD16 *blue,
680fda9279dSmrg		  int size)
681fda9279dSmrg{
682fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
683fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
684fda9279dSmrg	int ret;
685fda9279dSmrg
686fda9279dSmrg	ret = drmModeCrtcSetGamma(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
687fda9279dSmrg				  size, red, green, blue);
688fda9279dSmrg	if (ret != 0) {
689fda9279dSmrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
69022d74663Smrg			   "failed to set gamma with %d entries: %s\n",
69122d74663Smrg			   size, strerror(-ret));
692fda9279dSmrg	}
693fda9279dSmrg}
694fda9279dSmrg
695fda9279dSmrg#ifdef NOUVEAU_PIXMAP_SHARING
696fda9279dSmrgstatic Bool
697fda9279dSmrgdrmmode_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix)
698fda9279dSmrg{
699fda9279dSmrg	ScreenPtr screen = xf86ScrnToScreen(crtc->scrn);
700fda9279dSmrg	PixmapPtr screenpix = screen->GetScreenPixmap(screen);
701fda9279dSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
702fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
703374ff59dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
704fda9279dSmrg	int c, total_width = 0, max_height = 0, this_x = 0;
705fda9279dSmrg	if (!ppix) {
706374ff59dSmrg		if (crtc->randr_crtc->scanout_pixmap) {
70722d74663Smrg#ifdef HAS_DIRTYTRACKING_DRAWABLE_SRC
70822d74663Smrg			PixmapStopDirtyTracking(&crtc->randr_crtc->scanout_pixmap->drawable, screenpix);
70922d74663Smrg#else
71022d74663Smrg			PixmapStopDirtyTracking(crtc->randr_crtc->scanout_pixmap, screenpix);
71122d74663Smrg#endif
712374ff59dSmrg			if (drmmode && drmmode->fb_id) {
713374ff59dSmrg				drmModeRmFB(drmmode->fd, drmmode->fb_id);
714374ff59dSmrg				drmmode->fb_id = 0;
715374ff59dSmrg			}
716374ff59dSmrg		}
717fda9279dSmrg		drmmode_crtc->scanout_pixmap_x = 0;
718fda9279dSmrg		return TRUE;
719fda9279dSmrg	}
720fda9279dSmrg
721fda9279dSmrg	/* iterate over all the attached crtcs -
722fda9279dSmrg	   work out bounding box */
723fda9279dSmrg	for (c = 0; c < xf86_config->num_crtc; c++) {
724fda9279dSmrg		xf86CrtcPtr iter = xf86_config->crtc[c];
725fda9279dSmrg		if (!iter->enabled && iter != crtc)
726fda9279dSmrg			continue;
727fda9279dSmrg		if (iter == crtc) {
728fda9279dSmrg			this_x = total_width;
729fda9279dSmrg			total_width += ppix->drawable.width;
730fda9279dSmrg			if (max_height < ppix->drawable.height)
731fda9279dSmrg				max_height = ppix->drawable.height;
732fda9279dSmrg		} else {
733fda9279dSmrg			total_width += iter->mode.HDisplay;
734fda9279dSmrg			if (max_height < iter->mode.VDisplay)
735fda9279dSmrg				max_height = iter->mode.VDisplay;
736fda9279dSmrg		}
737374ff59dSmrg#if !defined(HAS_DIRTYTRACKING_ROTATION) && !defined(HAS_DIRTYTRACKING2)
738fda9279dSmrg	if (iter != crtc) {
739fda9279dSmrg		ErrorF("Cannot do multiple crtcs without X server dirty tracking 2 interface\n");
740fda9279dSmrg		return FALSE;
741fda9279dSmrg	}
742fda9279dSmrg#endif
743fda9279dSmrg	}
744fda9279dSmrg
745fda9279dSmrg	if (total_width != screenpix->drawable.width ||
746fda9279dSmrg	    max_height != screenpix->drawable.height) {
747fda9279dSmrg		Bool ret;
748fda9279dSmrg		ret = drmmode_xf86crtc_resize(crtc->scrn, total_width, max_height);
749fda9279dSmrg		if (ret == FALSE)
750fda9279dSmrg			return FALSE;
751fda9279dSmrg
752fda9279dSmrg		screenpix = screen->GetScreenPixmap(screen);
753fda9279dSmrg		screen->width = screenpix->drawable.width = total_width;
754fda9279dSmrg		screen->height = screenpix->drawable.height = max_height;
755fda9279dSmrg	}
756fda9279dSmrg	drmmode_crtc->scanout_pixmap_x = this_x;
75722d74663Smrg
75822d74663Smrg#ifdef HAS_DIRTYTRACKING_DRAWABLE_SRC
75922d74663Smrg	PixmapStartDirtyTracking(&ppix->drawable, screenpix, 0, 0, this_x, 0, RR_Rotate_0);
76022d74663Smrg#elif defined(HAS_DIRTYTRACKING_ROTATION)
76122d74663Smrg	PixmapStartDirtyTracking(ppix, screenpix, 0, 0, this_x, 0, RR_Rotate_0);
762a33a703bSmrg#elif defined(HAS_DIRTYTRACKING2)
763fda9279dSmrg	PixmapStartDirtyTracking2(ppix, screenpix, 0, 0, this_x, 0);
764fda9279dSmrg#else
765fda9279dSmrg	PixmapStartDirtyTracking(ppix, screenpix, 0, 0);
766fda9279dSmrg#endif
767fda9279dSmrg	return TRUE;
768fda9279dSmrg}
769fda9279dSmrg#endif
770fda9279dSmrg
771fda9279dSmrgstatic const xf86CrtcFuncsRec drmmode_crtc_funcs = {
772fda9279dSmrg	.dpms = drmmode_crtc_dpms,
773fda9279dSmrg	.set_mode_major = drmmode_set_mode_major,
774fda9279dSmrg	.set_cursor_position = drmmode_set_cursor_position,
775fda9279dSmrg	.show_cursor = drmmode_show_cursor,
776fda9279dSmrg	.hide_cursor = drmmode_hide_cursor,
777fda9279dSmrg	.load_cursor_argb = drmmode_load_cursor_argb,
778fda9279dSmrg	.shadow_create = drmmode_crtc_shadow_create,
779fda9279dSmrg	.shadow_allocate = drmmode_crtc_shadow_allocate,
780fda9279dSmrg	.shadow_destroy = drmmode_crtc_shadow_destroy,
781fda9279dSmrg	.gamma_set = drmmode_gamma_set,
782fda9279dSmrg
783fda9279dSmrg#ifdef NOUVEAU_PIXMAP_SHARING
784fda9279dSmrg	.set_scanout_pixmap = drmmode_set_scanout_pixmap,
785fda9279dSmrg#endif
786fda9279dSmrg};
787fda9279dSmrg
788fda9279dSmrg
789fda9279dSmrgstatic unsigned int
79022d74663Smrgdrmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res, int num)
791fda9279dSmrg{
792fda9279dSmrg	NVPtr pNv = NVPTR(pScrn);
793fda9279dSmrg	NVEntPtr pNVEnt = NVEntPriv(pScrn);
794fda9279dSmrg	xf86CrtcPtr crtc;
795fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc;
796fda9279dSmrg	int ret;
797fda9279dSmrg
798fda9279dSmrg	crtc = xf86CrtcCreate(pScrn, &drmmode_crtc_funcs);
799fda9279dSmrg	if (crtc == NULL)
800fda9279dSmrg		return 0;
801fda9279dSmrg
802fda9279dSmrg	drmmode_crtc = xnfcalloc(sizeof(drmmode_crtc_private_rec), 1);
803fda9279dSmrg	drmmode_crtc->mode_crtc = drmModeGetCrtc(drmmode->fd,
80422d74663Smrg						 mode_res->crtcs[num]);
805fda9279dSmrg	drmmode_crtc->drmmode = drmmode;
806fda9279dSmrg	drmmode_crtc->hw_crtc_index = num;
807fda9279dSmrg
808fda9279dSmrg	ret = nouveau_bo_new(pNv->dev, NOUVEAU_BO_GART | NOUVEAU_BO_MAP, 0,
809fda9279dSmrg			     64*64*4, NULL, &drmmode_crtc->cursor);
810fda9279dSmrg	assert(ret == 0);
811fda9279dSmrg
812fda9279dSmrg	crtc->driver_private = drmmode_crtc;
813fda9279dSmrg
814fda9279dSmrg	/* Mark num'th crtc as in use on this device. */
815fda9279dSmrg	pNVEnt->assigned_crtcs |= (1 << num);
816fda9279dSmrg	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
817fda9279dSmrg		   "Allocated crtc nr. %d to this screen.\n", num);
818fda9279dSmrg
819fda9279dSmrg	return 1;
820fda9279dSmrg}
821fda9279dSmrg
822fda9279dSmrgstatic xf86OutputStatus
823fda9279dSmrgdrmmode_output_detect(xf86OutputPtr output)
824fda9279dSmrg{
825fda9279dSmrg	/* go to the hw and retrieve a new output struct */
826fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
827fda9279dSmrg	drmmode_ptr drmmode = drmmode_output->drmmode;
828fda9279dSmrg	xf86OutputStatus status;
82922d74663Smrg
83022d74663Smrg	if (drmmode_output->output_id == -1)
83122d74663Smrg		return XF86OutputStatusDisconnected;
83222d74663Smrg
833fda9279dSmrg	drmModeFreeConnector(drmmode_output->mode_output);
834fda9279dSmrg
835fda9279dSmrg	drmmode_output->mode_output =
836fda9279dSmrg		drmModeGetConnector(drmmode->fd, drmmode_output->output_id);
837fda9279dSmrg
83822d74663Smrg	if (!drmmode_output->mode_output) {
83922d74663Smrg		drmmode_output->output_id = -1;
840fda9279dSmrg		return XF86OutputStatusDisconnected;
84122d74663Smrg	}
842fda9279dSmrg
843fda9279dSmrg	switch (drmmode_output->mode_output->connection) {
844fda9279dSmrg	case DRM_MODE_CONNECTED:
845fda9279dSmrg		status = XF86OutputStatusConnected;
846fda9279dSmrg		break;
847fda9279dSmrg	case DRM_MODE_DISCONNECTED:
848fda9279dSmrg		status = XF86OutputStatusDisconnected;
849fda9279dSmrg		break;
850fda9279dSmrg	default:
851fda9279dSmrg	case DRM_MODE_UNKNOWNCONNECTION:
852fda9279dSmrg		status = XF86OutputStatusUnknown;
853fda9279dSmrg		break;
854fda9279dSmrg	}
855fda9279dSmrg	return status;
856fda9279dSmrg}
857fda9279dSmrg
858fda9279dSmrgstatic Bool
859fda9279dSmrgdrmmode_output_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
860fda9279dSmrg{
861fda9279dSmrg	if (mode->type & M_T_DEFAULT)
862fda9279dSmrg		/* Default modes are harmful here. */
863fda9279dSmrg		return MODE_BAD;
864fda9279dSmrg
865fda9279dSmrg	return MODE_OK;
866fda9279dSmrg}
867fda9279dSmrg
86822d74663Smrgstatic int
86922d74663Smrgkoutput_get_prop_idx(int fd, drmModeConnectorPtr koutput,
87022d74663Smrg		     int type, const char *name)
87122d74663Smrg{
87222d74663Smrg	int idx = -1;
87322d74663Smrg
87422d74663Smrg	for (int i = 0; i < koutput->count_props; i++) {
87522d74663Smrg		drmModePropertyPtr prop = drmModeGetProperty(fd, koutput->props[i]);
87622d74663Smrg
87722d74663Smrg		if (!prop)
87822d74663Smrg			continue;
87922d74663Smrg
88022d74663Smrg		if (drm_property_type_is(prop, type) && !strcmp(prop->name, name))
88122d74663Smrg			idx = i;
88222d74663Smrg
88322d74663Smrg		drmModeFreeProperty(prop);
88422d74663Smrg
88522d74663Smrg		if (idx > -1)
88622d74663Smrg			break;
88722d74663Smrg	}
88822d74663Smrg
88922d74663Smrg	return idx;
89022d74663Smrg}
89122d74663Smrg
89222d74663Smrgstatic drmModePropertyBlobPtr
89322d74663Smrgkoutput_get_prop_blob(int fd, drmModeConnectorPtr koutput, const char *name)
89422d74663Smrg{
89522d74663Smrg	drmModePropertyBlobPtr blob = NULL;
89622d74663Smrg	int idx = koutput_get_prop_idx(fd, koutput, DRM_MODE_PROP_BLOB, name);
89722d74663Smrg
89822d74663Smrg	if (idx > -1)
89922d74663Smrg		blob = drmModeGetPropertyBlob(fd, koutput->prop_values[idx]);
90022d74663Smrg
90122d74663Smrg	return blob;
90222d74663Smrg}
90322d74663Smrg
90422d74663Smrgstatic void
90522d74663Smrgdrmmode_output_attach_tile(xf86OutputPtr output)
90622d74663Smrg{
90722d74663Smrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
90822d74663Smrg	drmModeConnectorPtr koutput = drmmode_output->mode_output;
90922d74663Smrg	drmmode_ptr drmmode = drmmode_output->drmmode;
91022d74663Smrg	struct xf86CrtcTileInfo tile_info, *set = NULL;
91122d74663Smrg
91222d74663Smrg	if (!koutput) {
91322d74663Smrg		xf86OutputSetTile(output, NULL);
91422d74663Smrg		return;
91522d74663Smrg	}
91622d74663Smrg
91722d74663Smrg	drmModeFreePropertyBlob(drmmode_output->tile_blob);
91822d74663Smrg
91922d74663Smrg	/* look for a TILE property */
92022d74663Smrg	drmmode_output->tile_blob =
92122d74663Smrg		koutput_get_prop_blob(drmmode->fd, koutput, "TILE");
92222d74663Smrg
92322d74663Smrg	if (drmmode_output->tile_blob) {
92422d74663Smrg		if (xf86OutputParseKMSTile(drmmode_output->tile_blob->data, drmmode_output->tile_blob->length, &tile_info) == TRUE)
92522d74663Smrg			set = &tile_info;
92622d74663Smrg	}
92722d74663Smrg	xf86OutputSetTile(output, set);
92822d74663Smrg}
92922d74663Smrg
93022d74663Smrg
931fda9279dSmrgstatic DisplayModePtr
932fda9279dSmrgdrmmode_output_get_modes(xf86OutputPtr output)
933fda9279dSmrg{
934fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
935fda9279dSmrg	drmModeConnectorPtr koutput = drmmode_output->mode_output;
936fda9279dSmrg	drmmode_ptr drmmode = drmmode_output->drmmode;
937fda9279dSmrg	int i;
938fda9279dSmrg	DisplayModePtr Modes = NULL, Mode;
939fda9279dSmrg	xf86MonPtr ddc_mon = NULL;
940fda9279dSmrg
941fda9279dSmrg	if (!koutput)
942fda9279dSmrg		return NULL;
943fda9279dSmrg
944fda9279dSmrg	/* look for an EDID property */
94522d74663Smrg	drmmode_output->edid_blob =
94622d74663Smrg		koutput_get_prop_blob(drmmode->fd, koutput, "EDID");
947fda9279dSmrg
948fda9279dSmrg	if (drmmode_output->edid_blob) {
949fda9279dSmrg		ddc_mon = xf86InterpretEDID(output->scrn->scrnIndex,
950fda9279dSmrg					    drmmode_output->edid_blob->data);
951fda9279dSmrg		if (ddc_mon && drmmode_output->edid_blob->length > 128)
952fda9279dSmrg			ddc_mon->flags |= MONITOR_EDID_COMPLETE_RAWDATA;
953fda9279dSmrg	}
954fda9279dSmrg	xf86OutputSetEDID(output, ddc_mon);
955fda9279dSmrg
95622d74663Smrg	drmmode_output_attach_tile(output);
95722d74663Smrg
958fda9279dSmrg	/* modes should already be available */
959fda9279dSmrg	for (i = 0; i < koutput->count_modes; i++) {
960fda9279dSmrg		Mode = xnfalloc(sizeof(DisplayModeRec));
961fda9279dSmrg
962fda9279dSmrg		drmmode_ConvertFromKMode(output->scrn, &koutput->modes[i],
963fda9279dSmrg					 Mode);
964fda9279dSmrg		Modes = xf86ModesAdd(Modes, Mode);
965fda9279dSmrg
966fda9279dSmrg	}
967fda9279dSmrg	return Modes;
968fda9279dSmrg}
969fda9279dSmrg
970fda9279dSmrgstatic void
971fda9279dSmrgdrmmode_output_destroy(xf86OutputPtr output)
972fda9279dSmrg{
973fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
974fda9279dSmrg	int i;
975fda9279dSmrg
976fda9279dSmrg	if (drmmode_output->edid_blob)
977fda9279dSmrg		drmModeFreePropertyBlob(drmmode_output->edid_blob);
97822d74663Smrg	if (drmmode_output->tile_blob)
97922d74663Smrg		drmModeFreePropertyBlob(drmmode_output->tile_blob);
980fda9279dSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
981fda9279dSmrg		drmModeFreeProperty(drmmode_output->props[i].mode_prop);
982fda9279dSmrg		free(drmmode_output->props[i].atoms);
983fda9279dSmrg	}
984fda9279dSmrg	drmModeFreeConnector(drmmode_output->mode_output);
985fda9279dSmrg	free(drmmode_output);
986fda9279dSmrg	output->driver_private = NULL;
987fda9279dSmrg}
988fda9279dSmrg
989fda9279dSmrgstatic void
990fda9279dSmrgdrmmode_output_dpms(xf86OutputPtr output, int mode)
991fda9279dSmrg{
992fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
993fda9279dSmrg	drmModeConnectorPtr koutput = drmmode_output->mode_output;
994fda9279dSmrg	drmModePropertyPtr props;
995fda9279dSmrg	drmmode_ptr drmmode = drmmode_output->drmmode;
996fda9279dSmrg	int mode_id = -1, i;
997fda9279dSmrg
99822d74663Smrg	if (!koutput)
99922d74663Smrg		return;
100022d74663Smrg
1001fda9279dSmrg	for (i = 0; i < koutput->count_props; i++) {
1002fda9279dSmrg		props = drmModeGetProperty(drmmode->fd, koutput->props[i]);
100311c9f444Sriastradh		if (props && (props->flags & DRM_MODE_PROP_ENUM)) {
1004fda9279dSmrg			if (!strcmp(props->name, "DPMS")) {
1005fda9279dSmrg				mode_id = koutput->props[i];
1006fda9279dSmrg				drmModeFreeProperty(props);
1007fda9279dSmrg				break;
1008fda9279dSmrg			}
1009fda9279dSmrg			drmModeFreeProperty(props);
1010fda9279dSmrg		}
1011fda9279dSmrg	}
1012fda9279dSmrg
1013fda9279dSmrg	if (mode_id < 0)
1014fda9279dSmrg		return;
1015fda9279dSmrg
1016fda9279dSmrg	drmModeConnectorSetProperty(drmmode->fd, koutput->connector_id,
1017fda9279dSmrg				    mode_id, mode);
1018fda9279dSmrg}
1019fda9279dSmrg
1020fda9279dSmrgstatic Bool
1021fda9279dSmrgdrmmode_property_ignore(drmModePropertyPtr prop)
1022fda9279dSmrg{
1023fda9279dSmrg	if (!prop)
1024fda9279dSmrg	    return TRUE;
1025fda9279dSmrg	/* ignore blob prop */
1026fda9279dSmrg	if (prop->flags & DRM_MODE_PROP_BLOB)
1027fda9279dSmrg		return TRUE;
1028fda9279dSmrg	/* ignore standard property */
1029fda9279dSmrg	if (!strcmp(prop->name, "EDID") ||
1030fda9279dSmrg	    !strcmp(prop->name, "DPMS"))
1031fda9279dSmrg		return TRUE;
1032fda9279dSmrg
1033fda9279dSmrg	return FALSE;
1034fda9279dSmrg}
1035fda9279dSmrg
1036fda9279dSmrgstatic void
1037fda9279dSmrgdrmmode_output_create_resources(xf86OutputPtr output)
1038fda9279dSmrg{
1039fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
1040fda9279dSmrg	drmModeConnectorPtr mode_output = drmmode_output->mode_output;
1041fda9279dSmrg	drmmode_ptr drmmode = drmmode_output->drmmode;
1042fda9279dSmrg	drmModePropertyPtr drmmode_prop;
1043fda9279dSmrg	uint32_t value;
1044fda9279dSmrg	int i, j, err;
1045fda9279dSmrg
1046fda9279dSmrg	drmmode_output->props = calloc(mode_output->count_props, sizeof(drmmode_prop_rec));
1047fda9279dSmrg	if (!drmmode_output->props)
1048fda9279dSmrg		return;
1049fda9279dSmrg
1050fda9279dSmrg	drmmode_output->num_props = 0;
1051fda9279dSmrg	for (i = 0, j = 0; i < mode_output->count_props; i++) {
1052fda9279dSmrg		drmmode_prop = drmModeGetProperty(drmmode->fd, mode_output->props[i]);
1053fda9279dSmrg		if (drmmode_property_ignore(drmmode_prop)) {
1054fda9279dSmrg			drmModeFreeProperty(drmmode_prop);
1055fda9279dSmrg			continue;
1056fda9279dSmrg		}
1057fda9279dSmrg		drmmode_output->props[j].mode_prop = drmmode_prop;
1058fda9279dSmrg		drmmode_output->props[j].index = i;
1059fda9279dSmrg		drmmode_output->num_props++;
1060fda9279dSmrg		j++;
1061fda9279dSmrg	}
1062fda9279dSmrg
1063fda9279dSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
1064fda9279dSmrg		drmmode_prop_ptr p = &drmmode_output->props[i];
1065fda9279dSmrg		drmmode_prop = p->mode_prop;
1066fda9279dSmrg
1067fda9279dSmrg		value = drmmode_output->mode_output->prop_values[p->index];
1068fda9279dSmrg
1069fda9279dSmrg		if (drmmode_prop->flags & DRM_MODE_PROP_RANGE) {
1070fda9279dSmrg			INT32 range[2];
1071fda9279dSmrg
1072fda9279dSmrg			p->num_atoms = 1;
1073fda9279dSmrg			p->atoms = calloc(p->num_atoms, sizeof(Atom));
1074fda9279dSmrg			if (!p->atoms)
1075fda9279dSmrg				continue;
1076fda9279dSmrg			p->atoms[0] = MakeAtom(drmmode_prop->name, strlen(drmmode_prop->name), TRUE);
1077fda9279dSmrg			range[0] = drmmode_prop->values[0];
1078fda9279dSmrg			range[1] = drmmode_prop->values[1];
1079fda9279dSmrg			err = RRConfigureOutputProperty(output->randr_output, p->atoms[0],
1080fda9279dSmrg							FALSE, TRUE,
1081fda9279dSmrg							drmmode_prop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE,
1082fda9279dSmrg							2, range);
1083fda9279dSmrg			if (err != 0) {
1084fda9279dSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
1085fda9279dSmrg					   "RRConfigureOutputProperty error, %d\n", err);
1086fda9279dSmrg			}
1087fda9279dSmrg			err = RRChangeOutputProperty(output->randr_output, p->atoms[0],
1088fda9279dSmrg						     XA_INTEGER, 32, PropModeReplace, 1,
1089fda9279dSmrg						     &value, FALSE, FALSE);
1090fda9279dSmrg			if (err != 0) {
1091fda9279dSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
1092fda9279dSmrg					   "RRChangeOutputProperty error, %d\n", err);
1093fda9279dSmrg			}
1094fda9279dSmrg		} else if (drmmode_prop->flags & DRM_MODE_PROP_ENUM) {
1095fda9279dSmrg			p->num_atoms = drmmode_prop->count_enums + 1;
1096fda9279dSmrg			p->atoms = calloc(p->num_atoms, sizeof(Atom));
1097fda9279dSmrg			if (!p->atoms)
1098fda9279dSmrg				continue;
1099fda9279dSmrg			p->atoms[0] = MakeAtom(drmmode_prop->name, strlen(drmmode_prop->name), TRUE);
1100fda9279dSmrg			for (j = 1; j <= drmmode_prop->count_enums; j++) {
1101fda9279dSmrg				struct drm_mode_property_enum *e = &drmmode_prop->enums[j-1];
1102fda9279dSmrg				p->atoms[j] = MakeAtom(e->name, strlen(e->name), TRUE);
1103fda9279dSmrg			}
1104fda9279dSmrg			err = RRConfigureOutputProperty(output->randr_output, p->atoms[0],
1105fda9279dSmrg							FALSE, FALSE,
1106fda9279dSmrg							drmmode_prop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE,
1107fda9279dSmrg							p->num_atoms - 1, (INT32 *)&p->atoms[1]);
1108fda9279dSmrg			if (err != 0) {
1109fda9279dSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
1110fda9279dSmrg					   "RRConfigureOutputProperty error, %d\n", err);
1111fda9279dSmrg			}
1112fda9279dSmrg			for (j = 0; j < drmmode_prop->count_enums; j++)
1113fda9279dSmrg				if (drmmode_prop->enums[j].value == value)
1114fda9279dSmrg					break;
1115fda9279dSmrg			/* there's always a matching value */
1116fda9279dSmrg			err = RRChangeOutputProperty(output->randr_output, p->atoms[0],
1117fda9279dSmrg						     XA_ATOM, 32, PropModeReplace, 1, &p->atoms[j+1], FALSE, FALSE);
1118fda9279dSmrg			if (err != 0) {
1119fda9279dSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
1120fda9279dSmrg					   "RRChangeOutputProperty error, %d\n", err);
1121fda9279dSmrg			}
1122fda9279dSmrg		}
1123fda9279dSmrg	}
1124fda9279dSmrg}
1125fda9279dSmrg
1126fda9279dSmrgstatic Bool
1127fda9279dSmrgdrmmode_output_set_property(xf86OutputPtr output, Atom property,
1128fda9279dSmrg			    RRPropertyValuePtr value)
1129fda9279dSmrg{
1130fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
1131fda9279dSmrg	drmmode_ptr drmmode = drmmode_output->drmmode;
1132fda9279dSmrg	int i, ret;
1133fda9279dSmrg
1134fda9279dSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
1135fda9279dSmrg		drmmode_prop_ptr p = &drmmode_output->props[i];
1136fda9279dSmrg
1137fda9279dSmrg		if (p->atoms[0] != property)
1138fda9279dSmrg			continue;
1139fda9279dSmrg
1140fda9279dSmrg		if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
1141fda9279dSmrg			uint32_t val;
1142fda9279dSmrg
1143fda9279dSmrg			if (value->type != XA_INTEGER || value->format != 32 ||
1144fda9279dSmrg			    value->size != 1)
1145fda9279dSmrg				return FALSE;
1146fda9279dSmrg			val = *(uint32_t *)value->data;
1147fda9279dSmrg
1148fda9279dSmrg			ret = drmModeConnectorSetProperty(drmmode->fd, drmmode_output->output_id,
1149fda9279dSmrg							  p->mode_prop->prop_id, (uint64_t)val);
1150fda9279dSmrg
1151fda9279dSmrg			if (ret)
1152fda9279dSmrg				return FALSE;
1153fda9279dSmrg
1154fda9279dSmrg			return TRUE;
1155fda9279dSmrg
1156fda9279dSmrg		} else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
1157fda9279dSmrg			Atom	atom;
1158fda9279dSmrg			const char	*name;
1159fda9279dSmrg			int		j;
1160fda9279dSmrg
1161fda9279dSmrg			if (value->type != XA_ATOM || value->format != 32 || value->size != 1)
1162fda9279dSmrg				return FALSE;
1163fda9279dSmrg			memcpy(&atom, value->data, 4);
116422d74663Smrg			if (!(name = NameForAtom(atom)))
116522d74663Smrg				return FALSE;
1166fda9279dSmrg
1167fda9279dSmrg			/* search for matching name string, then set its value down */
1168fda9279dSmrg			for (j = 0; j < p->mode_prop->count_enums; j++) {
1169fda9279dSmrg				if (!strcmp(p->mode_prop->enums[j].name, name)) {
1170fda9279dSmrg					ret = drmModeConnectorSetProperty(drmmode->fd,
1171fda9279dSmrg									  drmmode_output->output_id,
1172fda9279dSmrg									  p->mode_prop->prop_id,
1173fda9279dSmrg									  p->mode_prop->enums[j].value);
1174fda9279dSmrg
1175fda9279dSmrg					if (ret)
1176fda9279dSmrg						return FALSE;
1177fda9279dSmrg
1178fda9279dSmrg					return TRUE;
1179fda9279dSmrg				}
1180fda9279dSmrg			}
1181fda9279dSmrg
1182fda9279dSmrg			return FALSE;
1183fda9279dSmrg		}
1184fda9279dSmrg	}
1185fda9279dSmrg
1186fda9279dSmrg	return TRUE;
1187fda9279dSmrg}
1188fda9279dSmrg
1189fda9279dSmrgstatic Bool
1190fda9279dSmrgdrmmode_output_get_property(xf86OutputPtr output, Atom property)
1191fda9279dSmrg{
1192fda9279dSmrg
1193fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
1194fda9279dSmrg	drmmode_ptr drmmode = drmmode_output->drmmode;
1195fda9279dSmrg	uint32_t value;
1196fda9279dSmrg	int err, i;
1197fda9279dSmrg
1198fda9279dSmrg	if (output->scrn->vtSema) {
1199fda9279dSmrg		drmModeFreeConnector(drmmode_output->mode_output);
1200fda9279dSmrg		drmmode_output->mode_output =
1201fda9279dSmrg			drmModeGetConnector(drmmode->fd, drmmode_output->output_id);
1202fda9279dSmrg	}
1203fda9279dSmrg
1204fda9279dSmrg	if (!drmmode_output->mode_output)
1205fda9279dSmrg		return FALSE;
1206fda9279dSmrg
1207fda9279dSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
1208fda9279dSmrg		drmmode_prop_ptr p = &drmmode_output->props[i];
1209fda9279dSmrg		if (p->atoms[0] != property)
1210fda9279dSmrg			continue;
1211fda9279dSmrg
1212fda9279dSmrg		value = drmmode_output->mode_output->prop_values[p->index];
1213fda9279dSmrg
1214fda9279dSmrg		if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
1215fda9279dSmrg			err = RRChangeOutputProperty(output->randr_output,
1216fda9279dSmrg						     property, XA_INTEGER, 32,
1217fda9279dSmrg						     PropModeReplace, 1, &value,
1218fda9279dSmrg						     FALSE, FALSE);
1219fda9279dSmrg
1220fda9279dSmrg			return !err;
1221fda9279dSmrg		} else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
1222fda9279dSmrg			int		j;
1223fda9279dSmrg
1224fda9279dSmrg			/* search for matching name string, then set its value down */
1225fda9279dSmrg			for (j = 0; j < p->mode_prop->count_enums; j++) {
1226fda9279dSmrg				if (p->mode_prop->enums[j].value == value)
1227fda9279dSmrg					break;
1228fda9279dSmrg			}
1229fda9279dSmrg
1230fda9279dSmrg			err = RRChangeOutputProperty(output->randr_output, property,
1231fda9279dSmrg						     XA_ATOM, 32, PropModeReplace, 1,
1232fda9279dSmrg						     &p->atoms[j+1], FALSE, FALSE);
1233fda9279dSmrg
1234fda9279dSmrg			return !err;
1235fda9279dSmrg		}
1236fda9279dSmrg	}
1237fda9279dSmrg
1238fda9279dSmrg	return FALSE;
1239fda9279dSmrg}
1240fda9279dSmrg
1241fda9279dSmrgstatic const xf86OutputFuncsRec drmmode_output_funcs = {
1242fda9279dSmrg	.create_resources = drmmode_output_create_resources,
1243fda9279dSmrg	.dpms = drmmode_output_dpms,
1244fda9279dSmrg	.detect = drmmode_output_detect,
1245fda9279dSmrg	.mode_valid = drmmode_output_mode_valid,
1246fda9279dSmrg	.get_modes = drmmode_output_get_modes,
1247fda9279dSmrg	.set_property = drmmode_output_set_property,
1248fda9279dSmrg	.get_property = drmmode_output_get_property,
1249fda9279dSmrg	.destroy = drmmode_output_destroy
1250fda9279dSmrg};
1251fda9279dSmrg
1252fda9279dSmrgstatic int subpixel_conv_table[7] = { 0, SubPixelUnknown,
1253fda9279dSmrg				      SubPixelHorizontalRGB,
1254fda9279dSmrg				      SubPixelHorizontalBGR,
1255fda9279dSmrg				      SubPixelVerticalRGB,
1256fda9279dSmrg				      SubPixelVerticalBGR,
1257fda9279dSmrg				      SubPixelNone };
1258fda9279dSmrg
1259fda9279dSmrgconst char *output_names[] = { "None",
1260fda9279dSmrg			       "VGA",
1261fda9279dSmrg			       "DVI-I",
1262fda9279dSmrg			       "DVI-D",
1263fda9279dSmrg			       "DVI-A",
1264fda9279dSmrg			       "Composite",
1265fda9279dSmrg			       "SVIDEO",
1266fda9279dSmrg			       "LVDS",
1267fda9279dSmrg			       "CTV",
1268fda9279dSmrg			       "DIN",
1269fda9279dSmrg			       "DP",
1270fda9279dSmrg			       "HDMI",
1271fda9279dSmrg			       "HDMI",
1272fda9279dSmrg			       "TV",
1273fda9279dSmrg			       "eDP",
1274fda9279dSmrg};
1275fda9279dSmrg#define NUM_OUTPUT_NAMES (sizeof(output_names) / sizeof(output_names[0]))
1276fda9279dSmrg
1277fda9279dSmrgstatic Bool
1278fda9279dSmrgdrmmode_zaphod_match(ScrnInfoPtr pScrn, const char *s, char *output_name)
1279fda9279dSmrg{
1280fda9279dSmrg    int i = 0;
1281fda9279dSmrg    char s1[20];
1282fda9279dSmrg
1283fda9279dSmrg    do {
1284fda9279dSmrg	switch(*s) {
1285fda9279dSmrg	case ',':
1286fda9279dSmrg	    s1[i] = '\0';
1287fda9279dSmrg	    i = 0;
1288fda9279dSmrg	    if (strcmp(s1, output_name) == 0)
1289fda9279dSmrg		return TRUE;
1290fda9279dSmrg	    break;
1291fda9279dSmrg	case ' ':
1292fda9279dSmrg	case '\t':
1293fda9279dSmrg	case '\n':
1294fda9279dSmrg	case '\r':
1295fda9279dSmrg	    break;
1296fda9279dSmrg	default:
1297fda9279dSmrg	    s1[i] = *s;
1298fda9279dSmrg	    i++;
1299fda9279dSmrg	    break;
1300fda9279dSmrg	}
1301fda9279dSmrg    } while(*s++);
1302fda9279dSmrg
1303fda9279dSmrg    s1[i] = '\0';
1304fda9279dSmrg    if (strcmp(s1, output_name) == 0)
1305fda9279dSmrg	return TRUE;
1306fda9279dSmrg
1307fda9279dSmrg    return FALSE;
1308fda9279dSmrg}
1309fda9279dSmrg
131022d74663Smrgstatic xf86OutputPtr find_output(ScrnInfoPtr pScrn, int id)
131122d74663Smrg{
131222d74663Smrg	xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
131322d74663Smrg	int i;
131422d74663Smrg	for (i = 0; i < xf86_config->num_output; i++) {
131522d74663Smrg		xf86OutputPtr output = xf86_config->output[i];
131622d74663Smrg		drmmode_output_private_ptr drmmode_output;
131722d74663Smrg
131822d74663Smrg		drmmode_output = output->driver_private;
131922d74663Smrg		if (drmmode_output->output_id == id)
132022d74663Smrg			return output;
132122d74663Smrg	}
132222d74663Smrg	return NULL;
132322d74663Smrg}
132422d74663Smrg
132522d74663Smrgstatic int parse_path_blob(drmModePropertyBlobPtr path_blob, int *conn_base_id, char **path)
132622d74663Smrg{
132722d74663Smrg	char *conn;
132822d74663Smrg	char conn_id[5];
132922d74663Smrg	int id, len;
133022d74663Smrg	char *blob_data;
133122d74663Smrg
133222d74663Smrg	if (!path_blob)
133322d74663Smrg		return -1;
133422d74663Smrg
133522d74663Smrg	blob_data = path_blob->data;
133622d74663Smrg	/* we only handle MST paths for now */
133722d74663Smrg	if (strncmp(blob_data, "mst:", 4))
133822d74663Smrg		return -1;
133922d74663Smrg
134022d74663Smrg	conn = strchr(blob_data + 4, '-');
134122d74663Smrg	if (!conn)
134222d74663Smrg		return -1;
134322d74663Smrg	len = conn - (blob_data + 4);
134422d74663Smrg	if (len + 1 > 5)
134522d74663Smrg		return -1;
134622d74663Smrg	memcpy(conn_id, blob_data + 4, len);
134722d74663Smrg	conn_id[len] = '\0';
134822d74663Smrg	id = strtoul(conn_id, NULL, 10);
134922d74663Smrg
135022d74663Smrg	*conn_base_id = id;
135122d74663Smrg
135222d74663Smrg	*path = conn + 1;
135322d74663Smrg	return 0;
135422d74663Smrg}
135522d74663Smrg
135622d74663Smrgstatic void
135722d74663Smrgdrmmode_create_name(ScrnInfoPtr pScrn, drmModeConnectorPtr koutput, char *name,
135822d74663Smrg                    drmModePropertyBlobPtr path_blob)
135922d74663Smrg{
136022d74663Smrg	int ret;
136122d74663Smrg	char *extra_path;
136222d74663Smrg	int conn_id;
136322d74663Smrg	xf86OutputPtr output;
136422d74663Smrg
136522d74663Smrg	ret = parse_path_blob(path_blob, &conn_id, &extra_path);
136622d74663Smrg	if (ret == -1)
136722d74663Smrg		goto fallback;
136822d74663Smrg
136922d74663Smrg	output = find_output(pScrn, conn_id);
137022d74663Smrg	if (!output)
137122d74663Smrg		goto fallback;
137222d74663Smrg
137322d74663Smrg	snprintf(name, 32, "%s-%s", output->name, extra_path);
137422d74663Smrg	return;
137522d74663Smrg
137622d74663Smrgfallback:
137722d74663Smrg	if (koutput->connector_type >= ARRAY_SIZE(output_names))
137822d74663Smrg		snprintf(name, 32, "Unknown%d-%d", koutput->connector_type, koutput->connector_type_id);
137922d74663Smrg	else if (pScrn->is_gpu)
138022d74663Smrg		snprintf(name, 32, "%s-%d-%d", output_names[koutput->connector_type], pScrn->scrnIndex - GPU_SCREEN_OFFSET + 1, koutput->connector_type_id);
138122d74663Smrg	else
138222d74663Smrg		snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], koutput->connector_type_id);
138322d74663Smrg}
138422d74663Smrg
1385fda9279dSmrgstatic unsigned int
138622d74663Smrgdrmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res, int num, Bool dynamic, int crtcshift)
1387fda9279dSmrg{
1388fda9279dSmrg	NVPtr pNv = NVPTR(pScrn);
1389fda9279dSmrg	xf86OutputPtr output;
139022d74663Smrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
1391fda9279dSmrg	drmModeConnectorPtr koutput;
1392fda9279dSmrg	drmModeEncoderPtr kencoder;
1393fda9279dSmrg	drmmode_output_private_ptr drmmode_output;
1394fda9279dSmrg	const char *s;
1395fda9279dSmrg	char name[32];
139622d74663Smrg	drmModePropertyBlobPtr path_blob = NULL;
139722d74663Smrg	int i;
1398fda9279dSmrg
1399fda9279dSmrg	koutput = drmModeGetConnector(drmmode->fd,
140022d74663Smrg				      mode_res->connectors[num]);
1401fda9279dSmrg	if (!koutput)
1402fda9279dSmrg		return 0;
1403fda9279dSmrg
140422d74663Smrg	path_blob = koutput_get_prop_blob(drmmode->fd, koutput, "PATH");
140522d74663Smrg
140622d74663Smrg	drmmode_create_name(pScrn, koutput, name, path_blob);
140722d74663Smrg
140822d74663Smrg	if (path_blob)
140922d74663Smrg		drmModeFreePropertyBlob(path_blob);
141022d74663Smrg
141122d74663Smrg	if (path_blob && dynamic) {
141222d74663Smrg		/* see if we have an output with this name already
141322d74663Smrg		   and hook stuff up */
141422d74663Smrg		for (i = 0; i < xf86_config->num_output; i++) {
141522d74663Smrg			output = xf86_config->output[i];
141622d74663Smrg
141722d74663Smrg			if (strncmp(output->name, name, 32))
141822d74663Smrg				continue;
141922d74663Smrg
142022d74663Smrg			drmmode_output = output->driver_private;
142122d74663Smrg			drmmode_output->output_id = mode_res->connectors[num];
142222d74663Smrg			drmmode_output->mode_output = koutput;
142322d74663Smrg			return 1;
142422d74663Smrg		}
142522d74663Smrg	}
142622d74663Smrg
142722d74663Smrg
1428fda9279dSmrg	kencoder = drmModeGetEncoder(drmmode->fd, koutput->encoders[0]);
1429fda9279dSmrg	if (!kencoder) {
1430fda9279dSmrg		drmModeFreeConnector(koutput);
1431fda9279dSmrg		return 0;
1432fda9279dSmrg	}
1433fda9279dSmrg
1434fda9279dSmrg	if (xf86IsEntityShared(pScrn->entityList[0])) {
1435fda9279dSmrg		s = xf86GetOptValString(pNv->Options, OPTION_ZAPHOD_HEADS);
1436fda9279dSmrg		if (s) {
1437fda9279dSmrg			if (!drmmode_zaphod_match(pScrn, s, name)) {
1438fda9279dSmrg				drmModeFreeEncoder(kencoder);
1439fda9279dSmrg				drmModeFreeConnector(koutput);
1440fda9279dSmrg				return 0;
1441fda9279dSmrg			}
1442fda9279dSmrg		} else {
1443fda9279dSmrg			if (pNv->Primary && (num != 0)) {
1444fda9279dSmrg				drmModeFreeEncoder(kencoder);
1445fda9279dSmrg				drmModeFreeConnector(koutput);
1446fda9279dSmrg				return 0;
1447fda9279dSmrg			} else
1448fda9279dSmrg			if (pNv->Secondary && (num != 1)) {
1449fda9279dSmrg				drmModeFreeEncoder(kencoder);
1450fda9279dSmrg				drmModeFreeConnector(koutput);
1451fda9279dSmrg				return 0;
1452fda9279dSmrg			}
1453fda9279dSmrg		}
1454fda9279dSmrg	}
1455fda9279dSmrg
1456fda9279dSmrg	output = xf86OutputCreate (pScrn, &drmmode_output_funcs, name);
1457fda9279dSmrg	if (!output) {
1458fda9279dSmrg		drmModeFreeEncoder(kencoder);
1459fda9279dSmrg		drmModeFreeConnector(koutput);
1460fda9279dSmrg		return 0;
1461fda9279dSmrg	}
1462fda9279dSmrg
1463fda9279dSmrg	drmmode_output = calloc(sizeof(drmmode_output_private_rec), 1);
1464fda9279dSmrg	if (!drmmode_output) {
1465fda9279dSmrg		xf86OutputDestroy(output);
1466fda9279dSmrg		drmModeFreeConnector(koutput);
1467fda9279dSmrg		drmModeFreeEncoder(kencoder);
1468fda9279dSmrg		return 0;
1469fda9279dSmrg	}
1470fda9279dSmrg
147122d74663Smrg	drmmode_output->output_id = mode_res->connectors[num];
1472fda9279dSmrg	drmmode_output->mode_output = koutput;
1473fda9279dSmrg	drmmode_output->mode_encoder = kencoder;
1474fda9279dSmrg	drmmode_output->drmmode = drmmode;
1475fda9279dSmrg	output->mm_width = koutput->mmWidth;
1476fda9279dSmrg	output->mm_height = koutput->mmHeight;
1477fda9279dSmrg
1478fda9279dSmrg	output->subpixel_order = subpixel_conv_table[koutput->subpixel];
1479fda9279dSmrg	output->driver_private = drmmode_output;
1480fda9279dSmrg
1481a33a703bSmrg	output->possible_crtcs = kencoder->possible_crtcs >> crtcshift;
1482a33a703bSmrg	output->possible_clones = kencoder->possible_clones >> crtcshift;
1483fda9279dSmrg
1484fda9279dSmrg	output->interlaceAllowed = true;
1485fda9279dSmrg	output->doubleScanAllowed = true;
1486fda9279dSmrg
148722d74663Smrg	if (dynamic)
148822d74663Smrg		output->randr_output = RROutputCreate(xf86ScrnToScreen(pScrn), output->name, strlen(output->name), output);
148922d74663Smrg
1490fda9279dSmrg	return 1;
1491fda9279dSmrg}
1492fda9279dSmrg
1493fda9279dSmrgstatic Bool
1494fda9279dSmrgdrmmode_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height)
1495fda9279dSmrg{
1496fda9279dSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
1497fda9279dSmrg	ScreenPtr screen = xf86ScrnToScreen(scrn);
1498fda9279dSmrg	NVPtr pNv = NVPTR(scrn);
1499fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = NULL;
1500fda9279dSmrg	drmmode_ptr drmmode = NULL;
1501fda9279dSmrg	uint32_t old_width, old_height, old_pitch, old_fb_id = 0;
1502fda9279dSmrg	struct nouveau_bo *old_bo = NULL;
1503fda9279dSmrg	int ret, i, pitch;
1504fda9279dSmrg	PixmapPtr ppix;
1505fda9279dSmrg
1506fda9279dSmrg	if (xf86_config->num_crtc) {
1507fda9279dSmrg		drmmode_crtc = xf86_config->crtc[0]->driver_private;
1508fda9279dSmrg		drmmode = drmmode_crtc->drmmode;
1509fda9279dSmrg	}
1510fda9279dSmrg	ErrorF("resize called %d %d\n", width, height);
1511fda9279dSmrg
1512fda9279dSmrg	if (scrn->virtualX == width && scrn->virtualY == height)
1513fda9279dSmrg		return TRUE;
1514fda9279dSmrg
1515fda9279dSmrg	old_width = scrn->virtualX;
1516fda9279dSmrg	old_height = scrn->virtualY;
1517fda9279dSmrg	old_pitch = scrn->displayWidth;
1518fda9279dSmrg	if (drmmode)
1519fda9279dSmrg		old_fb_id = drmmode->fb_id;
1520fda9279dSmrg	nouveau_bo_ref(pNv->scanout, &old_bo);
1521fda9279dSmrg	nouveau_bo_ref(NULL, &pNv->scanout);
1522fda9279dSmrg
1523fda9279dSmrg	ret = nouveau_allocate_surface(scrn, width, height,
1524fda9279dSmrg				       scrn->bitsPerPixel,
1525fda9279dSmrg				       NOUVEAU_CREATE_PIXMAP_SCANOUT,
1526fda9279dSmrg				       &pitch, &pNv->scanout);
1527fda9279dSmrg	if (!ret)
1528fda9279dSmrg		goto fail;
1529fda9279dSmrg
1530fda9279dSmrg	scrn->virtualX = width;
1531fda9279dSmrg	scrn->virtualY = height;
1532fda9279dSmrg	scrn->displayWidth = pitch / (scrn->bitsPerPixel >> 3);
1533fda9279dSmrg
1534fda9279dSmrg	nouveau_bo_map(pNv->scanout, NOUVEAU_BO_RDWR, pNv->client);
1535fda9279dSmrg
1536fda9279dSmrg	if (drmmode) {
1537fda9279dSmrg		ret = drmModeAddFB(drmmode->fd, width, height, scrn->depth,
1538fda9279dSmrg				  scrn->bitsPerPixel, pitch, pNv->scanout->handle,
1539fda9279dSmrg				  &drmmode->fb_id);
1540fda9279dSmrg		if (ret)
1541fda9279dSmrg			goto fail;
1542fda9279dSmrg	}
1543fda9279dSmrg
1544fda9279dSmrg	if (pNv->ShadowPtr) {
1545fda9279dSmrg		free(pNv->ShadowPtr);
1546fda9279dSmrg		pNv->ShadowPitch = pitch;
1547fda9279dSmrg		pNv->ShadowPtr = malloc(pNv->ShadowPitch * height);
1548fda9279dSmrg	}
1549fda9279dSmrg
1550fda9279dSmrg	ppix = screen->GetScreenPixmap(screen);
1551fda9279dSmrg	if (pNv->AccelMethod >= NONE)
1552fda9279dSmrg		nouveau_bo_ref(pNv->scanout, &drmmode_pixmap(ppix)->bo);
1553fda9279dSmrg	screen->ModifyPixmapHeader(ppix, width, height, -1, -1, pitch,
1554fda9279dSmrg				   (pNv->AccelMethod > NONE || pNv->ShadowPtr) ?
1555fda9279dSmrg				   pNv->ShadowPtr : pNv->scanout->map);
1556fda9279dSmrg#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 9
1557fda9279dSmrg	scrn->pixmapPrivate.ptr = ppix->devPrivate.ptr;
1558fda9279dSmrg#endif
1559fda9279dSmrg
1560fda9279dSmrg	if (pNv->AccelMethod == EXA) {
1561fda9279dSmrg		pNv->EXADriverPtr->PrepareSolid(ppix, GXcopy, ~0, 0);
1562fda9279dSmrg		pNv->EXADriverPtr->Solid(ppix, 0, 0, width, height);
1563fda9279dSmrg		pNv->EXADriverPtr->DoneSolid(ppix);
1564fda9279dSmrg		nouveau_bo_map(pNv->scanout, NOUVEAU_BO_RDWR, pNv->client);
1565fda9279dSmrg	} else {
1566fda9279dSmrg		memset(pNv->scanout->map, 0x00, pNv->scanout->size);
1567fda9279dSmrg	}
1568fda9279dSmrg
1569fda9279dSmrg	for (i = 0; i < xf86_config->num_crtc; i++) {
1570fda9279dSmrg		xf86CrtcPtr crtc = xf86_config->crtc[i];
1571fda9279dSmrg
1572fda9279dSmrg		if (!crtc->enabled)
1573fda9279dSmrg			continue;
1574fda9279dSmrg
1575fda9279dSmrg		drmmode_set_mode_major(crtc, &crtc->mode,
1576fda9279dSmrg				       crtc->rotation, crtc->x, crtc->y);
1577fda9279dSmrg	}
1578fda9279dSmrg
1579fda9279dSmrg	if (old_fb_id)
1580fda9279dSmrg		drmModeRmFB(drmmode->fd, old_fb_id);
1581fda9279dSmrg	nouveau_bo_ref(NULL, &old_bo);
1582fda9279dSmrg
1583fda9279dSmrg	return TRUE;
1584fda9279dSmrg
1585fda9279dSmrg fail:
1586fda9279dSmrg	nouveau_bo_ref(old_bo, &pNv->scanout);
1587fda9279dSmrg	scrn->virtualX = old_width;
1588fda9279dSmrg	scrn->virtualY = old_height;
1589fda9279dSmrg	scrn->displayWidth = old_pitch;
1590fda9279dSmrg	if (drmmode)
1591fda9279dSmrg		drmmode->fb_id = old_fb_id;
1592fda9279dSmrg
1593fda9279dSmrg	return FALSE;
1594fda9279dSmrg}
1595fda9279dSmrg
1596fda9279dSmrgstatic const xf86CrtcConfigFuncsRec drmmode_xf86crtc_config_funcs = {
1597fda9279dSmrg	drmmode_xf86crtc_resize
1598fda9279dSmrg};
1599fda9279dSmrg
1600fda9279dSmrgBool drmmode_pre_init(ScrnInfoPtr pScrn, int fd, int cpp)
1601fda9279dSmrg{
1602fda9279dSmrg	drmmode_ptr drmmode;
160322d74663Smrg	drmModeResPtr mode_res;
1604fda9279dSmrg	NVEntPtr pNVEnt = NVEntPriv(pScrn);
1605fda9279dSmrg	int i;
1606fda9279dSmrg	unsigned int crtcs_needed = 0;
1607a33a703bSmrg	int crtcshift;
1608fda9279dSmrg
16091090d90aSmrg	drmmode = xnfcalloc(sizeof(*drmmode), 1);
1610fda9279dSmrg	drmmode->fd = fd;
1611fda9279dSmrg	drmmode->fb_id = 0;
1612fda9279dSmrg
1613fda9279dSmrg	xf86CrtcConfigInit(pScrn, &drmmode_xf86crtc_config_funcs);
1614fda9279dSmrg
1615fda9279dSmrg	drmmode->cpp = cpp;
161622d74663Smrg	mode_res = drmModeGetResources(drmmode->fd);
161722d74663Smrg	if (!mode_res)
1618fda9279dSmrg		return FALSE;
1619fda9279dSmrg
162022d74663Smrg	xf86CrtcSetSizeRange(pScrn, 320, 200, mode_res->max_width,
162122d74663Smrg			     mode_res->max_height);
1622fda9279dSmrg
162322d74663Smrg	if (!mode_res->count_connectors ||
162422d74663Smrg	    !mode_res->count_crtcs) {
1625fda9279dSmrg		free(drmmode);
1626fda9279dSmrg		goto done;
1627fda9279dSmrg	}
1628fda9279dSmrg
1629fda9279dSmrg	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Initializing outputs ...\n");
1630a33a703bSmrg	crtcshift = ffs(pNVEnt->assigned_crtcs ^ 0xffffffff) - 1;
163122d74663Smrg	for (i = 0; i < mode_res->count_connectors; i++)
163222d74663Smrg		crtcs_needed += drmmode_output_init(pScrn, drmmode, mode_res, i, FALSE, crtcshift);
1633fda9279dSmrg
1634fda9279dSmrg	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
1635fda9279dSmrg		   "%d crtcs needed for screen.\n", crtcs_needed);
1636fda9279dSmrg
163722d74663Smrg	for (i = 0; i < mode_res->count_crtcs; i++) {
1638fda9279dSmrg		if (!xf86IsEntityShared(pScrn->entityList[0]) ||
1639fda9279dSmrg		    (crtcs_needed && !(pNVEnt->assigned_crtcs & (1 << i))))
164022d74663Smrg			crtcs_needed -= drmmode_crtc_init(pScrn, drmmode, mode_res, i);
1641fda9279dSmrg	}
1642fda9279dSmrg
1643fda9279dSmrg	/* All ZaphodHeads outputs provided with matching crtcs? */
1644fda9279dSmrg	if (xf86IsEntityShared(pScrn->entityList[0]) && (crtcs_needed > 0))
1645fda9279dSmrg		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
1646fda9279dSmrg			   "%d ZaphodHeads crtcs unavailable. Trouble!\n",
1647fda9279dSmrg			   crtcs_needed);
1648fda9279dSmrg
1649fda9279dSmrgdone:
165022d74663Smrg	drmModeFreeResources(mode_res);
165122d74663Smrg
1652fda9279dSmrg#ifdef NOUVEAU_PIXMAP_SHARING
1653fda9279dSmrg	xf86ProviderSetup(pScrn, NULL, "nouveau");
1654fda9279dSmrg#endif
1655fda9279dSmrg
1656fda9279dSmrg	xf86InitialConfiguration(pScrn, TRUE);
1657fda9279dSmrg
1658fda9279dSmrg	return TRUE;
1659fda9279dSmrg}
1660fda9279dSmrg
1661fda9279dSmrgvoid
1662fda9279dSmrgdrmmode_adjust_frame(ScrnInfoPtr scrn, int x, int y)
1663fda9279dSmrg{
1664fda9279dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
1665fda9279dSmrg	xf86OutputPtr output = config->output[config->compat_output];
1666fda9279dSmrg	xf86CrtcPtr crtc = output->crtc;
1667fda9279dSmrg
1668fda9279dSmrg	if (!crtc || !crtc->enabled)
1669fda9279dSmrg		return;
1670fda9279dSmrg
1671fda9279dSmrg	drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation, x, y);
1672fda9279dSmrg}
1673fda9279dSmrg
1674fda9279dSmrgvoid
1675fda9279dSmrgdrmmode_remove_fb(ScrnInfoPtr pScrn)
1676fda9279dSmrg{
1677fda9279dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
1678fda9279dSmrg	xf86CrtcPtr crtc = NULL;
1679fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc;
1680fda9279dSmrg	drmmode_ptr drmmode;
1681fda9279dSmrg
1682fda9279dSmrg	if (config && config->num_crtc)
1683fda9279dSmrg		crtc = config->crtc[0];
1684fda9279dSmrg	if (!crtc)
1685fda9279dSmrg		return;
1686fda9279dSmrg
1687fda9279dSmrg	drmmode_crtc = crtc->driver_private;
1688fda9279dSmrg	drmmode = drmmode_crtc->drmmode;
1689fda9279dSmrg
1690fda9279dSmrg	if (drmmode->fb_id)
1691fda9279dSmrg		drmModeRmFB(drmmode->fd, drmmode->fb_id);
1692fda9279dSmrg	drmmode->fb_id = 0;
1693fda9279dSmrg}
1694fda9279dSmrg
1695fda9279dSmrgint
1696fda9279dSmrgdrmmode_cursor_init(ScreenPtr pScreen)
1697fda9279dSmrg{
1698fda9279dSmrg	NVPtr pNv = NVPTR(xf86ScreenToScrn(pScreen));
1699fda9279dSmrg	int size = nv_cursor_width(pNv);
1700fda9279dSmrg	int flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP |
1701fda9279dSmrg		    HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_32 |
1702fda9279dSmrg		    (pNv->dev->chipset >= 0x11 ? HARDWARE_CURSOR_ARGB : 0) |
1703fda9279dSmrg		    HARDWARE_CURSOR_UPDATE_UNHIDDEN;
1704fda9279dSmrg
1705fda9279dSmrg	return xf86_cursors_init(pScreen, size, size, flags);
1706fda9279dSmrg}
1707fda9279dSmrg
1708fda9279dSmrg#ifdef HAVE_LIBUDEV
170922d74663Smrg
171022d74663Smrg#define DRM_MODE_LINK_STATUS_GOOD       0
171122d74663Smrg#define DRM_MODE_LINK_STATUS_BAD        1
171222d74663Smrg
1713fda9279dSmrgstatic void
1714fda9279dSmrgdrmmode_handle_uevents(ScrnInfoPtr scrn)
1715fda9279dSmrg{
171622d74663Smrg    struct udev_device *dev;
171722d74663Smrg    drmmode_ptr drmmode = drmmode_from_scrn(scrn);
171822d74663Smrg    drmModeResPtr mode_res;
171922d74663Smrg    xf86CrtcConfigPtr  config = XF86_CRTC_CONFIG_PTR(scrn);
172022d74663Smrg    int i, j;
172122d74663Smrg    Bool found = FALSE;
172222d74663Smrg    Bool changed = FALSE;
172322d74663Smrg
172422d74663Smrg    while ((dev = udev_monitor_receive_device(drmmode->uevent_monitor))) {
172522d74663Smrg        udev_device_unref(dev);
172622d74663Smrg        found = TRUE;
172722d74663Smrg    }
172822d74663Smrg    if (!found)
172922d74663Smrg        return;
173022d74663Smrg
173122d74663Smrg    /* Try to re-set the mode on all the connectors with a BAD link-state:
173222d74663Smrg     * This may happen if a link degrades and a new modeset is necessary, using
173322d74663Smrg     * different link-training parameters. If the kernel found that the current
173422d74663Smrg     * mode is not achievable anymore, it should have pruned the mode before
173522d74663Smrg     * sending the hotplug event. Try to re-set the currently-set mode to keep
173622d74663Smrg     * the display alive, this will fail if the mode has been pruned.
173722d74663Smrg     * In any case, we will send randr events for the Desktop Environment to
173822d74663Smrg     * deal with it, if it wants to.
173922d74663Smrg     */
174022d74663Smrg    for (i = 0; i < config->num_output; i++) {
174122d74663Smrg        xf86OutputPtr output = config->output[i];
174222d74663Smrg        xf86CrtcPtr crtc = output->crtc;
174322d74663Smrg        drmmode_output_private_ptr drmmode_output = output->driver_private;
174422d74663Smrg        uint32_t con_id, idx;
174522d74663Smrg        drmModeConnectorPtr koutput;
174622d74663Smrg
174722d74663Smrg        if (crtc == NULL || drmmode_output->mode_output == NULL)
174822d74663Smrg            continue;
174922d74663Smrg
175022d74663Smrg        con_id = drmmode_output->mode_output->connector_id;
175122d74663Smrg        /* Get an updated view of the properties for the current connector and
175222d74663Smrg         * look for the link-status property
175322d74663Smrg         */
175422d74663Smrg        koutput = drmModeGetConnectorCurrent(drmmode->fd, con_id);
175522d74663Smrg        if (!koutput)
175622d74663Smrg            continue;
175722d74663Smrg
175822d74663Smrg        idx = koutput_get_prop_idx(drmmode->fd, koutput,
175922d74663Smrg                DRM_MODE_PROP_ENUM, "link-status");
176022d74663Smrg
176122d74663Smrg        if ((idx > -1) &&
176222d74663Smrg                (koutput->prop_values[idx] == DRM_MODE_LINK_STATUS_BAD)) {
176322d74663Smrg            /* the connector got a link failure, re-set the current mode */
176422d74663Smrg            drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation,
176522d74663Smrg                                   crtc->x, crtc->y);
176622d74663Smrg
176722d74663Smrg            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
176822d74663Smrg                       "hotplug event: connector %u's link-state is BAD, "
176922d74663Smrg                       "tried resetting the current mode. You may be left"
177022d74663Smrg                       "with a black screen if this fails...\n", con_id);
177122d74663Smrg        }
177222d74663Smrg
177322d74663Smrg        drmModeFreeConnector(koutput);
177422d74663Smrg    }
177522d74663Smrg
177622d74663Smrg    mode_res = drmModeGetResources(drmmode->fd);
177722d74663Smrg    if (!mode_res)
177822d74663Smrg        goto out;
177922d74663Smrg
178022d74663Smrg    if (mode_res->count_crtcs != config->num_crtc) {
178122d74663Smrg        ErrorF("number of CRTCs changed - failed to handle, %d vs %d\n", mode_res->count_crtcs, config->num_crtc);
178222d74663Smrg        goto out_free_res;
178322d74663Smrg    }
178422d74663Smrg
178522d74663Smrg    /* figure out if we have gotten rid of any connectors
178622d74663Smrg       traverse old output list looking for outputs */
178722d74663Smrg    for (i = 0; i < config->num_output; i++) {
178822d74663Smrg        xf86OutputPtr output = config->output[i];
178922d74663Smrg        drmmode_output_private_ptr drmmode_output;
179022d74663Smrg
179122d74663Smrg        drmmode_output = output->driver_private;
179222d74663Smrg        found = FALSE;
179322d74663Smrg        for (j = 0; j < mode_res->count_connectors; j++) {
179422d74663Smrg            if (mode_res->connectors[j] == drmmode_output->output_id) {
179522d74663Smrg                found = TRUE;
179622d74663Smrg                break;
179722d74663Smrg            }
179822d74663Smrg        }
179922d74663Smrg        if (found)
180022d74663Smrg            continue;
180122d74663Smrg
180222d74663Smrg        drmModeFreeConnector(drmmode_output->mode_output);
180322d74663Smrg        drmmode_output->mode_output = NULL;
180422d74663Smrg        drmmode_output->output_id = -1;
180522d74663Smrg
180622d74663Smrg        changed = TRUE;
180722d74663Smrg    }
180822d74663Smrg
180922d74663Smrg    /* find new output ids we don't have outputs for */
181022d74663Smrg    for (i = 0; i < mode_res->count_connectors; i++) {
181122d74663Smrg        found = FALSE;
181222d74663Smrg
181322d74663Smrg        for (j = 0; j < config->num_output; j++) {
181422d74663Smrg            xf86OutputPtr output = config->output[j];
181522d74663Smrg            drmmode_output_private_ptr drmmode_output;
181622d74663Smrg
181722d74663Smrg            drmmode_output = output->driver_private;
181822d74663Smrg            if (mode_res->connectors[i] == drmmode_output->output_id) {
181922d74663Smrg                found = TRUE;
182022d74663Smrg                break;
182122d74663Smrg            }
182222d74663Smrg        }
182322d74663Smrg        if (found)
182422d74663Smrg            continue;
182522d74663Smrg
182622d74663Smrg        changed = TRUE;
182722d74663Smrg        drmmode_output_init(scrn, drmmode, mode_res, i, TRUE, 0);
182822d74663Smrg    }
182922d74663Smrg
183022d74663Smrg    if (changed) {
183122d74663Smrg        RRSetChanged(xf86ScrnToScreen(scrn));
183222d74663Smrg        RRTellChanged(xf86ScrnToScreen(scrn));
183322d74663Smrg    }
183422d74663Smrg
183522d74663Smrgout_free_res:
183622d74663Smrg    drmModeFreeResources(mode_res);
183722d74663Smrgout:
183822d74663Smrg    RRGetInfo(xf86ScrnToScreen(scrn), TRUE);
183922d74663Smrg}
1840fda9279dSmrg
184122d74663Smrg#undef DRM_MODE_LINK_STATUS_BAD
184222d74663Smrg#undef DRM_MODE_LINK_STATUS_GOOD
1843fda9279dSmrg
184422d74663Smrg#endif
1845fda9279dSmrg
1846374ff59dSmrg#if HAVE_NOTIFY_FD
1847374ff59dSmrgstatic void
1848374ff59dSmrgdrmmode_udev_notify(int fd, int notify, void *data)
1849374ff59dSmrg{
1850374ff59dSmrg	ScrnInfoPtr scrn = data;
1851374ff59dSmrg	drmmode_handle_uevents(scrn);
1852374ff59dSmrg}
1853374ff59dSmrg#endif
1854374ff59dSmrg
18551090d90aSmrgstatic bool has_randr(void)
18561090d90aSmrg{
18571090d90aSmrg#if HAS_DIXREGISTERPRIVATEKEY
18581090d90aSmrg	return dixPrivateKeyRegistered(rrPrivKey);
18591090d90aSmrg#else
18601090d90aSmrg	return *rrPrivKey;
18611090d90aSmrg#endif
18621090d90aSmrg}
18631090d90aSmrg
1864fda9279dSmrgstatic void
1865fda9279dSmrgdrmmode_uevent_init(ScrnInfoPtr scrn)
1866fda9279dSmrg{
1867fda9279dSmrg#ifdef HAVE_LIBUDEV
1868fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
1869fda9279dSmrg	struct udev *u;
1870fda9279dSmrg	struct udev_monitor *mon;
1871fda9279dSmrg
18721090d90aSmrg	/* RandR will be disabled if Xinerama is active, and so generating
18731090d90aSmrg	 * RR hotplug events is then forbidden.
18741090d90aSmrg	 */
18751090d90aSmrg	if (!has_randr())
18761090d90aSmrg		return;
18771090d90aSmrg
1878fda9279dSmrg	u = udev_new();
1879fda9279dSmrg	if (!u)
1880fda9279dSmrg		return;
1881fda9279dSmrg	mon = udev_monitor_new_from_netlink(u, "udev");
1882fda9279dSmrg	if (!mon) {
1883fda9279dSmrg		udev_unref(u);
1884fda9279dSmrg		return;
1885fda9279dSmrg	}
1886fda9279dSmrg
1887fda9279dSmrg	if (udev_monitor_filter_add_match_subsystem_devtype(mon,
1888fda9279dSmrg							    "drm",
1889fda9279dSmrg							    "drm_minor") < 0 ||
1890fda9279dSmrg	    udev_monitor_enable_receiving(mon) < 0) {
1891fda9279dSmrg		udev_monitor_unref(mon);
1892fda9279dSmrg		udev_unref(u);
1893fda9279dSmrg		return;
1894fda9279dSmrg	}
1895fda9279dSmrg
1896374ff59dSmrg#if HAVE_NOTIFY_FD
1897374ff59dSmrg	SetNotifyFd(udev_monitor_get_fd(mon), drmmode_udev_notify, X_NOTIFY_READ, scrn);
1898374ff59dSmrg#else
1899fda9279dSmrg	AddGeneralSocket(udev_monitor_get_fd(mon));
1900374ff59dSmrg#endif
1901fda9279dSmrg	drmmode->uevent_monitor = mon;
1902fda9279dSmrg#endif
1903fda9279dSmrg}
1904fda9279dSmrg
1905fda9279dSmrgstatic void
1906fda9279dSmrgdrmmode_uevent_fini(ScrnInfoPtr scrn)
1907fda9279dSmrg{
1908fda9279dSmrg#ifdef HAVE_LIBUDEV
1909fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
1910fda9279dSmrg
1911fda9279dSmrg	if (drmmode->uevent_monitor) {
1912fda9279dSmrg		struct udev *u = udev_monitor_get_udev(drmmode->uevent_monitor);
1913fda9279dSmrg
1914374ff59dSmrg#if HAVE_NOTIFY_FD
1915374ff59dSmrg		RemoveNotifyFd(udev_monitor_get_fd(drmmode->uevent_monitor));
1916374ff59dSmrg#else
1917fda9279dSmrg		RemoveGeneralSocket(udev_monitor_get_fd(drmmode->uevent_monitor));
1918374ff59dSmrg#endif
1919fda9279dSmrg		udev_monitor_unref(drmmode->uevent_monitor);
1920fda9279dSmrg		udev_unref(u);
1921fda9279dSmrg	}
1922fda9279dSmrg#endif
1923fda9279dSmrg}
1924fda9279dSmrg
1925374ff59dSmrg#if HAVE_NOTIFY_FD
1926374ff59dSmrgstatic void
1927374ff59dSmrgdrmmode_notify_fd(int fd, int notify, void *data)
1928374ff59dSmrg{
1929374ff59dSmrg	ScrnInfoPtr scrn = data;
1930374ff59dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
1931374ff59dSmrg	drmHandleEvent(drmmode->fd, &drmmode->event_context);
1932374ff59dSmrg}
1933374ff59dSmrg#else
1934374ff59dSmrg
1935fda9279dSmrgstatic void
1936fda9279dSmrgdrmmode_wakeup_handler(pointer data, int err, pointer p)
1937fda9279dSmrg{
1938fda9279dSmrg	ScrnInfoPtr scrn = data;
1939fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
1940fda9279dSmrg	fd_set *read_mask = p;
1941fda9279dSmrg
1942fda9279dSmrg	if (scrn == NULL || err < 0)
1943fda9279dSmrg		return;
1944fda9279dSmrg
1945fda9279dSmrg	if (FD_ISSET(drmmode->fd, read_mask))
1946fda9279dSmrg		drmHandleEvent(drmmode->fd, &drmmode->event_context);
1947fda9279dSmrg
1948fda9279dSmrg#ifdef HAVE_LIBUDEV
1949fda9279dSmrg	if (FD_ISSET(udev_monitor_get_fd(drmmode->uevent_monitor), read_mask))
1950fda9279dSmrg		drmmode_handle_uevents(scrn);
1951fda9279dSmrg#endif
1952fda9279dSmrg}
1953374ff59dSmrg#endif
1954fda9279dSmrg
1955fda9279dSmrgvoid
1956fda9279dSmrgdrmmode_screen_init(ScreenPtr pScreen)
1957fda9279dSmrg{
1958fda9279dSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
1959fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
1960fda9279dSmrg	NVEntPtr pNVEnt = NVEntPriv(scrn);
1961fda9279dSmrg
1962fda9279dSmrg	/* Setup handler for DRM events */
1963fda9279dSmrg	drmmode_event_init(scrn);
1964fda9279dSmrg
1965fda9279dSmrg	/* Setup handler for udevevents */
1966fda9279dSmrg	drmmode_uevent_init(scrn);
1967fda9279dSmrg
1968fda9279dSmrg	/* Register wakeup handler only once per servergen, so ZaphodHeads work */
1969fda9279dSmrg	if (pNVEnt->fd_wakeup_registered != serverGeneration) {
1970fda9279dSmrg		/* Register a wakeup handler to get informed on DRM events */
1971374ff59dSmrg#if HAVE_NOTIFY_FD
1972374ff59dSmrg		SetNotifyFd(drmmode->fd, drmmode_notify_fd, X_NOTIFY_READ, scrn);
1973374ff59dSmrg#else
1974fda9279dSmrg		AddGeneralSocket(drmmode->fd);
1975fda9279dSmrg		RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
1976fda9279dSmrg		                               drmmode_wakeup_handler, scrn);
1977374ff59dSmrg#endif
1978fda9279dSmrg		pNVEnt->fd_wakeup_registered = serverGeneration;
1979fda9279dSmrg		pNVEnt->fd_wakeup_ref = 1;
1980fda9279dSmrg	}
1981fda9279dSmrg	else
1982fda9279dSmrg		pNVEnt->fd_wakeup_ref++;
1983fda9279dSmrg}
1984fda9279dSmrg
1985fda9279dSmrgvoid
1986fda9279dSmrgdrmmode_screen_fini(ScreenPtr pScreen)
1987fda9279dSmrg{
1988fda9279dSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
1989fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
1990fda9279dSmrg	NVEntPtr pNVEnt = NVEntPriv(scrn);
1991fda9279dSmrg
1992fda9279dSmrg	/* Unregister wakeup handler after last x-screen for this servergen dies. */
1993fda9279dSmrg	if (pNVEnt->fd_wakeup_registered == serverGeneration &&
1994fda9279dSmrg		!--pNVEnt->fd_wakeup_ref) {
1995fda9279dSmrg
1996374ff59dSmrg#if HAVE_NOTIFY_FD
1997374ff59dSmrg		RemoveNotifyFd(drmmode->fd);
1998374ff59dSmrg#else
1999fda9279dSmrg		/* Unregister wakeup handler */
2000fda9279dSmrg		RemoveBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
2001fda9279dSmrg		                             drmmode_wakeup_handler, scrn);
2002fda9279dSmrg		RemoveGeneralSocket(drmmode->fd);
2003374ff59dSmrg#endif
2004fda9279dSmrg	}
2005fda9279dSmrg
2006fda9279dSmrg	/* Tear down udev event handler */
2007fda9279dSmrg	drmmode_uevent_fini(scrn);
2008fda9279dSmrg
2009fda9279dSmrg	/* Tear down DRM event handler */
2010fda9279dSmrg	drmmode_event_fini(scrn);
2011fda9279dSmrg}
2012