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
122a5560a61Smrgxf86_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
144fda9279dSmrgstruct drmmode_event {
145fda9279dSmrg	struct xorg_list head;
146fda9279dSmrg	drmmode_ptr drmmode;
147fda9279dSmrg	uint64_t name;
148fda9279dSmrg	void (*func)(void *, uint64_t, uint64_t, uint32_t);
149fda9279dSmrg};
150fda9279dSmrg
151fda9279dSmrgstatic struct xorg_list
152fda9279dSmrgdrmmode_events = {
153fda9279dSmrg	.next = &drmmode_events,
154fda9279dSmrg	.prev = &drmmode_events,
155fda9279dSmrg};
156fda9279dSmrg
1572a7e9763Smrgstatic bool warned = false;
1582a7e9763Smrg
159fda9279dSmrgstatic void
160fda9279dSmrgdrmmode_event_handler(int fd, unsigned int frame, unsigned int tv_sec,
161fda9279dSmrg		      unsigned int tv_usec, void *event_data)
162fda9279dSmrg{
163fda9279dSmrg	const uint64_t ust = (uint64_t)tv_sec * 1000000 + tv_usec;
164fda9279dSmrg	struct drmmode_event *e = event_data;
165fda9279dSmrg
1662a7e9763Smrg	int counter = 0;
1672a7e9763Smrg
168fda9279dSmrg	xorg_list_for_each_entry(e, &drmmode_events, head) {
1692a7e9763Smrg		counter++;
170fda9279dSmrg		if (e == event_data) {
171fda9279dSmrg			xorg_list_del(&e->head);
172fda9279dSmrg			e->func((void *)(e + 1), e->name, ust, frame);
173fda9279dSmrg			free(e);
174fda9279dSmrg			break;
175fda9279dSmrg		}
176fda9279dSmrg	}
1772a7e9763Smrg
1782a7e9763Smrg	if (counter > 100 && !warned) {
1792a7e9763Smrg		xf86DrvMsg(0, X_WARNING,
1802a7e9763Smrg			   "Event handler iterated %d times\n", counter);
1812a7e9763Smrg		warned = true;
1822a7e9763Smrg	}
183fda9279dSmrg}
184fda9279dSmrg
185fda9279dSmrgvoid
186fda9279dSmrgdrmmode_event_abort(ScrnInfoPtr scrn, uint64_t name, bool pending)
187fda9279dSmrg{
188fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
189fda9279dSmrg	struct drmmode_event *e, *t;
190fda9279dSmrg
191fda9279dSmrg	xorg_list_for_each_entry_safe(e, t, &drmmode_events, head) {
192fda9279dSmrg		if (e->drmmode == drmmode && e->name == name) {
193fda9279dSmrg			xorg_list_del(&e->head);
194fda9279dSmrg			if (!pending)
195fda9279dSmrg				free(e);
196fda9279dSmrg			break;
197fda9279dSmrg		}
198fda9279dSmrg	}
199fda9279dSmrg}
200fda9279dSmrg
201fda9279dSmrgvoid *
202fda9279dSmrgdrmmode_event_queue(ScrnInfoPtr scrn, uint64_t name, unsigned size,
203fda9279dSmrg		    void (*func)(void *, uint64_t, uint64_t, uint32_t),
204fda9279dSmrg		    void **event_data)
205fda9279dSmrg{
206fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
207fda9279dSmrg	struct drmmode_event *e;
208fda9279dSmrg
209fda9279dSmrg	e = *event_data = calloc(1, sizeof(*e) + size);
210fda9279dSmrg	if (e) {
211fda9279dSmrg		e->drmmode = drmmode;
212fda9279dSmrg		e->name = name;
213fda9279dSmrg		e->func = func;
214fda9279dSmrg		xorg_list_append(&e->head, &drmmode_events);
215fda9279dSmrg		return (void *)(e + 1);
216fda9279dSmrg	}
217fda9279dSmrg
218fda9279dSmrg	return NULL;
219fda9279dSmrg}
220fda9279dSmrg
221fda9279dSmrgint
222fda9279dSmrgdrmmode_event_flush(ScrnInfoPtr scrn)
223fda9279dSmrg{
224fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
225fda9279dSmrg	return drmHandleEvent(drmmode->fd, &drmmode->event_context);
226fda9279dSmrg}
227fda9279dSmrg
228fda9279dSmrgvoid
229fda9279dSmrgdrmmode_event_fini(ScrnInfoPtr scrn)
230fda9279dSmrg{
231fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
232fda9279dSmrg	struct drmmode_event *e, *t;
233fda9279dSmrg
234fda9279dSmrg	xorg_list_for_each_entry_safe(e, t, &drmmode_events, head) {
235fda9279dSmrg		if (e->drmmode == drmmode) {
236fda9279dSmrg			xorg_list_del(&e->head);
237fda9279dSmrg			free(e);
238fda9279dSmrg		}
239fda9279dSmrg	}
240fda9279dSmrg}
241fda9279dSmrg
242fda9279dSmrgvoid
243fda9279dSmrgdrmmode_event_init(ScrnInfoPtr scrn)
244fda9279dSmrg{
245fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
246fda9279dSmrg	drmmode->event_context.version = DRM_EVENT_CONTEXT_VERSION;
247fda9279dSmrg	drmmode->event_context.vblank_handler = drmmode_event_handler;
248fda9279dSmrg	drmmode->event_context.page_flip_handler = drmmode_event_handler;
249fda9279dSmrg}
250fda9279dSmrg
251fda9279dSmrgstatic PixmapPtr
252fda9279dSmrgdrmmode_pixmap_wrap(ScreenPtr pScreen, int width, int height, int depth,
253fda9279dSmrg		    int bpp, int pitch, struct nouveau_bo *bo, void *data)
254fda9279dSmrg{
255fda9279dSmrg	NVPtr pNv = NVPTR(xf86ScreenToScrn(pScreen));
256fda9279dSmrg	PixmapPtr ppix;
257fda9279dSmrg
258fda9279dSmrg	if (pNv->AccelMethod > NONE)
259fda9279dSmrg		data = NULL;
260fda9279dSmrg
261fda9279dSmrg	ppix = pScreen->CreatePixmap(pScreen, 0, 0, depth, 0);
262fda9279dSmrg	if (!ppix)
263fda9279dSmrg		return NULL;
264fda9279dSmrg
265fda9279dSmrg	pScreen->ModifyPixmapHeader(ppix, width, height, depth, bpp,
266fda9279dSmrg				    pitch, data);
267fda9279dSmrg	if (pNv->AccelMethod > NONE)
268fda9279dSmrg		nouveau_bo_ref(bo, &drmmode_pixmap(ppix)->bo);
269fda9279dSmrg
270fda9279dSmrg	return ppix;
271fda9279dSmrg}
272fda9279dSmrg
273fda9279dSmrgstatic void
274fda9279dSmrgdrmmode_ConvertFromKMode(ScrnInfoPtr scrn, drmModeModeInfo *kmode,
275fda9279dSmrg			 DisplayModePtr	mode)
276fda9279dSmrg{
277fda9279dSmrg	memset(mode, 0, sizeof(DisplayModeRec));
278fda9279dSmrg	mode->status = MODE_OK;
279fda9279dSmrg
280fda9279dSmrg	mode->Clock = kmode->clock;
281fda9279dSmrg
282fda9279dSmrg	mode->HDisplay = kmode->hdisplay;
283fda9279dSmrg	mode->HSyncStart = kmode->hsync_start;
284fda9279dSmrg	mode->HSyncEnd = kmode->hsync_end;
285fda9279dSmrg	mode->HTotal = kmode->htotal;
286fda9279dSmrg	mode->HSkew = kmode->hskew;
287fda9279dSmrg
288fda9279dSmrg	mode->VDisplay = kmode->vdisplay;
289fda9279dSmrg	mode->VSyncStart = kmode->vsync_start;
290fda9279dSmrg	mode->VSyncEnd = kmode->vsync_end;
291fda9279dSmrg	mode->VTotal = kmode->vtotal;
292fda9279dSmrg	mode->VScan = kmode->vscan;
293fda9279dSmrg
294fda9279dSmrg	mode->Flags = kmode->flags; //& FLAG_BITS;
295fda9279dSmrg	mode->name = strdup(kmode->name);
296fda9279dSmrg
297fda9279dSmrg	if (kmode->type & DRM_MODE_TYPE_DRIVER)
298fda9279dSmrg		mode->type = M_T_DRIVER;
299fda9279dSmrg	if (kmode->type & DRM_MODE_TYPE_PREFERRED)
300fda9279dSmrg		mode->type |= M_T_PREFERRED;
301fda9279dSmrg	xf86SetModeCrtc (mode, scrn->adjustFlags);
302fda9279dSmrg}
303fda9279dSmrg
304fda9279dSmrgstatic void
305fda9279dSmrgdrmmode_ConvertToKMode(ScrnInfoPtr scrn, drmModeModeInfo *kmode,
306fda9279dSmrg		       DisplayModePtr mode)
307fda9279dSmrg{
308fda9279dSmrg	memset(kmode, 0, sizeof(*kmode));
309fda9279dSmrg
310fda9279dSmrg	kmode->clock = mode->Clock;
311fda9279dSmrg	kmode->hdisplay = mode->HDisplay;
312fda9279dSmrg	kmode->hsync_start = mode->HSyncStart;
313fda9279dSmrg	kmode->hsync_end = mode->HSyncEnd;
314fda9279dSmrg	kmode->htotal = mode->HTotal;
315fda9279dSmrg	kmode->hskew = mode->HSkew;
316fda9279dSmrg
317fda9279dSmrg	kmode->vdisplay = mode->VDisplay;
318fda9279dSmrg	kmode->vsync_start = mode->VSyncStart;
319fda9279dSmrg	kmode->vsync_end = mode->VSyncEnd;
320fda9279dSmrg	kmode->vtotal = mode->VTotal;
321fda9279dSmrg	kmode->vscan = mode->VScan;
322fda9279dSmrg
323fda9279dSmrg	kmode->flags = mode->Flags; //& FLAG_BITS;
324fda9279dSmrg	if (mode->name)
325fda9279dSmrg		strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN);
326fda9279dSmrg	kmode->name[DRM_DISPLAY_MODE_LEN-1] = 0;
327fda9279dSmrg
328fda9279dSmrg}
329fda9279dSmrg
330fda9279dSmrgstatic void
3311090d90aSmrgdrmmode_crtc_dpms(xf86CrtcPtr crtc, int mode)
332fda9279dSmrg{
3331090d90aSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
3341090d90aSmrg	drmmode_crtc->dpms_mode = mode;
335fda9279dSmrg}
336fda9279dSmrg
337fda9279dSmrgvoid
338fda9279dSmrgdrmmode_fbcon_copy(ScreenPtr pScreen)
339fda9279dSmrg{
340fda9279dSmrg	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
341fda9279dSmrg	NVPtr pNv = NVPTR(pScrn);
342fda9279dSmrg	if (nouveau_bo_map(pNv->scanout, NOUVEAU_BO_WR, pNv->client))
343fda9279dSmrg		return;
344fda9279dSmrg	memset(pNv->scanout->map, 0x00, pNv->scanout->size);
345fda9279dSmrg}
346fda9279dSmrg
347fda9279dSmrgstatic Bool
348fda9279dSmrgdrmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
349fda9279dSmrg		       Rotation rotation, int x, int y)
350fda9279dSmrg{
351fda9279dSmrg	ScrnInfoPtr pScrn = crtc->scrn;
352fda9279dSmrg	NVPtr pNv = NVPTR(pScrn);
353fda9279dSmrg	xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
354fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
355fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
356fda9279dSmrg	uint32_t *output_ids;
357fda9279dSmrg	int output_count = 0;
358fda9279dSmrg	int ret = TRUE;
359fda9279dSmrg	int i;
360fda9279dSmrg	int fb_id;
361fda9279dSmrg	drmModeModeInfo kmode;
362fda9279dSmrg
363fda9279dSmrg	if (drmmode->fb_id == 0) {
364fda9279dSmrg		unsigned int pitch =
365fda9279dSmrg			pScrn->displayWidth * (pScrn->bitsPerPixel / 8);
366fda9279dSmrg
367fda9279dSmrg		ret = drmModeAddFB(drmmode->fd,
368fda9279dSmrg				   pScrn->virtualX, pScrn->virtualY,
369fda9279dSmrg				   pScrn->depth, pScrn->bitsPerPixel,
370fda9279dSmrg				   pitch, pNv->scanout->handle,
371fda9279dSmrg				   &drmmode->fb_id);
372fda9279dSmrg		if (ret < 0) {
373fda9279dSmrg			ErrorF("failed to add fb\n");
374fda9279dSmrg			return FALSE;
375fda9279dSmrg		}
376fda9279dSmrg	}
377fda9279dSmrg
378fda9279dSmrg	if (!xf86CrtcRotate(crtc))
379fda9279dSmrg		return FALSE;
380fda9279dSmrg
381fda9279dSmrg	output_ids = calloc(sizeof(uint32_t), xf86_config->num_output);
382fda9279dSmrg	if (!output_ids)
383fda9279dSmrg		return FALSE;
384fda9279dSmrg
385fda9279dSmrg	for (i = 0; i < xf86_config->num_output; i++) {
386fda9279dSmrg		xf86OutputPtr output = xf86_config->output[i];
387fda9279dSmrg		drmmode_output_private_ptr drmmode_output;
388fda9279dSmrg
389fda9279dSmrg		if (output->crtc != crtc)
390fda9279dSmrg			continue;
391fda9279dSmrg
392fda9279dSmrg		drmmode_output = output->driver_private;
39322d74663Smrg		if (drmmode_output->output_id == -1)
39422d74663Smrg			continue;
395fda9279dSmrg		output_ids[output_count] =
396fda9279dSmrg			drmmode_output->mode_output->connector_id;
397fda9279dSmrg		output_count++;
398fda9279dSmrg	}
399fda9279dSmrg
400fda9279dSmrg	drmmode_ConvertToKMode(crtc->scrn, &kmode, mode);
401fda9279dSmrg
402fda9279dSmrg	fb_id = drmmode->fb_id;
403fda9279dSmrg#ifdef NOUVEAU_PIXMAP_SHARING
404fda9279dSmrg	if (crtc->randr_crtc && crtc->randr_crtc->scanout_pixmap) {
405fda9279dSmrg		x = drmmode_crtc->scanout_pixmap_x;
406fda9279dSmrg		y = 0;
407fda9279dSmrg	} else
408fda9279dSmrg#endif
409fda9279dSmrg	if (drmmode_crtc->rotate_fb_id) {
410fda9279dSmrg		fb_id = drmmode_crtc->rotate_fb_id;
411fda9279dSmrg		x = 0;
412fda9279dSmrg		y = 0;
413fda9279dSmrg	}
414fda9279dSmrg
415fda9279dSmrg	ret = drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
416fda9279dSmrg			     fb_id, x, y, output_ids, output_count, &kmode);
417fda9279dSmrg	free(output_ids);
418fda9279dSmrg
419fda9279dSmrg	if (ret) {
420fda9279dSmrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
421fda9279dSmrg			   "failed to set mode: %s\n", strerror(-ret));
422fda9279dSmrg		return FALSE;
423fda9279dSmrg	}
424fda9279dSmrg
425fda9279dSmrg	/* Work around some xserver stupidity */
426fda9279dSmrg	for (i = 0; i < xf86_config->num_output; i++) {
427fda9279dSmrg		xf86OutputPtr output = xf86_config->output[i];
428fda9279dSmrg
429fda9279dSmrg		if (output->crtc != crtc)
430fda9279dSmrg			continue;
431fda9279dSmrg
432fda9279dSmrg		drmmode_output_dpms(output, DPMSModeOn);
433fda9279dSmrg	}
434fda9279dSmrg
435fda9279dSmrg	crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
436fda9279dSmrg			       crtc->gamma_blue, crtc->gamma_size);
437fda9279dSmrg
438861b9feeSmrg#ifdef HAVE_XF86_CURSOR_RESET_CURSOR
439861b9feeSmrg	xf86CursorResetCursor(crtc->scrn->pScreen);
44022d74663Smrg#else
441fda9279dSmrg	xf86_reload_cursors(crtc->scrn->pScreen);
442861b9feeSmrg#endif
443fda9279dSmrg
444fda9279dSmrg	return TRUE;
445fda9279dSmrg}
446fda9279dSmrg
447fda9279dSmrgstatic void
448fda9279dSmrgdrmmode_set_cursor_position (xf86CrtcPtr crtc, int x, int y)
449fda9279dSmrg{
450fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
451fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
452fda9279dSmrg
453fda9279dSmrg	drmModeMoveCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, x, y);
454fda9279dSmrg}
455fda9279dSmrg
456fda9279dSmrgstatic void
457fda9279dSmrgconvert_cursor(CARD32 *dst, CARD32 *src, int dw, int sw)
458fda9279dSmrg{
459fda9279dSmrg	int i, j;
460fda9279dSmrg
461fda9279dSmrg	for (j = 0;  j < sw; j++) {
462fda9279dSmrg		for (i = 0; i < sw; i++) {
463fda9279dSmrg			dst[j * dw + i] = src[j * sw + i];
464fda9279dSmrg		}
465fda9279dSmrg	}
466fda9279dSmrg}
467fda9279dSmrg
468fda9279dSmrgstatic void
469fda9279dSmrgdrmmode_load_cursor_argb (xf86CrtcPtr crtc, CARD32 *image)
470fda9279dSmrg{
471fda9279dSmrg	NVPtr pNv = NVPTR(crtc->scrn);
472fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
473fda9279dSmrg	struct nouveau_bo *cursor = drmmode_crtc->cursor;
474fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
475fda9279dSmrg
476fda9279dSmrg	nouveau_bo_map(cursor, NOUVEAU_BO_WR, pNv->client);
477fda9279dSmrg	convert_cursor(cursor->map, image, 64, nv_cursor_width(pNv));
478fda9279dSmrg
479fda9279dSmrg	if (drmmode_crtc->cursor_visible) {
480fda9279dSmrg		drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
481fda9279dSmrg				 cursor->handle, 64, 64);
482fda9279dSmrg	}
483fda9279dSmrg}
484fda9279dSmrg
485fda9279dSmrgstatic void
486fda9279dSmrgdrmmode_hide_cursor (xf86CrtcPtr crtc)
487fda9279dSmrg{
488fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
489fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
490fda9279dSmrg
491fda9279dSmrg	drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
492fda9279dSmrg			 0, 64, 64);
493fda9279dSmrg	drmmode_crtc->cursor_visible = FALSE;
494fda9279dSmrg}
495fda9279dSmrg
496fda9279dSmrgstatic void
497fda9279dSmrgdrmmode_show_cursor (xf86CrtcPtr crtc)
498fda9279dSmrg{
499fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
500fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
501fda9279dSmrg
502fda9279dSmrg	drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
503fda9279dSmrg			 drmmode_crtc->cursor->handle, 64, 64);
504fda9279dSmrg	drmmode_crtc->cursor_visible = TRUE;
505fda9279dSmrg}
506fda9279dSmrg
507fda9279dSmrgstatic void *
508fda9279dSmrgdrmmode_crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
509fda9279dSmrg{
510fda9279dSmrg	ScrnInfoPtr scrn = crtc->scrn;
511fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
512fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
513fda9279dSmrg	void *virtual;
514fda9279dSmrg	int ret;
515fda9279dSmrg
516fda9279dSmrg	ret = nouveau_allocate_surface(scrn, width, height,
517fda9279dSmrg				       scrn->bitsPerPixel,
518fda9279dSmrg				       NOUVEAU_CREATE_PIXMAP_SCANOUT,
519fda9279dSmrg				       &drmmode_crtc->rotate_pitch,
520fda9279dSmrg				       &drmmode_crtc->rotate_bo);
521fda9279dSmrg	if (!ret) {
522fda9279dSmrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
523fda9279dSmrg			   "Couldn't allocate shadow memory for rotated CRTC\n");
524fda9279dSmrg		return NULL;
525fda9279dSmrg	}
526fda9279dSmrg
527fda9279dSmrg	ret = nouveau_bo_map(drmmode_crtc->rotate_bo, NOUVEAU_BO_RDWR,
528fda9279dSmrg			     NVPTR(scrn)->client);
529fda9279dSmrg	if (ret) {
530fda9279dSmrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
531fda9279dSmrg			   "Couldn't get virtual address of shadow scanout\n");
532fda9279dSmrg		nouveau_bo_ref(NULL, &drmmode_crtc->rotate_bo);
533fda9279dSmrg		return NULL;
534fda9279dSmrg	}
535fda9279dSmrg	virtual = drmmode_crtc->rotate_bo->map;
536fda9279dSmrg
537fda9279dSmrg	ret = drmModeAddFB(drmmode->fd, width, height, crtc->scrn->depth,
538fda9279dSmrg			   crtc->scrn->bitsPerPixel, drmmode_crtc->rotate_pitch,
539fda9279dSmrg			   drmmode_crtc->rotate_bo->handle,
540fda9279dSmrg			   &drmmode_crtc->rotate_fb_id);
541fda9279dSmrg	if (ret) {
542fda9279dSmrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
543fda9279dSmrg			   "Error adding FB for shadow scanout: %s\n",
544fda9279dSmrg			   strerror(-ret));
545fda9279dSmrg		nouveau_bo_ref(NULL, &drmmode_crtc->rotate_bo);
546fda9279dSmrg		return NULL;
547fda9279dSmrg	}
548fda9279dSmrg
549fda9279dSmrg	return virtual;
550fda9279dSmrg}
551fda9279dSmrg
552fda9279dSmrgstatic PixmapPtr
553fda9279dSmrgdrmmode_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
554fda9279dSmrg{
555fda9279dSmrg	ScrnInfoPtr pScrn = crtc->scrn;
556fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
557fda9279dSmrg	PixmapPtr rotate_pixmap;
558fda9279dSmrg
559fda9279dSmrg	if (!data)
560fda9279dSmrg		data = drmmode_crtc_shadow_allocate (crtc, width, height);
561fda9279dSmrg
562fda9279dSmrg	rotate_pixmap = drmmode_pixmap_wrap(pScrn->pScreen, width, height,
563fda9279dSmrg					    pScrn->depth, pScrn->bitsPerPixel,
564fda9279dSmrg					    drmmode_crtc->rotate_pitch,
565fda9279dSmrg					    drmmode_crtc->rotate_bo, data);
566fda9279dSmrg
567fda9279dSmrg	drmmode_crtc->rotate_pixmap = rotate_pixmap;
568fda9279dSmrg	return drmmode_crtc->rotate_pixmap;
569fda9279dSmrg}
570fda9279dSmrg
571fda9279dSmrgstatic void
572fda9279dSmrgdrmmode_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *data)
573fda9279dSmrg{
574fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
575fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
576fda9279dSmrg
577fda9279dSmrg	if (rotate_pixmap)
578fda9279dSmrg		FreeScratchPixmapHeader(rotate_pixmap);
579fda9279dSmrg
580fda9279dSmrg	if (data) {
581fda9279dSmrg		drmModeRmFB(drmmode->fd, drmmode_crtc->rotate_fb_id);
582fda9279dSmrg		drmmode_crtc->rotate_fb_id = 0;
583fda9279dSmrg		nouveau_bo_ref(NULL, &drmmode_crtc->rotate_bo);
584fda9279dSmrg		drmmode_crtc->rotate_pixmap = NULL;
585fda9279dSmrg	}
586fda9279dSmrg}
587fda9279dSmrg
588fda9279dSmrgstatic void
589fda9279dSmrgdrmmode_gamma_set(xf86CrtcPtr crtc, CARD16 *red, CARD16 *green, CARD16 *blue,
590fda9279dSmrg		  int size)
591fda9279dSmrg{
592fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
593fda9279dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
594fda9279dSmrg	int ret;
595fda9279dSmrg
596fda9279dSmrg	ret = drmModeCrtcSetGamma(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
597fda9279dSmrg				  size, red, green, blue);
598fda9279dSmrg	if (ret != 0) {
599fda9279dSmrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
60022d74663Smrg			   "failed to set gamma with %d entries: %s\n",
60122d74663Smrg			   size, strerror(-ret));
602fda9279dSmrg	}
603fda9279dSmrg}
604fda9279dSmrg
605fda9279dSmrg#ifdef NOUVEAU_PIXMAP_SHARING
606fda9279dSmrgstatic Bool
607fda9279dSmrgdrmmode_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix)
608fda9279dSmrg{
609fda9279dSmrg	ScreenPtr screen = xf86ScrnToScreen(crtc->scrn);
610fda9279dSmrg	PixmapPtr screenpix = screen->GetScreenPixmap(screen);
611fda9279dSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
612fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
613374ff59dSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
614fda9279dSmrg	int c, total_width = 0, max_height = 0, this_x = 0;
615fda9279dSmrg	if (!ppix) {
616374ff59dSmrg		if (crtc->randr_crtc->scanout_pixmap) {
61722d74663Smrg#ifdef HAS_DIRTYTRACKING_DRAWABLE_SRC
61822d74663Smrg			PixmapStopDirtyTracking(&crtc->randr_crtc->scanout_pixmap->drawable, screenpix);
61922d74663Smrg#else
62022d74663Smrg			PixmapStopDirtyTracking(crtc->randr_crtc->scanout_pixmap, screenpix);
62122d74663Smrg#endif
622374ff59dSmrg			if (drmmode && drmmode->fb_id) {
623374ff59dSmrg				drmModeRmFB(drmmode->fd, drmmode->fb_id);
624374ff59dSmrg				drmmode->fb_id = 0;
625374ff59dSmrg			}
626374ff59dSmrg		}
627fda9279dSmrg		drmmode_crtc->scanout_pixmap_x = 0;
628fda9279dSmrg		return TRUE;
629fda9279dSmrg	}
630fda9279dSmrg
631fda9279dSmrg	/* iterate over all the attached crtcs -
632fda9279dSmrg	   work out bounding box */
633fda9279dSmrg	for (c = 0; c < xf86_config->num_crtc; c++) {
634fda9279dSmrg		xf86CrtcPtr iter = xf86_config->crtc[c];
635fda9279dSmrg		if (!iter->enabled && iter != crtc)
636fda9279dSmrg			continue;
637fda9279dSmrg		if (iter == crtc) {
638fda9279dSmrg			this_x = total_width;
639fda9279dSmrg			total_width += ppix->drawable.width;
640fda9279dSmrg			if (max_height < ppix->drawable.height)
641fda9279dSmrg				max_height = ppix->drawable.height;
642fda9279dSmrg		} else {
643fda9279dSmrg			total_width += iter->mode.HDisplay;
644fda9279dSmrg			if (max_height < iter->mode.VDisplay)
645fda9279dSmrg				max_height = iter->mode.VDisplay;
646fda9279dSmrg		}
647fda9279dSmrg	}
648fda9279dSmrg
649fda9279dSmrg	if (total_width != screenpix->drawable.width ||
650fda9279dSmrg	    max_height != screenpix->drawable.height) {
651fda9279dSmrg		Bool ret;
652fda9279dSmrg		ret = drmmode_xf86crtc_resize(crtc->scrn, total_width, max_height);
653fda9279dSmrg		if (ret == FALSE)
654fda9279dSmrg			return FALSE;
655fda9279dSmrg
656fda9279dSmrg		screenpix = screen->GetScreenPixmap(screen);
657fda9279dSmrg		screen->width = screenpix->drawable.width = total_width;
658fda9279dSmrg		screen->height = screenpix->drawable.height = max_height;
659fda9279dSmrg	}
660fda9279dSmrg	drmmode_crtc->scanout_pixmap_x = this_x;
66122d74663Smrg
66222d74663Smrg#ifdef HAS_DIRTYTRACKING_DRAWABLE_SRC
66322d74663Smrg	PixmapStartDirtyTracking(&ppix->drawable, screenpix, 0, 0, this_x, 0, RR_Rotate_0);
664fda9279dSmrg#else
665a5560a61Smrg	PixmapStartDirtyTracking(ppix, screenpix, 0, 0, this_x, 0, RR_Rotate_0);
666fda9279dSmrg#endif
667fda9279dSmrg	return TRUE;
668fda9279dSmrg}
669fda9279dSmrg#endif
670fda9279dSmrg
671fda9279dSmrgstatic const xf86CrtcFuncsRec drmmode_crtc_funcs = {
672fda9279dSmrg	.dpms = drmmode_crtc_dpms,
673fda9279dSmrg	.set_mode_major = drmmode_set_mode_major,
674fda9279dSmrg	.set_cursor_position = drmmode_set_cursor_position,
675fda9279dSmrg	.show_cursor = drmmode_show_cursor,
676fda9279dSmrg	.hide_cursor = drmmode_hide_cursor,
677fda9279dSmrg	.load_cursor_argb = drmmode_load_cursor_argb,
678fda9279dSmrg	.shadow_create = drmmode_crtc_shadow_create,
679fda9279dSmrg	.shadow_allocate = drmmode_crtc_shadow_allocate,
680fda9279dSmrg	.shadow_destroy = drmmode_crtc_shadow_destroy,
681fda9279dSmrg	.gamma_set = drmmode_gamma_set,
682fda9279dSmrg
683fda9279dSmrg#ifdef NOUVEAU_PIXMAP_SHARING
684fda9279dSmrg	.set_scanout_pixmap = drmmode_set_scanout_pixmap,
685fda9279dSmrg#endif
686fda9279dSmrg};
687fda9279dSmrg
688fda9279dSmrg
689fda9279dSmrgstatic unsigned int
69022d74663Smrgdrmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res, int num)
691fda9279dSmrg{
692fda9279dSmrg	NVPtr pNv = NVPTR(pScrn);
693fda9279dSmrg	NVEntPtr pNVEnt = NVEntPriv(pScrn);
694fda9279dSmrg	xf86CrtcPtr crtc;
695fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc;
696fda9279dSmrg	int ret;
697fda9279dSmrg
698fda9279dSmrg	crtc = xf86CrtcCreate(pScrn, &drmmode_crtc_funcs);
699fda9279dSmrg	if (crtc == NULL)
700fda9279dSmrg		return 0;
701fda9279dSmrg
702a5560a61Smrg	drmmode_crtc = XNFcallocarray(sizeof(drmmode_crtc_private_rec), 1);
703fda9279dSmrg	drmmode_crtc->mode_crtc = drmModeGetCrtc(drmmode->fd,
70422d74663Smrg						 mode_res->crtcs[num]);
705fda9279dSmrg	drmmode_crtc->drmmode = drmmode;
706fda9279dSmrg	drmmode_crtc->hw_crtc_index = num;
707fda9279dSmrg
708fda9279dSmrg	ret = nouveau_bo_new(pNv->dev, NOUVEAU_BO_GART | NOUVEAU_BO_MAP, 0,
709fda9279dSmrg			     64*64*4, NULL, &drmmode_crtc->cursor);
710fda9279dSmrg	assert(ret == 0);
711fda9279dSmrg
712fda9279dSmrg	crtc->driver_private = drmmode_crtc;
713fda9279dSmrg
714fda9279dSmrg	/* Mark num'th crtc as in use on this device. */
715fda9279dSmrg	pNVEnt->assigned_crtcs |= (1 << num);
716fda9279dSmrg	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
717fda9279dSmrg		   "Allocated crtc nr. %d to this screen.\n", num);
718fda9279dSmrg
719fda9279dSmrg	return 1;
720fda9279dSmrg}
721fda9279dSmrg
722fda9279dSmrgstatic xf86OutputStatus
723fda9279dSmrgdrmmode_output_detect(xf86OutputPtr output)
724fda9279dSmrg{
725fda9279dSmrg	/* go to the hw and retrieve a new output struct */
726fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
727fda9279dSmrg	drmmode_ptr drmmode = drmmode_output->drmmode;
728fda9279dSmrg	xf86OutputStatus status;
72922d74663Smrg
73022d74663Smrg	if (drmmode_output->output_id == -1)
73122d74663Smrg		return XF86OutputStatusDisconnected;
73222d74663Smrg
733fda9279dSmrg	drmModeFreeConnector(drmmode_output->mode_output);
734fda9279dSmrg
735fda9279dSmrg	drmmode_output->mode_output =
736fda9279dSmrg		drmModeGetConnector(drmmode->fd, drmmode_output->output_id);
737fda9279dSmrg
73822d74663Smrg	if (!drmmode_output->mode_output) {
73922d74663Smrg		drmmode_output->output_id = -1;
740fda9279dSmrg		return XF86OutputStatusDisconnected;
74122d74663Smrg	}
742fda9279dSmrg
743fda9279dSmrg	switch (drmmode_output->mode_output->connection) {
744fda9279dSmrg	case DRM_MODE_CONNECTED:
745fda9279dSmrg		status = XF86OutputStatusConnected;
746fda9279dSmrg		break;
747fda9279dSmrg	case DRM_MODE_DISCONNECTED:
748fda9279dSmrg		status = XF86OutputStatusDisconnected;
749fda9279dSmrg		break;
750fda9279dSmrg	default:
751fda9279dSmrg	case DRM_MODE_UNKNOWNCONNECTION:
752fda9279dSmrg		status = XF86OutputStatusUnknown;
753fda9279dSmrg		break;
754fda9279dSmrg	}
755fda9279dSmrg	return status;
756fda9279dSmrg}
757fda9279dSmrg
758fda9279dSmrgstatic Bool
759fda9279dSmrgdrmmode_output_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
760fda9279dSmrg{
761fda9279dSmrg	if (mode->type & M_T_DEFAULT)
762fda9279dSmrg		/* Default modes are harmful here. */
763fda9279dSmrg		return MODE_BAD;
764fda9279dSmrg
765fda9279dSmrg	return MODE_OK;
766fda9279dSmrg}
767fda9279dSmrg
76822d74663Smrgstatic int
76922d74663Smrgkoutput_get_prop_idx(int fd, drmModeConnectorPtr koutput,
77022d74663Smrg		     int type, const char *name)
77122d74663Smrg{
77222d74663Smrg	int idx = -1;
77322d74663Smrg
77422d74663Smrg	for (int i = 0; i < koutput->count_props; i++) {
77522d74663Smrg		drmModePropertyPtr prop = drmModeGetProperty(fd, koutput->props[i]);
77622d74663Smrg
77722d74663Smrg		if (!prop)
77822d74663Smrg			continue;
77922d74663Smrg
78022d74663Smrg		if (drm_property_type_is(prop, type) && !strcmp(prop->name, name))
78122d74663Smrg			idx = i;
78222d74663Smrg
78322d74663Smrg		drmModeFreeProperty(prop);
78422d74663Smrg
78522d74663Smrg		if (idx > -1)
78622d74663Smrg			break;
78722d74663Smrg	}
78822d74663Smrg
78922d74663Smrg	return idx;
79022d74663Smrg}
79122d74663Smrg
79222d74663Smrgstatic drmModePropertyBlobPtr
79322d74663Smrgkoutput_get_prop_blob(int fd, drmModeConnectorPtr koutput, const char *name)
79422d74663Smrg{
79522d74663Smrg	drmModePropertyBlobPtr blob = NULL;
79622d74663Smrg	int idx = koutput_get_prop_idx(fd, koutput, DRM_MODE_PROP_BLOB, name);
79722d74663Smrg
79822d74663Smrg	if (idx > -1)
79922d74663Smrg		blob = drmModeGetPropertyBlob(fd, koutput->prop_values[idx]);
80022d74663Smrg
80122d74663Smrg	return blob;
80222d74663Smrg}
80322d74663Smrg
80422d74663Smrgstatic void
80522d74663Smrgdrmmode_output_attach_tile(xf86OutputPtr output)
80622d74663Smrg{
80722d74663Smrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
80822d74663Smrg	drmModeConnectorPtr koutput = drmmode_output->mode_output;
80922d74663Smrg	drmmode_ptr drmmode = drmmode_output->drmmode;
81022d74663Smrg	struct xf86CrtcTileInfo tile_info, *set = NULL;
81122d74663Smrg
81222d74663Smrg	if (!koutput) {
81322d74663Smrg		xf86OutputSetTile(output, NULL);
81422d74663Smrg		return;
81522d74663Smrg	}
81622d74663Smrg
81722d74663Smrg	drmModeFreePropertyBlob(drmmode_output->tile_blob);
81822d74663Smrg
81922d74663Smrg	/* look for a TILE property */
82022d74663Smrg	drmmode_output->tile_blob =
82122d74663Smrg		koutput_get_prop_blob(drmmode->fd, koutput, "TILE");
82222d74663Smrg
82322d74663Smrg	if (drmmode_output->tile_blob) {
82422d74663Smrg		if (xf86OutputParseKMSTile(drmmode_output->tile_blob->data, drmmode_output->tile_blob->length, &tile_info) == TRUE)
82522d74663Smrg			set = &tile_info;
82622d74663Smrg	}
82722d74663Smrg	xf86OutputSetTile(output, set);
82822d74663Smrg}
82922d74663Smrg
83022d74663Smrg
831fda9279dSmrgstatic DisplayModePtr
832fda9279dSmrgdrmmode_output_get_modes(xf86OutputPtr output)
833fda9279dSmrg{
834fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
835fda9279dSmrg	drmModeConnectorPtr koutput = drmmode_output->mode_output;
836fda9279dSmrg	drmmode_ptr drmmode = drmmode_output->drmmode;
837fda9279dSmrg	int i;
838fda9279dSmrg	DisplayModePtr Modes = NULL, Mode;
839fda9279dSmrg	xf86MonPtr ddc_mon = NULL;
840fda9279dSmrg
841fda9279dSmrg	if (!koutput)
842fda9279dSmrg		return NULL;
843fda9279dSmrg
844fda9279dSmrg	/* look for an EDID property */
84522d74663Smrg	drmmode_output->edid_blob =
84622d74663Smrg		koutput_get_prop_blob(drmmode->fd, koutput, "EDID");
847fda9279dSmrg
848fda9279dSmrg	if (drmmode_output->edid_blob) {
849fda9279dSmrg		ddc_mon = xf86InterpretEDID(output->scrn->scrnIndex,
850fda9279dSmrg					    drmmode_output->edid_blob->data);
851fda9279dSmrg		if (ddc_mon && drmmode_output->edid_blob->length > 128)
852fda9279dSmrg			ddc_mon->flags |= MONITOR_EDID_COMPLETE_RAWDATA;
853fda9279dSmrg	}
854fda9279dSmrg	xf86OutputSetEDID(output, ddc_mon);
855fda9279dSmrg
85622d74663Smrg	drmmode_output_attach_tile(output);
85722d74663Smrg
858fda9279dSmrg	/* modes should already be available */
859fda9279dSmrg	for (i = 0; i < koutput->count_modes; i++) {
860a5560a61Smrg		Mode = XNFalloc(sizeof(DisplayModeRec));
861fda9279dSmrg
862fda9279dSmrg		drmmode_ConvertFromKMode(output->scrn, &koutput->modes[i],
863fda9279dSmrg					 Mode);
864fda9279dSmrg		Modes = xf86ModesAdd(Modes, Mode);
865fda9279dSmrg
866fda9279dSmrg	}
867fda9279dSmrg	return Modes;
868fda9279dSmrg}
869fda9279dSmrg
870fda9279dSmrgstatic void
871fda9279dSmrgdrmmode_output_destroy(xf86OutputPtr output)
872fda9279dSmrg{
873fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
874fda9279dSmrg	int i;
875fda9279dSmrg
876fda9279dSmrg	if (drmmode_output->edid_blob)
877fda9279dSmrg		drmModeFreePropertyBlob(drmmode_output->edid_blob);
87822d74663Smrg	if (drmmode_output->tile_blob)
87922d74663Smrg		drmModeFreePropertyBlob(drmmode_output->tile_blob);
880fda9279dSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
881fda9279dSmrg		drmModeFreeProperty(drmmode_output->props[i].mode_prop);
882fda9279dSmrg		free(drmmode_output->props[i].atoms);
883fda9279dSmrg	}
884fda9279dSmrg	drmModeFreeConnector(drmmode_output->mode_output);
885fda9279dSmrg	free(drmmode_output);
886fda9279dSmrg	output->driver_private = NULL;
887fda9279dSmrg}
888fda9279dSmrg
889fda9279dSmrgstatic void
890fda9279dSmrgdrmmode_output_dpms(xf86OutputPtr output, int mode)
891fda9279dSmrg{
892fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
893fda9279dSmrg	drmModeConnectorPtr koutput = drmmode_output->mode_output;
894fda9279dSmrg	drmModePropertyPtr props;
895fda9279dSmrg	drmmode_ptr drmmode = drmmode_output->drmmode;
896fda9279dSmrg	int mode_id = -1, i;
897fda9279dSmrg
89822d74663Smrg	if (!koutput)
89922d74663Smrg		return;
90022d74663Smrg
901fda9279dSmrg	for (i = 0; i < koutput->count_props; i++) {
902fda9279dSmrg		props = drmModeGetProperty(drmmode->fd, koutput->props[i]);
90311c9f444Sriastradh		if (props && (props->flags & DRM_MODE_PROP_ENUM)) {
904fda9279dSmrg			if (!strcmp(props->name, "DPMS")) {
905fda9279dSmrg				mode_id = koutput->props[i];
906fda9279dSmrg				drmModeFreeProperty(props);
907fda9279dSmrg				break;
908fda9279dSmrg			}
909fda9279dSmrg			drmModeFreeProperty(props);
910fda9279dSmrg		}
911fda9279dSmrg	}
912fda9279dSmrg
913fda9279dSmrg	if (mode_id < 0)
914fda9279dSmrg		return;
915fda9279dSmrg
916fda9279dSmrg	drmModeConnectorSetProperty(drmmode->fd, koutput->connector_id,
917fda9279dSmrg				    mode_id, mode);
918fda9279dSmrg}
919fda9279dSmrg
920fda9279dSmrgstatic Bool
921fda9279dSmrgdrmmode_property_ignore(drmModePropertyPtr prop)
922fda9279dSmrg{
923fda9279dSmrg	if (!prop)
924fda9279dSmrg	    return TRUE;
925fda9279dSmrg	/* ignore blob prop */
926fda9279dSmrg	if (prop->flags & DRM_MODE_PROP_BLOB)
927fda9279dSmrg		return TRUE;
928fda9279dSmrg	/* ignore standard property */
929fda9279dSmrg	if (!strcmp(prop->name, "EDID") ||
930fda9279dSmrg	    !strcmp(prop->name, "DPMS"))
931fda9279dSmrg		return TRUE;
932fda9279dSmrg
933fda9279dSmrg	return FALSE;
934fda9279dSmrg}
935fda9279dSmrg
936fda9279dSmrgstatic void
937fda9279dSmrgdrmmode_output_create_resources(xf86OutputPtr output)
938fda9279dSmrg{
939fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
940fda9279dSmrg	drmModeConnectorPtr mode_output = drmmode_output->mode_output;
941fda9279dSmrg	drmmode_ptr drmmode = drmmode_output->drmmode;
942fda9279dSmrg	drmModePropertyPtr drmmode_prop;
943fda9279dSmrg	uint32_t value;
944fda9279dSmrg	int i, j, err;
945fda9279dSmrg
946fda9279dSmrg	drmmode_output->props = calloc(mode_output->count_props, sizeof(drmmode_prop_rec));
947fda9279dSmrg	if (!drmmode_output->props)
948fda9279dSmrg		return;
949fda9279dSmrg
950fda9279dSmrg	drmmode_output->num_props = 0;
951fda9279dSmrg	for (i = 0, j = 0; i < mode_output->count_props; i++) {
952fda9279dSmrg		drmmode_prop = drmModeGetProperty(drmmode->fd, mode_output->props[i]);
953fda9279dSmrg		if (drmmode_property_ignore(drmmode_prop)) {
954fda9279dSmrg			drmModeFreeProperty(drmmode_prop);
955fda9279dSmrg			continue;
956fda9279dSmrg		}
957fda9279dSmrg		drmmode_output->props[j].mode_prop = drmmode_prop;
958fda9279dSmrg		drmmode_output->props[j].index = i;
959fda9279dSmrg		drmmode_output->num_props++;
960fda9279dSmrg		j++;
961fda9279dSmrg	}
962fda9279dSmrg
963fda9279dSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
964fda9279dSmrg		drmmode_prop_ptr p = &drmmode_output->props[i];
965fda9279dSmrg		drmmode_prop = p->mode_prop;
966fda9279dSmrg
967fda9279dSmrg		value = drmmode_output->mode_output->prop_values[p->index];
968fda9279dSmrg
969fda9279dSmrg		if (drmmode_prop->flags & DRM_MODE_PROP_RANGE) {
970fda9279dSmrg			INT32 range[2];
971fda9279dSmrg
972fda9279dSmrg			p->num_atoms = 1;
973fda9279dSmrg			p->atoms = calloc(p->num_atoms, sizeof(Atom));
974fda9279dSmrg			if (!p->atoms)
975fda9279dSmrg				continue;
976fda9279dSmrg			p->atoms[0] = MakeAtom(drmmode_prop->name, strlen(drmmode_prop->name), TRUE);
977fda9279dSmrg			range[0] = drmmode_prop->values[0];
978fda9279dSmrg			range[1] = drmmode_prop->values[1];
979fda9279dSmrg			err = RRConfigureOutputProperty(output->randr_output, p->atoms[0],
980fda9279dSmrg							FALSE, TRUE,
981fda9279dSmrg							drmmode_prop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE,
982fda9279dSmrg							2, range);
983fda9279dSmrg			if (err != 0) {
984fda9279dSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
985fda9279dSmrg					   "RRConfigureOutputProperty error, %d\n", err);
986fda9279dSmrg			}
987fda9279dSmrg			err = RRChangeOutputProperty(output->randr_output, p->atoms[0],
988fda9279dSmrg						     XA_INTEGER, 32, PropModeReplace, 1,
989fda9279dSmrg						     &value, FALSE, FALSE);
990fda9279dSmrg			if (err != 0) {
991fda9279dSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
992fda9279dSmrg					   "RRChangeOutputProperty error, %d\n", err);
993fda9279dSmrg			}
994fda9279dSmrg		} else if (drmmode_prop->flags & DRM_MODE_PROP_ENUM) {
995fda9279dSmrg			p->num_atoms = drmmode_prop->count_enums + 1;
996fda9279dSmrg			p->atoms = calloc(p->num_atoms, sizeof(Atom));
997fda9279dSmrg			if (!p->atoms)
998fda9279dSmrg				continue;
999fda9279dSmrg			p->atoms[0] = MakeAtom(drmmode_prop->name, strlen(drmmode_prop->name), TRUE);
1000fda9279dSmrg			for (j = 1; j <= drmmode_prop->count_enums; j++) {
1001fda9279dSmrg				struct drm_mode_property_enum *e = &drmmode_prop->enums[j-1];
1002fda9279dSmrg				p->atoms[j] = MakeAtom(e->name, strlen(e->name), TRUE);
1003fda9279dSmrg			}
1004fda9279dSmrg			err = RRConfigureOutputProperty(output->randr_output, p->atoms[0],
1005fda9279dSmrg							FALSE, FALSE,
1006fda9279dSmrg							drmmode_prop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE,
1007fda9279dSmrg							p->num_atoms - 1, (INT32 *)&p->atoms[1]);
1008fda9279dSmrg			if (err != 0) {
1009fda9279dSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
1010fda9279dSmrg					   "RRConfigureOutputProperty error, %d\n", err);
1011fda9279dSmrg			}
1012fda9279dSmrg			for (j = 0; j < drmmode_prop->count_enums; j++)
1013fda9279dSmrg				if (drmmode_prop->enums[j].value == value)
1014fda9279dSmrg					break;
1015fda9279dSmrg			/* there's always a matching value */
1016fda9279dSmrg			err = RRChangeOutputProperty(output->randr_output, p->atoms[0],
1017fda9279dSmrg						     XA_ATOM, 32, PropModeReplace, 1, &p->atoms[j+1], FALSE, FALSE);
1018fda9279dSmrg			if (err != 0) {
1019fda9279dSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
1020fda9279dSmrg					   "RRChangeOutputProperty error, %d\n", err);
1021fda9279dSmrg			}
1022fda9279dSmrg		}
1023fda9279dSmrg	}
1024fda9279dSmrg}
1025fda9279dSmrg
1026fda9279dSmrgstatic Bool
1027fda9279dSmrgdrmmode_output_set_property(xf86OutputPtr output, Atom property,
1028fda9279dSmrg			    RRPropertyValuePtr value)
1029fda9279dSmrg{
1030fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
1031fda9279dSmrg	drmmode_ptr drmmode = drmmode_output->drmmode;
1032fda9279dSmrg	int i, ret;
1033fda9279dSmrg
1034fda9279dSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
1035fda9279dSmrg		drmmode_prop_ptr p = &drmmode_output->props[i];
1036fda9279dSmrg
1037fda9279dSmrg		if (p->atoms[0] != property)
1038fda9279dSmrg			continue;
1039fda9279dSmrg
1040fda9279dSmrg		if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
1041fda9279dSmrg			uint32_t val;
1042fda9279dSmrg
1043fda9279dSmrg			if (value->type != XA_INTEGER || value->format != 32 ||
1044fda9279dSmrg			    value->size != 1)
1045fda9279dSmrg				return FALSE;
1046fda9279dSmrg			val = *(uint32_t *)value->data;
1047fda9279dSmrg
1048fda9279dSmrg			ret = drmModeConnectorSetProperty(drmmode->fd, drmmode_output->output_id,
1049fda9279dSmrg							  p->mode_prop->prop_id, (uint64_t)val);
1050fda9279dSmrg
1051fda9279dSmrg			if (ret)
1052fda9279dSmrg				return FALSE;
1053fda9279dSmrg
1054fda9279dSmrg			return TRUE;
1055fda9279dSmrg
1056fda9279dSmrg		} else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
1057fda9279dSmrg			Atom	atom;
1058fda9279dSmrg			const char	*name;
1059fda9279dSmrg			int		j;
1060fda9279dSmrg
1061fda9279dSmrg			if (value->type != XA_ATOM || value->format != 32 || value->size != 1)
1062fda9279dSmrg				return FALSE;
1063fda9279dSmrg			memcpy(&atom, value->data, 4);
106422d74663Smrg			if (!(name = NameForAtom(atom)))
106522d74663Smrg				return FALSE;
1066fda9279dSmrg
1067fda9279dSmrg			/* search for matching name string, then set its value down */
1068fda9279dSmrg			for (j = 0; j < p->mode_prop->count_enums; j++) {
1069fda9279dSmrg				if (!strcmp(p->mode_prop->enums[j].name, name)) {
1070fda9279dSmrg					ret = drmModeConnectorSetProperty(drmmode->fd,
1071fda9279dSmrg									  drmmode_output->output_id,
1072fda9279dSmrg									  p->mode_prop->prop_id,
1073fda9279dSmrg									  p->mode_prop->enums[j].value);
1074fda9279dSmrg
1075fda9279dSmrg					if (ret)
1076fda9279dSmrg						return FALSE;
1077fda9279dSmrg
1078fda9279dSmrg					return TRUE;
1079fda9279dSmrg				}
1080fda9279dSmrg			}
1081fda9279dSmrg
1082fda9279dSmrg			return FALSE;
1083fda9279dSmrg		}
1084fda9279dSmrg	}
1085fda9279dSmrg
1086fda9279dSmrg	return TRUE;
1087fda9279dSmrg}
1088fda9279dSmrg
1089fda9279dSmrgstatic Bool
1090fda9279dSmrgdrmmode_output_get_property(xf86OutputPtr output, Atom property)
1091fda9279dSmrg{
1092fda9279dSmrg
1093fda9279dSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
1094fda9279dSmrg	drmmode_ptr drmmode = drmmode_output->drmmode;
1095fda9279dSmrg	uint32_t value;
1096fda9279dSmrg	int err, i;
1097fda9279dSmrg
1098fda9279dSmrg	if (output->scrn->vtSema) {
1099fda9279dSmrg		drmModeFreeConnector(drmmode_output->mode_output);
1100fda9279dSmrg		drmmode_output->mode_output =
1101fda9279dSmrg			drmModeGetConnector(drmmode->fd, drmmode_output->output_id);
1102fda9279dSmrg	}
1103fda9279dSmrg
1104fda9279dSmrg	if (!drmmode_output->mode_output)
1105fda9279dSmrg		return FALSE;
1106fda9279dSmrg
1107fda9279dSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
1108fda9279dSmrg		drmmode_prop_ptr p = &drmmode_output->props[i];
1109fda9279dSmrg		if (p->atoms[0] != property)
1110fda9279dSmrg			continue;
1111fda9279dSmrg
1112fda9279dSmrg		value = drmmode_output->mode_output->prop_values[p->index];
1113fda9279dSmrg
1114fda9279dSmrg		if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
1115fda9279dSmrg			err = RRChangeOutputProperty(output->randr_output,
1116fda9279dSmrg						     property, XA_INTEGER, 32,
1117fda9279dSmrg						     PropModeReplace, 1, &value,
1118fda9279dSmrg						     FALSE, FALSE);
1119fda9279dSmrg
1120fda9279dSmrg			return !err;
1121fda9279dSmrg		} else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
1122fda9279dSmrg			int		j;
1123fda9279dSmrg
1124fda9279dSmrg			/* search for matching name string, then set its value down */
1125fda9279dSmrg			for (j = 0; j < p->mode_prop->count_enums; j++) {
1126fda9279dSmrg				if (p->mode_prop->enums[j].value == value)
1127fda9279dSmrg					break;
1128fda9279dSmrg			}
1129fda9279dSmrg
1130fda9279dSmrg			err = RRChangeOutputProperty(output->randr_output, property,
1131fda9279dSmrg						     XA_ATOM, 32, PropModeReplace, 1,
1132fda9279dSmrg						     &p->atoms[j+1], FALSE, FALSE);
1133fda9279dSmrg
1134fda9279dSmrg			return !err;
1135fda9279dSmrg		}
1136fda9279dSmrg	}
1137fda9279dSmrg
1138fda9279dSmrg	return FALSE;
1139fda9279dSmrg}
1140fda9279dSmrg
1141fda9279dSmrgstatic const xf86OutputFuncsRec drmmode_output_funcs = {
1142fda9279dSmrg	.create_resources = drmmode_output_create_resources,
1143fda9279dSmrg	.dpms = drmmode_output_dpms,
1144fda9279dSmrg	.detect = drmmode_output_detect,
1145fda9279dSmrg	.mode_valid = drmmode_output_mode_valid,
1146fda9279dSmrg	.get_modes = drmmode_output_get_modes,
1147fda9279dSmrg	.set_property = drmmode_output_set_property,
1148fda9279dSmrg	.get_property = drmmode_output_get_property,
1149fda9279dSmrg	.destroy = drmmode_output_destroy
1150fda9279dSmrg};
1151fda9279dSmrg
1152fda9279dSmrgstatic int subpixel_conv_table[7] = { 0, SubPixelUnknown,
1153fda9279dSmrg				      SubPixelHorizontalRGB,
1154fda9279dSmrg				      SubPixelHorizontalBGR,
1155fda9279dSmrg				      SubPixelVerticalRGB,
1156fda9279dSmrg				      SubPixelVerticalBGR,
1157fda9279dSmrg				      SubPixelNone };
1158fda9279dSmrg
1159fda9279dSmrgconst char *output_names[] = { "None",
1160fda9279dSmrg			       "VGA",
1161fda9279dSmrg			       "DVI-I",
1162fda9279dSmrg			       "DVI-D",
1163fda9279dSmrg			       "DVI-A",
1164fda9279dSmrg			       "Composite",
1165fda9279dSmrg			       "SVIDEO",
1166fda9279dSmrg			       "LVDS",
1167fda9279dSmrg			       "CTV",
1168fda9279dSmrg			       "DIN",
1169fda9279dSmrg			       "DP",
1170fda9279dSmrg			       "HDMI",
1171fda9279dSmrg			       "HDMI",
1172fda9279dSmrg			       "TV",
1173fda9279dSmrg			       "eDP",
1174fda9279dSmrg};
1175fda9279dSmrg#define NUM_OUTPUT_NAMES (sizeof(output_names) / sizeof(output_names[0]))
1176fda9279dSmrg
1177fda9279dSmrgstatic Bool
1178fda9279dSmrgdrmmode_zaphod_match(ScrnInfoPtr pScrn, const char *s, char *output_name)
1179fda9279dSmrg{
1180fda9279dSmrg    int i = 0;
1181fda9279dSmrg    char s1[20];
1182fda9279dSmrg
1183fda9279dSmrg    do {
1184fda9279dSmrg	switch(*s) {
1185fda9279dSmrg	case ',':
1186fda9279dSmrg	    s1[i] = '\0';
1187fda9279dSmrg	    i = 0;
1188fda9279dSmrg	    if (strcmp(s1, output_name) == 0)
1189fda9279dSmrg		return TRUE;
1190fda9279dSmrg	    break;
1191fda9279dSmrg	case ' ':
1192fda9279dSmrg	case '\t':
1193fda9279dSmrg	case '\n':
1194fda9279dSmrg	case '\r':
1195fda9279dSmrg	    break;
1196fda9279dSmrg	default:
1197fda9279dSmrg	    s1[i] = *s;
1198fda9279dSmrg	    i++;
1199fda9279dSmrg	    break;
1200fda9279dSmrg	}
1201fda9279dSmrg    } while(*s++);
1202fda9279dSmrg
1203fda9279dSmrg    s1[i] = '\0';
1204fda9279dSmrg    if (strcmp(s1, output_name) == 0)
1205fda9279dSmrg	return TRUE;
1206fda9279dSmrg
1207fda9279dSmrg    return FALSE;
1208fda9279dSmrg}
1209fda9279dSmrg
121022d74663Smrgstatic xf86OutputPtr find_output(ScrnInfoPtr pScrn, int id)
121122d74663Smrg{
121222d74663Smrg	xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
121322d74663Smrg	int i;
121422d74663Smrg	for (i = 0; i < xf86_config->num_output; i++) {
121522d74663Smrg		xf86OutputPtr output = xf86_config->output[i];
121622d74663Smrg		drmmode_output_private_ptr drmmode_output;
121722d74663Smrg
121822d74663Smrg		drmmode_output = output->driver_private;
121922d74663Smrg		if (drmmode_output->output_id == id)
122022d74663Smrg			return output;
122122d74663Smrg	}
122222d74663Smrg	return NULL;
122322d74663Smrg}
122422d74663Smrg
122522d74663Smrgstatic int parse_path_blob(drmModePropertyBlobPtr path_blob, int *conn_base_id, char **path)
122622d74663Smrg{
122722d74663Smrg	char *conn;
122822d74663Smrg	char conn_id[5];
122922d74663Smrg	int id, len;
123022d74663Smrg	char *blob_data;
123122d74663Smrg
123222d74663Smrg	if (!path_blob)
123322d74663Smrg		return -1;
123422d74663Smrg
123522d74663Smrg	blob_data = path_blob->data;
123622d74663Smrg	/* we only handle MST paths for now */
123722d74663Smrg	if (strncmp(blob_data, "mst:", 4))
123822d74663Smrg		return -1;
123922d74663Smrg
124022d74663Smrg	conn = strchr(blob_data + 4, '-');
124122d74663Smrg	if (!conn)
124222d74663Smrg		return -1;
124322d74663Smrg	len = conn - (blob_data + 4);
124422d74663Smrg	if (len + 1 > 5)
124522d74663Smrg		return -1;
124622d74663Smrg	memcpy(conn_id, blob_data + 4, len);
124722d74663Smrg	conn_id[len] = '\0';
124822d74663Smrg	id = strtoul(conn_id, NULL, 10);
124922d74663Smrg
125022d74663Smrg	*conn_base_id = id;
125122d74663Smrg
125222d74663Smrg	*path = conn + 1;
125322d74663Smrg	return 0;
125422d74663Smrg}
125522d74663Smrg
125622d74663Smrgstatic void
125722d74663Smrgdrmmode_create_name(ScrnInfoPtr pScrn, drmModeConnectorPtr koutput, char *name,
125822d74663Smrg                    drmModePropertyBlobPtr path_blob)
125922d74663Smrg{
126022d74663Smrg	int ret;
126122d74663Smrg	char *extra_path;
126222d74663Smrg	int conn_id;
126322d74663Smrg	xf86OutputPtr output;
126422d74663Smrg
126522d74663Smrg	ret = parse_path_blob(path_blob, &conn_id, &extra_path);
126622d74663Smrg	if (ret == -1)
126722d74663Smrg		goto fallback;
126822d74663Smrg
126922d74663Smrg	output = find_output(pScrn, conn_id);
127022d74663Smrg	if (!output)
127122d74663Smrg		goto fallback;
127222d74663Smrg
127322d74663Smrg	snprintf(name, 32, "%s-%s", output->name, extra_path);
127422d74663Smrg	return;
127522d74663Smrg
127622d74663Smrgfallback:
127722d74663Smrg	if (koutput->connector_type >= ARRAY_SIZE(output_names))
127822d74663Smrg		snprintf(name, 32, "Unknown%d-%d", koutput->connector_type, koutput->connector_type_id);
127922d74663Smrg	else if (pScrn->is_gpu)
128022d74663Smrg		snprintf(name, 32, "%s-%d-%d", output_names[koutput->connector_type], pScrn->scrnIndex - GPU_SCREEN_OFFSET + 1, koutput->connector_type_id);
128122d74663Smrg	else
128222d74663Smrg		snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], koutput->connector_type_id);
128322d74663Smrg}
128422d74663Smrg
1285fda9279dSmrgstatic unsigned int
128622d74663Smrgdrmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res, int num, Bool dynamic, int crtcshift)
1287fda9279dSmrg{
1288fda9279dSmrg	NVPtr pNv = NVPTR(pScrn);
1289fda9279dSmrg	xf86OutputPtr output;
129022d74663Smrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
1291fda9279dSmrg	drmModeConnectorPtr koutput;
1292fda9279dSmrg	drmModeEncoderPtr kencoder;
1293fda9279dSmrg	drmmode_output_private_ptr drmmode_output;
1294fda9279dSmrg	const char *s;
1295fda9279dSmrg	char name[32];
129622d74663Smrg	drmModePropertyBlobPtr path_blob = NULL;
129722d74663Smrg	int i;
1298fda9279dSmrg
1299fda9279dSmrg	koutput = drmModeGetConnector(drmmode->fd,
130022d74663Smrg				      mode_res->connectors[num]);
1301fda9279dSmrg	if (!koutput)
1302fda9279dSmrg		return 0;
1303fda9279dSmrg
130422d74663Smrg	path_blob = koutput_get_prop_blob(drmmode->fd, koutput, "PATH");
130522d74663Smrg
130622d74663Smrg	drmmode_create_name(pScrn, koutput, name, path_blob);
130722d74663Smrg
130822d74663Smrg	if (path_blob)
130922d74663Smrg		drmModeFreePropertyBlob(path_blob);
131022d74663Smrg
131122d74663Smrg	if (path_blob && dynamic) {
131222d74663Smrg		/* see if we have an output with this name already
131322d74663Smrg		   and hook stuff up */
131422d74663Smrg		for (i = 0; i < xf86_config->num_output; i++) {
131522d74663Smrg			output = xf86_config->output[i];
131622d74663Smrg
131722d74663Smrg			if (strncmp(output->name, name, 32))
131822d74663Smrg				continue;
131922d74663Smrg
132022d74663Smrg			drmmode_output = output->driver_private;
132122d74663Smrg			drmmode_output->output_id = mode_res->connectors[num];
132222d74663Smrg			drmmode_output->mode_output = koutput;
132322d74663Smrg			return 1;
132422d74663Smrg		}
132522d74663Smrg	}
132622d74663Smrg
132722d74663Smrg
1328fda9279dSmrg	kencoder = drmModeGetEncoder(drmmode->fd, koutput->encoders[0]);
1329fda9279dSmrg	if (!kencoder) {
1330fda9279dSmrg		drmModeFreeConnector(koutput);
1331fda9279dSmrg		return 0;
1332fda9279dSmrg	}
1333fda9279dSmrg
1334fda9279dSmrg	if (xf86IsEntityShared(pScrn->entityList[0])) {
1335fda9279dSmrg		s = xf86GetOptValString(pNv->Options, OPTION_ZAPHOD_HEADS);
1336fda9279dSmrg		if (s) {
1337fda9279dSmrg			if (!drmmode_zaphod_match(pScrn, s, name)) {
1338fda9279dSmrg				drmModeFreeEncoder(kencoder);
1339fda9279dSmrg				drmModeFreeConnector(koutput);
1340fda9279dSmrg				return 0;
1341fda9279dSmrg			}
1342fda9279dSmrg		} else {
1343fda9279dSmrg			if (pNv->Primary && (num != 0)) {
1344fda9279dSmrg				drmModeFreeEncoder(kencoder);
1345fda9279dSmrg				drmModeFreeConnector(koutput);
1346fda9279dSmrg				return 0;
1347fda9279dSmrg			} else
1348fda9279dSmrg			if (pNv->Secondary && (num != 1)) {
1349fda9279dSmrg				drmModeFreeEncoder(kencoder);
1350fda9279dSmrg				drmModeFreeConnector(koutput);
1351fda9279dSmrg				return 0;
1352fda9279dSmrg			}
1353fda9279dSmrg		}
1354fda9279dSmrg	}
1355fda9279dSmrg
1356fda9279dSmrg	output = xf86OutputCreate (pScrn, &drmmode_output_funcs, name);
1357fda9279dSmrg	if (!output) {
1358fda9279dSmrg		drmModeFreeEncoder(kencoder);
1359fda9279dSmrg		drmModeFreeConnector(koutput);
1360fda9279dSmrg		return 0;
1361fda9279dSmrg	}
1362fda9279dSmrg
1363fda9279dSmrg	drmmode_output = calloc(sizeof(drmmode_output_private_rec), 1);
1364fda9279dSmrg	if (!drmmode_output) {
1365fda9279dSmrg		xf86OutputDestroy(output);
1366fda9279dSmrg		drmModeFreeConnector(koutput);
1367fda9279dSmrg		drmModeFreeEncoder(kencoder);
1368fda9279dSmrg		return 0;
1369fda9279dSmrg	}
1370fda9279dSmrg
137122d74663Smrg	drmmode_output->output_id = mode_res->connectors[num];
1372fda9279dSmrg	drmmode_output->mode_output = koutput;
1373fda9279dSmrg	drmmode_output->mode_encoder = kencoder;
1374fda9279dSmrg	drmmode_output->drmmode = drmmode;
1375fda9279dSmrg	output->mm_width = koutput->mmWidth;
1376fda9279dSmrg	output->mm_height = koutput->mmHeight;
1377fda9279dSmrg
1378fda9279dSmrg	output->subpixel_order = subpixel_conv_table[koutput->subpixel];
1379fda9279dSmrg	output->driver_private = drmmode_output;
1380fda9279dSmrg
1381a33a703bSmrg	output->possible_crtcs = kencoder->possible_crtcs >> crtcshift;
1382a33a703bSmrg	output->possible_clones = kencoder->possible_clones >> crtcshift;
1383fda9279dSmrg
1384fda9279dSmrg	output->interlaceAllowed = true;
1385fda9279dSmrg	output->doubleScanAllowed = true;
1386fda9279dSmrg
138722d74663Smrg	if (dynamic)
138822d74663Smrg		output->randr_output = RROutputCreate(xf86ScrnToScreen(pScrn), output->name, strlen(output->name), output);
138922d74663Smrg
1390fda9279dSmrg	return 1;
1391fda9279dSmrg}
1392fda9279dSmrg
1393fda9279dSmrgstatic Bool
1394fda9279dSmrgdrmmode_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height)
1395fda9279dSmrg{
1396fda9279dSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
1397fda9279dSmrg	ScreenPtr screen = xf86ScrnToScreen(scrn);
1398fda9279dSmrg	NVPtr pNv = NVPTR(scrn);
1399fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc = NULL;
1400fda9279dSmrg	drmmode_ptr drmmode = NULL;
1401fda9279dSmrg	uint32_t old_width, old_height, old_pitch, old_fb_id = 0;
1402fda9279dSmrg	struct nouveau_bo *old_bo = NULL;
1403fda9279dSmrg	int ret, i, pitch;
1404fda9279dSmrg	PixmapPtr ppix;
1405fda9279dSmrg
1406fda9279dSmrg	if (xf86_config->num_crtc) {
1407fda9279dSmrg		drmmode_crtc = xf86_config->crtc[0]->driver_private;
1408fda9279dSmrg		drmmode = drmmode_crtc->drmmode;
1409fda9279dSmrg	}
1410fda9279dSmrg	ErrorF("resize called %d %d\n", width, height);
1411fda9279dSmrg
1412fda9279dSmrg	if (scrn->virtualX == width && scrn->virtualY == height)
1413fda9279dSmrg		return TRUE;
1414fda9279dSmrg
1415fda9279dSmrg	old_width = scrn->virtualX;
1416fda9279dSmrg	old_height = scrn->virtualY;
1417fda9279dSmrg	old_pitch = scrn->displayWidth;
1418fda9279dSmrg	if (drmmode)
1419fda9279dSmrg		old_fb_id = drmmode->fb_id;
1420fda9279dSmrg	nouveau_bo_ref(pNv->scanout, &old_bo);
1421fda9279dSmrg	nouveau_bo_ref(NULL, &pNv->scanout);
1422fda9279dSmrg
1423fda9279dSmrg	ret = nouveau_allocate_surface(scrn, width, height,
1424fda9279dSmrg				       scrn->bitsPerPixel,
1425fda9279dSmrg				       NOUVEAU_CREATE_PIXMAP_SCANOUT,
1426fda9279dSmrg				       &pitch, &pNv->scanout);
1427fda9279dSmrg	if (!ret)
1428fda9279dSmrg		goto fail;
1429fda9279dSmrg
1430fda9279dSmrg	scrn->virtualX = width;
1431fda9279dSmrg	scrn->virtualY = height;
1432fda9279dSmrg	scrn->displayWidth = pitch / (scrn->bitsPerPixel >> 3);
1433fda9279dSmrg
1434fda9279dSmrg	nouveau_bo_map(pNv->scanout, NOUVEAU_BO_RDWR, pNv->client);
1435fda9279dSmrg
1436fda9279dSmrg	if (drmmode) {
1437fda9279dSmrg		ret = drmModeAddFB(drmmode->fd, width, height, scrn->depth,
1438fda9279dSmrg				  scrn->bitsPerPixel, pitch, pNv->scanout->handle,
1439fda9279dSmrg				  &drmmode->fb_id);
1440fda9279dSmrg		if (ret)
1441fda9279dSmrg			goto fail;
1442fda9279dSmrg	}
1443fda9279dSmrg
1444fda9279dSmrg	if (pNv->ShadowPtr) {
1445fda9279dSmrg		free(pNv->ShadowPtr);
1446fda9279dSmrg		pNv->ShadowPitch = pitch;
1447fda9279dSmrg		pNv->ShadowPtr = malloc(pNv->ShadowPitch * height);
1448fda9279dSmrg	}
1449fda9279dSmrg
1450fda9279dSmrg	ppix = screen->GetScreenPixmap(screen);
14512a7e9763Smrg	if (pNv->AccelMethod > NONE)
1452fda9279dSmrg		nouveau_bo_ref(pNv->scanout, &drmmode_pixmap(ppix)->bo);
1453fda9279dSmrg	screen->ModifyPixmapHeader(ppix, width, height, -1, -1, pitch,
1454fda9279dSmrg				   (pNv->AccelMethod > NONE || pNv->ShadowPtr) ?
1455fda9279dSmrg				   pNv->ShadowPtr : pNv->scanout->map);
1456fda9279dSmrg
1457fda9279dSmrg	if (pNv->AccelMethod == EXA) {
1458fda9279dSmrg		pNv->EXADriverPtr->PrepareSolid(ppix, GXcopy, ~0, 0);
1459fda9279dSmrg		pNv->EXADriverPtr->Solid(ppix, 0, 0, width, height);
1460fda9279dSmrg		pNv->EXADriverPtr->DoneSolid(ppix);
1461fda9279dSmrg		nouveau_bo_map(pNv->scanout, NOUVEAU_BO_RDWR, pNv->client);
1462fda9279dSmrg	} else {
1463fda9279dSmrg		memset(pNv->scanout->map, 0x00, pNv->scanout->size);
1464fda9279dSmrg	}
1465fda9279dSmrg
1466fda9279dSmrg	for (i = 0; i < xf86_config->num_crtc; i++) {
1467fda9279dSmrg		xf86CrtcPtr crtc = xf86_config->crtc[i];
1468fda9279dSmrg
1469fda9279dSmrg		if (!crtc->enabled)
1470fda9279dSmrg			continue;
1471fda9279dSmrg
1472fda9279dSmrg		drmmode_set_mode_major(crtc, &crtc->mode,
1473fda9279dSmrg				       crtc->rotation, crtc->x, crtc->y);
1474fda9279dSmrg	}
1475fda9279dSmrg
1476fda9279dSmrg	if (old_fb_id)
1477fda9279dSmrg		drmModeRmFB(drmmode->fd, old_fb_id);
1478fda9279dSmrg	nouveau_bo_ref(NULL, &old_bo);
1479fda9279dSmrg
1480fda9279dSmrg	return TRUE;
1481fda9279dSmrg
1482fda9279dSmrg fail:
1483fda9279dSmrg	nouveau_bo_ref(old_bo, &pNv->scanout);
1484fda9279dSmrg	scrn->virtualX = old_width;
1485fda9279dSmrg	scrn->virtualY = old_height;
1486fda9279dSmrg	scrn->displayWidth = old_pitch;
1487fda9279dSmrg	if (drmmode)
1488fda9279dSmrg		drmmode->fb_id = old_fb_id;
1489fda9279dSmrg
1490fda9279dSmrg	return FALSE;
1491fda9279dSmrg}
1492fda9279dSmrg
1493fda9279dSmrgstatic const xf86CrtcConfigFuncsRec drmmode_xf86crtc_config_funcs = {
1494fda9279dSmrg	drmmode_xf86crtc_resize
1495fda9279dSmrg};
1496fda9279dSmrg
1497fda9279dSmrgBool drmmode_pre_init(ScrnInfoPtr pScrn, int fd, int cpp)
1498fda9279dSmrg{
1499fda9279dSmrg	drmmode_ptr drmmode;
150022d74663Smrg	drmModeResPtr mode_res;
1501fda9279dSmrg	NVEntPtr pNVEnt = NVEntPriv(pScrn);
1502fda9279dSmrg	int i;
1503fda9279dSmrg	unsigned int crtcs_needed = 0;
1504a33a703bSmrg	int crtcshift;
1505fda9279dSmrg
1506a5560a61Smrg	drmmode = XNFcallocarray(sizeof(*drmmode), 1);
1507fda9279dSmrg	drmmode->fd = fd;
1508fda9279dSmrg	drmmode->fb_id = 0;
1509fda9279dSmrg
1510fda9279dSmrg	xf86CrtcConfigInit(pScrn, &drmmode_xf86crtc_config_funcs);
1511fda9279dSmrg
1512fda9279dSmrg	drmmode->cpp = cpp;
151322d74663Smrg	mode_res = drmModeGetResources(drmmode->fd);
151422d74663Smrg	if (!mode_res)
1515fda9279dSmrg		return FALSE;
1516fda9279dSmrg
151722d74663Smrg	xf86CrtcSetSizeRange(pScrn, 320, 200, mode_res->max_width,
151822d74663Smrg			     mode_res->max_height);
1519fda9279dSmrg
152022d74663Smrg	if (!mode_res->count_connectors ||
152122d74663Smrg	    !mode_res->count_crtcs) {
1522fda9279dSmrg		free(drmmode);
1523fda9279dSmrg		goto done;
1524fda9279dSmrg	}
1525fda9279dSmrg
1526fda9279dSmrg	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Initializing outputs ...\n");
1527a33a703bSmrg	crtcshift = ffs(pNVEnt->assigned_crtcs ^ 0xffffffff) - 1;
152822d74663Smrg	for (i = 0; i < mode_res->count_connectors; i++)
152922d74663Smrg		crtcs_needed += drmmode_output_init(pScrn, drmmode, mode_res, i, FALSE, crtcshift);
1530fda9279dSmrg
1531fda9279dSmrg	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
1532fda9279dSmrg		   "%d crtcs needed for screen.\n", crtcs_needed);
1533fda9279dSmrg
153422d74663Smrg	for (i = 0; i < mode_res->count_crtcs; i++) {
1535fda9279dSmrg		if (!xf86IsEntityShared(pScrn->entityList[0]) ||
1536fda9279dSmrg		    (crtcs_needed && !(pNVEnt->assigned_crtcs & (1 << i))))
153722d74663Smrg			crtcs_needed -= drmmode_crtc_init(pScrn, drmmode, mode_res, i);
1538fda9279dSmrg	}
1539fda9279dSmrg
1540fda9279dSmrg	/* All ZaphodHeads outputs provided with matching crtcs? */
1541fda9279dSmrg	if (xf86IsEntityShared(pScrn->entityList[0]) && (crtcs_needed > 0))
1542fda9279dSmrg		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
1543fda9279dSmrg			   "%d ZaphodHeads crtcs unavailable. Trouble!\n",
1544fda9279dSmrg			   crtcs_needed);
1545fda9279dSmrg
1546fda9279dSmrgdone:
154722d74663Smrg	drmModeFreeResources(mode_res);
154822d74663Smrg
1549fda9279dSmrg#ifdef NOUVEAU_PIXMAP_SHARING
1550fda9279dSmrg	xf86ProviderSetup(pScrn, NULL, "nouveau");
1551fda9279dSmrg#endif
1552fda9279dSmrg
1553fda9279dSmrg	xf86InitialConfiguration(pScrn, TRUE);
1554fda9279dSmrg
1555fda9279dSmrg	return TRUE;
1556fda9279dSmrg}
1557fda9279dSmrg
1558fda9279dSmrgvoid
1559fda9279dSmrgdrmmode_adjust_frame(ScrnInfoPtr scrn, int x, int y)
1560fda9279dSmrg{
1561fda9279dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
1562fda9279dSmrg	xf86OutputPtr output = config->output[config->compat_output];
1563fda9279dSmrg	xf86CrtcPtr crtc = output->crtc;
1564fda9279dSmrg
1565fda9279dSmrg	if (!crtc || !crtc->enabled)
1566fda9279dSmrg		return;
1567fda9279dSmrg
1568fda9279dSmrg	drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation, x, y);
1569fda9279dSmrg}
1570fda9279dSmrg
1571fda9279dSmrgvoid
1572fda9279dSmrgdrmmode_remove_fb(ScrnInfoPtr pScrn)
1573fda9279dSmrg{
1574fda9279dSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
1575fda9279dSmrg	xf86CrtcPtr crtc = NULL;
1576fda9279dSmrg	drmmode_crtc_private_ptr drmmode_crtc;
1577fda9279dSmrg	drmmode_ptr drmmode;
1578fda9279dSmrg
1579fda9279dSmrg	if (config && config->num_crtc)
1580fda9279dSmrg		crtc = config->crtc[0];
1581fda9279dSmrg	if (!crtc)
1582fda9279dSmrg		return;
1583fda9279dSmrg
1584fda9279dSmrg	drmmode_crtc = crtc->driver_private;
1585fda9279dSmrg	drmmode = drmmode_crtc->drmmode;
1586fda9279dSmrg
1587fda9279dSmrg	if (drmmode->fb_id)
1588fda9279dSmrg		drmModeRmFB(drmmode->fd, drmmode->fb_id);
1589fda9279dSmrg	drmmode->fb_id = 0;
1590fda9279dSmrg}
1591fda9279dSmrg
1592fda9279dSmrgint
1593fda9279dSmrgdrmmode_cursor_init(ScreenPtr pScreen)
1594fda9279dSmrg{
1595fda9279dSmrg	NVPtr pNv = NVPTR(xf86ScreenToScrn(pScreen));
1596fda9279dSmrg	int size = nv_cursor_width(pNv);
1597fda9279dSmrg	int flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP |
1598fda9279dSmrg		    HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_32 |
1599fda9279dSmrg		    (pNv->dev->chipset >= 0x11 ? HARDWARE_CURSOR_ARGB : 0) |
1600fda9279dSmrg		    HARDWARE_CURSOR_UPDATE_UNHIDDEN;
1601fda9279dSmrg
1602fda9279dSmrg	return xf86_cursors_init(pScreen, size, size, flags);
1603fda9279dSmrg}
1604fda9279dSmrg
1605fda9279dSmrg#ifdef HAVE_LIBUDEV
160622d74663Smrg
160722d74663Smrg#define DRM_MODE_LINK_STATUS_GOOD       0
160822d74663Smrg#define DRM_MODE_LINK_STATUS_BAD        1
160922d74663Smrg
1610fda9279dSmrgstatic void
1611fda9279dSmrgdrmmode_handle_uevents(ScrnInfoPtr scrn)
1612fda9279dSmrg{
161322d74663Smrg    struct udev_device *dev;
161422d74663Smrg    drmmode_ptr drmmode = drmmode_from_scrn(scrn);
161522d74663Smrg    drmModeResPtr mode_res;
161622d74663Smrg    xf86CrtcConfigPtr  config = XF86_CRTC_CONFIG_PTR(scrn);
161722d74663Smrg    int i, j;
161822d74663Smrg    Bool found = FALSE;
161922d74663Smrg    Bool changed = FALSE;
162022d74663Smrg
162122d74663Smrg    while ((dev = udev_monitor_receive_device(drmmode->uevent_monitor))) {
162222d74663Smrg        udev_device_unref(dev);
162322d74663Smrg        found = TRUE;
162422d74663Smrg    }
162522d74663Smrg    if (!found)
162622d74663Smrg        return;
162722d74663Smrg
162822d74663Smrg    /* Try to re-set the mode on all the connectors with a BAD link-state:
162922d74663Smrg     * This may happen if a link degrades and a new modeset is necessary, using
163022d74663Smrg     * different link-training parameters. If the kernel found that the current
163122d74663Smrg     * mode is not achievable anymore, it should have pruned the mode before
163222d74663Smrg     * sending the hotplug event. Try to re-set the currently-set mode to keep
163322d74663Smrg     * the display alive, this will fail if the mode has been pruned.
163422d74663Smrg     * In any case, we will send randr events for the Desktop Environment to
163522d74663Smrg     * deal with it, if it wants to.
163622d74663Smrg     */
163722d74663Smrg    for (i = 0; i < config->num_output; i++) {
163822d74663Smrg        xf86OutputPtr output = config->output[i];
163922d74663Smrg        xf86CrtcPtr crtc = output->crtc;
164022d74663Smrg        drmmode_output_private_ptr drmmode_output = output->driver_private;
164122d74663Smrg        uint32_t con_id, idx;
164222d74663Smrg        drmModeConnectorPtr koutput;
164322d74663Smrg
164422d74663Smrg        if (crtc == NULL || drmmode_output->mode_output == NULL)
164522d74663Smrg            continue;
164622d74663Smrg
164722d74663Smrg        con_id = drmmode_output->mode_output->connector_id;
164822d74663Smrg        /* Get an updated view of the properties for the current connector and
164922d74663Smrg         * look for the link-status property
165022d74663Smrg         */
165122d74663Smrg        koutput = drmModeGetConnectorCurrent(drmmode->fd, con_id);
165222d74663Smrg        if (!koutput)
165322d74663Smrg            continue;
165422d74663Smrg
165522d74663Smrg        idx = koutput_get_prop_idx(drmmode->fd, koutput,
165622d74663Smrg                DRM_MODE_PROP_ENUM, "link-status");
165722d74663Smrg
165822d74663Smrg        if ((idx > -1) &&
165922d74663Smrg                (koutput->prop_values[idx] == DRM_MODE_LINK_STATUS_BAD)) {
166022d74663Smrg            /* the connector got a link failure, re-set the current mode */
166122d74663Smrg            drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation,
166222d74663Smrg                                   crtc->x, crtc->y);
166322d74663Smrg
166422d74663Smrg            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
166522d74663Smrg                       "hotplug event: connector %u's link-state is BAD, "
166622d74663Smrg                       "tried resetting the current mode. You may be left"
166722d74663Smrg                       "with a black screen if this fails...\n", con_id);
166822d74663Smrg        }
166922d74663Smrg
167022d74663Smrg        drmModeFreeConnector(koutput);
167122d74663Smrg    }
167222d74663Smrg
167322d74663Smrg    mode_res = drmModeGetResources(drmmode->fd);
167422d74663Smrg    if (!mode_res)
167522d74663Smrg        goto out;
167622d74663Smrg
167722d74663Smrg    if (mode_res->count_crtcs != config->num_crtc) {
167822d74663Smrg        ErrorF("number of CRTCs changed - failed to handle, %d vs %d\n", mode_res->count_crtcs, config->num_crtc);
167922d74663Smrg        goto out_free_res;
168022d74663Smrg    }
168122d74663Smrg
168222d74663Smrg    /* figure out if we have gotten rid of any connectors
168322d74663Smrg       traverse old output list looking for outputs */
168422d74663Smrg    for (i = 0; i < config->num_output; i++) {
168522d74663Smrg        xf86OutputPtr output = config->output[i];
168622d74663Smrg        drmmode_output_private_ptr drmmode_output;
168722d74663Smrg
168822d74663Smrg        drmmode_output = output->driver_private;
168922d74663Smrg        found = FALSE;
169022d74663Smrg        for (j = 0; j < mode_res->count_connectors; j++) {
169122d74663Smrg            if (mode_res->connectors[j] == drmmode_output->output_id) {
169222d74663Smrg                found = TRUE;
169322d74663Smrg                break;
169422d74663Smrg            }
169522d74663Smrg        }
169622d74663Smrg        if (found)
169722d74663Smrg            continue;
169822d74663Smrg
169922d74663Smrg        drmModeFreeConnector(drmmode_output->mode_output);
170022d74663Smrg        drmmode_output->mode_output = NULL;
170122d74663Smrg        drmmode_output->output_id = -1;
170222d74663Smrg
170322d74663Smrg        changed = TRUE;
170422d74663Smrg    }
170522d74663Smrg
170622d74663Smrg    /* find new output ids we don't have outputs for */
170722d74663Smrg    for (i = 0; i < mode_res->count_connectors; i++) {
170822d74663Smrg        found = FALSE;
170922d74663Smrg
171022d74663Smrg        for (j = 0; j < config->num_output; j++) {
171122d74663Smrg            xf86OutputPtr output = config->output[j];
171222d74663Smrg            drmmode_output_private_ptr drmmode_output;
171322d74663Smrg
171422d74663Smrg            drmmode_output = output->driver_private;
171522d74663Smrg            if (mode_res->connectors[i] == drmmode_output->output_id) {
171622d74663Smrg                found = TRUE;
171722d74663Smrg                break;
171822d74663Smrg            }
171922d74663Smrg        }
172022d74663Smrg        if (found)
172122d74663Smrg            continue;
172222d74663Smrg
172322d74663Smrg        changed = TRUE;
172422d74663Smrg        drmmode_output_init(scrn, drmmode, mode_res, i, TRUE, 0);
172522d74663Smrg    }
172622d74663Smrg
172722d74663Smrg    if (changed) {
172822d74663Smrg        RRSetChanged(xf86ScrnToScreen(scrn));
172922d74663Smrg        RRTellChanged(xf86ScrnToScreen(scrn));
173022d74663Smrg    }
173122d74663Smrg
173222d74663Smrgout_free_res:
173322d74663Smrg    drmModeFreeResources(mode_res);
173422d74663Smrgout:
173522d74663Smrg    RRGetInfo(xf86ScrnToScreen(scrn), TRUE);
173622d74663Smrg}
1737fda9279dSmrg
173822d74663Smrg#undef DRM_MODE_LINK_STATUS_BAD
173922d74663Smrg#undef DRM_MODE_LINK_STATUS_GOOD
1740fda9279dSmrg
174122d74663Smrg#endif
1742fda9279dSmrg
1743c694fe25Smrg#ifdef HAVE_LIBUDEV
1744374ff59dSmrg#if HAVE_NOTIFY_FD
1745374ff59dSmrgstatic void
1746374ff59dSmrgdrmmode_udev_notify(int fd, int notify, void *data)
1747374ff59dSmrg{
1748374ff59dSmrg	ScrnInfoPtr scrn = data;
1749374ff59dSmrg	drmmode_handle_uevents(scrn);
1750374ff59dSmrg}
1751374ff59dSmrg#endif
1752c694fe25Smrg#endif
1753374ff59dSmrg
17541090d90aSmrgstatic bool has_randr(void)
17551090d90aSmrg{
17561090d90aSmrg	return dixPrivateKeyRegistered(rrPrivKey);
17571090d90aSmrg}
17581090d90aSmrg
1759fda9279dSmrgstatic void
1760fda9279dSmrgdrmmode_uevent_init(ScrnInfoPtr scrn)
1761fda9279dSmrg{
1762fda9279dSmrg#ifdef HAVE_LIBUDEV
1763fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
1764fda9279dSmrg	struct udev *u;
1765fda9279dSmrg	struct udev_monitor *mon;
1766fda9279dSmrg
17671090d90aSmrg	/* RandR will be disabled if Xinerama is active, and so generating
17681090d90aSmrg	 * RR hotplug events is then forbidden.
17691090d90aSmrg	 */
17701090d90aSmrg	if (!has_randr())
17711090d90aSmrg		return;
17721090d90aSmrg
1773fda9279dSmrg	u = udev_new();
1774fda9279dSmrg	if (!u)
1775fda9279dSmrg		return;
1776fda9279dSmrg	mon = udev_monitor_new_from_netlink(u, "udev");
1777fda9279dSmrg	if (!mon) {
1778fda9279dSmrg		udev_unref(u);
1779fda9279dSmrg		return;
1780fda9279dSmrg	}
1781fda9279dSmrg
1782fda9279dSmrg	if (udev_monitor_filter_add_match_subsystem_devtype(mon,
1783fda9279dSmrg							    "drm",
1784fda9279dSmrg							    "drm_minor") < 0 ||
1785fda9279dSmrg	    udev_monitor_enable_receiving(mon) < 0) {
1786fda9279dSmrg		udev_monitor_unref(mon);
1787fda9279dSmrg		udev_unref(u);
1788fda9279dSmrg		return;
1789fda9279dSmrg	}
1790fda9279dSmrg
1791374ff59dSmrg#if HAVE_NOTIFY_FD
1792374ff59dSmrg	SetNotifyFd(udev_monitor_get_fd(mon), drmmode_udev_notify, X_NOTIFY_READ, scrn);
1793374ff59dSmrg#else
1794fda9279dSmrg	AddGeneralSocket(udev_monitor_get_fd(mon));
1795374ff59dSmrg#endif
1796fda9279dSmrg	drmmode->uevent_monitor = mon;
1797fda9279dSmrg#endif
1798fda9279dSmrg}
1799fda9279dSmrg
1800fda9279dSmrgstatic void
1801fda9279dSmrgdrmmode_uevent_fini(ScrnInfoPtr scrn)
1802fda9279dSmrg{
1803fda9279dSmrg#ifdef HAVE_LIBUDEV
1804fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
1805fda9279dSmrg
1806fda9279dSmrg	if (drmmode->uevent_monitor) {
1807fda9279dSmrg		struct udev *u = udev_monitor_get_udev(drmmode->uevent_monitor);
1808fda9279dSmrg
1809374ff59dSmrg#if HAVE_NOTIFY_FD
1810374ff59dSmrg		RemoveNotifyFd(udev_monitor_get_fd(drmmode->uevent_monitor));
1811374ff59dSmrg#else
1812fda9279dSmrg		RemoveGeneralSocket(udev_monitor_get_fd(drmmode->uevent_monitor));
1813374ff59dSmrg#endif
1814fda9279dSmrg		udev_monitor_unref(drmmode->uevent_monitor);
1815fda9279dSmrg		udev_unref(u);
1816fda9279dSmrg	}
1817fda9279dSmrg#endif
1818fda9279dSmrg}
1819fda9279dSmrg
1820374ff59dSmrg#if HAVE_NOTIFY_FD
1821374ff59dSmrgstatic void
1822374ff59dSmrgdrmmode_notify_fd(int fd, int notify, void *data)
1823374ff59dSmrg{
1824374ff59dSmrg	ScrnInfoPtr scrn = data;
1825374ff59dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
1826374ff59dSmrg	drmHandleEvent(drmmode->fd, &drmmode->event_context);
1827374ff59dSmrg}
1828374ff59dSmrg#else
1829374ff59dSmrg
1830fda9279dSmrgstatic void
1831fda9279dSmrgdrmmode_wakeup_handler(pointer data, int err, pointer p)
1832fda9279dSmrg{
1833fda9279dSmrg	ScrnInfoPtr scrn = data;
1834fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
1835fda9279dSmrg	fd_set *read_mask = p;
1836fda9279dSmrg
1837fda9279dSmrg	if (scrn == NULL || err < 0)
1838fda9279dSmrg		return;
1839fda9279dSmrg
1840fda9279dSmrg	if (FD_ISSET(drmmode->fd, read_mask))
1841fda9279dSmrg		drmHandleEvent(drmmode->fd, &drmmode->event_context);
1842fda9279dSmrg
1843fda9279dSmrg#ifdef HAVE_LIBUDEV
1844fda9279dSmrg	if (FD_ISSET(udev_monitor_get_fd(drmmode->uevent_monitor), read_mask))
1845fda9279dSmrg		drmmode_handle_uevents(scrn);
1846fda9279dSmrg#endif
1847fda9279dSmrg}
1848374ff59dSmrg#endif
1849fda9279dSmrg
1850fda9279dSmrgvoid
1851fda9279dSmrgdrmmode_screen_init(ScreenPtr pScreen)
1852fda9279dSmrg{
1853fda9279dSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
1854fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
1855fda9279dSmrg	NVEntPtr pNVEnt = NVEntPriv(scrn);
1856fda9279dSmrg
1857fda9279dSmrg	/* Setup handler for DRM events */
1858fda9279dSmrg	drmmode_event_init(scrn);
1859fda9279dSmrg
1860fda9279dSmrg	/* Setup handler for udevevents */
1861fda9279dSmrg	drmmode_uevent_init(scrn);
1862fda9279dSmrg
1863fda9279dSmrg	/* Register wakeup handler only once per servergen, so ZaphodHeads work */
1864fda9279dSmrg	if (pNVEnt->fd_wakeup_registered != serverGeneration) {
1865fda9279dSmrg		/* Register a wakeup handler to get informed on DRM events */
1866374ff59dSmrg#if HAVE_NOTIFY_FD
1867374ff59dSmrg		SetNotifyFd(drmmode->fd, drmmode_notify_fd, X_NOTIFY_READ, scrn);
1868374ff59dSmrg#else
1869fda9279dSmrg		AddGeneralSocket(drmmode->fd);
1870fda9279dSmrg		RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
1871fda9279dSmrg		                               drmmode_wakeup_handler, scrn);
1872374ff59dSmrg#endif
1873fda9279dSmrg		pNVEnt->fd_wakeup_registered = serverGeneration;
1874fda9279dSmrg		pNVEnt->fd_wakeup_ref = 1;
1875fda9279dSmrg	}
1876fda9279dSmrg	else
1877fda9279dSmrg		pNVEnt->fd_wakeup_ref++;
1878fda9279dSmrg}
1879fda9279dSmrg
1880fda9279dSmrgvoid
1881fda9279dSmrgdrmmode_screen_fini(ScreenPtr pScreen)
1882fda9279dSmrg{
1883fda9279dSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
1884fda9279dSmrg	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
1885fda9279dSmrg	NVEntPtr pNVEnt = NVEntPriv(scrn);
1886fda9279dSmrg
1887fda9279dSmrg	/* Unregister wakeup handler after last x-screen for this servergen dies. */
1888fda9279dSmrg	if (pNVEnt->fd_wakeup_registered == serverGeneration &&
1889fda9279dSmrg		!--pNVEnt->fd_wakeup_ref) {
1890fda9279dSmrg
1891374ff59dSmrg#if HAVE_NOTIFY_FD
1892374ff59dSmrg		RemoveNotifyFd(drmmode->fd);
1893374ff59dSmrg#else
1894fda9279dSmrg		/* Unregister wakeup handler */
1895fda9279dSmrg		RemoveBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
1896fda9279dSmrg		                             drmmode_wakeup_handler, scrn);
1897fda9279dSmrg		RemoveGeneralSocket(drmmode->fd);
1898374ff59dSmrg#endif
1899fda9279dSmrg	}
1900fda9279dSmrg
1901fda9279dSmrg	/* Tear down udev event handler */
1902fda9279dSmrg	drmmode_uevent_fini(scrn);
1903fda9279dSmrg
1904fda9279dSmrg	/* Tear down DRM event handler */
1905fda9279dSmrg	drmmode_event_fini(scrn);
1906fda9279dSmrg}
1907