drmmode_display.c revision 2a7e9763
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
1652a7e9763Smrgstatic bool warned = false;
1662a7e9763Smrg
167fda9279dSmrgstatic void
168fda9279dSmrgdrmmode_event_handler(int fd, unsigned int frame, unsigned int tv_sec,
169fda9279dSmrg		      unsigned int tv_usec, void *event_data)
170fda9279dSmrg{
171fda9279dSmrg	const uint64_t ust = (uint64_t)tv_sec * 1000000 + tv_usec;
172fda9279dSmrg	struct drmmode_event *e = event_data;
173fda9279dSmrg
1742a7e9763Smrg	int counter = 0;
1752a7e9763Smrg
176fda9279dSmrg	xorg_list_for_each_entry(e, &drmmode_events, head) {
1772a7e9763Smrg		counter++;
178fda9279dSmrg		if (e == event_data) {
179fda9279dSmrg			xorg_list_del(&e->head);
180fda9279dSmrg			e->func((void *)(e + 1), e->name, ust, frame);
181fda9279dSmrg			free(e);
182fda9279dSmrg			break;
183fda9279dSmrg		}
184fda9279dSmrg	}
1852a7e9763Smrg
1862a7e9763Smrg	if (counter > 100 && !warned) {
1872a7e9763Smrg		xf86DrvMsg(0, X_WARNING,
1882a7e9763Smrg			   "Event handler iterated %d times\n", counter);
1892a7e9763Smrg		warned = true;
1902a7e9763Smrg	}
191fda9279dSmrg}
192fda9279dSmrg
193fda9279dSmrgvoid
194fda9279dSmrgdrmmode_event_abort(ScrnInfoPtr scrn, uint64_t name, bool pending)
195fda9279dSmrg{
196fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
197fda9279dSmrg	struct drmmode_event *e, *t;
198fda9279dSmrg
199fda9279dSmrg	xorg_list_for_each_entry_safe(e, t, &drmmode_events, head) {
200fda9279dSmrg		if (e->drmmode == drmmode && e->name == name) {
201fda9279dSmrg			xorg_list_del(&e->head);
202fda9279dSmrg			if (!pending)
203fda9279dSmrg				free(e);
204fda9279dSmrg			break;
205fda9279dSmrg		}
206fda9279dSmrg	}
207fda9279dSmrg}
208fda9279dSmrg
209fda9279dSmrgvoid *
210fda9279dSmrgdrmmode_event_queue(ScrnInfoPtr scrn, uint64_t name, unsigned size,
211fda9279dSmrg		    void (*func)(void *, uint64_t, uint64_t, uint32_t),
212fda9279dSmrg		    void **event_data)
213fda9279dSmrg{
214fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
215fda9279dSmrg	struct drmmode_event *e;
216fda9279dSmrg
217fda9279dSmrg	e = *event_data = calloc(1, sizeof(*e) + size);
218fda9279dSmrg	if (e) {
219fda9279dSmrg		e->drmmode = drmmode;
220fda9279dSmrg		e->name = name;
221fda9279dSmrg		e->func = func;
222fda9279dSmrg		xorg_list_append(&e->head, &drmmode_events);
223fda9279dSmrg		return (void *)(e + 1);
224fda9279dSmrg	}
225fda9279dSmrg
226fda9279dSmrg	return NULL;
227fda9279dSmrg}
228fda9279dSmrg
229fda9279dSmrgint
230fda9279dSmrgdrmmode_event_flush(ScrnInfoPtr scrn)
231fda9279dSmrg{
232fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
233fda9279dSmrg	return drmHandleEvent(drmmode->fd, &drmmode->event_context);
234fda9279dSmrg}
235fda9279dSmrg
236fda9279dSmrgvoid
237fda9279dSmrgdrmmode_event_fini(ScrnInfoPtr scrn)
238fda9279dSmrg{
239fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
240fda9279dSmrg	struct drmmode_event *e, *t;
241fda9279dSmrg
242fda9279dSmrg	xorg_list_for_each_entry_safe(e, t, &drmmode_events, head) {
243fda9279dSmrg		if (e->drmmode == drmmode) {
244fda9279dSmrg			xorg_list_del(&e->head);
245fda9279dSmrg			free(e);
246fda9279dSmrg		}
247fda9279dSmrg	}
248fda9279dSmrg}
249fda9279dSmrg
250fda9279dSmrgvoid
251fda9279dSmrgdrmmode_event_init(ScrnInfoPtr scrn)
252fda9279dSmrg{
253fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
254fda9279dSmrg	drmmode->event_context.version = DRM_EVENT_CONTEXT_VERSION;
255fda9279dSmrg	drmmode->event_context.vblank_handler = drmmode_event_handler;
256fda9279dSmrg	drmmode->event_context.page_flip_handler = drmmode_event_handler;
257fda9279dSmrg}
258fda9279dSmrg
259fda9279dSmrgstatic PixmapPtr
260fda9279dSmrgdrmmode_pixmap_wrap(ScreenPtr pScreen, int width, int height, int depth,
261fda9279dSmrg		    int bpp, int pitch, struct nouveau_bo *bo, void *data)
262fda9279dSmrg{
263fda9279dSmrg	NVPtr pNv = NVPTR(xf86ScreenToScrn(pScreen));
264fda9279dSmrg	PixmapPtr ppix;
265fda9279dSmrg
266fda9279dSmrg	if (pNv->AccelMethod > NONE)
267fda9279dSmrg		data = NULL;
268fda9279dSmrg
269fda9279dSmrg	ppix = pScreen->CreatePixmap(pScreen, 0, 0, depth, 0);
270fda9279dSmrg	if (!ppix)
271fda9279dSmrg		return NULL;
272fda9279dSmrg
273fda9279dSmrg	pScreen->ModifyPixmapHeader(ppix, width, height, depth, bpp,
274fda9279dSmrg				    pitch, data);
275fda9279dSmrg	if (pNv->AccelMethod > NONE)
276fda9279dSmrg		nouveau_bo_ref(bo, &drmmode_pixmap(ppix)->bo);
277fda9279dSmrg
278fda9279dSmrg	return ppix;
279fda9279dSmrg}
280fda9279dSmrg
281fda9279dSmrgstatic void
282fda9279dSmrgdrmmode_ConvertFromKMode(ScrnInfoPtr scrn, drmModeModeInfo *kmode,
283fda9279dSmrg			 DisplayModePtr	mode)
284fda9279dSmrg{
285fda9279dSmrg	memset(mode, 0, sizeof(DisplayModeRec));
286fda9279dSmrg	mode->status = MODE_OK;
287fda9279dSmrg
288fda9279dSmrg	mode->Clock = kmode->clock;
289fda9279dSmrg
290fda9279dSmrg	mode->HDisplay = kmode->hdisplay;
291fda9279dSmrg	mode->HSyncStart = kmode->hsync_start;
292fda9279dSmrg	mode->HSyncEnd = kmode->hsync_end;
293fda9279dSmrg	mode->HTotal = kmode->htotal;
294fda9279dSmrg	mode->HSkew = kmode->hskew;
295fda9279dSmrg
296fda9279dSmrg	mode->VDisplay = kmode->vdisplay;
297fda9279dSmrg	mode->VSyncStart = kmode->vsync_start;
298fda9279dSmrg	mode->VSyncEnd = kmode->vsync_end;
299fda9279dSmrg	mode->VTotal = kmode->vtotal;
300fda9279dSmrg	mode->VScan = kmode->vscan;
301fda9279dSmrg
302fda9279dSmrg	mode->Flags = kmode->flags; //& FLAG_BITS;
303fda9279dSmrg	mode->name = strdup(kmode->name);
304fda9279dSmrg
305fda9279dSmrg	if (kmode->type & DRM_MODE_TYPE_DRIVER)
306fda9279dSmrg		mode->type = M_T_DRIVER;
307fda9279dSmrg	if (kmode->type & DRM_MODE_TYPE_PREFERRED)
308fda9279dSmrg		mode->type |= M_T_PREFERRED;
309fda9279dSmrg	xf86SetModeCrtc (mode, scrn->adjustFlags);
310fda9279dSmrg}
311fda9279dSmrg
312fda9279dSmrgstatic void
313fda9279dSmrgdrmmode_ConvertToKMode(ScrnInfoPtr scrn, drmModeModeInfo *kmode,
314fda9279dSmrg		       DisplayModePtr mode)
315fda9279dSmrg{
316fda9279dSmrg	memset(kmode, 0, sizeof(*kmode));
317fda9279dSmrg
318fda9279dSmrg	kmode->clock = mode->Clock;
319fda9279dSmrg	kmode->hdisplay = mode->HDisplay;
320fda9279dSmrg	kmode->hsync_start = mode->HSyncStart;
321fda9279dSmrg	kmode->hsync_end = mode->HSyncEnd;
322fda9279dSmrg	kmode->htotal = mode->HTotal;
323fda9279dSmrg	kmode->hskew = mode->HSkew;
324fda9279dSmrg
325fda9279dSmrg	kmode->vdisplay = mode->VDisplay;
326fda9279dSmrg	kmode->vsync_start = mode->VSyncStart;
327fda9279dSmrg	kmode->vsync_end = mode->VSyncEnd;
328fda9279dSmrg	kmode->vtotal = mode->VTotal;
329fda9279dSmrg	kmode->vscan = mode->VScan;
330fda9279dSmrg
331fda9279dSmrg	kmode->flags = mode->Flags; //& FLAG_BITS;
332fda9279dSmrg	if (mode->name)
333fda9279dSmrg		strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN);
334fda9279dSmrg	kmode->name[DRM_DISPLAY_MODE_LEN-1] = 0;
335fda9279dSmrg
336fda9279dSmrg}
337fda9279dSmrg
338fda9279dSmrgstatic void
3391090d90aSmrgdrmmode_crtc_dpms(xf86CrtcPtr crtc, int mode)
340fda9279dSmrg{
3411090d90aSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
3421090d90aSmrg	drmmode_crtc->dpms_mode = mode;
343fda9279dSmrg}
344fda9279dSmrg
345fda9279dSmrgvoid
346fda9279dSmrgdrmmode_fbcon_copy(ScreenPtr pScreen)
347fda9279dSmrg{
348fda9279dSmrg	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
349fda9279dSmrg	NVPtr pNv = NVPTR(pScrn);
350fda9279dSmrg#if XORG_VERSION_CURRENT >= 10999001
351fda9279dSmrg	ExaDriverPtr exa = pNv->EXADriverPtr;
352fda9279dSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
353fda9279dSmrg	struct nouveau_bo *bo = NULL;
354a33a703bSmrg	PixmapPtr pspix, pdpix = NULL;
355fda9279dSmrg	drmModeFBPtr fb;
356fda9279dSmrg	unsigned w = pScrn->virtualX, h = pScrn->virtualY;
357fda9279dSmrg	int i, ret, fbcon_id = 0;
358fda9279dSmrg
359fda9279dSmrg	if (pNv->AccelMethod != EXA)
360fda9279dSmrg		goto fallback;
361fda9279dSmrg
362a33a703bSmrg	pdpix = drmmode_pixmap_wrap(pScreen, pScrn->virtualX,
363a33a703bSmrg				    pScrn->virtualY, pScrn->depth,
364a33a703bSmrg				    pScrn->bitsPerPixel, pScrn->displayWidth *
365a33a703bSmrg				    pScrn->bitsPerPixel / 8, pNv->scanout,
366a33a703bSmrg				    NULL);
367a33a703bSmrg	if (!pdpix) {
368a33a703bSmrg		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
369a33a703bSmrg			   "Failed to init scanout pixmap for fbcon mirror\n");
370a33a703bSmrg		goto fallback;
371a33a703bSmrg	}
372a33a703bSmrg
373fda9279dSmrg	for (i = 0; i < xf86_config->num_crtc; i++) {
374fda9279dSmrg		drmmode_crtc_private_ptr drmmode_crtc =
375fda9279dSmrg			xf86_config->crtc[i]->driver_private;
376fda9279dSmrg
377fda9279dSmrg		if (drmmode_crtc->mode_crtc->buffer_id)
378fda9279dSmrg			fbcon_id = drmmode_crtc->mode_crtc->buffer_id;
379fda9279dSmrg	}
380fda9279dSmrg
381fda9279dSmrg	if (!fbcon_id)
382fda9279dSmrg		goto fallback;
383fda9279dSmrg
384fda9279dSmrg	fb = drmModeGetFB(pNv->dev->fd, fbcon_id);
385fda9279dSmrg	if (!fb) {
386fda9279dSmrg		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
387fda9279dSmrg			   "Failed to retrieve fbcon fb: id %d\n", fbcon_id);
388fda9279dSmrg		goto fallback;
389fda9279dSmrg	}
390fda9279dSmrg
391fda9279dSmrg	if (fb->depth != pScrn->depth || fb->width != w || fb->height != h) {
392fda9279dSmrg		drmFree(fb);
393fda9279dSmrg		goto fallback;
394fda9279dSmrg	}
395fda9279dSmrg
396fda9279dSmrg	ret = nouveau_bo_wrap(pNv->dev, fb->handle, &bo);
397fda9279dSmrg	if (ret) {
398fda9279dSmrg		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
399fda9279dSmrg			   "Failed to retrieve fbcon buffer: handle=0x%08x\n",
400fda9279dSmrg			   fb->handle);
401fda9279dSmrg		drmFree(fb);
402fda9279dSmrg		goto fallback;
403fda9279dSmrg	}
404fda9279dSmrg
405fda9279dSmrg	pspix = drmmode_pixmap_wrap(pScreen, fb->width, fb->height,
406fda9279dSmrg				    fb->depth, fb->bpp, fb->pitch, bo, NULL);
407fda9279dSmrg	nouveau_bo_ref(NULL, &bo);
408fda9279dSmrg	drmFree(fb);
409fda9279dSmrg	if (!pspix) {
410fda9279dSmrg		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
411fda9279dSmrg			   "Failed to create pixmap for fbcon contents\n");
412fda9279dSmrg		goto fallback;
413fda9279dSmrg	}
414fda9279dSmrg
415fda9279dSmrg	exa->PrepareCopy(pspix, pdpix, 0, 0, GXcopy, ~0);
416fda9279dSmrg	exa->Copy(pdpix, 0, 0, 0, 0, w, h);
417fda9279dSmrg	exa->DoneCopy(pdpix);
418fda9279dSmrg	PUSH_KICK(pNv->pushbuf);
419fda9279dSmrg
420fda9279dSmrg	/* wait for completion before continuing, avoids seeing a momentary
421fda9279dSmrg	 * flash of "corruption" on occasion
422fda9279dSmrg	 */
423fda9279dSmrg	nouveau_bo_wait(pNv->scanout, NOUVEAU_BO_RDWR, pNv->client);
424fda9279dSmrg
425fda9279dSmrg	pScreen->DestroyPixmap(pdpix);
426fda9279dSmrg	pScreen->DestroyPixmap(pspix);
427fda9279dSmrg	pScreen->canDoBGNoneRoot = TRUE;
428fda9279dSmrg	return;
429fda9279dSmrg
430fda9279dSmrgfallback:
431a33a703bSmrg	if (pdpix) {
432a33a703bSmrg		if (exa->PrepareSolid(pdpix, GXcopy, ~0, 0)) {
433a33a703bSmrg			exa->Solid(pdpix, 0, 0, w, h);
434a33a703bSmrg			exa->DoneSolid(pdpix);
435a33a703bSmrg			PUSH_KICK(pNv->pushbuf);
436a33a703bSmrg			nouveau_bo_wait(pNv->scanout, NOUVEAU_BO_RDWR, pNv->client);
437a33a703bSmrg			pScreen->DestroyPixmap(pdpix);
438a33a703bSmrg			return;
439a33a703bSmrg		}
440a33a703bSmrg		pScreen->DestroyPixmap(pdpix);
441a33a703bSmrg	}
442fda9279dSmrg#endif
443fda9279dSmrg	if (nouveau_bo_map(pNv->scanout, NOUVEAU_BO_WR, pNv->client))
444fda9279dSmrg		return;
445fda9279dSmrg	memset(pNv->scanout->map, 0x00, pNv->scanout->size);
446fda9279dSmrg}
447fda9279dSmrg
448fda9279dSmrgstatic Bool
449fda9279dSmrgdrmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
450fda9279dSmrg		       Rotation rotation, int x, int y)
451fda9279dSmrg{
452fda9279dSmrg	ScrnInfoPtr pScrn = crtc->scrn;
453fda9279dSmrg	NVPtr pNv = NVPTR(pScrn);
454fda9279dSmrg	xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
455fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
456fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
457fda9279dSmrg	uint32_t *output_ids;
458fda9279dSmrg	int output_count = 0;
459fda9279dSmrg	int ret = TRUE;
460fda9279dSmrg	int i;
461fda9279dSmrg	int fb_id;
462fda9279dSmrg	drmModeModeInfo kmode;
463fda9279dSmrg
464fda9279dSmrg	if (drmmode->fb_id == 0) {
465fda9279dSmrg		unsigned int pitch =
466fda9279dSmrg			pScrn->displayWidth * (pScrn->bitsPerPixel / 8);
467fda9279dSmrg
468fda9279dSmrg		ret = drmModeAddFB(drmmode->fd,
469fda9279dSmrg				   pScrn->virtualX, pScrn->virtualY,
470fda9279dSmrg				   pScrn->depth, pScrn->bitsPerPixel,
471fda9279dSmrg				   pitch, pNv->scanout->handle,
472fda9279dSmrg				   &drmmode->fb_id);
473fda9279dSmrg		if (ret < 0) {
474fda9279dSmrg			ErrorF("failed to add fb\n");
475fda9279dSmrg			return FALSE;
476fda9279dSmrg		}
477fda9279dSmrg	}
478fda9279dSmrg
479fda9279dSmrg	if (!xf86CrtcRotate(crtc))
480fda9279dSmrg		return FALSE;
481fda9279dSmrg
482fda9279dSmrg	output_ids = calloc(sizeof(uint32_t), xf86_config->num_output);
483fda9279dSmrg	if (!output_ids)
484fda9279dSmrg		return FALSE;
485fda9279dSmrg
486fda9279dSmrg	for (i = 0; i < xf86_config->num_output; i++) {
487fda9279dSmrg		xf86OutputPtr output = xf86_config->output[i];
488fda9279dSmrg		drmmode_output_private_ptr drmmode_output;
489fda9279dSmrg
490fda9279dSmrg		if (output->crtc != crtc)
491fda9279dSmrg			continue;
492fda9279dSmrg
493fda9279dSmrg		drmmode_output = output->driver_private;
49422d74663Smrg		if (drmmode_output->output_id == -1)
49522d74663Smrg			continue;
496fda9279dSmrg		output_ids[output_count] =
497fda9279dSmrg			drmmode_output->mode_output->connector_id;
498fda9279dSmrg		output_count++;
499fda9279dSmrg	}
500fda9279dSmrg
501fda9279dSmrg	drmmode_ConvertToKMode(crtc->scrn, &kmode, mode);
502fda9279dSmrg
503fda9279dSmrg	fb_id = drmmode->fb_id;
504fda9279dSmrg#ifdef NOUVEAU_PIXMAP_SHARING
505fda9279dSmrg	if (crtc->randr_crtc && crtc->randr_crtc->scanout_pixmap) {
506fda9279dSmrg		x = drmmode_crtc->scanout_pixmap_x;
507fda9279dSmrg		y = 0;
508fda9279dSmrg	} else
509fda9279dSmrg#endif
510fda9279dSmrg	if (drmmode_crtc->rotate_fb_id) {
511fda9279dSmrg		fb_id = drmmode_crtc->rotate_fb_id;
512fda9279dSmrg		x = 0;
513fda9279dSmrg		y = 0;
514fda9279dSmrg	}
515fda9279dSmrg
516fda9279dSmrg	ret = drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
517fda9279dSmrg			     fb_id, x, y, output_ids, output_count, &kmode);
518fda9279dSmrg	free(output_ids);
519fda9279dSmrg
520fda9279dSmrg	if (ret) {
521fda9279dSmrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
522fda9279dSmrg			   "failed to set mode: %s\n", strerror(-ret));
523fda9279dSmrg		return FALSE;
524fda9279dSmrg	}
525fda9279dSmrg
526fda9279dSmrg	/* Work around some xserver stupidity */
527fda9279dSmrg	for (i = 0; i < xf86_config->num_output; i++) {
528fda9279dSmrg		xf86OutputPtr output = xf86_config->output[i];
529fda9279dSmrg
530fda9279dSmrg		if (output->crtc != crtc)
531fda9279dSmrg			continue;
532fda9279dSmrg
533fda9279dSmrg		drmmode_output_dpms(output, DPMSModeOn);
534fda9279dSmrg	}
535fda9279dSmrg
536fda9279dSmrg	crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
537fda9279dSmrg			       crtc->gamma_blue, crtc->gamma_size);
538fda9279dSmrg
539861b9feeSmrg#ifdef HAVE_XF86_CURSOR_RESET_CURSOR
540861b9feeSmrg	xf86CursorResetCursor(crtc->scrn->pScreen);
54122d74663Smrg#else
542fda9279dSmrg	xf86_reload_cursors(crtc->scrn->pScreen);
543861b9feeSmrg#endif
544fda9279dSmrg
545fda9279dSmrg	return TRUE;
546fda9279dSmrg}
547fda9279dSmrg
548fda9279dSmrgstatic void
549fda9279dSmrgdrmmode_set_cursor_position (xf86CrtcPtr crtc, int x, int y)
550fda9279dSmrg{
551fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
552fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
553fda9279dSmrg
554fda9279dSmrg	drmModeMoveCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, x, y);
555fda9279dSmrg}
556fda9279dSmrg
557fda9279dSmrgstatic void
558fda9279dSmrgconvert_cursor(CARD32 *dst, CARD32 *src, int dw, int sw)
559fda9279dSmrg{
560fda9279dSmrg	int i, j;
561fda9279dSmrg
562fda9279dSmrg	for (j = 0;  j < sw; j++) {
563fda9279dSmrg		for (i = 0; i < sw; i++) {
564fda9279dSmrg			dst[j * dw + i] = src[j * sw + i];
565fda9279dSmrg		}
566fda9279dSmrg	}
567fda9279dSmrg}
568fda9279dSmrg
569fda9279dSmrgstatic void
570fda9279dSmrgdrmmode_load_cursor_argb (xf86CrtcPtr crtc, CARD32 *image)
571fda9279dSmrg{
572fda9279dSmrg	NVPtr pNv = NVPTR(crtc->scrn);
573fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
574fda9279dSmrg	struct nouveau_bo *cursor = drmmode_crtc->cursor;
575fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
576fda9279dSmrg
577fda9279dSmrg	nouveau_bo_map(cursor, NOUVEAU_BO_WR, pNv->client);
578fda9279dSmrg	convert_cursor(cursor->map, image, 64, nv_cursor_width(pNv));
579fda9279dSmrg
580fda9279dSmrg	if (drmmode_crtc->cursor_visible) {
581fda9279dSmrg		drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
582fda9279dSmrg				 cursor->handle, 64, 64);
583fda9279dSmrg	}
584fda9279dSmrg}
585fda9279dSmrg
586fda9279dSmrgstatic void
587fda9279dSmrgdrmmode_hide_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			 0, 64, 64);
594fda9279dSmrg	drmmode_crtc->cursor_visible = FALSE;
595fda9279dSmrg}
596fda9279dSmrg
597fda9279dSmrgstatic void
598fda9279dSmrgdrmmode_show_cursor (xf86CrtcPtr crtc)
599fda9279dSmrg{
600fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
601fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
602fda9279dSmrg
603fda9279dSmrg	drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
604fda9279dSmrg			 drmmode_crtc->cursor->handle, 64, 64);
605fda9279dSmrg	drmmode_crtc->cursor_visible = TRUE;
606fda9279dSmrg}
607fda9279dSmrg
608fda9279dSmrgstatic void *
609fda9279dSmrgdrmmode_crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
610fda9279dSmrg{
611fda9279dSmrg	ScrnInfoPtr scrn = crtc->scrn;
612fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
613fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
614fda9279dSmrg	void *virtual;
615fda9279dSmrg	int ret;
616fda9279dSmrg
617fda9279dSmrg	ret = nouveau_allocate_surface(scrn, width, height,
618fda9279dSmrg				       scrn->bitsPerPixel,
619fda9279dSmrg				       NOUVEAU_CREATE_PIXMAP_SCANOUT,
620fda9279dSmrg				       &drmmode_crtc->rotate_pitch,
621fda9279dSmrg				       &drmmode_crtc->rotate_bo);
622fda9279dSmrg	if (!ret) {
623fda9279dSmrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
624fda9279dSmrg			   "Couldn't allocate shadow memory for rotated CRTC\n");
625fda9279dSmrg		return NULL;
626fda9279dSmrg	}
627fda9279dSmrg
628fda9279dSmrg	ret = nouveau_bo_map(drmmode_crtc->rotate_bo, NOUVEAU_BO_RDWR,
629fda9279dSmrg			     NVPTR(scrn)->client);
630fda9279dSmrg	if (ret) {
631fda9279dSmrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
632fda9279dSmrg			   "Couldn't get virtual address of shadow scanout\n");
633fda9279dSmrg		nouveau_bo_ref(NULL, &drmmode_crtc->rotate_bo);
634fda9279dSmrg		return NULL;
635fda9279dSmrg	}
636fda9279dSmrg	virtual = drmmode_crtc->rotate_bo->map;
637fda9279dSmrg
638fda9279dSmrg	ret = drmModeAddFB(drmmode->fd, width, height, crtc->scrn->depth,
639fda9279dSmrg			   crtc->scrn->bitsPerPixel, drmmode_crtc->rotate_pitch,
640fda9279dSmrg			   drmmode_crtc->rotate_bo->handle,
641fda9279dSmrg			   &drmmode_crtc->rotate_fb_id);
642fda9279dSmrg	if (ret) {
643fda9279dSmrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
644fda9279dSmrg			   "Error adding FB for shadow scanout: %s\n",
645fda9279dSmrg			   strerror(-ret));
646fda9279dSmrg		nouveau_bo_ref(NULL, &drmmode_crtc->rotate_bo);
647fda9279dSmrg		return NULL;
648fda9279dSmrg	}
649fda9279dSmrg
650fda9279dSmrg	return virtual;
651fda9279dSmrg}
652fda9279dSmrg
653fda9279dSmrgstatic PixmapPtr
654fda9279dSmrgdrmmode_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
655fda9279dSmrg{
656fda9279dSmrg	ScrnInfoPtr pScrn = crtc->scrn;
657fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
658fda9279dSmrg	PixmapPtr rotate_pixmap;
659fda9279dSmrg
660fda9279dSmrg	if (!data)
661fda9279dSmrg		data = drmmode_crtc_shadow_allocate (crtc, width, height);
662fda9279dSmrg
663fda9279dSmrg	rotate_pixmap = drmmode_pixmap_wrap(pScrn->pScreen, width, height,
664fda9279dSmrg					    pScrn->depth, pScrn->bitsPerPixel,
665fda9279dSmrg					    drmmode_crtc->rotate_pitch,
666fda9279dSmrg					    drmmode_crtc->rotate_bo, data);
667fda9279dSmrg
668fda9279dSmrg	drmmode_crtc->rotate_pixmap = rotate_pixmap;
669fda9279dSmrg	return drmmode_crtc->rotate_pixmap;
670fda9279dSmrg}
671fda9279dSmrg
672fda9279dSmrgstatic void
673fda9279dSmrgdrmmode_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *data)
674fda9279dSmrg{
675fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
676fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
677fda9279dSmrg
678fda9279dSmrg	if (rotate_pixmap)
679fda9279dSmrg		FreeScratchPixmapHeader(rotate_pixmap);
680fda9279dSmrg
681fda9279dSmrg	if (data) {
682fda9279dSmrg		drmModeRmFB(drmmode->fd, drmmode_crtc->rotate_fb_id);
683fda9279dSmrg		drmmode_crtc->rotate_fb_id = 0;
684fda9279dSmrg		nouveau_bo_ref(NULL, &drmmode_crtc->rotate_bo);
685fda9279dSmrg		drmmode_crtc->rotate_pixmap = NULL;
686fda9279dSmrg	}
687fda9279dSmrg}
688fda9279dSmrg
689fda9279dSmrgstatic void
690fda9279dSmrgdrmmode_gamma_set(xf86CrtcPtr crtc, CARD16 *red, CARD16 *green, CARD16 *blue,
691fda9279dSmrg		  int size)
692fda9279dSmrg{
693fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
694fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
695fda9279dSmrg	int ret;
696fda9279dSmrg
697fda9279dSmrg	ret = drmModeCrtcSetGamma(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
698fda9279dSmrg				  size, red, green, blue);
699fda9279dSmrg	if (ret != 0) {
700fda9279dSmrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
70122d74663Smrg			   "failed to set gamma with %d entries: %s\n",
70222d74663Smrg			   size, strerror(-ret));
703fda9279dSmrg	}
704fda9279dSmrg}
705fda9279dSmrg
706fda9279dSmrg#ifdef NOUVEAU_PIXMAP_SHARING
707fda9279dSmrgstatic Bool
708fda9279dSmrgdrmmode_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix)
709fda9279dSmrg{
710fda9279dSmrg	ScreenPtr screen = xf86ScrnToScreen(crtc->scrn);
711fda9279dSmrg	PixmapPtr screenpix = screen->GetScreenPixmap(screen);
712fda9279dSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
713fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
714374ff59dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
715fda9279dSmrg	int c, total_width = 0, max_height = 0, this_x = 0;
716fda9279dSmrg	if (!ppix) {
717374ff59dSmrg		if (crtc->randr_crtc->scanout_pixmap) {
71822d74663Smrg#ifdef HAS_DIRTYTRACKING_DRAWABLE_SRC
71922d74663Smrg			PixmapStopDirtyTracking(&crtc->randr_crtc->scanout_pixmap->drawable, screenpix);
72022d74663Smrg#else
72122d74663Smrg			PixmapStopDirtyTracking(crtc->randr_crtc->scanout_pixmap, screenpix);
72222d74663Smrg#endif
723374ff59dSmrg			if (drmmode && drmmode->fb_id) {
724374ff59dSmrg				drmModeRmFB(drmmode->fd, drmmode->fb_id);
725374ff59dSmrg				drmmode->fb_id = 0;
726374ff59dSmrg			}
727374ff59dSmrg		}
728fda9279dSmrg		drmmode_crtc->scanout_pixmap_x = 0;
729fda9279dSmrg		return TRUE;
730fda9279dSmrg	}
731fda9279dSmrg
732fda9279dSmrg	/* iterate over all the attached crtcs -
733fda9279dSmrg	   work out bounding box */
734fda9279dSmrg	for (c = 0; c < xf86_config->num_crtc; c++) {
735fda9279dSmrg		xf86CrtcPtr iter = xf86_config->crtc[c];
736fda9279dSmrg		if (!iter->enabled && iter != crtc)
737fda9279dSmrg			continue;
738fda9279dSmrg		if (iter == crtc) {
739fda9279dSmrg			this_x = total_width;
740fda9279dSmrg			total_width += ppix->drawable.width;
741fda9279dSmrg			if (max_height < ppix->drawable.height)
742fda9279dSmrg				max_height = ppix->drawable.height;
743fda9279dSmrg		} else {
744fda9279dSmrg			total_width += iter->mode.HDisplay;
745fda9279dSmrg			if (max_height < iter->mode.VDisplay)
746fda9279dSmrg				max_height = iter->mode.VDisplay;
747fda9279dSmrg		}
748374ff59dSmrg#if !defined(HAS_DIRTYTRACKING_ROTATION) && !defined(HAS_DIRTYTRACKING2)
749fda9279dSmrg	if (iter != crtc) {
750fda9279dSmrg		ErrorF("Cannot do multiple crtcs without X server dirty tracking 2 interface\n");
751fda9279dSmrg		return FALSE;
752fda9279dSmrg	}
753fda9279dSmrg#endif
754fda9279dSmrg	}
755fda9279dSmrg
756fda9279dSmrg	if (total_width != screenpix->drawable.width ||
757fda9279dSmrg	    max_height != screenpix->drawable.height) {
758fda9279dSmrg		Bool ret;
759fda9279dSmrg		ret = drmmode_xf86crtc_resize(crtc->scrn, total_width, max_height);
760fda9279dSmrg		if (ret == FALSE)
761fda9279dSmrg			return FALSE;
762fda9279dSmrg
763fda9279dSmrg		screenpix = screen->GetScreenPixmap(screen);
764fda9279dSmrg		screen->width = screenpix->drawable.width = total_width;
765fda9279dSmrg		screen->height = screenpix->drawable.height = max_height;
766fda9279dSmrg	}
767fda9279dSmrg	drmmode_crtc->scanout_pixmap_x = this_x;
76822d74663Smrg
76922d74663Smrg#ifdef HAS_DIRTYTRACKING_DRAWABLE_SRC
77022d74663Smrg	PixmapStartDirtyTracking(&ppix->drawable, screenpix, 0, 0, this_x, 0, RR_Rotate_0);
77122d74663Smrg#elif defined(HAS_DIRTYTRACKING_ROTATION)
77222d74663Smrg	PixmapStartDirtyTracking(ppix, screenpix, 0, 0, this_x, 0, RR_Rotate_0);
773a33a703bSmrg#elif defined(HAS_DIRTYTRACKING2)
774fda9279dSmrg	PixmapStartDirtyTracking2(ppix, screenpix, 0, 0, this_x, 0);
775fda9279dSmrg#else
776fda9279dSmrg	PixmapStartDirtyTracking(ppix, screenpix, 0, 0);
777fda9279dSmrg#endif
778fda9279dSmrg	return TRUE;
779fda9279dSmrg}
780fda9279dSmrg#endif
781fda9279dSmrg
782fda9279dSmrgstatic const xf86CrtcFuncsRec drmmode_crtc_funcs = {
783fda9279dSmrg	.dpms = drmmode_crtc_dpms,
784fda9279dSmrg	.set_mode_major = drmmode_set_mode_major,
785fda9279dSmrg	.set_cursor_position = drmmode_set_cursor_position,
786fda9279dSmrg	.show_cursor = drmmode_show_cursor,
787fda9279dSmrg	.hide_cursor = drmmode_hide_cursor,
788fda9279dSmrg	.load_cursor_argb = drmmode_load_cursor_argb,
789fda9279dSmrg	.shadow_create = drmmode_crtc_shadow_create,
790fda9279dSmrg	.shadow_allocate = drmmode_crtc_shadow_allocate,
791fda9279dSmrg	.shadow_destroy = drmmode_crtc_shadow_destroy,
792fda9279dSmrg	.gamma_set = drmmode_gamma_set,
793fda9279dSmrg
794fda9279dSmrg#ifdef NOUVEAU_PIXMAP_SHARING
795fda9279dSmrg	.set_scanout_pixmap = drmmode_set_scanout_pixmap,
796fda9279dSmrg#endif
797fda9279dSmrg};
798fda9279dSmrg
799fda9279dSmrg
800fda9279dSmrgstatic unsigned int
80122d74663Smrgdrmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res, int num)
802fda9279dSmrg{
803fda9279dSmrg	NVPtr pNv = NVPTR(pScrn);
804fda9279dSmrg	NVEntPtr pNVEnt = NVEntPriv(pScrn);
805fda9279dSmrg	xf86CrtcPtr crtc;
806fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc;
807fda9279dSmrg	int ret;
808fda9279dSmrg
809fda9279dSmrg	crtc = xf86CrtcCreate(pScrn, &drmmode_crtc_funcs);
810fda9279dSmrg	if (crtc == NULL)
811fda9279dSmrg		return 0;
812fda9279dSmrg
813fda9279dSmrg	drmmode_crtc = xnfcalloc(sizeof(drmmode_crtc_private_rec), 1);
814fda9279dSmrg	drmmode_crtc->mode_crtc = drmModeGetCrtc(drmmode->fd,
81522d74663Smrg						 mode_res->crtcs[num]);
816fda9279dSmrg	drmmode_crtc->drmmode = drmmode;
817fda9279dSmrg	drmmode_crtc->hw_crtc_index = num;
818fda9279dSmrg
819fda9279dSmrg	ret = nouveau_bo_new(pNv->dev, NOUVEAU_BO_GART | NOUVEAU_BO_MAP, 0,
820fda9279dSmrg			     64*64*4, NULL, &drmmode_crtc->cursor);
821fda9279dSmrg	assert(ret == 0);
822fda9279dSmrg
823fda9279dSmrg	crtc->driver_private = drmmode_crtc;
824fda9279dSmrg
825fda9279dSmrg	/* Mark num'th crtc as in use on this device. */
826fda9279dSmrg	pNVEnt->assigned_crtcs |= (1 << num);
827fda9279dSmrg	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
828fda9279dSmrg		   "Allocated crtc nr. %d to this screen.\n", num);
829fda9279dSmrg
830fda9279dSmrg	return 1;
831fda9279dSmrg}
832fda9279dSmrg
833fda9279dSmrgstatic xf86OutputStatus
834fda9279dSmrgdrmmode_output_detect(xf86OutputPtr output)
835fda9279dSmrg{
836fda9279dSmrg	/* go to the hw and retrieve a new output struct */
837fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
838fda9279dSmrg	drmmode_ptr drmmode = drmmode_output->drmmode;
839fda9279dSmrg	xf86OutputStatus status;
84022d74663Smrg
84122d74663Smrg	if (drmmode_output->output_id == -1)
84222d74663Smrg		return XF86OutputStatusDisconnected;
84322d74663Smrg
844fda9279dSmrg	drmModeFreeConnector(drmmode_output->mode_output);
845fda9279dSmrg
846fda9279dSmrg	drmmode_output->mode_output =
847fda9279dSmrg		drmModeGetConnector(drmmode->fd, drmmode_output->output_id);
848fda9279dSmrg
84922d74663Smrg	if (!drmmode_output->mode_output) {
85022d74663Smrg		drmmode_output->output_id = -1;
851fda9279dSmrg		return XF86OutputStatusDisconnected;
85222d74663Smrg	}
853fda9279dSmrg
854fda9279dSmrg	switch (drmmode_output->mode_output->connection) {
855fda9279dSmrg	case DRM_MODE_CONNECTED:
856fda9279dSmrg		status = XF86OutputStatusConnected;
857fda9279dSmrg		break;
858fda9279dSmrg	case DRM_MODE_DISCONNECTED:
859fda9279dSmrg		status = XF86OutputStatusDisconnected;
860fda9279dSmrg		break;
861fda9279dSmrg	default:
862fda9279dSmrg	case DRM_MODE_UNKNOWNCONNECTION:
863fda9279dSmrg		status = XF86OutputStatusUnknown;
864fda9279dSmrg		break;
865fda9279dSmrg	}
866fda9279dSmrg	return status;
867fda9279dSmrg}
868fda9279dSmrg
869fda9279dSmrgstatic Bool
870fda9279dSmrgdrmmode_output_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
871fda9279dSmrg{
872fda9279dSmrg	if (mode->type & M_T_DEFAULT)
873fda9279dSmrg		/* Default modes are harmful here. */
874fda9279dSmrg		return MODE_BAD;
875fda9279dSmrg
876fda9279dSmrg	return MODE_OK;
877fda9279dSmrg}
878fda9279dSmrg
87922d74663Smrgstatic int
88022d74663Smrgkoutput_get_prop_idx(int fd, drmModeConnectorPtr koutput,
88122d74663Smrg		     int type, const char *name)
88222d74663Smrg{
88322d74663Smrg	int idx = -1;
88422d74663Smrg
88522d74663Smrg	for (int i = 0; i < koutput->count_props; i++) {
88622d74663Smrg		drmModePropertyPtr prop = drmModeGetProperty(fd, koutput->props[i]);
88722d74663Smrg
88822d74663Smrg		if (!prop)
88922d74663Smrg			continue;
89022d74663Smrg
89122d74663Smrg		if (drm_property_type_is(prop, type) && !strcmp(prop->name, name))
89222d74663Smrg			idx = i;
89322d74663Smrg
89422d74663Smrg		drmModeFreeProperty(prop);
89522d74663Smrg
89622d74663Smrg		if (idx > -1)
89722d74663Smrg			break;
89822d74663Smrg	}
89922d74663Smrg
90022d74663Smrg	return idx;
90122d74663Smrg}
90222d74663Smrg
90322d74663Smrgstatic drmModePropertyBlobPtr
90422d74663Smrgkoutput_get_prop_blob(int fd, drmModeConnectorPtr koutput, const char *name)
90522d74663Smrg{
90622d74663Smrg	drmModePropertyBlobPtr blob = NULL;
90722d74663Smrg	int idx = koutput_get_prop_idx(fd, koutput, DRM_MODE_PROP_BLOB, name);
90822d74663Smrg
90922d74663Smrg	if (idx > -1)
91022d74663Smrg		blob = drmModeGetPropertyBlob(fd, koutput->prop_values[idx]);
91122d74663Smrg
91222d74663Smrg	return blob;
91322d74663Smrg}
91422d74663Smrg
91522d74663Smrgstatic void
91622d74663Smrgdrmmode_output_attach_tile(xf86OutputPtr output)
91722d74663Smrg{
91822d74663Smrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
91922d74663Smrg	drmModeConnectorPtr koutput = drmmode_output->mode_output;
92022d74663Smrg	drmmode_ptr drmmode = drmmode_output->drmmode;
92122d74663Smrg	struct xf86CrtcTileInfo tile_info, *set = NULL;
92222d74663Smrg
92322d74663Smrg	if (!koutput) {
92422d74663Smrg		xf86OutputSetTile(output, NULL);
92522d74663Smrg		return;
92622d74663Smrg	}
92722d74663Smrg
92822d74663Smrg	drmModeFreePropertyBlob(drmmode_output->tile_blob);
92922d74663Smrg
93022d74663Smrg	/* look for a TILE property */
93122d74663Smrg	drmmode_output->tile_blob =
93222d74663Smrg		koutput_get_prop_blob(drmmode->fd, koutput, "TILE");
93322d74663Smrg
93422d74663Smrg	if (drmmode_output->tile_blob) {
93522d74663Smrg		if (xf86OutputParseKMSTile(drmmode_output->tile_blob->data, drmmode_output->tile_blob->length, &tile_info) == TRUE)
93622d74663Smrg			set = &tile_info;
93722d74663Smrg	}
93822d74663Smrg	xf86OutputSetTile(output, set);
93922d74663Smrg}
94022d74663Smrg
94122d74663Smrg
942fda9279dSmrgstatic DisplayModePtr
943fda9279dSmrgdrmmode_output_get_modes(xf86OutputPtr output)
944fda9279dSmrg{
945fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
946fda9279dSmrg	drmModeConnectorPtr koutput = drmmode_output->mode_output;
947fda9279dSmrg	drmmode_ptr drmmode = drmmode_output->drmmode;
948fda9279dSmrg	int i;
949fda9279dSmrg	DisplayModePtr Modes = NULL, Mode;
950fda9279dSmrg	xf86MonPtr ddc_mon = NULL;
951fda9279dSmrg
952fda9279dSmrg	if (!koutput)
953fda9279dSmrg		return NULL;
954fda9279dSmrg
955fda9279dSmrg	/* look for an EDID property */
95622d74663Smrg	drmmode_output->edid_blob =
95722d74663Smrg		koutput_get_prop_blob(drmmode->fd, koutput, "EDID");
958fda9279dSmrg
959fda9279dSmrg	if (drmmode_output->edid_blob) {
960fda9279dSmrg		ddc_mon = xf86InterpretEDID(output->scrn->scrnIndex,
961fda9279dSmrg					    drmmode_output->edid_blob->data);
962fda9279dSmrg		if (ddc_mon && drmmode_output->edid_blob->length > 128)
963fda9279dSmrg			ddc_mon->flags |= MONITOR_EDID_COMPLETE_RAWDATA;
964fda9279dSmrg	}
965fda9279dSmrg	xf86OutputSetEDID(output, ddc_mon);
966fda9279dSmrg
96722d74663Smrg	drmmode_output_attach_tile(output);
96822d74663Smrg
969fda9279dSmrg	/* modes should already be available */
970fda9279dSmrg	for (i = 0; i < koutput->count_modes; i++) {
971fda9279dSmrg		Mode = xnfalloc(sizeof(DisplayModeRec));
972fda9279dSmrg
973fda9279dSmrg		drmmode_ConvertFromKMode(output->scrn, &koutput->modes[i],
974fda9279dSmrg					 Mode);
975fda9279dSmrg		Modes = xf86ModesAdd(Modes, Mode);
976fda9279dSmrg
977fda9279dSmrg	}
978fda9279dSmrg	return Modes;
979fda9279dSmrg}
980fda9279dSmrg
981fda9279dSmrgstatic void
982fda9279dSmrgdrmmode_output_destroy(xf86OutputPtr output)
983fda9279dSmrg{
984fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
985fda9279dSmrg	int i;
986fda9279dSmrg
987fda9279dSmrg	if (drmmode_output->edid_blob)
988fda9279dSmrg		drmModeFreePropertyBlob(drmmode_output->edid_blob);
98922d74663Smrg	if (drmmode_output->tile_blob)
99022d74663Smrg		drmModeFreePropertyBlob(drmmode_output->tile_blob);
991fda9279dSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
992fda9279dSmrg		drmModeFreeProperty(drmmode_output->props[i].mode_prop);
993fda9279dSmrg		free(drmmode_output->props[i].atoms);
994fda9279dSmrg	}
995fda9279dSmrg	drmModeFreeConnector(drmmode_output->mode_output);
996fda9279dSmrg	free(drmmode_output);
997fda9279dSmrg	output->driver_private = NULL;
998fda9279dSmrg}
999fda9279dSmrg
1000fda9279dSmrgstatic void
1001fda9279dSmrgdrmmode_output_dpms(xf86OutputPtr output, int mode)
1002fda9279dSmrg{
1003fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
1004fda9279dSmrg	drmModeConnectorPtr koutput = drmmode_output->mode_output;
1005fda9279dSmrg	drmModePropertyPtr props;
1006fda9279dSmrg	drmmode_ptr drmmode = drmmode_output->drmmode;
1007fda9279dSmrg	int mode_id = -1, i;
1008fda9279dSmrg
100922d74663Smrg	if (!koutput)
101022d74663Smrg		return;
101122d74663Smrg
1012fda9279dSmrg	for (i = 0; i < koutput->count_props; i++) {
1013fda9279dSmrg		props = drmModeGetProperty(drmmode->fd, koutput->props[i]);
101411c9f444Sriastradh		if (props && (props->flags & DRM_MODE_PROP_ENUM)) {
1015fda9279dSmrg			if (!strcmp(props->name, "DPMS")) {
1016fda9279dSmrg				mode_id = koutput->props[i];
1017fda9279dSmrg				drmModeFreeProperty(props);
1018fda9279dSmrg				break;
1019fda9279dSmrg			}
1020fda9279dSmrg			drmModeFreeProperty(props);
1021fda9279dSmrg		}
1022fda9279dSmrg	}
1023fda9279dSmrg
1024fda9279dSmrg	if (mode_id < 0)
1025fda9279dSmrg		return;
1026fda9279dSmrg
1027fda9279dSmrg	drmModeConnectorSetProperty(drmmode->fd, koutput->connector_id,
1028fda9279dSmrg				    mode_id, mode);
1029fda9279dSmrg}
1030fda9279dSmrg
1031fda9279dSmrgstatic Bool
1032fda9279dSmrgdrmmode_property_ignore(drmModePropertyPtr prop)
1033fda9279dSmrg{
1034fda9279dSmrg	if (!prop)
1035fda9279dSmrg	    return TRUE;
1036fda9279dSmrg	/* ignore blob prop */
1037fda9279dSmrg	if (prop->flags & DRM_MODE_PROP_BLOB)
1038fda9279dSmrg		return TRUE;
1039fda9279dSmrg	/* ignore standard property */
1040fda9279dSmrg	if (!strcmp(prop->name, "EDID") ||
1041fda9279dSmrg	    !strcmp(prop->name, "DPMS"))
1042fda9279dSmrg		return TRUE;
1043fda9279dSmrg
1044fda9279dSmrg	return FALSE;
1045fda9279dSmrg}
1046fda9279dSmrg
1047fda9279dSmrgstatic void
1048fda9279dSmrgdrmmode_output_create_resources(xf86OutputPtr output)
1049fda9279dSmrg{
1050fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
1051fda9279dSmrg	drmModeConnectorPtr mode_output = drmmode_output->mode_output;
1052fda9279dSmrg	drmmode_ptr drmmode = drmmode_output->drmmode;
1053fda9279dSmrg	drmModePropertyPtr drmmode_prop;
1054fda9279dSmrg	uint32_t value;
1055fda9279dSmrg	int i, j, err;
1056fda9279dSmrg
1057fda9279dSmrg	drmmode_output->props = calloc(mode_output->count_props, sizeof(drmmode_prop_rec));
1058fda9279dSmrg	if (!drmmode_output->props)
1059fda9279dSmrg		return;
1060fda9279dSmrg
1061fda9279dSmrg	drmmode_output->num_props = 0;
1062fda9279dSmrg	for (i = 0, j = 0; i < mode_output->count_props; i++) {
1063fda9279dSmrg		drmmode_prop = drmModeGetProperty(drmmode->fd, mode_output->props[i]);
1064fda9279dSmrg		if (drmmode_property_ignore(drmmode_prop)) {
1065fda9279dSmrg			drmModeFreeProperty(drmmode_prop);
1066fda9279dSmrg			continue;
1067fda9279dSmrg		}
1068fda9279dSmrg		drmmode_output->props[j].mode_prop = drmmode_prop;
1069fda9279dSmrg		drmmode_output->props[j].index = i;
1070fda9279dSmrg		drmmode_output->num_props++;
1071fda9279dSmrg		j++;
1072fda9279dSmrg	}
1073fda9279dSmrg
1074fda9279dSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
1075fda9279dSmrg		drmmode_prop_ptr p = &drmmode_output->props[i];
1076fda9279dSmrg		drmmode_prop = p->mode_prop;
1077fda9279dSmrg
1078fda9279dSmrg		value = drmmode_output->mode_output->prop_values[p->index];
1079fda9279dSmrg
1080fda9279dSmrg		if (drmmode_prop->flags & DRM_MODE_PROP_RANGE) {
1081fda9279dSmrg			INT32 range[2];
1082fda9279dSmrg
1083fda9279dSmrg			p->num_atoms = 1;
1084fda9279dSmrg			p->atoms = calloc(p->num_atoms, sizeof(Atom));
1085fda9279dSmrg			if (!p->atoms)
1086fda9279dSmrg				continue;
1087fda9279dSmrg			p->atoms[0] = MakeAtom(drmmode_prop->name, strlen(drmmode_prop->name), TRUE);
1088fda9279dSmrg			range[0] = drmmode_prop->values[0];
1089fda9279dSmrg			range[1] = drmmode_prop->values[1];
1090fda9279dSmrg			err = RRConfigureOutputProperty(output->randr_output, p->atoms[0],
1091fda9279dSmrg							FALSE, TRUE,
1092fda9279dSmrg							drmmode_prop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE,
1093fda9279dSmrg							2, range);
1094fda9279dSmrg			if (err != 0) {
1095fda9279dSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
1096fda9279dSmrg					   "RRConfigureOutputProperty error, %d\n", err);
1097fda9279dSmrg			}
1098fda9279dSmrg			err = RRChangeOutputProperty(output->randr_output, p->atoms[0],
1099fda9279dSmrg						     XA_INTEGER, 32, PropModeReplace, 1,
1100fda9279dSmrg						     &value, FALSE, FALSE);
1101fda9279dSmrg			if (err != 0) {
1102fda9279dSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
1103fda9279dSmrg					   "RRChangeOutputProperty error, %d\n", err);
1104fda9279dSmrg			}
1105fda9279dSmrg		} else if (drmmode_prop->flags & DRM_MODE_PROP_ENUM) {
1106fda9279dSmrg			p->num_atoms = drmmode_prop->count_enums + 1;
1107fda9279dSmrg			p->atoms = calloc(p->num_atoms, sizeof(Atom));
1108fda9279dSmrg			if (!p->atoms)
1109fda9279dSmrg				continue;
1110fda9279dSmrg			p->atoms[0] = MakeAtom(drmmode_prop->name, strlen(drmmode_prop->name), TRUE);
1111fda9279dSmrg			for (j = 1; j <= drmmode_prop->count_enums; j++) {
1112fda9279dSmrg				struct drm_mode_property_enum *e = &drmmode_prop->enums[j-1];
1113fda9279dSmrg				p->atoms[j] = MakeAtom(e->name, strlen(e->name), TRUE);
1114fda9279dSmrg			}
1115fda9279dSmrg			err = RRConfigureOutputProperty(output->randr_output, p->atoms[0],
1116fda9279dSmrg							FALSE, FALSE,
1117fda9279dSmrg							drmmode_prop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE,
1118fda9279dSmrg							p->num_atoms - 1, (INT32 *)&p->atoms[1]);
1119fda9279dSmrg			if (err != 0) {
1120fda9279dSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
1121fda9279dSmrg					   "RRConfigureOutputProperty error, %d\n", err);
1122fda9279dSmrg			}
1123fda9279dSmrg			for (j = 0; j < drmmode_prop->count_enums; j++)
1124fda9279dSmrg				if (drmmode_prop->enums[j].value == value)
1125fda9279dSmrg					break;
1126fda9279dSmrg			/* there's always a matching value */
1127fda9279dSmrg			err = RRChangeOutputProperty(output->randr_output, p->atoms[0],
1128fda9279dSmrg						     XA_ATOM, 32, PropModeReplace, 1, &p->atoms[j+1], FALSE, FALSE);
1129fda9279dSmrg			if (err != 0) {
1130fda9279dSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
1131fda9279dSmrg					   "RRChangeOutputProperty error, %d\n", err);
1132fda9279dSmrg			}
1133fda9279dSmrg		}
1134fda9279dSmrg	}
1135fda9279dSmrg}
1136fda9279dSmrg
1137fda9279dSmrgstatic Bool
1138fda9279dSmrgdrmmode_output_set_property(xf86OutputPtr output, Atom property,
1139fda9279dSmrg			    RRPropertyValuePtr value)
1140fda9279dSmrg{
1141fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
1142fda9279dSmrg	drmmode_ptr drmmode = drmmode_output->drmmode;
1143fda9279dSmrg	int i, ret;
1144fda9279dSmrg
1145fda9279dSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
1146fda9279dSmrg		drmmode_prop_ptr p = &drmmode_output->props[i];
1147fda9279dSmrg
1148fda9279dSmrg		if (p->atoms[0] != property)
1149fda9279dSmrg			continue;
1150fda9279dSmrg
1151fda9279dSmrg		if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
1152fda9279dSmrg			uint32_t val;
1153fda9279dSmrg
1154fda9279dSmrg			if (value->type != XA_INTEGER || value->format != 32 ||
1155fda9279dSmrg			    value->size != 1)
1156fda9279dSmrg				return FALSE;
1157fda9279dSmrg			val = *(uint32_t *)value->data;
1158fda9279dSmrg
1159fda9279dSmrg			ret = drmModeConnectorSetProperty(drmmode->fd, drmmode_output->output_id,
1160fda9279dSmrg							  p->mode_prop->prop_id, (uint64_t)val);
1161fda9279dSmrg
1162fda9279dSmrg			if (ret)
1163fda9279dSmrg				return FALSE;
1164fda9279dSmrg
1165fda9279dSmrg			return TRUE;
1166fda9279dSmrg
1167fda9279dSmrg		} else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
1168fda9279dSmrg			Atom	atom;
1169fda9279dSmrg			const char	*name;
1170fda9279dSmrg			int		j;
1171fda9279dSmrg
1172fda9279dSmrg			if (value->type != XA_ATOM || value->format != 32 || value->size != 1)
1173fda9279dSmrg				return FALSE;
1174fda9279dSmrg			memcpy(&atom, value->data, 4);
117522d74663Smrg			if (!(name = NameForAtom(atom)))
117622d74663Smrg				return FALSE;
1177fda9279dSmrg
1178fda9279dSmrg			/* search for matching name string, then set its value down */
1179fda9279dSmrg			for (j = 0; j < p->mode_prop->count_enums; j++) {
1180fda9279dSmrg				if (!strcmp(p->mode_prop->enums[j].name, name)) {
1181fda9279dSmrg					ret = drmModeConnectorSetProperty(drmmode->fd,
1182fda9279dSmrg									  drmmode_output->output_id,
1183fda9279dSmrg									  p->mode_prop->prop_id,
1184fda9279dSmrg									  p->mode_prop->enums[j].value);
1185fda9279dSmrg
1186fda9279dSmrg					if (ret)
1187fda9279dSmrg						return FALSE;
1188fda9279dSmrg
1189fda9279dSmrg					return TRUE;
1190fda9279dSmrg				}
1191fda9279dSmrg			}
1192fda9279dSmrg
1193fda9279dSmrg			return FALSE;
1194fda9279dSmrg		}
1195fda9279dSmrg	}
1196fda9279dSmrg
1197fda9279dSmrg	return TRUE;
1198fda9279dSmrg}
1199fda9279dSmrg
1200fda9279dSmrgstatic Bool
1201fda9279dSmrgdrmmode_output_get_property(xf86OutputPtr output, Atom property)
1202fda9279dSmrg{
1203fda9279dSmrg
1204fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
1205fda9279dSmrg	drmmode_ptr drmmode = drmmode_output->drmmode;
1206fda9279dSmrg	uint32_t value;
1207fda9279dSmrg	int err, i;
1208fda9279dSmrg
1209fda9279dSmrg	if (output->scrn->vtSema) {
1210fda9279dSmrg		drmModeFreeConnector(drmmode_output->mode_output);
1211fda9279dSmrg		drmmode_output->mode_output =
1212fda9279dSmrg			drmModeGetConnector(drmmode->fd, drmmode_output->output_id);
1213fda9279dSmrg	}
1214fda9279dSmrg
1215fda9279dSmrg	if (!drmmode_output->mode_output)
1216fda9279dSmrg		return FALSE;
1217fda9279dSmrg
1218fda9279dSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
1219fda9279dSmrg		drmmode_prop_ptr p = &drmmode_output->props[i];
1220fda9279dSmrg		if (p->atoms[0] != property)
1221fda9279dSmrg			continue;
1222fda9279dSmrg
1223fda9279dSmrg		value = drmmode_output->mode_output->prop_values[p->index];
1224fda9279dSmrg
1225fda9279dSmrg		if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
1226fda9279dSmrg			err = RRChangeOutputProperty(output->randr_output,
1227fda9279dSmrg						     property, XA_INTEGER, 32,
1228fda9279dSmrg						     PropModeReplace, 1, &value,
1229fda9279dSmrg						     FALSE, FALSE);
1230fda9279dSmrg
1231fda9279dSmrg			return !err;
1232fda9279dSmrg		} else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
1233fda9279dSmrg			int		j;
1234fda9279dSmrg
1235fda9279dSmrg			/* search for matching name string, then set its value down */
1236fda9279dSmrg			for (j = 0; j < p->mode_prop->count_enums; j++) {
1237fda9279dSmrg				if (p->mode_prop->enums[j].value == value)
1238fda9279dSmrg					break;
1239fda9279dSmrg			}
1240fda9279dSmrg
1241fda9279dSmrg			err = RRChangeOutputProperty(output->randr_output, property,
1242fda9279dSmrg						     XA_ATOM, 32, PropModeReplace, 1,
1243fda9279dSmrg						     &p->atoms[j+1], FALSE, FALSE);
1244fda9279dSmrg
1245fda9279dSmrg			return !err;
1246fda9279dSmrg		}
1247fda9279dSmrg	}
1248fda9279dSmrg
1249fda9279dSmrg	return FALSE;
1250fda9279dSmrg}
1251fda9279dSmrg
1252fda9279dSmrgstatic const xf86OutputFuncsRec drmmode_output_funcs = {
1253fda9279dSmrg	.create_resources = drmmode_output_create_resources,
1254fda9279dSmrg	.dpms = drmmode_output_dpms,
1255fda9279dSmrg	.detect = drmmode_output_detect,
1256fda9279dSmrg	.mode_valid = drmmode_output_mode_valid,
1257fda9279dSmrg	.get_modes = drmmode_output_get_modes,
1258fda9279dSmrg	.set_property = drmmode_output_set_property,
1259fda9279dSmrg	.get_property = drmmode_output_get_property,
1260fda9279dSmrg	.destroy = drmmode_output_destroy
1261fda9279dSmrg};
1262fda9279dSmrg
1263fda9279dSmrgstatic int subpixel_conv_table[7] = { 0, SubPixelUnknown,
1264fda9279dSmrg				      SubPixelHorizontalRGB,
1265fda9279dSmrg				      SubPixelHorizontalBGR,
1266fda9279dSmrg				      SubPixelVerticalRGB,
1267fda9279dSmrg				      SubPixelVerticalBGR,
1268fda9279dSmrg				      SubPixelNone };
1269fda9279dSmrg
1270fda9279dSmrgconst char *output_names[] = { "None",
1271fda9279dSmrg			       "VGA",
1272fda9279dSmrg			       "DVI-I",
1273fda9279dSmrg			       "DVI-D",
1274fda9279dSmrg			       "DVI-A",
1275fda9279dSmrg			       "Composite",
1276fda9279dSmrg			       "SVIDEO",
1277fda9279dSmrg			       "LVDS",
1278fda9279dSmrg			       "CTV",
1279fda9279dSmrg			       "DIN",
1280fda9279dSmrg			       "DP",
1281fda9279dSmrg			       "HDMI",
1282fda9279dSmrg			       "HDMI",
1283fda9279dSmrg			       "TV",
1284fda9279dSmrg			       "eDP",
1285fda9279dSmrg};
1286fda9279dSmrg#define NUM_OUTPUT_NAMES (sizeof(output_names) / sizeof(output_names[0]))
1287fda9279dSmrg
1288fda9279dSmrgstatic Bool
1289fda9279dSmrgdrmmode_zaphod_match(ScrnInfoPtr pScrn, const char *s, char *output_name)
1290fda9279dSmrg{
1291fda9279dSmrg    int i = 0;
1292fda9279dSmrg    char s1[20];
1293fda9279dSmrg
1294fda9279dSmrg    do {
1295fda9279dSmrg	switch(*s) {
1296fda9279dSmrg	case ',':
1297fda9279dSmrg	    s1[i] = '\0';
1298fda9279dSmrg	    i = 0;
1299fda9279dSmrg	    if (strcmp(s1, output_name) == 0)
1300fda9279dSmrg		return TRUE;
1301fda9279dSmrg	    break;
1302fda9279dSmrg	case ' ':
1303fda9279dSmrg	case '\t':
1304fda9279dSmrg	case '\n':
1305fda9279dSmrg	case '\r':
1306fda9279dSmrg	    break;
1307fda9279dSmrg	default:
1308fda9279dSmrg	    s1[i] = *s;
1309fda9279dSmrg	    i++;
1310fda9279dSmrg	    break;
1311fda9279dSmrg	}
1312fda9279dSmrg    } while(*s++);
1313fda9279dSmrg
1314fda9279dSmrg    s1[i] = '\0';
1315fda9279dSmrg    if (strcmp(s1, output_name) == 0)
1316fda9279dSmrg	return TRUE;
1317fda9279dSmrg
1318fda9279dSmrg    return FALSE;
1319fda9279dSmrg}
1320fda9279dSmrg
132122d74663Smrgstatic xf86OutputPtr find_output(ScrnInfoPtr pScrn, int id)
132222d74663Smrg{
132322d74663Smrg	xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
132422d74663Smrg	int i;
132522d74663Smrg	for (i = 0; i < xf86_config->num_output; i++) {
132622d74663Smrg		xf86OutputPtr output = xf86_config->output[i];
132722d74663Smrg		drmmode_output_private_ptr drmmode_output;
132822d74663Smrg
132922d74663Smrg		drmmode_output = output->driver_private;
133022d74663Smrg		if (drmmode_output->output_id == id)
133122d74663Smrg			return output;
133222d74663Smrg	}
133322d74663Smrg	return NULL;
133422d74663Smrg}
133522d74663Smrg
133622d74663Smrgstatic int parse_path_blob(drmModePropertyBlobPtr path_blob, int *conn_base_id, char **path)
133722d74663Smrg{
133822d74663Smrg	char *conn;
133922d74663Smrg	char conn_id[5];
134022d74663Smrg	int id, len;
134122d74663Smrg	char *blob_data;
134222d74663Smrg
134322d74663Smrg	if (!path_blob)
134422d74663Smrg		return -1;
134522d74663Smrg
134622d74663Smrg	blob_data = path_blob->data;
134722d74663Smrg	/* we only handle MST paths for now */
134822d74663Smrg	if (strncmp(blob_data, "mst:", 4))
134922d74663Smrg		return -1;
135022d74663Smrg
135122d74663Smrg	conn = strchr(blob_data + 4, '-');
135222d74663Smrg	if (!conn)
135322d74663Smrg		return -1;
135422d74663Smrg	len = conn - (blob_data + 4);
135522d74663Smrg	if (len + 1 > 5)
135622d74663Smrg		return -1;
135722d74663Smrg	memcpy(conn_id, blob_data + 4, len);
135822d74663Smrg	conn_id[len] = '\0';
135922d74663Smrg	id = strtoul(conn_id, NULL, 10);
136022d74663Smrg
136122d74663Smrg	*conn_base_id = id;
136222d74663Smrg
136322d74663Smrg	*path = conn + 1;
136422d74663Smrg	return 0;
136522d74663Smrg}
136622d74663Smrg
136722d74663Smrgstatic void
136822d74663Smrgdrmmode_create_name(ScrnInfoPtr pScrn, drmModeConnectorPtr koutput, char *name,
136922d74663Smrg                    drmModePropertyBlobPtr path_blob)
137022d74663Smrg{
137122d74663Smrg	int ret;
137222d74663Smrg	char *extra_path;
137322d74663Smrg	int conn_id;
137422d74663Smrg	xf86OutputPtr output;
137522d74663Smrg
137622d74663Smrg	ret = parse_path_blob(path_blob, &conn_id, &extra_path);
137722d74663Smrg	if (ret == -1)
137822d74663Smrg		goto fallback;
137922d74663Smrg
138022d74663Smrg	output = find_output(pScrn, conn_id);
138122d74663Smrg	if (!output)
138222d74663Smrg		goto fallback;
138322d74663Smrg
138422d74663Smrg	snprintf(name, 32, "%s-%s", output->name, extra_path);
138522d74663Smrg	return;
138622d74663Smrg
138722d74663Smrgfallback:
138822d74663Smrg	if (koutput->connector_type >= ARRAY_SIZE(output_names))
138922d74663Smrg		snprintf(name, 32, "Unknown%d-%d", koutput->connector_type, koutput->connector_type_id);
139022d74663Smrg	else if (pScrn->is_gpu)
139122d74663Smrg		snprintf(name, 32, "%s-%d-%d", output_names[koutput->connector_type], pScrn->scrnIndex - GPU_SCREEN_OFFSET + 1, koutput->connector_type_id);
139222d74663Smrg	else
139322d74663Smrg		snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], koutput->connector_type_id);
139422d74663Smrg}
139522d74663Smrg
1396fda9279dSmrgstatic unsigned int
139722d74663Smrgdrmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res, int num, Bool dynamic, int crtcshift)
1398fda9279dSmrg{
1399fda9279dSmrg	NVPtr pNv = NVPTR(pScrn);
1400fda9279dSmrg	xf86OutputPtr output;
140122d74663Smrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
1402fda9279dSmrg	drmModeConnectorPtr koutput;
1403fda9279dSmrg	drmModeEncoderPtr kencoder;
1404fda9279dSmrg	drmmode_output_private_ptr drmmode_output;
1405fda9279dSmrg	const char *s;
1406fda9279dSmrg	char name[32];
140722d74663Smrg	drmModePropertyBlobPtr path_blob = NULL;
140822d74663Smrg	int i;
1409fda9279dSmrg
1410fda9279dSmrg	koutput = drmModeGetConnector(drmmode->fd,
141122d74663Smrg				      mode_res->connectors[num]);
1412fda9279dSmrg	if (!koutput)
1413fda9279dSmrg		return 0;
1414fda9279dSmrg
141522d74663Smrg	path_blob = koutput_get_prop_blob(drmmode->fd, koutput, "PATH");
141622d74663Smrg
141722d74663Smrg	drmmode_create_name(pScrn, koutput, name, path_blob);
141822d74663Smrg
141922d74663Smrg	if (path_blob)
142022d74663Smrg		drmModeFreePropertyBlob(path_blob);
142122d74663Smrg
142222d74663Smrg	if (path_blob && dynamic) {
142322d74663Smrg		/* see if we have an output with this name already
142422d74663Smrg		   and hook stuff up */
142522d74663Smrg		for (i = 0; i < xf86_config->num_output; i++) {
142622d74663Smrg			output = xf86_config->output[i];
142722d74663Smrg
142822d74663Smrg			if (strncmp(output->name, name, 32))
142922d74663Smrg				continue;
143022d74663Smrg
143122d74663Smrg			drmmode_output = output->driver_private;
143222d74663Smrg			drmmode_output->output_id = mode_res->connectors[num];
143322d74663Smrg			drmmode_output->mode_output = koutput;
143422d74663Smrg			return 1;
143522d74663Smrg		}
143622d74663Smrg	}
143722d74663Smrg
143822d74663Smrg
1439fda9279dSmrg	kencoder = drmModeGetEncoder(drmmode->fd, koutput->encoders[0]);
1440fda9279dSmrg	if (!kencoder) {
1441fda9279dSmrg		drmModeFreeConnector(koutput);
1442fda9279dSmrg		return 0;
1443fda9279dSmrg	}
1444fda9279dSmrg
1445fda9279dSmrg	if (xf86IsEntityShared(pScrn->entityList[0])) {
1446fda9279dSmrg		s = xf86GetOptValString(pNv->Options, OPTION_ZAPHOD_HEADS);
1447fda9279dSmrg		if (s) {
1448fda9279dSmrg			if (!drmmode_zaphod_match(pScrn, s, name)) {
1449fda9279dSmrg				drmModeFreeEncoder(kencoder);
1450fda9279dSmrg				drmModeFreeConnector(koutput);
1451fda9279dSmrg				return 0;
1452fda9279dSmrg			}
1453fda9279dSmrg		} else {
1454fda9279dSmrg			if (pNv->Primary && (num != 0)) {
1455fda9279dSmrg				drmModeFreeEncoder(kencoder);
1456fda9279dSmrg				drmModeFreeConnector(koutput);
1457fda9279dSmrg				return 0;
1458fda9279dSmrg			} else
1459fda9279dSmrg			if (pNv->Secondary && (num != 1)) {
1460fda9279dSmrg				drmModeFreeEncoder(kencoder);
1461fda9279dSmrg				drmModeFreeConnector(koutput);
1462fda9279dSmrg				return 0;
1463fda9279dSmrg			}
1464fda9279dSmrg		}
1465fda9279dSmrg	}
1466fda9279dSmrg
1467fda9279dSmrg	output = xf86OutputCreate (pScrn, &drmmode_output_funcs, name);
1468fda9279dSmrg	if (!output) {
1469fda9279dSmrg		drmModeFreeEncoder(kencoder);
1470fda9279dSmrg		drmModeFreeConnector(koutput);
1471fda9279dSmrg		return 0;
1472fda9279dSmrg	}
1473fda9279dSmrg
1474fda9279dSmrg	drmmode_output = calloc(sizeof(drmmode_output_private_rec), 1);
1475fda9279dSmrg	if (!drmmode_output) {
1476fda9279dSmrg		xf86OutputDestroy(output);
1477fda9279dSmrg		drmModeFreeConnector(koutput);
1478fda9279dSmrg		drmModeFreeEncoder(kencoder);
1479fda9279dSmrg		return 0;
1480fda9279dSmrg	}
1481fda9279dSmrg
148222d74663Smrg	drmmode_output->output_id = mode_res->connectors[num];
1483fda9279dSmrg	drmmode_output->mode_output = koutput;
1484fda9279dSmrg	drmmode_output->mode_encoder = kencoder;
1485fda9279dSmrg	drmmode_output->drmmode = drmmode;
1486fda9279dSmrg	output->mm_width = koutput->mmWidth;
1487fda9279dSmrg	output->mm_height = koutput->mmHeight;
1488fda9279dSmrg
1489fda9279dSmrg	output->subpixel_order = subpixel_conv_table[koutput->subpixel];
1490fda9279dSmrg	output->driver_private = drmmode_output;
1491fda9279dSmrg
1492a33a703bSmrg	output->possible_crtcs = kencoder->possible_crtcs >> crtcshift;
1493a33a703bSmrg	output->possible_clones = kencoder->possible_clones >> crtcshift;
1494fda9279dSmrg
1495fda9279dSmrg	output->interlaceAllowed = true;
1496fda9279dSmrg	output->doubleScanAllowed = true;
1497fda9279dSmrg
149822d74663Smrg	if (dynamic)
149922d74663Smrg		output->randr_output = RROutputCreate(xf86ScrnToScreen(pScrn), output->name, strlen(output->name), output);
150022d74663Smrg
1501fda9279dSmrg	return 1;
1502fda9279dSmrg}
1503fda9279dSmrg
1504fda9279dSmrgstatic Bool
1505fda9279dSmrgdrmmode_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height)
1506fda9279dSmrg{
1507fda9279dSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
1508fda9279dSmrg	ScreenPtr screen = xf86ScrnToScreen(scrn);
1509fda9279dSmrg	NVPtr pNv = NVPTR(scrn);
1510fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = NULL;
1511fda9279dSmrg	drmmode_ptr drmmode = NULL;
1512fda9279dSmrg	uint32_t old_width, old_height, old_pitch, old_fb_id = 0;
1513fda9279dSmrg	struct nouveau_bo *old_bo = NULL;
1514fda9279dSmrg	int ret, i, pitch;
1515fda9279dSmrg	PixmapPtr ppix;
1516fda9279dSmrg
1517fda9279dSmrg	if (xf86_config->num_crtc) {
1518fda9279dSmrg		drmmode_crtc = xf86_config->crtc[0]->driver_private;
1519fda9279dSmrg		drmmode = drmmode_crtc->drmmode;
1520fda9279dSmrg	}
1521fda9279dSmrg	ErrorF("resize called %d %d\n", width, height);
1522fda9279dSmrg
1523fda9279dSmrg	if (scrn->virtualX == width && scrn->virtualY == height)
1524fda9279dSmrg		return TRUE;
1525fda9279dSmrg
1526fda9279dSmrg	old_width = scrn->virtualX;
1527fda9279dSmrg	old_height = scrn->virtualY;
1528fda9279dSmrg	old_pitch = scrn->displayWidth;
1529fda9279dSmrg	if (drmmode)
1530fda9279dSmrg		old_fb_id = drmmode->fb_id;
1531fda9279dSmrg	nouveau_bo_ref(pNv->scanout, &old_bo);
1532fda9279dSmrg	nouveau_bo_ref(NULL, &pNv->scanout);
1533fda9279dSmrg
1534fda9279dSmrg	ret = nouveau_allocate_surface(scrn, width, height,
1535fda9279dSmrg				       scrn->bitsPerPixel,
1536fda9279dSmrg				       NOUVEAU_CREATE_PIXMAP_SCANOUT,
1537fda9279dSmrg				       &pitch, &pNv->scanout);
1538fda9279dSmrg	if (!ret)
1539fda9279dSmrg		goto fail;
1540fda9279dSmrg
1541fda9279dSmrg	scrn->virtualX = width;
1542fda9279dSmrg	scrn->virtualY = height;
1543fda9279dSmrg	scrn->displayWidth = pitch / (scrn->bitsPerPixel >> 3);
1544fda9279dSmrg
1545fda9279dSmrg	nouveau_bo_map(pNv->scanout, NOUVEAU_BO_RDWR, pNv->client);
1546fda9279dSmrg
1547fda9279dSmrg	if (drmmode) {
1548fda9279dSmrg		ret = drmModeAddFB(drmmode->fd, width, height, scrn->depth,
1549fda9279dSmrg				  scrn->bitsPerPixel, pitch, pNv->scanout->handle,
1550fda9279dSmrg				  &drmmode->fb_id);
1551fda9279dSmrg		if (ret)
1552fda9279dSmrg			goto fail;
1553fda9279dSmrg	}
1554fda9279dSmrg
1555fda9279dSmrg	if (pNv->ShadowPtr) {
1556fda9279dSmrg		free(pNv->ShadowPtr);
1557fda9279dSmrg		pNv->ShadowPitch = pitch;
1558fda9279dSmrg		pNv->ShadowPtr = malloc(pNv->ShadowPitch * height);
1559fda9279dSmrg	}
1560fda9279dSmrg
1561fda9279dSmrg	ppix = screen->GetScreenPixmap(screen);
15622a7e9763Smrg	if (pNv->AccelMethod > NONE)
1563fda9279dSmrg		nouveau_bo_ref(pNv->scanout, &drmmode_pixmap(ppix)->bo);
1564fda9279dSmrg	screen->ModifyPixmapHeader(ppix, width, height, -1, -1, pitch,
1565fda9279dSmrg				   (pNv->AccelMethod > NONE || pNv->ShadowPtr) ?
1566fda9279dSmrg				   pNv->ShadowPtr : pNv->scanout->map);
1567fda9279dSmrg#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 9
1568fda9279dSmrg	scrn->pixmapPrivate.ptr = ppix->devPrivate.ptr;
1569fda9279dSmrg#endif
1570fda9279dSmrg
1571fda9279dSmrg	if (pNv->AccelMethod == EXA) {
1572fda9279dSmrg		pNv->EXADriverPtr->PrepareSolid(ppix, GXcopy, ~0, 0);
1573fda9279dSmrg		pNv->EXADriverPtr->Solid(ppix, 0, 0, width, height);
1574fda9279dSmrg		pNv->EXADriverPtr->DoneSolid(ppix);
1575fda9279dSmrg		nouveau_bo_map(pNv->scanout, NOUVEAU_BO_RDWR, pNv->client);
1576fda9279dSmrg	} else {
1577fda9279dSmrg		memset(pNv->scanout->map, 0x00, pNv->scanout->size);
1578fda9279dSmrg	}
1579fda9279dSmrg
1580fda9279dSmrg	for (i = 0; i < xf86_config->num_crtc; i++) {
1581fda9279dSmrg		xf86CrtcPtr crtc = xf86_config->crtc[i];
1582fda9279dSmrg
1583fda9279dSmrg		if (!crtc->enabled)
1584fda9279dSmrg			continue;
1585fda9279dSmrg
1586fda9279dSmrg		drmmode_set_mode_major(crtc, &crtc->mode,
1587fda9279dSmrg				       crtc->rotation, crtc->x, crtc->y);
1588fda9279dSmrg	}
1589fda9279dSmrg
1590fda9279dSmrg	if (old_fb_id)
1591fda9279dSmrg		drmModeRmFB(drmmode->fd, old_fb_id);
1592fda9279dSmrg	nouveau_bo_ref(NULL, &old_bo);
1593fda9279dSmrg
1594fda9279dSmrg	return TRUE;
1595fda9279dSmrg
1596fda9279dSmrg fail:
1597fda9279dSmrg	nouveau_bo_ref(old_bo, &pNv->scanout);
1598fda9279dSmrg	scrn->virtualX = old_width;
1599fda9279dSmrg	scrn->virtualY = old_height;
1600fda9279dSmrg	scrn->displayWidth = old_pitch;
1601fda9279dSmrg	if (drmmode)
1602fda9279dSmrg		drmmode->fb_id = old_fb_id;
1603fda9279dSmrg
1604fda9279dSmrg	return FALSE;
1605fda9279dSmrg}
1606fda9279dSmrg
1607fda9279dSmrgstatic const xf86CrtcConfigFuncsRec drmmode_xf86crtc_config_funcs = {
1608fda9279dSmrg	drmmode_xf86crtc_resize
1609fda9279dSmrg};
1610fda9279dSmrg
1611fda9279dSmrgBool drmmode_pre_init(ScrnInfoPtr pScrn, int fd, int cpp)
1612fda9279dSmrg{
1613fda9279dSmrg	drmmode_ptr drmmode;
161422d74663Smrg	drmModeResPtr mode_res;
1615fda9279dSmrg	NVEntPtr pNVEnt = NVEntPriv(pScrn);
1616fda9279dSmrg	int i;
1617fda9279dSmrg	unsigned int crtcs_needed = 0;
1618a33a703bSmrg	int crtcshift;
1619fda9279dSmrg
16201090d90aSmrg	drmmode = xnfcalloc(sizeof(*drmmode), 1);
1621fda9279dSmrg	drmmode->fd = fd;
1622fda9279dSmrg	drmmode->fb_id = 0;
1623fda9279dSmrg
1624fda9279dSmrg	xf86CrtcConfigInit(pScrn, &drmmode_xf86crtc_config_funcs);
1625fda9279dSmrg
1626fda9279dSmrg	drmmode->cpp = cpp;
162722d74663Smrg	mode_res = drmModeGetResources(drmmode->fd);
162822d74663Smrg	if (!mode_res)
1629fda9279dSmrg		return FALSE;
1630fda9279dSmrg
163122d74663Smrg	xf86CrtcSetSizeRange(pScrn, 320, 200, mode_res->max_width,
163222d74663Smrg			     mode_res->max_height);
1633fda9279dSmrg
163422d74663Smrg	if (!mode_res->count_connectors ||
163522d74663Smrg	    !mode_res->count_crtcs) {
1636fda9279dSmrg		free(drmmode);
1637fda9279dSmrg		goto done;
1638fda9279dSmrg	}
1639fda9279dSmrg
1640fda9279dSmrg	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Initializing outputs ...\n");
1641a33a703bSmrg	crtcshift = ffs(pNVEnt->assigned_crtcs ^ 0xffffffff) - 1;
164222d74663Smrg	for (i = 0; i < mode_res->count_connectors; i++)
164322d74663Smrg		crtcs_needed += drmmode_output_init(pScrn, drmmode, mode_res, i, FALSE, crtcshift);
1644fda9279dSmrg
1645fda9279dSmrg	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
1646fda9279dSmrg		   "%d crtcs needed for screen.\n", crtcs_needed);
1647fda9279dSmrg
164822d74663Smrg	for (i = 0; i < mode_res->count_crtcs; i++) {
1649fda9279dSmrg		if (!xf86IsEntityShared(pScrn->entityList[0]) ||
1650fda9279dSmrg		    (crtcs_needed && !(pNVEnt->assigned_crtcs & (1 << i))))
165122d74663Smrg			crtcs_needed -= drmmode_crtc_init(pScrn, drmmode, mode_res, i);
1652fda9279dSmrg	}
1653fda9279dSmrg
1654fda9279dSmrg	/* All ZaphodHeads outputs provided with matching crtcs? */
1655fda9279dSmrg	if (xf86IsEntityShared(pScrn->entityList[0]) && (crtcs_needed > 0))
1656fda9279dSmrg		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
1657fda9279dSmrg			   "%d ZaphodHeads crtcs unavailable. Trouble!\n",
1658fda9279dSmrg			   crtcs_needed);
1659fda9279dSmrg
1660fda9279dSmrgdone:
166122d74663Smrg	drmModeFreeResources(mode_res);
166222d74663Smrg
1663fda9279dSmrg#ifdef NOUVEAU_PIXMAP_SHARING
1664fda9279dSmrg	xf86ProviderSetup(pScrn, NULL, "nouveau");
1665fda9279dSmrg#endif
1666fda9279dSmrg
1667fda9279dSmrg	xf86InitialConfiguration(pScrn, TRUE);
1668fda9279dSmrg
1669fda9279dSmrg	return TRUE;
1670fda9279dSmrg}
1671fda9279dSmrg
1672fda9279dSmrgvoid
1673fda9279dSmrgdrmmode_adjust_frame(ScrnInfoPtr scrn, int x, int y)
1674fda9279dSmrg{
1675fda9279dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
1676fda9279dSmrg	xf86OutputPtr output = config->output[config->compat_output];
1677fda9279dSmrg	xf86CrtcPtr crtc = output->crtc;
1678fda9279dSmrg
1679fda9279dSmrg	if (!crtc || !crtc->enabled)
1680fda9279dSmrg		return;
1681fda9279dSmrg
1682fda9279dSmrg	drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation, x, y);
1683fda9279dSmrg}
1684fda9279dSmrg
1685fda9279dSmrgvoid
1686fda9279dSmrgdrmmode_remove_fb(ScrnInfoPtr pScrn)
1687fda9279dSmrg{
1688fda9279dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
1689fda9279dSmrg	xf86CrtcPtr crtc = NULL;
1690fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc;
1691fda9279dSmrg	drmmode_ptr drmmode;
1692fda9279dSmrg
1693fda9279dSmrg	if (config && config->num_crtc)
1694fda9279dSmrg		crtc = config->crtc[0];
1695fda9279dSmrg	if (!crtc)
1696fda9279dSmrg		return;
1697fda9279dSmrg
1698fda9279dSmrg	drmmode_crtc = crtc->driver_private;
1699fda9279dSmrg	drmmode = drmmode_crtc->drmmode;
1700fda9279dSmrg
1701fda9279dSmrg	if (drmmode->fb_id)
1702fda9279dSmrg		drmModeRmFB(drmmode->fd, drmmode->fb_id);
1703fda9279dSmrg	drmmode->fb_id = 0;
1704fda9279dSmrg}
1705fda9279dSmrg
1706fda9279dSmrgint
1707fda9279dSmrgdrmmode_cursor_init(ScreenPtr pScreen)
1708fda9279dSmrg{
1709fda9279dSmrg	NVPtr pNv = NVPTR(xf86ScreenToScrn(pScreen));
1710fda9279dSmrg	int size = nv_cursor_width(pNv);
1711fda9279dSmrg	int flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP |
1712fda9279dSmrg		    HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_32 |
1713fda9279dSmrg		    (pNv->dev->chipset >= 0x11 ? HARDWARE_CURSOR_ARGB : 0) |
1714fda9279dSmrg		    HARDWARE_CURSOR_UPDATE_UNHIDDEN;
1715fda9279dSmrg
1716fda9279dSmrg	return xf86_cursors_init(pScreen, size, size, flags);
1717fda9279dSmrg}
1718fda9279dSmrg
1719fda9279dSmrg#ifdef HAVE_LIBUDEV
172022d74663Smrg
172122d74663Smrg#define DRM_MODE_LINK_STATUS_GOOD       0
172222d74663Smrg#define DRM_MODE_LINK_STATUS_BAD        1
172322d74663Smrg
1724fda9279dSmrgstatic void
1725fda9279dSmrgdrmmode_handle_uevents(ScrnInfoPtr scrn)
1726fda9279dSmrg{
172722d74663Smrg    struct udev_device *dev;
172822d74663Smrg    drmmode_ptr drmmode = drmmode_from_scrn(scrn);
172922d74663Smrg    drmModeResPtr mode_res;
173022d74663Smrg    xf86CrtcConfigPtr  config = XF86_CRTC_CONFIG_PTR(scrn);
173122d74663Smrg    int i, j;
173222d74663Smrg    Bool found = FALSE;
173322d74663Smrg    Bool changed = FALSE;
173422d74663Smrg
173522d74663Smrg    while ((dev = udev_monitor_receive_device(drmmode->uevent_monitor))) {
173622d74663Smrg        udev_device_unref(dev);
173722d74663Smrg        found = TRUE;
173822d74663Smrg    }
173922d74663Smrg    if (!found)
174022d74663Smrg        return;
174122d74663Smrg
174222d74663Smrg    /* Try to re-set the mode on all the connectors with a BAD link-state:
174322d74663Smrg     * This may happen if a link degrades and a new modeset is necessary, using
174422d74663Smrg     * different link-training parameters. If the kernel found that the current
174522d74663Smrg     * mode is not achievable anymore, it should have pruned the mode before
174622d74663Smrg     * sending the hotplug event. Try to re-set the currently-set mode to keep
174722d74663Smrg     * the display alive, this will fail if the mode has been pruned.
174822d74663Smrg     * In any case, we will send randr events for the Desktop Environment to
174922d74663Smrg     * deal with it, if it wants to.
175022d74663Smrg     */
175122d74663Smrg    for (i = 0; i < config->num_output; i++) {
175222d74663Smrg        xf86OutputPtr output = config->output[i];
175322d74663Smrg        xf86CrtcPtr crtc = output->crtc;
175422d74663Smrg        drmmode_output_private_ptr drmmode_output = output->driver_private;
175522d74663Smrg        uint32_t con_id, idx;
175622d74663Smrg        drmModeConnectorPtr koutput;
175722d74663Smrg
175822d74663Smrg        if (crtc == NULL || drmmode_output->mode_output == NULL)
175922d74663Smrg            continue;
176022d74663Smrg
176122d74663Smrg        con_id = drmmode_output->mode_output->connector_id;
176222d74663Smrg        /* Get an updated view of the properties for the current connector and
176322d74663Smrg         * look for the link-status property
176422d74663Smrg         */
176522d74663Smrg        koutput = drmModeGetConnectorCurrent(drmmode->fd, con_id);
176622d74663Smrg        if (!koutput)
176722d74663Smrg            continue;
176822d74663Smrg
176922d74663Smrg        idx = koutput_get_prop_idx(drmmode->fd, koutput,
177022d74663Smrg                DRM_MODE_PROP_ENUM, "link-status");
177122d74663Smrg
177222d74663Smrg        if ((idx > -1) &&
177322d74663Smrg                (koutput->prop_values[idx] == DRM_MODE_LINK_STATUS_BAD)) {
177422d74663Smrg            /* the connector got a link failure, re-set the current mode */
177522d74663Smrg            drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation,
177622d74663Smrg                                   crtc->x, crtc->y);
177722d74663Smrg
177822d74663Smrg            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
177922d74663Smrg                       "hotplug event: connector %u's link-state is BAD, "
178022d74663Smrg                       "tried resetting the current mode. You may be left"
178122d74663Smrg                       "with a black screen if this fails...\n", con_id);
178222d74663Smrg        }
178322d74663Smrg
178422d74663Smrg        drmModeFreeConnector(koutput);
178522d74663Smrg    }
178622d74663Smrg
178722d74663Smrg    mode_res = drmModeGetResources(drmmode->fd);
178822d74663Smrg    if (!mode_res)
178922d74663Smrg        goto out;
179022d74663Smrg
179122d74663Smrg    if (mode_res->count_crtcs != config->num_crtc) {
179222d74663Smrg        ErrorF("number of CRTCs changed - failed to handle, %d vs %d\n", mode_res->count_crtcs, config->num_crtc);
179322d74663Smrg        goto out_free_res;
179422d74663Smrg    }
179522d74663Smrg
179622d74663Smrg    /* figure out if we have gotten rid of any connectors
179722d74663Smrg       traverse old output list looking for outputs */
179822d74663Smrg    for (i = 0; i < config->num_output; i++) {
179922d74663Smrg        xf86OutputPtr output = config->output[i];
180022d74663Smrg        drmmode_output_private_ptr drmmode_output;
180122d74663Smrg
180222d74663Smrg        drmmode_output = output->driver_private;
180322d74663Smrg        found = FALSE;
180422d74663Smrg        for (j = 0; j < mode_res->count_connectors; j++) {
180522d74663Smrg            if (mode_res->connectors[j] == drmmode_output->output_id) {
180622d74663Smrg                found = TRUE;
180722d74663Smrg                break;
180822d74663Smrg            }
180922d74663Smrg        }
181022d74663Smrg        if (found)
181122d74663Smrg            continue;
181222d74663Smrg
181322d74663Smrg        drmModeFreeConnector(drmmode_output->mode_output);
181422d74663Smrg        drmmode_output->mode_output = NULL;
181522d74663Smrg        drmmode_output->output_id = -1;
181622d74663Smrg
181722d74663Smrg        changed = TRUE;
181822d74663Smrg    }
181922d74663Smrg
182022d74663Smrg    /* find new output ids we don't have outputs for */
182122d74663Smrg    for (i = 0; i < mode_res->count_connectors; i++) {
182222d74663Smrg        found = FALSE;
182322d74663Smrg
182422d74663Smrg        for (j = 0; j < config->num_output; j++) {
182522d74663Smrg            xf86OutputPtr output = config->output[j];
182622d74663Smrg            drmmode_output_private_ptr drmmode_output;
182722d74663Smrg
182822d74663Smrg            drmmode_output = output->driver_private;
182922d74663Smrg            if (mode_res->connectors[i] == drmmode_output->output_id) {
183022d74663Smrg                found = TRUE;
183122d74663Smrg                break;
183222d74663Smrg            }
183322d74663Smrg        }
183422d74663Smrg        if (found)
183522d74663Smrg            continue;
183622d74663Smrg
183722d74663Smrg        changed = TRUE;
183822d74663Smrg        drmmode_output_init(scrn, drmmode, mode_res, i, TRUE, 0);
183922d74663Smrg    }
184022d74663Smrg
184122d74663Smrg    if (changed) {
184222d74663Smrg        RRSetChanged(xf86ScrnToScreen(scrn));
184322d74663Smrg        RRTellChanged(xf86ScrnToScreen(scrn));
184422d74663Smrg    }
184522d74663Smrg
184622d74663Smrgout_free_res:
184722d74663Smrg    drmModeFreeResources(mode_res);
184822d74663Smrgout:
184922d74663Smrg    RRGetInfo(xf86ScrnToScreen(scrn), TRUE);
185022d74663Smrg}
1851fda9279dSmrg
185222d74663Smrg#undef DRM_MODE_LINK_STATUS_BAD
185322d74663Smrg#undef DRM_MODE_LINK_STATUS_GOOD
1854fda9279dSmrg
185522d74663Smrg#endif
1856fda9279dSmrg
1857c694fe25Smrg#ifdef HAVE_LIBUDEV
1858374ff59dSmrg#if HAVE_NOTIFY_FD
1859374ff59dSmrgstatic void
1860374ff59dSmrgdrmmode_udev_notify(int fd, int notify, void *data)
1861374ff59dSmrg{
1862374ff59dSmrg	ScrnInfoPtr scrn = data;
1863374ff59dSmrg	drmmode_handle_uevents(scrn);
1864374ff59dSmrg}
1865374ff59dSmrg#endif
1866c694fe25Smrg#endif
1867374ff59dSmrg
18681090d90aSmrgstatic bool has_randr(void)
18691090d90aSmrg{
18701090d90aSmrg#if HAS_DIXREGISTERPRIVATEKEY
18711090d90aSmrg	return dixPrivateKeyRegistered(rrPrivKey);
18721090d90aSmrg#else
18731090d90aSmrg	return *rrPrivKey;
18741090d90aSmrg#endif
18751090d90aSmrg}
18761090d90aSmrg
1877fda9279dSmrgstatic void
1878fda9279dSmrgdrmmode_uevent_init(ScrnInfoPtr scrn)
1879fda9279dSmrg{
1880fda9279dSmrg#ifdef HAVE_LIBUDEV
1881fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
1882fda9279dSmrg	struct udev *u;
1883fda9279dSmrg	struct udev_monitor *mon;
1884fda9279dSmrg
18851090d90aSmrg	/* RandR will be disabled if Xinerama is active, and so generating
18861090d90aSmrg	 * RR hotplug events is then forbidden.
18871090d90aSmrg	 */
18881090d90aSmrg	if (!has_randr())
18891090d90aSmrg		return;
18901090d90aSmrg
1891fda9279dSmrg	u = udev_new();
1892fda9279dSmrg	if (!u)
1893fda9279dSmrg		return;
1894fda9279dSmrg	mon = udev_monitor_new_from_netlink(u, "udev");
1895fda9279dSmrg	if (!mon) {
1896fda9279dSmrg		udev_unref(u);
1897fda9279dSmrg		return;
1898fda9279dSmrg	}
1899fda9279dSmrg
1900fda9279dSmrg	if (udev_monitor_filter_add_match_subsystem_devtype(mon,
1901fda9279dSmrg							    "drm",
1902fda9279dSmrg							    "drm_minor") < 0 ||
1903fda9279dSmrg	    udev_monitor_enable_receiving(mon) < 0) {
1904fda9279dSmrg		udev_monitor_unref(mon);
1905fda9279dSmrg		udev_unref(u);
1906fda9279dSmrg		return;
1907fda9279dSmrg	}
1908fda9279dSmrg
1909374ff59dSmrg#if HAVE_NOTIFY_FD
1910374ff59dSmrg	SetNotifyFd(udev_monitor_get_fd(mon), drmmode_udev_notify, X_NOTIFY_READ, scrn);
1911374ff59dSmrg#else
1912fda9279dSmrg	AddGeneralSocket(udev_monitor_get_fd(mon));
1913374ff59dSmrg#endif
1914fda9279dSmrg	drmmode->uevent_monitor = mon;
1915fda9279dSmrg#endif
1916fda9279dSmrg}
1917fda9279dSmrg
1918fda9279dSmrgstatic void
1919fda9279dSmrgdrmmode_uevent_fini(ScrnInfoPtr scrn)
1920fda9279dSmrg{
1921fda9279dSmrg#ifdef HAVE_LIBUDEV
1922fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
1923fda9279dSmrg
1924fda9279dSmrg	if (drmmode->uevent_monitor) {
1925fda9279dSmrg		struct udev *u = udev_monitor_get_udev(drmmode->uevent_monitor);
1926fda9279dSmrg
1927374ff59dSmrg#if HAVE_NOTIFY_FD
1928374ff59dSmrg		RemoveNotifyFd(udev_monitor_get_fd(drmmode->uevent_monitor));
1929374ff59dSmrg#else
1930fda9279dSmrg		RemoveGeneralSocket(udev_monitor_get_fd(drmmode->uevent_monitor));
1931374ff59dSmrg#endif
1932fda9279dSmrg		udev_monitor_unref(drmmode->uevent_monitor);
1933fda9279dSmrg		udev_unref(u);
1934fda9279dSmrg	}
1935fda9279dSmrg#endif
1936fda9279dSmrg}
1937fda9279dSmrg
1938374ff59dSmrg#if HAVE_NOTIFY_FD
1939374ff59dSmrgstatic void
1940374ff59dSmrgdrmmode_notify_fd(int fd, int notify, void *data)
1941374ff59dSmrg{
1942374ff59dSmrg	ScrnInfoPtr scrn = data;
1943374ff59dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
1944374ff59dSmrg	drmHandleEvent(drmmode->fd, &drmmode->event_context);
1945374ff59dSmrg}
1946374ff59dSmrg#else
1947374ff59dSmrg
1948fda9279dSmrgstatic void
1949fda9279dSmrgdrmmode_wakeup_handler(pointer data, int err, pointer p)
1950fda9279dSmrg{
1951fda9279dSmrg	ScrnInfoPtr scrn = data;
1952fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
1953fda9279dSmrg	fd_set *read_mask = p;
1954fda9279dSmrg
1955fda9279dSmrg	if (scrn == NULL || err < 0)
1956fda9279dSmrg		return;
1957fda9279dSmrg
1958fda9279dSmrg	if (FD_ISSET(drmmode->fd, read_mask))
1959fda9279dSmrg		drmHandleEvent(drmmode->fd, &drmmode->event_context);
1960fda9279dSmrg
1961fda9279dSmrg#ifdef HAVE_LIBUDEV
1962fda9279dSmrg	if (FD_ISSET(udev_monitor_get_fd(drmmode->uevent_monitor), read_mask))
1963fda9279dSmrg		drmmode_handle_uevents(scrn);
1964fda9279dSmrg#endif
1965fda9279dSmrg}
1966374ff59dSmrg#endif
1967fda9279dSmrg
1968fda9279dSmrgvoid
1969fda9279dSmrgdrmmode_screen_init(ScreenPtr pScreen)
1970fda9279dSmrg{
1971fda9279dSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
1972fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
1973fda9279dSmrg	NVEntPtr pNVEnt = NVEntPriv(scrn);
1974fda9279dSmrg
1975fda9279dSmrg	/* Setup handler for DRM events */
1976fda9279dSmrg	drmmode_event_init(scrn);
1977fda9279dSmrg
1978fda9279dSmrg	/* Setup handler for udevevents */
1979fda9279dSmrg	drmmode_uevent_init(scrn);
1980fda9279dSmrg
1981fda9279dSmrg	/* Register wakeup handler only once per servergen, so ZaphodHeads work */
1982fda9279dSmrg	if (pNVEnt->fd_wakeup_registered != serverGeneration) {
1983fda9279dSmrg		/* Register a wakeup handler to get informed on DRM events */
1984374ff59dSmrg#if HAVE_NOTIFY_FD
1985374ff59dSmrg		SetNotifyFd(drmmode->fd, drmmode_notify_fd, X_NOTIFY_READ, scrn);
1986374ff59dSmrg#else
1987fda9279dSmrg		AddGeneralSocket(drmmode->fd);
1988fda9279dSmrg		RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
1989fda9279dSmrg		                               drmmode_wakeup_handler, scrn);
1990374ff59dSmrg#endif
1991fda9279dSmrg		pNVEnt->fd_wakeup_registered = serverGeneration;
1992fda9279dSmrg		pNVEnt->fd_wakeup_ref = 1;
1993fda9279dSmrg	}
1994fda9279dSmrg	else
1995fda9279dSmrg		pNVEnt->fd_wakeup_ref++;
1996fda9279dSmrg}
1997fda9279dSmrg
1998fda9279dSmrgvoid
1999fda9279dSmrgdrmmode_screen_fini(ScreenPtr pScreen)
2000fda9279dSmrg{
2001fda9279dSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
2002fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
2003fda9279dSmrg	NVEntPtr pNVEnt = NVEntPriv(scrn);
2004fda9279dSmrg
2005fda9279dSmrg	/* Unregister wakeup handler after last x-screen for this servergen dies. */
2006fda9279dSmrg	if (pNVEnt->fd_wakeup_registered == serverGeneration &&
2007fda9279dSmrg		!--pNVEnt->fd_wakeup_ref) {
2008fda9279dSmrg
2009374ff59dSmrg#if HAVE_NOTIFY_FD
2010374ff59dSmrg		RemoveNotifyFd(drmmode->fd);
2011374ff59dSmrg#else
2012fda9279dSmrg		/* Unregister wakeup handler */
2013fda9279dSmrg		RemoveBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
2014fda9279dSmrg		                             drmmode_wakeup_handler, scrn);
2015fda9279dSmrg		RemoveGeneralSocket(drmmode->fd);
2016374ff59dSmrg#endif
2017fda9279dSmrg	}
2018fda9279dSmrg
2019fda9279dSmrg	/* Tear down udev event handler */
2020fda9279dSmrg	drmmode_uevent_fini(scrn);
2021fda9279dSmrg
2022fda9279dSmrg	/* Tear down DRM event handler */
2023fda9279dSmrg	drmmode_event_fini(scrn);
2024fda9279dSmrg}
2025