1/*
2 * Copyright © 2007 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 * Authors:
24 *    Dave Airlie <airlied@redhat.com>
25 *
26 */
27
28#ifdef HAVE_CONFIG_H
29#include "config.h"
30#endif
31
32#include <errno.h>
33#ifdef XF86DRM_MODE
34
35#include <sys/ioctl.h>
36#include "qxl_drmmode.h"
37#include "X11/Xatom.h"
38#include "xf86DDC.h"
39#include <X11/extensions/dpmsconst.h>
40#include <cursorstr.h>
41
42#include "qxl.h"
43#include "qxl_surface.h"
44
45static void drmmode_show_cursor (xf86CrtcPtr crtc);
46
47static void
48drmmode_ConvertFromKMode(ScrnInfoPtr	scrn,
49		     drmModeModeInfo *kmode,
50		     DisplayModePtr	mode)
51{
52	memset(mode, 0, sizeof(DisplayModeRec));
53	mode->status = MODE_OK;
54
55	mode->Clock = kmode->clock;
56
57	mode->HDisplay = kmode->hdisplay;
58	mode->HSyncStart = kmode->hsync_start;
59	mode->HSyncEnd = kmode->hsync_end;
60	mode->HTotal = kmode->htotal;
61	mode->HSkew = kmode->hskew;
62
63	mode->VDisplay = kmode->vdisplay;
64	mode->VSyncStart = kmode->vsync_start;
65	mode->VSyncEnd = kmode->vsync_end;
66	mode->VTotal = kmode->vtotal;
67	mode->VScan = kmode->vscan;
68
69	mode->Flags = kmode->flags; //& FLAG_BITS;
70	mode->name = strdup(kmode->name);
71
72	if (kmode->type & DRM_MODE_TYPE_DRIVER)
73		mode->type = M_T_DRIVER;
74	if (kmode->type & DRM_MODE_TYPE_PREFERRED)
75		mode->type |= M_T_PREFERRED;
76	xf86SetModeCrtc (mode, scrn->adjustFlags);
77}
78
79static void
80drmmode_ConvertToKMode(ScrnInfoPtr	scrn,
81		     drmModeModeInfo *kmode,
82		     DisplayModePtr	mode)
83{
84	memset(kmode, 0, sizeof(*kmode));
85
86	kmode->clock = mode->Clock;
87	kmode->hdisplay = mode->HDisplay;
88	kmode->hsync_start = mode->HSyncStart;
89	kmode->hsync_end = mode->HSyncEnd;
90	kmode->htotal = mode->HTotal;
91	kmode->hskew = mode->HSkew;
92
93	kmode->vdisplay = mode->VDisplay;
94	kmode->vsync_start = mode->VSyncStart;
95	kmode->vsync_end = mode->VSyncEnd;
96	kmode->vtotal = mode->VTotal;
97	kmode->vscan = mode->VScan;
98
99	kmode->flags = mode->Flags; //& FLAG_BITS;
100	if (mode->name)
101		strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN);
102	kmode->name[DRM_DISPLAY_MODE_LEN-1] = 0;
103
104}
105
106
107static void
108drmmode_crtc_dpms(xf86CrtcPtr crtc, int mode)
109{
110	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
111//	drmmode_ptr drmmode = drmmode_crtc->drmmode;
112
113	drmmode_crtc->dpms_mode = mode;
114
115#if 0
116	/* bonghits in the randr 1.2 - uses dpms to disable crtc - bad buzz */
117	if (mode == DPMSModeOff) {
118//		drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
119//			       0, 0, 0, NULL, 0, NULL);
120	}
121#endif
122}
123
124static Bool
125drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
126		     Rotation rotation, int x, int y)
127{
128	ScrnInfoPtr pScrn = crtc->scrn;
129	CursorPtr cursor;
130	xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
131	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
132	drmmode_ptr drmmode = drmmode_crtc->drmmode;
133	int saved_x, saved_y;
134	Rotation saved_rotation;
135	DisplayModeRec saved_mode;
136	uint32_t *output_ids;
137	int output_count = 0;
138	Bool ret = TRUE;
139	int i;
140	int fb_id;
141	drmModeModeInfo kmode;
142	int pitch;
143	int height;
144	qxl_screen_t *qxl = crtc->scrn->driverPrivate;
145
146	pitch = pScrn->displayWidth * ((pScrn->bitsPerPixel + 7) >> 3);
147	height = pScrn->virtualY;
148	if (drmmode->fb_id == 0) {
149		ret = drmModeAddFB(drmmode->fd,
150				   pScrn->virtualX, height,
151                                   pScrn->depth, pScrn->bitsPerPixel,
152				   pitch,
153				   qxl_kms_bo_get_handle(qxl->primary->bo),
154                                   &drmmode->fb_id);
155                if (ret < 0) {
156                        ErrorF("failed to add fb\n");
157                        return FALSE;
158                }
159        }
160
161	saved_mode = crtc->mode;
162	saved_x = crtc->x;
163	saved_y = crtc->y;
164	saved_rotation = crtc->rotation;
165
166	if (mode) {
167		crtc->mode = *mode;
168		crtc->x = x;
169		crtc->y = y;
170		crtc->rotation = rotation;
171#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,5,99,0,0)
172		crtc->transformPresent = FALSE;
173#endif
174	}
175
176	output_ids = calloc(sizeof(uint32_t), xf86_config->num_output);
177	if (!output_ids) {
178		ret = FALSE;
179		goto done;
180	}
181
182	if (mode) {
183		for (i = 0; i < xf86_config->num_output; i++) {
184			xf86OutputPtr output = xf86_config->output[i];
185			drmmode_output_private_ptr drmmode_output;
186
187			if (output->crtc != crtc)
188				continue;
189
190			drmmode_output = output->driver_private;
191			output_ids[output_count] = drmmode_output->mode_output->connector_id;
192			output_count++;
193		}
194
195#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,7,0,0,0)
196		crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
197				       crtc->gamma_blue, crtc->gamma_size);
198#endif
199
200		drmmode_ConvertToKMode(crtc->scrn, &kmode, mode);
201
202		fb_id = drmmode->fb_id;
203		if (drmmode_crtc->rotate_fb_id) {
204			fb_id = drmmode_crtc->rotate_fb_id;
205			x = y = 0;
206		}
207		ret = drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
208				     fb_id, x, y, output_ids, output_count, &kmode);
209		if (ret) {
210			xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
211				   "failed to set mode: %s", strerror(-ret));
212			return FALSE;
213		} else {
214			ret = TRUE;
215		}
216
217		if (crtc->scrn->pScreen)
218			xf86CrtcSetScreenSubpixelOrder(crtc->scrn->pScreen);
219		/* go through all the outputs and force DPMS them back on? */
220		for (i = 0; i < xf86_config->num_output; i++) {
221			xf86OutputPtr output = xf86_config->output[i];
222
223			if (output->crtc != crtc)
224				continue;
225
226			output->funcs->dpms(output, DPMSModeOn);
227		}
228	}
229
230#if 0
231	if (pScrn->pScreen &&
232		!xf86ReturnOptValBool(info->Options, OPTION_SW_CURSOR, FALSE))
233		xf86_reload_cursors(pScrn->pScreen);
234#endif
235
236done:
237	if (!ret) {
238		crtc->x = saved_x;
239		crtc->y = saved_y;
240		crtc->rotation = saved_rotation;
241		crtc->mode = saved_mode;
242	}
243#if defined(XF86_CRTC_VERSION) && XF86_CRTC_VERSION >= 3
244	else
245		crtc->active = TRUE;
246#endif
247
248        cursor = xf86_config->cursor;
249        if (cursor)
250            drmmode_show_cursor(crtc);
251
252	return ret;
253}
254
255static void
256drmmode_set_cursor_colors (xf86CrtcPtr crtc, int bg, int fg)
257{
258
259}
260
261static void
262drmmode_set_cursor_position (xf86CrtcPtr crtc, int x, int y)
263{
264	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
265	drmmode_ptr drmmode = drmmode_crtc->drmmode;
266
267	drmModeMoveCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, x, y);
268}
269
270static void
271drmmode_show_cursor (xf86CrtcPtr crtc)
272{
273	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
274	drmmode_ptr drmmode = drmmode_crtc->drmmode;
275	uint32_t handle = qxl_kms_bo_get_handle(drmmode_crtc->cursor_bo);
276	static Bool use_set_cursor2 = TRUE;
277
278	if (use_set_cursor2) {
279		xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
280		CursorPtr cursor = xf86_config->cursor;
281		int ret;
282		ret = drmModeSetCursor2(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, handle, 64, 64, cursor->bits->xhot, cursor->bits->yhot);
283		if (ret == -EINVAL)
284			use_set_cursor2 = FALSE;
285		else
286			return;
287	}
288	drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, handle, 64, 64);
289}
290
291static void
292drmmode_load_cursor_argb (xf86CrtcPtr crtc, CARD32 *image)
293{
294	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
295	int i;
296	uint32_t *ptr;
297
298	/* cursor should be mapped already */
299	ptr = (uint32_t *)(drmmode_crtc->cursor_ptr);
300
301	for (i = 0; i < 64 * 64; i++)
302		ptr[i] = image[i];
303
304	drmmode_show_cursor(crtc);
305}
306
307static void
308drmmode_hide_cursor (xf86CrtcPtr crtc)
309{
310	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
311	drmmode_ptr drmmode = drmmode_crtc->drmmode;
312
313	drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, 0, 64, 64);
314}
315
316static void
317drmmode_crtc_gamma_set(xf86CrtcPtr crtc, uint16_t *red, uint16_t *green,
318                      uint16_t *blue, int size)
319{
320	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
321	drmmode_ptr drmmode = drmmode_crtc->drmmode;
322
323	drmModeCrtcSetGamma(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
324			    size, red, green, blue);
325}
326static const xf86CrtcFuncsRec drmmode_crtc_funcs = {
327    .dpms = drmmode_crtc_dpms,
328    .set_mode_major = drmmode_set_mode_major,
329    .set_cursor_colors = drmmode_set_cursor_colors,
330    .set_cursor_position = drmmode_set_cursor_position,
331    .show_cursor = drmmode_show_cursor,
332    .hide_cursor = drmmode_hide_cursor,
333    .load_cursor_argb = drmmode_load_cursor_argb,
334
335    .gamma_set = drmmode_crtc_gamma_set,
336    .destroy = NULL, /* XXX */
337};
338
339static void
340drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num)
341{
342	qxl_screen_t *qxl = pScrn->driverPrivate;
343	xf86CrtcPtr crtc;
344	drmmode_crtc_private_ptr drmmode_crtc;
345
346	crtc = xf86CrtcCreate(pScrn, &drmmode_crtc_funcs);
347	if (crtc == NULL)
348		return;
349	crtc->driverIsPerformingTransform = FALSE;
350
351	drmmode_crtc = xnfcalloc(sizeof(drmmode_crtc_private_rec), 1);
352	drmmode_crtc->mode_crtc = drmModeGetCrtc(drmmode->fd, drmmode->mode_res->crtcs[num]);
353	drmmode_crtc->drmmode = drmmode;
354	crtc->driver_private = drmmode_crtc;
355
356	{
357		int cursor_size = 64*4*64;
358
359		drmmode_crtc->cursor_bo = qxl->bo_funcs->bo_alloc(qxl, cursor_size, "cursor");
360		if (!drmmode_crtc->cursor_bo) {
361			ErrorF("failed to allocate cursor buffer\n");
362			return;
363		}
364
365		drmmode_crtc->cursor_ptr = qxl->bo_funcs->bo_map(drmmode_crtc->cursor_bo);
366	}
367
368	return;
369}
370
371
372static xf86OutputStatus
373drmmode_output_detect(xf86OutputPtr output)
374{
375	/* go to the hw and retrieve a new output struct */
376	drmmode_output_private_ptr drmmode_output = output->driver_private;
377	drmmode_ptr drmmode = drmmode_output->drmmode;
378	xf86OutputStatus status;
379	drmModeFreeConnector(drmmode_output->mode_output);
380
381	drmmode_output->mode_output = drmModeGetConnector(drmmode->fd, drmmode_output->output_id);
382
383	switch (drmmode_output->mode_output->connection) {
384	case DRM_MODE_CONNECTED:
385		status = XF86OutputStatusConnected;
386		break;
387	case DRM_MODE_DISCONNECTED:
388		status = XF86OutputStatusDisconnected;
389		break;
390	default:
391	case DRM_MODE_UNKNOWNCONNECTION:
392		status = XF86OutputStatusUnknown;
393		break;
394	}
395	return status;
396}
397
398static Bool
399drmmode_output_mode_valid(xf86OutputPtr output, DisplayModePtr pModes)
400{
401	return MODE_OK;
402}
403
404static DisplayModePtr
405drmmode_output_get_modes(xf86OutputPtr output)
406{
407	drmmode_output_private_ptr drmmode_output = output->driver_private;
408	drmModeConnectorPtr koutput = drmmode_output->mode_output;
409	drmmode_ptr drmmode = drmmode_output->drmmode;
410	int i;
411	DisplayModePtr Modes = NULL, Mode;
412	drmModePropertyPtr props;
413	xf86MonPtr mon = NULL;
414
415	/* look for an EDID property */
416	for (i = 0; i < koutput->count_props; i++) {
417		props = drmModeGetProperty(drmmode->fd, koutput->props[i]);
418		if (props && (props->flags & DRM_MODE_PROP_BLOB)) {
419			if (!strcmp(props->name, "EDID")) {
420				if (drmmode_output->edid_blob)
421					drmModeFreePropertyBlob(drmmode_output->edid_blob);
422				drmmode_output->edid_blob = drmModeGetPropertyBlob(drmmode->fd, koutput->prop_values[i]);
423			}
424			drmModeFreeProperty(props);
425		}
426	}
427
428	if (drmmode_output->edid_blob) {
429		mon = xf86InterpretEDID(output->scrn->scrnIndex,
430					drmmode_output->edid_blob->data);
431		if (mon && drmmode_output->edid_blob->length > 128)
432			mon->flags |= MONITOR_EDID_COMPLETE_RAWDATA;
433	}
434	xf86OutputSetEDID(output, mon);
435
436	/* modes should already be available */
437	for (i = 0; i < koutput->count_modes; i++) {
438		Mode = xnfalloc(sizeof(DisplayModeRec));
439
440		drmmode_ConvertFromKMode(output->scrn, &koutput->modes[i], Mode);
441		Modes = xf86ModesAdd(Modes, Mode);
442
443	}
444	return Modes;
445}
446
447static void
448drmmode_output_destroy(xf86OutputPtr output)
449{
450	drmmode_output_private_ptr drmmode_output = output->driver_private;
451	int i;
452
453	if (drmmode_output->edid_blob)
454		drmModeFreePropertyBlob(drmmode_output->edid_blob);
455	for (i = 0; i < drmmode_output->num_props; i++) {
456		drmModeFreeProperty(drmmode_output->props[i].mode_prop);
457		free(drmmode_output->props[i].atoms);
458	}
459	for (i = 0; i < drmmode_output->mode_output->count_encoders; i++) {
460		drmModeFreeEncoder(drmmode_output->mode_encoders[i]);
461		free(drmmode_output->mode_encoders);
462	}
463	free(drmmode_output->props);
464	drmModeFreeConnector(drmmode_output->mode_output);
465	free(drmmode_output);
466	output->driver_private = NULL;
467}
468
469static void
470drmmode_output_dpms(xf86OutputPtr output, int mode)
471{
472	drmmode_output_private_ptr drmmode_output = output->driver_private;
473	drmModeConnectorPtr koutput = drmmode_output->mode_output;
474	drmmode_ptr drmmode = drmmode_output->drmmode;
475
476	drmModeConnectorSetProperty(drmmode->fd, koutput->connector_id,
477				    drmmode_output->dpms_enum_id, mode);
478	return;
479}
480
481
482static Bool
483drmmode_property_ignore(drmModePropertyPtr prop)
484{
485    if (!prop)
486	return TRUE;
487    /* ignore blob prop */
488    if (prop->flags & DRM_MODE_PROP_BLOB)
489	return TRUE;
490    /* ignore standard property */
491    if (!strcmp(prop->name, "EDID") ||
492	    !strcmp(prop->name, "DPMS"))
493	return TRUE;
494
495    return FALSE;
496}
497
498static void
499drmmode_output_create_resources(xf86OutputPtr output)
500{
501    drmmode_output_private_ptr drmmode_output = output->driver_private;
502    drmModeConnectorPtr mode_output = drmmode_output->mode_output;
503    drmmode_ptr drmmode = drmmode_output->drmmode;
504    drmModePropertyPtr drmmode_prop;
505    int i, j, err;
506
507    drmmode_output->props = calloc(mode_output->count_props, sizeof(drmmode_prop_rec));
508    if (!drmmode_output->props)
509	return;
510
511    drmmode_output->num_props = 0;
512    for (i = 0, j = 0; i < mode_output->count_props; i++) {
513	drmmode_prop = drmModeGetProperty(drmmode->fd, mode_output->props[i]);
514	if (drmmode_property_ignore(drmmode_prop)) {
515	    drmModeFreeProperty(drmmode_prop);
516	    continue;
517	}
518	drmmode_output->props[j].index = i;
519	drmmode_output->props[j].mode_prop = drmmode_prop;
520	drmmode_output->props[j].value = mode_output->prop_values[i];
521	drmmode_output->num_props++;
522	j++;
523    }
524
525    for (i = 0; i < drmmode_output->num_props; i++) {
526	drmmode_prop_ptr p = &drmmode_output->props[i];
527	drmmode_prop = p->mode_prop;
528
529	if (drmmode_prop->flags & DRM_MODE_PROP_RANGE) {
530	    INT32 qrange[2];
531	    INT32 value = p->value;
532
533	    p->num_atoms = 1;
534	    p->atoms = calloc(p->num_atoms, sizeof(Atom));
535	    if (!p->atoms)
536		continue;
537	    p->atoms[0] = MakeAtom(drmmode_prop->name, strlen(drmmode_prop->name), TRUE);
538	    qrange[0] = drmmode_prop->values[0];
539	    qrange[1] = drmmode_prop->values[1];
540	    err = RRConfigureOutputProperty(output->randr_output, p->atoms[0],
541		    FALSE, TRUE,
542		    drmmode_prop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE,
543		    2, qrange);
544	    if (err != 0) {
545		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
546			"RRConfigureOutputProperty error, %d\n", err);
547	    }
548	    err = RRChangeOutputProperty(output->randr_output, p->atoms[0],
549		    XA_INTEGER, 32, PropModeReplace, 1, &value, FALSE, TRUE);
550	    if (err != 0) {
551		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
552			"RRChangeOutputProperty error, %d\n", err);
553	    }
554	} else if (drmmode_prop->flags & DRM_MODE_PROP_ENUM) {
555	    p->num_atoms = drmmode_prop->count_enums + 1;
556	    p->atoms = calloc(p->num_atoms, sizeof(Atom));
557	    if (!p->atoms)
558		continue;
559	    p->atoms[0] = MakeAtom(drmmode_prop->name, strlen(drmmode_prop->name), TRUE);
560	    for (j = 1; j <= drmmode_prop->count_enums; j++) {
561		struct drm_mode_property_enum *e = &drmmode_prop->enums[j-1];
562		p->atoms[j] = MakeAtom(e->name, strlen(e->name), TRUE);
563	    }
564	    err = RRConfigureOutputProperty(output->randr_output, p->atoms[0],
565		    FALSE, FALSE,
566		    drmmode_prop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE,
567		    p->num_atoms - 1, (INT32 *)&p->atoms[1]);
568	    if (err != 0) {
569		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
570			"RRConfigureOutputProperty error, %d\n", err);
571	    }
572	    for (j = 0; j < drmmode_prop->count_enums; j++)
573		if (drmmode_prop->enums[j].value == p->value)
574		    break;
575	    /* there's always a matching value */
576	    err = RRChangeOutputProperty(output->randr_output, p->atoms[0],
577		    XA_ATOM, 32, PropModeReplace, 1, &p->atoms[j+1], FALSE, TRUE);
578	    if (err != 0) {
579		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
580			"RRChangeOutputProperty error, %d\n", err);
581	    }
582	}
583    }
584}
585
586static Bool
587drmmode_output_set_property(xf86OutputPtr output, Atom property,
588		RRPropertyValuePtr value)
589{
590    drmmode_output_private_ptr drmmode_output = output->driver_private;
591    drmmode_ptr drmmode = drmmode_output->drmmode;
592    int i;
593
594    for (i = 0; i < drmmode_output->num_props; i++) {
595	drmmode_prop_ptr p = &drmmode_output->props[i];
596
597	if (p->atoms[0] != property)
598	    continue;
599
600	if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
601	    uint32_t val;
602
603	    if (value->type != XA_INTEGER || value->format != 32 ||
604		    value->size != 1)
605		return FALSE;
606	    val = *(uint32_t *)value->data;
607
608	    drmModeConnectorSetProperty(drmmode->fd, drmmode_output->output_id,
609		    p->mode_prop->prop_id, (uint64_t)val);
610	    return TRUE;
611	} else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
612	    Atom	atom;
613	    const char	*name;
614	    int		j;
615
616	    if (value->type != XA_ATOM || value->format != 32 || value->size != 1)
617		return FALSE;
618	    memcpy(&atom, value->data, 4);
619	    if (!(name = NameForAtom(atom)))
620                return FALSE;
621
622	    /* search for matching name string, then set its value down */
623	    for (j = 0; j < p->mode_prop->count_enums; j++) {
624		if (!strcmp(p->mode_prop->enums[j].name, name)) {
625		    drmModeConnectorSetProperty(drmmode->fd, drmmode_output->output_id,
626			    p->mode_prop->prop_id, p->mode_prop->enums[j].value);
627		    return TRUE;
628		}
629	    }
630	}
631    }
632
633    return TRUE;
634}
635
636static Bool
637drmmode_output_get_property(xf86OutputPtr output, Atom property)
638{
639    drmmode_output_private_ptr drmmode_output = output->driver_private;
640    drmmode_ptr drmmode = drmmode_output->drmmode;
641    uint32_t value;
642    int err, i;
643
644    if (output->scrn->vtSema) {
645	drmModeFreeConnector(drmmode_output->mode_output);
646	drmmode_output->mode_output =
647	    drmModeGetConnector(drmmode->fd, drmmode_output->output_id);
648    }
649
650    if (!drmmode_output->mode_output)
651	return FALSE;
652
653    for (i = 0; i < drmmode_output->num_props; i++) {
654	drmmode_prop_ptr p = &drmmode_output->props[i];
655	if (p->atoms[0] != property)
656	    continue;
657
658	value = drmmode_output->mode_output->prop_values[p->index];
659
660	if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
661	    err = RRChangeOutputProperty(output->randr_output,
662					 property, XA_INTEGER, 32,
663					 PropModeReplace, 1, &value,
664					 FALSE, FALSE);
665
666	    return !err;
667	} else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
668	    int j;
669
670	    /* search for matching name string, then set its value down */
671	    for (j = 0; j < p->mode_prop->count_enums; j++) {
672		if (p->mode_prop->enums[j].value == value)
673		    break;
674	    }
675
676	    err = RRChangeOutputProperty(output->randr_output, property,
677					 XA_ATOM, 32, PropModeReplace, 1,
678					 &p->atoms[j+1], FALSE, FALSE);
679
680	    return !err;
681	}
682    }
683
684    return FALSE;
685}
686
687static const xf86OutputFuncsRec drmmode_output_funcs = {
688    .dpms = drmmode_output_dpms,
689    .create_resources = drmmode_output_create_resources,
690#ifdef RANDR_12_INTERFACE
691    .set_property = drmmode_output_set_property,
692    .get_property = drmmode_output_get_property,
693#endif
694#if 0
695
696    .save = drmmode_crt_save,
697    .restore = drmmode_crt_restore,
698    .mode_fixup = drmmode_crt_mode_fixup,
699    .prepare = drmmode_output_prepare,
700    .mode_set = drmmode_crt_mode_set,
701    .commit = drmmode_output_commit,
702#endif
703    .detect = drmmode_output_detect,
704    .mode_valid = drmmode_output_mode_valid,
705
706    .get_modes = drmmode_output_get_modes,
707    .destroy = drmmode_output_destroy
708};
709
710static int subpixel_conv_table[7] = { 0, SubPixelUnknown,
711				      SubPixelHorizontalRGB,
712				      SubPixelHorizontalBGR,
713				      SubPixelVerticalRGB,
714				      SubPixelVerticalBGR,
715				      SubPixelNone };
716
717const char *output_names[] = {
718    "None",
719    "VGA",
720    "DVI-I",
721    "DVI-D",
722    "DVI-A",
723    "Composite",
724    "SVIDEO",
725    "LVDS",
726    "Component",
727    "DIN",
728    "DP",
729    "HDMI",
730    "HDMI-B",
731    "TV",
732    "eDP",
733    "Virtual",
734    "DSI",
735    "DPI",
736};
737
738static void
739drmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num)
740{
741	xf86OutputPtr output;
742	drmModeConnectorPtr koutput;
743	drmModeEncoderPtr *kencoders = NULL;
744	drmmode_output_private_ptr drmmode_output;
745	drmModePropertyPtr props;
746	char name[32];
747	int i;
748
749	koutput = drmModeGetConnector(drmmode->fd, drmmode->mode_res->connectors[num]);
750	if (!koutput)
751		return;
752
753	kencoders = calloc(sizeof(drmModeEncoderPtr), koutput->count_encoders);
754	if (!kencoders) {
755		goto out_free_encoders;
756	}
757
758	for (i = 0; i < koutput->count_encoders; i++) {
759		kencoders[i] = drmModeGetEncoder(drmmode->fd, koutput->encoders[i]);
760		if (!kencoders[i]) {
761			goto out_free_encoders;
762		}
763	}
764
765	snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], koutput->connector_type_id);
766
767
768	output = xf86OutputCreate (pScrn, &drmmode_output_funcs, name);
769	if (!output) {
770		goto out_free_encoders;
771	}
772
773	drmmode_output = calloc(sizeof(drmmode_output_private_rec), 1);
774	if (!drmmode_output) {
775		xf86OutputDestroy(output);
776		goto out_free_encoders;
777	}
778
779	drmmode_output->output_id = drmmode->mode_res->connectors[num];
780	drmmode_output->mode_output = koutput;
781	drmmode_output->mode_encoders = kencoders;
782	drmmode_output->drmmode = drmmode;
783	output->mm_width = koutput->mmWidth;
784	output->mm_height = koutput->mmHeight;
785
786	output->subpixel_order = subpixel_conv_table[koutput->subpixel];
787	output->interlaceAllowed = TRUE;
788	output->doubleScanAllowed = TRUE;
789	output->driver_private = drmmode_output;
790
791	output->possible_crtcs = 0xffffffff;
792	for (i = 0; i < koutput->count_encoders; i++) {
793		output->possible_crtcs &= kencoders[i]->possible_crtcs;
794	}
795	/* work out the possible clones later */
796	output->possible_clones = 0;
797
798	for (i = 0; i < koutput->count_props; i++) {
799		props = drmModeGetProperty(drmmode->fd, koutput->props[i]);
800		if (props && (props->flags & DRM_MODE_PROP_ENUM)) {
801			if (!strcmp(props->name, "DPMS")) {
802				drmmode_output->dpms_enum_id = koutput->props[i];
803				drmModeFreeProperty(props);
804				break;
805			}
806			drmModeFreeProperty(props);
807		}
808	}
809
810	return;
811out_free_encoders:
812	if (kencoders){
813		for (i = 0; i < koutput->count_encoders; i++)
814			drmModeFreeEncoder(kencoders[i]);
815		free(kencoders);
816	}
817	drmModeFreeConnector(koutput);
818
819}
820
821static Bool
822drmmode_xf86crtc_resize (ScrnInfoPtr scrn, int width, int height)
823{
824	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
825	drmmode_crtc_private_ptr
826		    drmmode_crtc = xf86_config->crtc[0]->driver_private;
827	drmmode_ptr drmmode = drmmode_crtc->drmmode;
828	struct qxl_bo *old_front = NULL;
829	struct qxl_bo *front_bo;
830	qxl_screen_t *qxl = scrn->driverPrivate;
831	int cpp = (scrn->bitsPerPixel + 7) / 8;
832	int32_t pitch, old_pitch;
833	int ret, i;
834	uint32_t old_width, old_height, old_fb_id;
835	if (scrn->virtualX == width && scrn->virtualY == height)
836                return TRUE;
837
838	xf86DrvMsg(scrn->scrnIndex, X_INFO,
839                   "Allocate new frame buffer %dx%d stride\n",
840                   width, height);
841
842	front_bo = qxl->primary->bo;
843
844	pitch = width * cpp;
845	old_width = scrn->virtualX;
846	old_height = scrn->virtualY;
847	old_pitch = scrn->displayWidth;
848	old_fb_id = drmmode->fb_id;
849	old_front = front_bo;
850
851	scrn->virtualX = width;
852	scrn->virtualY = height;
853	scrn->displayWidth = pitch / cpp;
854
855	qxl->primary->bo = qxl->bo_funcs->create_primary(qxl, width, height, pitch, SPICE_SURFACE_FMT_32_xRGB);
856	if (!qxl->primary->bo)
857		goto fail;
858
859	ret = drmModeAddFB(drmmode->fd,
860			   width, height,
861			   scrn->depth, scrn->bitsPerPixel,
862			   pitch,
863			   qxl_kms_bo_get_handle(qxl->primary->bo),
864			   &drmmode->fb_id);
865	if (ret)
866		goto fail;
867
868	for (i = 0; i < xf86_config->num_crtc; i++) {
869		xf86CrtcPtr crtc = xf86_config->crtc[i];
870		if (!crtc->enabled)
871			continue;
872		if (!drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation,
873                                            crtc->x, crtc->y))
874                    goto fail;
875	}
876
877	{
878		void *dev_ptr = qxl->bo_funcs->bo_map(qxl->primary->bo);
879		uint32_t *dev_addr;
880		int format = scrn->bitsPerPixel == 16 ? PIXMAN_x1r5g5b5 : PIXMAN_x8r8g8b8;
881		dev_addr = dev_ptr;
882		pixman_image_unref(qxl->primary->dev_image);
883		pixman_image_unref (qxl->primary->host_image);
884
885		qxl->primary->dev_image = pixman_image_create_bits (format,
886								    width,
887								    height,
888								    (uint32_t *)dev_addr, pitch);
889
890		qxl->primary->host_image = pixman_image_create_bits (format,
891								    width,
892								    height,
893								    NULL, pitch);
894	}
895
896	/* fixup the surfaces */
897
898	if (old_fb_id)
899		drmModeRmFB(drmmode->fd, old_fb_id);
900	if (old_front)
901		qxl->bo_funcs->bo_decref(qxl, old_front);
902
903	return TRUE;
904fail:
905	qxl->primary->bo = old_front;
906	scrn->virtualX = old_width;
907	scrn->virtualY = old_height;
908	scrn->displayWidth = old_pitch;
909	drmmode->fb_id = old_fb_id;
910	return FALSE;
911}
912
913
914static const xf86CrtcConfigFuncsRec drmmode_xf86crtc_config_funcs = {
915	drmmode_xf86crtc_resize,
916};
917
918Bool drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int cpp)
919{
920	int i;
921
922	xf86CrtcConfigInit(pScrn, &drmmode_xf86crtc_config_funcs);
923
924	drmmode->scrn = pScrn;
925	drmmode->cpp = cpp;
926	drmmode->mode_res = drmModeGetResources(drmmode->fd);
927	if (!drmmode->mode_res)
928		return FALSE;
929
930	xf86CrtcSetSizeRange(pScrn, 320, 200, drmmode->mode_res->max_width, drmmode->mode_res->max_height);
931	for (i = 0; i < drmmode->mode_res->count_crtcs; i++)
932	        drmmode_crtc_init(pScrn, drmmode, i);
933
934	for (i = 0; i < drmmode->mode_res->count_connectors; i++)
935	    drmmode_output_init(pScrn, drmmode, i);
936
937#if XF86_CRTC_VERSION >= 5
938	xf86ProviderSetup(pScrn, NULL, "qxl");
939#endif
940	xf86InitialConfiguration(pScrn, TRUE);
941
942	return TRUE;
943}
944
945#ifdef HAVE_LIBUDEV
946static void
947drmmode_handle_uevents(int fd, void *closure)
948{
949	drmmode_ptr drmmode = closure;
950	ScrnInfoPtr scrn = drmmode->scrn;
951	struct udev_device *dev;
952	dev = udev_monitor_receive_device(drmmode->uevent_monitor);
953	if (!dev)
954		return;
955
956	RRGetInfo(xf86ScrnToScreen(scrn), TRUE);
957	udev_device_unref(dev);
958}
959#endif
960
961void qxl_drmmode_uevent_init(ScrnInfoPtr scrn, drmmode_ptr drmmode)
962{
963#ifdef HAVE_LIBUDEV
964	struct udev *u;
965	struct udev_monitor *mon;
966
967	u = udev_new();
968	if (!u)
969		return;
970	mon = udev_monitor_new_from_netlink(u, "udev");
971	if (!mon) {
972		udev_unref(u);
973		return;
974	}
975
976	if (udev_monitor_filter_add_match_subsystem_devtype(mon,
977							    "drm",
978							    "drm_minor") < 0 ||
979	    udev_monitor_enable_receiving(mon) < 0) {
980		udev_monitor_unref(mon);
981		udev_unref(u);
982		return;
983	}
984
985	drmmode->uevent_handler =
986		xf86AddGeneralHandler(udev_monitor_get_fd(mon),
987				      drmmode_handle_uevents,
988				      drmmode);
989
990	drmmode->uevent_monitor = mon;
991#endif
992}
993
994void qxl_drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr drmmode)
995{
996#ifdef HAVE_LIBUDEV
997	if (drmmode->uevent_handler) {
998		struct udev *u = udev_monitor_get_udev(drmmode->uevent_monitor);
999		xf86RemoveGeneralHandler(drmmode->uevent_handler);
1000
1001		udev_monitor_unref(drmmode->uevent_monitor);
1002		udev_unref(u);
1003	}
1004#endif
1005}
1006
1007#endif
1008