add HW cursor support using drm-plane
[gstreamer-omap:xf86-video-omap.git] / src / drmmode_display.c
1 /*
2  * Copyright © 2007 Red Hat, Inc.
3  * Copyright © 2008 Maarten Maathuis
4  * Copyright © 2011 Texas Instruments, Inc
5  *
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation
10  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11  * and/or sell copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the next
15  * paragraph) shall be included in all copies or substantial portions of the
16  * Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
21  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24  * SOFTWARE.
25  *
26  * Authors:
27  *    Dave Airlie <airlied@redhat.com>
28  *    Ian Elliott <ianelliottus@yahoo.com>
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 /* TODO cleanup #includes, remove unnecessary ones */
36
37 #include "xorgVersion.h"
38
39 #include <sys/stat.h>
40
41 #include <string.h>
42 #include <math.h>
43 #include <errno.h>
44 #include <unistd.h>
45 #include <stdlib.h>
46
47 /* All drivers should typically include these */
48 #include "xf86.h"
49 #include "xf86_OSproc.h"
50 #define PPC_MMIO_IS_BE
51 #include "compiler.h"
52 #include "mipointer.h"
53
54 /* All drivers implementing backing store need this */
55 #include "mibstore.h"
56
57 #include "micmap.h"
58
59 #include "xf86DDC.h"
60
61 #include "vbe.h"
62 #include "xf86RandR12.h"
63 #include "dixstruct.h"
64 #include "scrnintstr.h"
65 #include "fb.h"
66 #include "xf86cmap.h"
67 #include "shadowfb.h"
68
69 #include "xf86xv.h"
70 #include <X11/extensions/Xv.h>
71
72 #include "xf86Cursor.h"
73 #include "xf86DDC.h"
74
75 #include "region.h"
76
77 #include <X11/extensions/randr.h>
78
79 #ifdef HAVE_XEXTPROTO_71
80 #include <X11/extensions/dpmsconst.h>
81 #else
82 #define DPMS_SERVER
83 #include <X11/extensions/dpms.h>
84 #endif
85
86 #include "omap_driver.h"
87
88 #include "xf86Crtc.h"
89
90 #include "xf86drmMode.h"
91 #include "drm_fourcc.h"
92 #include "X11/Xatom.h"
93
94 #include <sys/ioctl.h>
95 #include <libudev.h>
96
97 typedef struct {
98         /* hardware cursor: */
99         drmModePlane *ovr;
100         struct omap_bo *bo;
101         uint32_t fb_id;
102         int x, y;
103         int visible;
104 } drmmode_cursor_rec, *drmmode_cursor_ptr;
105
106 typedef struct {
107         int fd;
108         uint32_t fb_id;
109         drmModeResPtr mode_res;
110         int cpp;
111         struct udev_monitor *uevent_monitor;
112         InputHandlerProc uevent_handler;
113         drmmode_cursor_ptr cursor;
114 } drmmode_rec, *drmmode_ptr;
115
116 typedef struct {
117         drmmode_ptr drmmode;
118         drmModeCrtcPtr mode_crtc;
119 } drmmode_crtc_private_rec, *drmmode_crtc_private_ptr;
120
121 typedef struct {
122         drmModePropertyPtr mode_prop;
123         int index; /* Index within the kernel-side property arrays for
124          * this connector. */
125         int num_atoms; /* if range prop, num_atoms == 1; if enum prop,
126          * num_atoms == num_enums + 1 */
127         Atom *atoms;
128 } drmmode_prop_rec, *drmmode_prop_ptr;
129
130 typedef struct {
131         drmmode_ptr drmmode;
132         int output_id;
133         drmModeConnectorPtr mode_output;
134         drmModeEncoderPtr mode_encoder;
135         drmModePropertyBlobPtr edid_blob;
136         int num_props;
137         drmmode_prop_ptr props;
138 } drmmode_output_private_rec, *drmmode_output_private_ptr;
139
140 static void drmmode_output_dpms(xf86OutputPtr output, int mode);
141 void drmmode_remove_fb(ScrnInfoPtr pScrn);
142
143 static drmmode_ptr
144 drmmode_from_scrn(ScrnInfoPtr pScrn)
145 {
146         xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
147         drmmode_crtc_private_ptr drmmode_crtc;
148
149         drmmode_crtc = xf86_config->crtc[0]->driver_private;
150         return drmmode_crtc->drmmode;
151 }
152
153 static void
154 drmmode_ConvertFromKMode(ScrnInfoPtr pScrn, drmModeModeInfo *kmode,
155                 DisplayModePtr  mode)
156 {
157         memset(mode, 0, sizeof(DisplayModeRec));
158         mode->status = MODE_OK;
159
160         mode->Clock = kmode->clock;
161
162         mode->HDisplay = kmode->hdisplay;
163         mode->HSyncStart = kmode->hsync_start;
164         mode->HSyncEnd = kmode->hsync_end;
165         mode->HTotal = kmode->htotal;
166         mode->HSkew = kmode->hskew;
167
168         mode->VDisplay = kmode->vdisplay;
169         mode->VSyncStart = kmode->vsync_start;
170         mode->VSyncEnd = kmode->vsync_end;
171         mode->VTotal = kmode->vtotal;
172         mode->VScan = kmode->vscan;
173
174         mode->Flags = kmode->flags; //& FLAG_BITS;
175         mode->name = strdup(kmode->name);
176
177         DEBUG_MSG("copy mode %s (%p %p)", kmode->name, mode->name, mode);
178
179         if (kmode->type & DRM_MODE_TYPE_DRIVER)
180                 mode->type = M_T_DRIVER;
181         if (kmode->type & DRM_MODE_TYPE_PREFERRED)
182                 mode->type |= M_T_PREFERRED;
183
184         xf86SetModeCrtc (mode, pScrn->adjustFlags);
185 }
186
187 static void
188 drmmode_ConvertToKMode(ScrnInfoPtr pScrn, drmModeModeInfo *kmode,
189                 DisplayModePtr mode)
190 {
191         memset(kmode, 0, sizeof(*kmode));
192
193         kmode->clock = mode->Clock;
194         kmode->hdisplay = mode->HDisplay;
195         kmode->hsync_start = mode->HSyncStart;
196         kmode->hsync_end = mode->HSyncEnd;
197         kmode->htotal = mode->HTotal;
198         kmode->hskew = mode->HSkew;
199
200         kmode->vdisplay = mode->VDisplay;
201         kmode->vsync_start = mode->VSyncStart;
202         kmode->vsync_end = mode->VSyncEnd;
203         kmode->vtotal = mode->VTotal;
204         kmode->vscan = mode->VScan;
205
206         kmode->flags = mode->Flags; //& FLAG_BITS;
207         if (mode->name)
208                 strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN);
209         kmode->name[DRM_DISPLAY_MODE_LEN-1] = 0;
210 }
211
212 static void
213 drmmode_crtc_dpms(xf86CrtcPtr drmmode_crtc, int mode)
214 {
215         // FIXME - Implement this function
216 }
217
218 static Bool
219 drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
220                 Rotation rotation, int x, int y)
221 {
222         ScrnInfoPtr pScrn = crtc->scrn;
223         OMAPPtr pOMAP = OMAPPTR(pScrn);
224         xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
225         drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
226         drmmode_ptr drmmode = drmmode_crtc->drmmode;
227         int saved_x, saved_y;
228         Rotation saved_rotation;
229         DisplayModeRec saved_mode;
230         uint32_t *output_ids = NULL;
231         int output_count = 0;
232         int ret = TRUE;
233         int i;
234         int fb_id;
235         drmModeModeInfo kmode;
236
237         TRACE_ENTER();
238
239         /* remove old fb if it exists */
240         drmmode_remove_fb(pScrn);
241
242         if (drmmode->fb_id == 0) {
243                 unsigned int pitch =
244                                 OMAPCalculateStride(pScrn->virtualX, pScrn->bitsPerPixel);
245
246                 DEBUG_MSG("create framebuffer: %dx%d",
247                                 pScrn->virtualX, pScrn->virtualY);
248
249                 ret = drmModeAddFB(drmmode->fd,
250                                 pScrn->virtualX, pScrn->virtualY,
251                                 pScrn->depth, pScrn->bitsPerPixel,
252                                 pitch, omap_bo_handle(pOMAP->scanout),
253                                 &drmmode->fb_id);
254                 if (ret < 0) {
255                         // Fixme - improve this error message:
256                         ErrorF("failed to add fb\n");
257                         return FALSE;
258                 }
259         }
260
261         /* Save the current mode in case there's a problem: */
262         saved_mode = crtc->mode;
263         saved_x = crtc->x;
264         saved_y = crtc->y;
265         saved_rotation = crtc->rotation;
266
267         /* Set the new mode: */
268         crtc->mode = *mode;
269         crtc->x = x;
270         crtc->y = y;
271         crtc->rotation = rotation;
272
273         output_ids = calloc(sizeof(uint32_t), xf86_config->num_output);
274         if (!output_ids) {
275                 // Fixme - have an error message?
276                 ret = FALSE;
277                 goto done;
278         }
279
280         for (i = 0; i < xf86_config->num_output; i++) {
281                 xf86OutputPtr output = xf86_config->output[i];
282                 drmmode_output_private_ptr drmmode_output;
283
284                 if (output->crtc != crtc)
285                         continue;
286
287                 drmmode_output = output->driver_private;
288                 output_ids[output_count] =
289                                 drmmode_output->mode_output->connector_id;
290                 output_count++;
291         }
292
293         if (!xf86CrtcRotate(crtc))
294                 goto done;
295
296         // Fixme - Intel puts this function here, and Nouveau puts it at the end
297         // of this function -> determine what's best for TI'S OMAP4:
298         crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
299                         crtc->gamma_blue, crtc->gamma_size);
300
301         drmmode_ConvertToKMode(crtc->scrn, &kmode, mode);
302
303         fb_id = drmmode->fb_id;
304
305         ret = drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
306                         fb_id, x, y, output_ids, output_count, &kmode);
307         if (ret) {
308                 xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
309                                 "failed to set mode: %s\n", strerror(-ret));
310         } else {
311                 ret = TRUE;
312         }
313
314         // FIXME - DO WE NEED TO CALL TO THE PVR EXA/DRI2 CODE TO UPDATE THEM???
315
316         /* Turn on any outputs on this crtc that may have been disabled: */
317         for (i = 0; i < xf86_config->num_output; i++) {
318                 xf86OutputPtr output = xf86_config->output[i];
319
320                 if (output->crtc != crtc)
321                         continue;
322
323                 drmmode_output_dpms(output, DPMSModeOn);
324         }
325
326         // TODO: only call this if we are not using sw cursor.. ie. bad to call this
327         // if we haven't called xf86InitCursor()!!
328         //      if (pScrn->pScreen)
329         //              xf86_reload_cursors(pScrn->pScreen);
330
331 done:
332         if (output_ids) {
333                 free(output_ids);
334         }
335         if (!ret) {
336                 /* If there was a problem, resture the old mode: */
337                 crtc->x = saved_x;
338                 crtc->y = saved_y;
339                 crtc->rotation = saved_rotation;
340                 crtc->mode = saved_mode;
341         }
342
343         TRACE_EXIT();
344         return ret;
345 }
346
347 static void
348 drmmode_hide_cursor(xf86CrtcPtr crtc)
349 {
350         drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
351         drmmode_ptr drmmode = drmmode_crtc->drmmode;
352         drmmode_cursor_ptr cursor = drmmode->cursor;
353
354         if (!cursor)
355                 return;
356
357         cursor->visible = FALSE;
358
359         /* set plane's fb_id to 0 to disable it */
360         drmModeSetPlane(drmmode->fd, cursor->ovr->plane_id,
361                         drmmode_crtc->mode_crtc->crtc_id, 0, 0,
362                         0, 0, 0, 0, 0, 0, 0, 0);
363 }
364
365 static void
366 drmmode_show_cursor(xf86CrtcPtr crtc)
367 {
368         drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
369         drmmode_ptr drmmode = drmmode_crtc->drmmode;
370         drmmode_cursor_ptr cursor = drmmode->cursor;
371
372         if (!cursor)
373                 return;
374
375         cursor->visible = TRUE;
376
377         /* note src coords (last 4 args) are in Q16 format */
378         drmModeSetPlane(drmmode->fd, cursor->ovr->plane_id,
379                         drmmode_crtc->mode_crtc->crtc_id, cursor->fb_id, 0,
380                         cursor->x, cursor->y, 64, 64,
381                         0, 0, 64<<16, 64<<16);
382 }
383
384 static void
385 drmmode_set_cursor_position(xf86CrtcPtr crtc, int x, int y)
386 {
387         drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
388         drmmode_ptr drmmode = drmmode_crtc->drmmode;
389         drmmode_cursor_ptr cursor = drmmode->cursor;
390
391         if (!cursor)
392                 return;
393
394         cursor->x = x;
395         cursor->y = y;
396
397         if (cursor->visible)
398                 drmmode_show_cursor(crtc);
399 }
400
401 static void
402 drmmode_load_cursor_argb(xf86CrtcPtr crtc, CARD32 *image)
403 {
404         drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
405         drmmode_ptr drmmode = drmmode_crtc->drmmode;
406         drmmode_cursor_ptr cursor = drmmode->cursor;
407
408         if (!cursor)
409                 return;
410
411         if (cursor->visible)
412                 drmmode_hide_cursor(crtc);
413
414         memcpy(omap_bo_map(cursor->bo), image, omap_bo_size(cursor->bo));
415
416         if (cursor->visible)
417                 drmmode_show_cursor(crtc);
418 }
419
420 Bool
421 drmmode_cursor_init(ScreenPtr pScreen)
422 {
423         ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
424         OMAPPtr pOMAP = OMAPPTR(pScrn);
425         drmmode_ptr drmmode = drmmode_from_scrn(pScrn);
426         drmmode_cursor_ptr cursor;
427         drmModePlaneRes *plane_resources;
428         drmModePlane *ovr;
429
430         /* technically we probably don't have any size limit.. since we
431          * are just using an overlay... but xserver will always create
432          * cursor images in the max size, so don't use width/height values
433          * that are too big
434          */
435         int w = 64, h = 64;
436         uint32_t handles[4], pitches[4], offsets[4]; /* we only use [0] */
437
438         if (drmmode->cursor) {
439                 INFO_MSG("cursor already initialized");
440                 return TRUE;
441         }
442
443         cursor = calloc(1, sizeof(drmmode_cursor_rec));
444
445         /* find an unused plane which can be used as a mouse cursor.  Note
446          * that we cheat a bit, in order to not burn one overlay per crtc,
447          * and only show the mouse cursor on one crtc at a time
448          */
449         plane_resources = drmModeGetPlaneResources(drmmode->fd);
450         if (!plane_resources) {
451                 ERROR_MSG("drmModeGetPlaneResources failed: %s", strerror(errno));
452                 return FALSE;
453         }
454
455         if (plane_resources->count_planes < 1) {
456                 ERROR_MSG("not enough planes for HW cursor");
457                 return FALSE;
458         }
459
460         ovr = drmModeGetPlane(drmmode->fd, plane_resources->planes[0]);
461         if (!ovr) {
462                 ERROR_MSG("drmModeGetPlane failed: %s\n", strerror(errno));
463                 return FALSE;
464         }
465
466         cursor->ovr = ovr;
467         cursor->bo  = omap_bo_new(pOMAP->dev, w*h*4,
468                         OMAP_BO_SCANOUT | OMAP_BO_WC);
469
470         handles[0] = omap_bo_handle(cursor->bo);
471         pitches[0] = w*4;
472         offsets[0] = 0;
473
474         if (drmModeAddFB2(drmmode->fd, w, h, DRM_FORMAT_ARGB8888,
475                         handles, pitches, offsets, &cursor->fb_id, 0)) {
476                 ERROR_MSG("drmModeAddFB2 failed: %s", strerror(errno));
477                 return FALSE;
478         }
479
480         if (xf86_cursors_init(pScreen, w, h, HARDWARE_CURSOR_ARGB)) {
481                 INFO_MSG("HW cursor initialized");
482                 drmmode->cursor = cursor;
483                 return TRUE;
484         }
485
486         // TODO cleanup when things fail..
487         return FALSE;
488 }
489
490 static void
491 drmmode_gamma_set(xf86CrtcPtr crtc, CARD16 *red, CARD16 *green, CARD16 *blue,
492                 int size)
493 {
494         drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
495         drmmode_ptr drmmode = drmmode_crtc->drmmode;
496         int ret;
497
498         ret = drmModeCrtcSetGamma(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
499                         size, red, green, blue);
500         if (ret != 0) {
501                 xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
502                                 "failed to set gamma: %s\n", strerror(-ret));
503         }
504 }
505
506 static const xf86CrtcFuncsRec drmmode_crtc_funcs = {
507                 .dpms = drmmode_crtc_dpms,
508                 .set_mode_major = drmmode_set_mode_major,
509                 .set_cursor_position = drmmode_set_cursor_position,
510                 .show_cursor = drmmode_show_cursor,
511                 .hide_cursor = drmmode_hide_cursor,
512                 .load_cursor_argb = drmmode_load_cursor_argb,
513                 .gamma_set = drmmode_gamma_set,
514 };
515
516
517 static void
518 drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num)
519 {
520         xf86CrtcPtr crtc;
521         drmmode_crtc_private_ptr drmmode_crtc;
522
523         TRACE_ENTER();
524
525         crtc = xf86CrtcCreate(pScrn, &drmmode_crtc_funcs);
526         if (crtc == NULL)
527                 return;
528
529         drmmode_crtc = xnfcalloc(sizeof(drmmode_crtc_private_rec), 1);
530         drmmode_crtc->mode_crtc = drmModeGetCrtc(drmmode->fd,
531                         drmmode->mode_res->crtcs[num]);
532         drmmode_crtc->drmmode = drmmode;
533
534         // FIXME - potentially add code to allocate a HW cursor here.
535
536         crtc->driver_private = drmmode_crtc;
537
538         TRACE_EXIT();
539         return;
540 }
541
542 static xf86OutputStatus
543 drmmode_output_detect(xf86OutputPtr output)
544 {
545         /* go to the hw and retrieve a new output struct */
546         drmmode_output_private_ptr drmmode_output = output->driver_private;
547         drmmode_ptr drmmode = drmmode_output->drmmode;
548         xf86OutputStatus status;
549         drmModeFreeConnector(drmmode_output->mode_output);
550
551         drmmode_output->mode_output =
552                         drmModeGetConnector(drmmode->fd, drmmode_output->output_id);
553
554         switch (drmmode_output->mode_output->connection) {
555         case DRM_MODE_CONNECTED:
556                 status = XF86OutputStatusConnected;
557                 break;
558         case DRM_MODE_DISCONNECTED:
559                 status = XF86OutputStatusDisconnected;
560                 break;
561         default:
562         case DRM_MODE_UNKNOWNCONNECTION:
563                 status = XF86OutputStatusUnknown;
564                 break;
565         }
566         return status;
567 }
568
569 static Bool
570 drmmode_output_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
571 {
572         if (mode->type & M_T_DEFAULT)
573                 /* Default modes are harmful here. */
574                 return MODE_BAD;
575
576         return MODE_OK;
577 }
578
579 static DisplayModePtr
580 drmmode_output_get_modes(xf86OutputPtr output)
581 {
582         ScrnInfoPtr pScrn = output->scrn;
583         drmmode_output_private_ptr drmmode_output = output->driver_private;
584         drmModeConnectorPtr koutput = drmmode_output->mode_output;
585         drmmode_ptr drmmode = drmmode_output->drmmode;
586         DisplayModePtr Modes = NULL, Mode;
587         drmModePropertyPtr props;
588         xf86MonPtr ddc_mon = NULL;
589         int i;
590
591         /* look for an EDID property */
592         for (i = 0; i < koutput->count_props; i++) {
593                 props = drmModeGetProperty(drmmode->fd, koutput->props[i]);
594                 if (!props || !(props->flags & DRM_MODE_PROP_BLOB))
595                         continue;
596
597                 if (!strcmp(props->name, "EDID")) {
598                         if (drmmode_output->edid_blob)
599                                 drmModeFreePropertyBlob(drmmode_output->edid_blob);
600                         drmmode_output->edid_blob =
601                                         drmModeGetPropertyBlob(drmmode->fd,
602                                                         koutput->prop_values[i]);
603                 }
604                 drmModeFreeProperty(props);
605         }
606
607         if (drmmode_output->edid_blob)
608                 ddc_mon = xf86InterpretEDID(pScrn->scrnIndex,
609                                 drmmode_output->edid_blob->data);
610
611         if (ddc_mon) {
612                 XF86_CRTC_CONFIG_PTR(pScrn)->debug_modes = TRUE;
613                 xf86PrintEDID(ddc_mon);
614                 xf86OutputSetEDID(output, ddc_mon);
615                 xf86SetDDCproperties(pScrn, ddc_mon);
616         }
617
618         DEBUG_MSG("count_modes: %d", koutput->count_modes);
619
620         /* modes should already be available */
621         for (i = 0; i < koutput->count_modes; i++) {
622                 Mode = xnfalloc(sizeof(DisplayModeRec));
623
624                 drmmode_ConvertFromKMode(pScrn, &koutput->modes[i],
625                                 Mode);
626                 Modes = xf86ModesAdd(Modes, Mode);
627
628         }
629         return Modes;
630 }
631
632 static void
633 drmmode_output_destroy(xf86OutputPtr output)
634 {
635         drmmode_output_private_ptr drmmode_output = output->driver_private;
636         int i;
637
638         if (drmmode_output->edid_blob)
639                 drmModeFreePropertyBlob(drmmode_output->edid_blob);
640         for (i = 0; i < drmmode_output->num_props; i++) {
641                 drmModeFreeProperty(drmmode_output->props[i].mode_prop);
642                 free(drmmode_output->props[i].atoms);
643         }
644         drmModeFreeConnector(drmmode_output->mode_output);
645         free(drmmode_output);
646         output->driver_private = NULL;
647 }
648
649 static void
650 drmmode_output_dpms(xf86OutputPtr output, int mode)
651 {
652         drmmode_output_private_ptr drmmode_output = output->driver_private;
653         drmModeConnectorPtr koutput = drmmode_output->mode_output;
654         drmModePropertyPtr props;
655         drmmode_ptr drmmode = drmmode_output->drmmode;
656         int mode_id = -1, i;
657
658         for (i = 0; i < koutput->count_props; i++) {
659                 props = drmModeGetProperty(drmmode->fd, koutput->props[i]);
660                 if (props && (props->flags && DRM_MODE_PROP_ENUM)) {
661                         if (!strcmp(props->name, "DPMS")) {
662                                 mode_id = koutput->props[i];
663                                 drmModeFreeProperty(props);
664                                 break;
665                         }
666                         drmModeFreeProperty(props);
667                 }
668         }
669
670         if (mode_id < 0)
671                 return;
672
673         drmModeConnectorSetProperty(drmmode->fd, koutput->connector_id,
674                         mode_id, mode);
675 }
676
677 static Bool
678 drmmode_property_ignore(drmModePropertyPtr prop)
679 {
680         if (!prop)
681                 return TRUE;
682         /* ignore blob prop */
683         if (prop->flags & DRM_MODE_PROP_BLOB)
684                 return TRUE;
685         /* ignore standard property */
686         if (!strcmp(prop->name, "EDID") ||
687                         !strcmp(prop->name, "DPMS"))
688                 return TRUE;
689
690         return FALSE;
691 }
692
693 static void
694 drmmode_output_create_resources(xf86OutputPtr output)
695 {
696         drmmode_output_private_ptr drmmode_output = output->driver_private;
697         drmModeConnectorPtr mode_output = drmmode_output->mode_output;
698         drmmode_ptr drmmode = drmmode_output->drmmode;
699         drmModePropertyPtr drmmode_prop;
700         uint32_t value;
701         int i, j, err;
702
703         drmmode_output->props = calloc(mode_output->count_props, sizeof(drmmode_prop_rec));
704         if (!drmmode_output->props)
705                 return;
706
707         drmmode_output->num_props = 0;
708         for (i = 0, j = 0; i < mode_output->count_props; i++) {
709                 drmmode_prop = drmModeGetProperty(drmmode->fd, mode_output->props[i]);
710                 if (drmmode_property_ignore(drmmode_prop)) {
711                         drmModeFreeProperty(drmmode_prop);
712                         continue;
713                 }
714                 drmmode_output->props[j].mode_prop = drmmode_prop;
715                 drmmode_output->props[j].index = i;
716                 drmmode_output->num_props++;
717                 j++;
718         }
719
720         for (i = 0; i < drmmode_output->num_props; i++) {
721                 drmmode_prop_ptr p = &drmmode_output->props[i];
722                 drmmode_prop = p->mode_prop;
723
724                 value = drmmode_output->mode_output->prop_values[p->index];
725
726                 if (drmmode_prop->flags & DRM_MODE_PROP_RANGE) {
727                         INT32 range[2];
728
729                         p->num_atoms = 1;
730                         p->atoms = calloc(p->num_atoms, sizeof(Atom));
731                         if (!p->atoms)
732                                 continue;
733                         p->atoms[0] = MakeAtom(drmmode_prop->name, strlen(drmmode_prop->name), TRUE);
734                         range[0] = drmmode_prop->values[0];
735                         range[1] = drmmode_prop->values[1];
736                         err = RRConfigureOutputProperty(output->randr_output, p->atoms[0],
737                                         FALSE, TRUE,
738                                         drmmode_prop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE,
739                                                         2, range);
740                         if (err != 0) {
741                                 xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
742                                                 "RRConfigureOutputProperty error, %d\n", err);
743                         }
744                         err = RRChangeOutputProperty(output->randr_output, p->atoms[0],
745                                         XA_INTEGER, 32, PropModeReplace, 1,
746                                         &value, FALSE, FALSE);
747                         if (err != 0) {
748                                 xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
749                                                 "RRChangeOutputProperty error, %d\n", err);
750                         }
751                 } else if (drmmode_prop->flags & DRM_MODE_PROP_ENUM) {
752                         p->num_atoms = drmmode_prop->count_enums + 1;
753                         p->atoms = calloc(p->num_atoms, sizeof(Atom));
754                         if (!p->atoms)
755                                 continue;
756                         p->atoms[0] = MakeAtom(drmmode_prop->name, strlen(drmmode_prop->name), TRUE);
757                         for (j = 1; j <= drmmode_prop->count_enums; j++) {
758                                 struct drm_mode_property_enum *e = &drmmode_prop->enums[j-1];
759                                 p->atoms[j] = MakeAtom(e->name, strlen(e->name), TRUE);
760                         }
761                         err = RRConfigureOutputProperty(output->randr_output, p->atoms[0],
762                                         FALSE, FALSE,
763                                         drmmode_prop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE,
764                                                         p->num_atoms - 1, (INT32 *)&p->atoms[1]);
765                         if (err != 0) {
766                                 xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
767                                                 "RRConfigureOutputProperty error, %d\n", err);
768                         }
769                         for (j = 0; j < drmmode_prop->count_enums; j++)
770                                 if (drmmode_prop->enums[j].value == value)
771                                         break;
772                         /* there's always a matching value */
773                         err = RRChangeOutputProperty(output->randr_output, p->atoms[0],
774                                         XA_ATOM, 32, PropModeReplace, 1, &p->atoms[j+1], FALSE, FALSE);
775                         if (err != 0) {
776                                 xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
777                                                 "RRChangeOutputProperty error, %d\n", err);
778                         }
779                 }
780         }
781 }
782
783 static Bool
784 drmmode_output_set_property(xf86OutputPtr output, Atom property,
785                 RRPropertyValuePtr value)
786 {
787         drmmode_output_private_ptr drmmode_output = output->driver_private;
788         drmmode_ptr drmmode = drmmode_output->drmmode;
789         int i, ret;
790
791         for (i = 0; i < drmmode_output->num_props; i++) {
792                 drmmode_prop_ptr p = &drmmode_output->props[i];
793
794                 if (p->atoms[0] != property)
795                         continue;
796
797                 if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
798                         uint32_t val;
799
800                         if (value->type != XA_INTEGER || value->format != 32 ||
801                                         value->size != 1)
802                                 return FALSE;
803                         val = *(uint32_t *)value->data;
804
805                         ret = drmModeConnectorSetProperty(drmmode->fd, drmmode_output->output_id,
806                                         p->mode_prop->prop_id, (uint64_t)val);
807
808                         if (ret)
809                                 return FALSE;
810
811                         return TRUE;
812
813                 } else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
814                         Atom    atom;
815                         const char      *name;
816                         int             j;
817
818                         if (value->type != XA_ATOM || value->format != 32 || value->size != 1)
819                                 return FALSE;
820                         memcpy(&atom, value->data, 4);
821                         name = NameForAtom(atom);
822
823                         /* search for matching name string, then set its value down */
824                         for (j = 0; j < p->mode_prop->count_enums; j++) {
825                                 if (!strcmp(p->mode_prop->enums[j].name, name)) {
826                                         ret = drmModeConnectorSetProperty(drmmode->fd,
827                                                         drmmode_output->output_id,
828                                                         p->mode_prop->prop_id,
829                                                         p->mode_prop->enums[j].value);
830
831                                         if (ret)
832                                                 return FALSE;
833
834                                         return TRUE;
835                                 }
836                         }
837
838                         return FALSE;
839                 }
840         }
841
842         return TRUE;
843 }
844
845 static Bool
846 drmmode_output_get_property(xf86OutputPtr output, Atom property)
847 {
848
849         drmmode_output_private_ptr drmmode_output = output->driver_private;
850         drmmode_ptr drmmode = drmmode_output->drmmode;
851         uint32_t value;
852         int err, i;
853
854         if (output->scrn->vtSema) {
855                 drmModeFreeConnector(drmmode_output->mode_output);
856                 drmmode_output->mode_output =
857                                 drmModeGetConnector(drmmode->fd, drmmode_output->output_id);
858         }
859
860         for (i = 0; i < drmmode_output->num_props; i++) {
861                 drmmode_prop_ptr p = &drmmode_output->props[i];
862                 if (p->atoms[0] != property)
863                         continue;
864
865                 value = drmmode_output->mode_output->prop_values[p->index];
866
867                 if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
868                         err = RRChangeOutputProperty(output->randr_output,
869                                         property, XA_INTEGER, 32,
870                                         PropModeReplace, 1, &value,
871                                         FALSE, FALSE);
872
873                         return !err;
874                 } else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
875                         int             j;
876
877                         /* search for matching name string, then set its value down */
878                         for (j = 0; j < p->mode_prop->count_enums; j++) {
879                                 if (p->mode_prop->enums[j].value == value)
880                                         break;
881                         }
882
883                         err = RRChangeOutputProperty(output->randr_output, property,
884                                         XA_ATOM, 32, PropModeReplace, 1,
885                                         &p->atoms[j+1], FALSE, FALSE);
886
887                         return !err;
888                 }
889         }
890
891         return FALSE;
892 }
893
894 static const xf86OutputFuncsRec drmmode_output_funcs = {
895                 .create_resources = drmmode_output_create_resources,
896                 .dpms = drmmode_output_dpms,
897                 .detect = drmmode_output_detect,
898                 .mode_valid = drmmode_output_mode_valid,
899                 .get_modes = drmmode_output_get_modes,
900                 .set_property = drmmode_output_set_property,
901                 .get_property = drmmode_output_get_property,
902                 .destroy = drmmode_output_destroy
903 };
904
905 // FIXME - Eliminate the following values that aren't accurate for OMAP4:
906 const char *output_names[] = { "None",
907                 "VGA",
908                 "DVI-I",
909                 "DVI-D",
910                 "DVI-A",
911                 "Composite",
912                 "SVIDEO",
913                 "LVDS",
914                 "CTV",
915                 "DIN",
916                 "DP",
917                 "HDMI",
918                 "HDMI",
919                 "TV",
920                 "eDP",
921 };
922 #define NUM_OUTPUT_NAMES (sizeof(output_names) / sizeof(output_names[0]))
923
924 static void
925 drmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num)
926 {
927         xf86OutputPtr output;
928         drmModeConnectorPtr koutput;
929         drmModeEncoderPtr kencoder;
930         drmmode_output_private_ptr drmmode_output;
931         char name[32];
932
933         TRACE_ENTER();
934
935         koutput = drmModeGetConnector(drmmode->fd,
936                         drmmode->mode_res->connectors[num]);
937         if (!koutput)
938                 return;
939
940         kencoder = drmModeGetEncoder(drmmode->fd, koutput->encoders[0]);
941         if (!kencoder) {
942                 drmModeFreeConnector(koutput);
943                 return;
944         }
945
946         if (koutput->connector_type >= NUM_OUTPUT_NAMES)
947                 snprintf(name, 32, "Unknown%d-%d", koutput->connector_type,
948                                 koutput->connector_type_id);
949         else
950                 snprintf(name, 32, "%s-%d",
951                                 output_names[koutput->connector_type],
952                                 koutput->connector_type_id);
953
954         output = xf86OutputCreate(pScrn, &drmmode_output_funcs, name);
955         if (!output) {
956                 drmModeFreeEncoder(kencoder);
957                 drmModeFreeConnector(koutput);
958                 return;
959         }
960
961         drmmode_output = calloc(sizeof(drmmode_output_private_rec), 1);
962         if (!drmmode_output) {
963                 xf86OutputDestroy(output);
964                 drmModeFreeConnector(koutput);
965                 drmModeFreeEncoder(kencoder);
966                 return;
967         }
968
969         drmmode_output->output_id = drmmode->mode_res->connectors[num];
970         drmmode_output->mode_output = koutput;
971         drmmode_output->mode_encoder = kencoder;
972         drmmode_output->drmmode = drmmode;
973
974         output->mm_width = koutput->mmWidth;
975         output->mm_height = koutput->mmHeight;
976         output->driver_private = drmmode_output;
977
978         output->possible_crtcs = kencoder->possible_crtcs;
979         output->possible_clones = kencoder->possible_clones;
980         output->interlaceAllowed = TRUE;
981
982         TRACE_EXIT();
983         return;
984 }
985
986 static Bool
987 drmmode_xf86crtc_resize(ScrnInfoPtr pScrn, int width, int height)
988 {
989         OMAPPtr pOMAP = OMAPPTR(pScrn);
990         ScreenPtr pScreen = pScrn->pScreen;
991         unsigned int pitch;
992
993         TRACE_ENTER();
994
995         /* if fb required size has changed, realloc! */
996
997         DEBUG_MSG("Resize!  %dx%d", width, height);
998
999         pScrn->virtualX = width;
1000         pScrn->virtualY = height;
1001
1002         pitch = OMAPCalculateStride(width, pScrn->bitsPerPixel);
1003
1004         if ((pitch * height) != omap_bo_size(pOMAP->scanout)) {
1005                 /* hmm, should we remove fb here.. we don't want to keep
1006                  * scanning out a deallocated buffer..
1007                  */
1008                 drmmode_remove_fb(pScrn);
1009
1010                 /* delete old scanout buffer */
1011                 omap_bo_del(pOMAP->scanout);
1012
1013                 DEBUG_MSG("allocating new scanout buffer: %dx%d (%d)",
1014                                 width, height, pitch);
1015
1016                 /* allocate new scanout buffer */
1017                 pOMAP->scanout = omap_bo_new(pOMAP->dev, height * pitch,
1018                                 OMAP_BO_SCANOUT | OMAP_BO_WC);
1019                 if (!pOMAP->scanout) {
1020                         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
1021                                         "Error reallocating scanout buffer\n");
1022                         return FALSE;
1023                 }
1024         }
1025
1026         if (pScreen && pScreen->ModifyPixmapHeader) {
1027                 PixmapPtr rootPixmap = pScreen->GetScreenPixmap(pScreen);
1028                 pScreen->ModifyPixmapHeader(rootPixmap,
1029                                 pScrn->virtualX, pScrn->virtualY,
1030                                 pScrn->depth, pScrn->bitsPerPixel, pitch,
1031                                 omap_bo_map(pOMAP->scanout));
1032         }
1033
1034         TRACE_EXIT();
1035         return TRUE;
1036 }
1037
1038 static const xf86CrtcConfigFuncsRec drmmode_xf86crtc_config_funcs = {
1039                 drmmode_xf86crtc_resize
1040 };
1041
1042
1043 Bool drmmode_pre_init(ScrnInfoPtr pScrn, int fd, int cpp)
1044 {
1045         drmmode_ptr drmmode;
1046         int i;
1047
1048         TRACE_ENTER();
1049
1050         drmmode = calloc(1, sizeof *drmmode);
1051         drmmode->fd = fd;
1052         drmmode->fb_id = 0;
1053
1054         xf86CrtcConfigInit(pScrn, &drmmode_xf86crtc_config_funcs);
1055
1056
1057         drmmode->cpp = cpp;
1058         drmmode->mode_res = drmModeGetResources(drmmode->fd);
1059         if (!drmmode->mode_res) {
1060                 return FALSE;
1061         } else {
1062                 DEBUG_MSG("Got KMS resources");
1063                 DEBUG_MSG("  %d connectors, %d encoders",
1064                                 drmmode->mode_res->count_connectors,
1065                                 drmmode->mode_res->count_encoders);
1066                 DEBUG_MSG("  %d crtcs, %d fbs",
1067                                 drmmode->mode_res->count_crtcs, drmmode->mode_res->count_fbs);
1068                 DEBUG_MSG("  %dx%d minimum resolution",
1069                                 drmmode->mode_res->min_width, drmmode->mode_res->min_height);
1070                 DEBUG_MSG("  %dx%d maximum resolution",
1071                                 drmmode->mode_res->max_width, drmmode->mode_res->max_height);
1072         }
1073         xf86CrtcSetSizeRange(pScrn, 320, 200, drmmode->mode_res->max_width,
1074                         drmmode->mode_res->max_height);
1075         for (i = 0; i < drmmode->mode_res->count_crtcs; i++)
1076                 drmmode_crtc_init(pScrn, drmmode, i);
1077
1078         for (i = 0; i < drmmode->mode_res->count_connectors; i++)
1079                 drmmode_output_init(pScrn, drmmode, i);
1080
1081         xf86InitialConfiguration(pScrn, TRUE);
1082
1083         TRACE_EXIT();
1084
1085         return TRUE;
1086 }
1087
1088 void
1089 drmmode_adjust_frame(ScrnInfoPtr pScrn, int x, int y, int flags)
1090 {
1091         xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
1092         xf86OutputPtr output = config->output[config->compat_output];
1093         xf86CrtcPtr crtc = output->crtc;
1094
1095         if (!crtc || !crtc->enabled)
1096                 return;
1097
1098         drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation, x, y);
1099 }
1100
1101 void
1102 drmmode_remove_fb(ScrnInfoPtr pScrn)
1103 {
1104         xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
1105         xf86CrtcPtr crtc = NULL;
1106         drmmode_crtc_private_ptr drmmode_crtc;
1107         drmmode_ptr drmmode;
1108
1109         if (config)
1110                 crtc = config->crtc[0];
1111         if (!crtc)
1112                 return;
1113
1114         drmmode_crtc = crtc->driver_private;
1115         drmmode = drmmode_crtc->drmmode;
1116
1117         if (drmmode->fb_id)
1118                 drmModeRmFB(drmmode->fd, drmmode->fb_id);
1119         drmmode->fb_id = 0;
1120 }
1121
1122 /*
1123  * Page Flipping
1124  */
1125
1126 static void
1127 page_flip_handler(int fd, unsigned int sequence, unsigned int tv_sec,
1128                 unsigned int tv_usec, void *user_data)
1129 {
1130         OMAPDRI2SwapComplete(user_data);
1131 }
1132
1133 static drmEventContext event_context = {
1134                 .version = DRM_EVENT_CONTEXT_VERSION,
1135                 .page_flip_handler = page_flip_handler,
1136 };
1137
1138 Bool
1139 drmmode_page_flip(DrawablePtr draw, uint32_t fb_id, void *priv)
1140 {
1141         ScrnInfoPtr scrn = xf86Screens[draw->pScreen->myNum];
1142         xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
1143         drmmode_crtc_private_ptr crtc = config->crtc[0]->driver_private;
1144         drmmode_ptr mode = crtc->drmmode;
1145         int ret, i;
1146
1147         /* if we can flip, we must be fullscreen.. so flip all CRTC's.. */
1148         for (i = 0; i < config->num_crtc; i++) {
1149                 crtc = config->crtc[i]->driver_private;
1150
1151                 if (!config->crtc[i]->enabled)
1152                         continue;
1153
1154                 ret = drmModePageFlip(mode->fd, crtc->mode_crtc->crtc_id,
1155                                 fb_id, DRM_MODE_PAGE_FLIP_EVENT, priv);
1156                 if (ret) {
1157                         xf86DrvMsg(scrn->scrnIndex, X_WARNING,
1158                                         "flip queue failed: %s\n", strerror(errno));
1159                         return FALSE;
1160                 }
1161         }
1162
1163         return TRUE;
1164 }
1165
1166 /*
1167  * Hot Plug Event handling:
1168  */
1169
1170 static void
1171 drmmode_handle_uevents(int fd, void *closure)
1172 {
1173         ScrnInfoPtr pScrn = closure;
1174         OMAPPtr pOMAP = OMAPPTR(pScrn);
1175         drmmode_ptr drmmode = drmmode_from_scrn(pScrn);
1176         struct udev_device *dev;
1177         const char *hotplug;
1178         struct stat s;
1179         dev_t udev_devnum;
1180
1181         dev = udev_monitor_receive_device(drmmode->uevent_monitor);
1182         if (!dev)
1183                 return;
1184
1185         // FIXME - Do we need to keep this code, which Rob originally wrote
1186         // (i.e. up thru the "if" statement)?:
1187
1188         /*
1189          * Check to make sure this event is directed at our
1190          * device (by comparing dev_t values), then make
1191          * sure it's a hotplug event (HOTPLUG=1)
1192          */
1193         udev_devnum = udev_device_get_devnum(dev);
1194         fstat(pOMAP->drmFD, &s);
1195
1196         hotplug = udev_device_get_property_value(dev, "HOTPLUG");
1197
1198         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "hotplug=%s, match=%d\n", hotplug,
1199                         memcmp(&s.st_rdev, &udev_devnum, sizeof (dev_t)));
1200
1201         if (memcmp(&s.st_rdev, &udev_devnum, sizeof (dev_t)) == 0 &&
1202                         hotplug && atoi(hotplug) == 1) {
1203                 RRGetInfo(screenInfo.screens[pScrn->scrnIndex], TRUE);
1204         }
1205         udev_device_unref(dev);
1206 }
1207
1208 static void
1209 drmmode_uevent_init(ScrnInfoPtr pScrn)
1210 {
1211         drmmode_ptr drmmode = drmmode_from_scrn(pScrn);
1212         struct udev *u;
1213         struct udev_monitor *mon;
1214
1215         TRACE_ENTER();
1216
1217         u = udev_new();
1218         if (!u)
1219                 return;
1220         mon = udev_monitor_new_from_netlink(u, "udev");
1221         if (!mon) {
1222                 udev_unref(u);
1223                 return;
1224         }
1225
1226         if (udev_monitor_filter_add_match_subsystem_devtype(mon,
1227                         "drm",
1228                         "drm_minor") < 0 ||
1229                         udev_monitor_enable_receiving(mon) < 0) {
1230                 udev_monitor_unref(mon);
1231                 udev_unref(u);
1232                 return;
1233         }
1234
1235         drmmode->uevent_handler =
1236                         xf86AddGeneralHandler(udev_monitor_get_fd(mon),
1237                                         drmmode_handle_uevents, pScrn);
1238
1239         drmmode->uevent_monitor = mon;
1240
1241         TRACE_EXIT();
1242 }
1243
1244 static void
1245 drmmode_uevent_fini(ScrnInfoPtr pScrn)
1246 {
1247         drmmode_ptr drmmode = drmmode_from_scrn(pScrn);
1248
1249         TRACE_ENTER();
1250
1251         if (drmmode->uevent_handler) {
1252                 struct udev *u = udev_monitor_get_udev(drmmode->uevent_monitor);
1253                 xf86RemoveGeneralHandler(drmmode->uevent_handler);
1254
1255                 udev_monitor_unref(drmmode->uevent_monitor);
1256                 udev_unref(u);
1257         }
1258
1259         TRACE_EXIT();
1260 }
1261
1262 static void
1263 drmmode_wakeup_handler(pointer data, int err, pointer p)
1264 {
1265         ScrnInfoPtr scrn = data;
1266         drmmode_ptr drmmode = drmmode_from_scrn(scrn);
1267         fd_set *read_mask = p;
1268
1269         if (scrn == NULL || err < 0)
1270                 return;
1271
1272         if (FD_ISSET(drmmode->fd, read_mask))
1273                 drmHandleEvent(drmmode->fd, &event_context);
1274 }
1275
1276 void
1277 drmmode_screen_init(ScrnInfoPtr pScrn)
1278 {
1279         drmmode_ptr drmmode = drmmode_from_scrn(pScrn);
1280
1281         drmmode_uevent_init(pScrn);
1282
1283         AddGeneralSocket(drmmode->fd);
1284
1285         /* Register a wakeup handler to get informed on DRM events */
1286         RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
1287                         drmmode_wakeup_handler, pScrn);
1288 }
1289
1290 void
1291 drmmode_screen_fini(ScrnInfoPtr pScrn)
1292 {
1293         drmmode_uevent_fini(pScrn);
1294 }