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 #include <sys/ioctl.h>
34 #include <sys/mman.h>
35 #include "xf86str.h"
36 #include "X11/Xatom.h"
37 #include "micmap.h"
38 #include "xf86cmap.h"
39 #include "xf86DDC.h"
40 
41 #include <xf86drm.h>
42 #include "xf86Crtc.h"
43 #include "via_driver.h"
44 
45 /* DPMS */
46 #ifdef HAVE_XEXTPROTO_71
47 #include <X11/extensions/dpmsconst.h>
48 #else
49 #define DPMS_SERVER
50 #include <X11/extensions/dpms.h>
51 #endif
52 
53 xf86CrtcPtr
window_belongs_to_crtc(ScrnInfoPtr pScrn,int x,int y,int w,int h)54 window_belongs_to_crtc(ScrnInfoPtr pScrn, int x, int y, int w, int h)
55 {
56     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
57     int largest = 0, area = 0, i;
58     BoxRec crtc_area, overlap;
59     xf86CrtcPtr best = NULL;
60 
61     for (i = 0; i < xf86_config->num_crtc; i++) {
62         xf86CrtcPtr crtc = xf86_config->crtc[i];
63 
64         if (crtc->enabled) {
65             crtc_area.x1 = crtc->x;
66             crtc_area.x2 = crtc->x + xf86ModeWidth(&crtc->mode, crtc->rotation);
67             crtc_area.y1 = crtc->y;
68             crtc_area.y2 = crtc->y + xf86ModeHeight(&crtc->mode, crtc->rotation);
69             overlap.x1 = crtc_area.x1 > x ? crtc_area.x1 : x;
70             overlap.x2 = crtc_area.x2 < x + w ? crtc_area.x2 : x + w;
71             overlap.y1 = crtc_area.y1 > y ? crtc_area.y1 : y;
72             overlap.y2 = crtc_area.y2 < y ? crtc_area.y2 : y + h;
73 
74             if (overlap.x1 >= overlap.x2 || overlap.y1 >= overlap.y2)
75                 overlap.x1 = overlap.x2 = overlap.y1 = overlap.y2 = 0;
76 
77             area = (overlap.x2 - overlap.x1) * (overlap.y2 - overlap.y1);
78             if (area > largest) {
79                 area = largest;
80                 best = crtc;
81             }
82         }
83     }
84     return best;
85 }
86 
87 static void
drmmode_ConvertFromKMode(ScrnInfoPtr pScrn,drmModeModeInfo * kmode,DisplayModePtr mode)88 drmmode_ConvertFromKMode(ScrnInfoPtr pScrn, drmModeModeInfo *kmode,
89                             DisplayModePtr mode)
90 {
91     memset(mode, 0, sizeof(DisplayModeRec));
92     mode->status = MODE_OK;
93 
94     mode->Clock = kmode->clock;
95 
96     mode->HDisplay = kmode->hdisplay;
97     mode->HSyncStart = kmode->hsync_start;
98     mode->HSyncEnd = kmode->hsync_end;
99     mode->HTotal = kmode->htotal;
100     mode->HSkew = kmode->hskew;
101 
102     mode->VDisplay = kmode->vdisplay;
103     mode->VSyncStart = kmode->vsync_start;
104     mode->VSyncEnd = kmode->vsync_end;
105     mode->VTotal = kmode->vtotal;
106     mode->VScan = kmode->vscan;
107 
108     mode->Flags = kmode->flags; //& FLAG_BITS;
109     mode->name = strdup(kmode->name);
110 
111     if (kmode->type & DRM_MODE_TYPE_DRIVER)
112         mode->type = M_T_DRIVER;
113     if (kmode->type & DRM_MODE_TYPE_PREFERRED)
114         mode->type |= M_T_PREFERRED;
115     xf86SetModeCrtc(mode, pScrn->adjustFlags);
116 }
117 
118 static void
drmmode_ConvertToKMode(ScrnInfoPtr pScrn,drmModeModeInfo * kmode,DisplayModePtr mode)119 drmmode_ConvertToKMode(ScrnInfoPtr pScrn, drmModeModeInfo *kmode,
120                         DisplayModePtr mode)
121 {
122     memset(kmode, 0, sizeof(*kmode));
123 
124     kmode->clock = mode->Clock;
125     kmode->hdisplay = mode->HDisplay;
126     kmode->hsync_start = mode->HSyncStart;
127     kmode->hsync_end = mode->HSyncEnd;
128     kmode->htotal = mode->HTotal;
129     kmode->hskew = mode->HSkew;
130 
131     kmode->vdisplay = mode->VDisplay;
132     kmode->vsync_start = mode->VSyncStart;
133     kmode->vsync_end = mode->VSyncEnd;
134     kmode->vtotal = mode->VTotal;
135     kmode->vscan = mode->VScan;
136 
137     kmode->flags = mode->Flags; //& FLAG_BITS;
138     if (mode->name)
139         strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN);
140     kmode->name[DRM_DISPLAY_MODE_LEN-1] = 0;
141 }
142 
143 static void
drmmode_crtc_dpms(xf86CrtcPtr crtc,int mode)144 drmmode_crtc_dpms(xf86CrtcPtr crtc, int mode)
145 {
146 #if 0
147     xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
148     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
149     drmmode_ptr drmmode = drmmode_crtc->drmmode;
150 
151     /* bonghits in the randr 1.2 - uses dpms to disable crtc - bad buzz */
152     if (mode == DPMSModeOff) {
153         drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
154 	                    0, 0, 0, NULL, 0, NULL);
155     }
156 #endif
157 }
158 
159 static Bool
drmmode_set_mode_major(xf86CrtcPtr crtc,DisplayModePtr mode,Rotation rotation,int x,int y)160 drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
161                         Rotation rotation, int x, int y)
162 {
163     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
164     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
165     drmmode_ptr drmmode = drmmode_crtc->drmmode;
166     ScrnInfoPtr pScrn = crtc->scrn;
167     int output_count = 0, ret, i;
168     uint32_t *output_ids = NULL;
169     drmModeModeInfo kmode;
170 
171     if (!mode || !xf86CrtcRotate(crtc))
172         return FALSE;
173 
174     output_ids = calloc(sizeof(uint32_t), xf86_config->num_output);
175     if (!output_ids)
176         return FALSE;
177 
178     for (i = 0; i < xf86_config->num_output; i++) {
179         xf86OutputPtr output = xf86_config->output[i];
180         drmmode_output_private_ptr drmmode_output;
181 
182         if (output->crtc != crtc)
183             continue;
184 
185         drmmode_output = output->driver_private;
186         output_ids[output_count] = drmmode_output->mode_output->connector_id;
187         output_count++;
188     }
189 
190     drmmode_ConvertToKMode(crtc->scrn, &kmode, mode);
191 
192     if (drmmode->fb_id == 0) {
193         ret = drmModeAddFB(drmmode->fd, pScrn->virtualX, pScrn->virtualY,
194                             pScrn->depth, pScrn->bitsPerPixel,
195                             drmmode->front_bo->pitch,
196                             drmmode->front_bo->handle,
197                             &drmmode->fb_id);
198         if (ret < 0) {
199             ErrorF("failed to add fb %d\n", ret);
200             goto done;
201         }
202     }
203 
204     ret = drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
205                          drmmode->fb_id, x, y, output_ids, output_count, &kmode);
206     if (ret) {
207         xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, "failed to set mode: %s",
208                    strerror(-ret));
209         goto done;
210     }
211 
212     if (crtc->scrn->pScreen)
213         xf86CrtcSetScreenSubpixelOrder(crtc->scrn->pScreen);
214 
215     /* go through all the outputs and force DPMS them back on? */
216     for (i = 0; i < xf86_config->num_output; i++) {
217         xf86OutputPtr output = xf86_config->output[i];
218 
219         if (output->crtc != crtc)
220             continue;
221 
222         output->funcs->dpms(output, DPMSModeOn);
223     }
224 
225 #if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,7,0,0,0)
226     crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
227                            crtc->gamma_blue, crtc->gamma_size);
228 #endif
229 
230     if (pScrn->pScreen && drmmode->hwcursor)
231         xf86_reload_cursors(pScrn->pScreen);
232 done:
233     free(output_ids);
234     return (ret < 0 ? FALSE : TRUE);
235 }
236 
237 static void
drmmode_set_cursor_colors(xf86CrtcPtr crtc,int bg,int fg)238 drmmode_set_cursor_colors (xf86CrtcPtr crtc, int bg, int fg)
239 {
240 }
241 
242 static void
drmmode_set_cursor_position(xf86CrtcPtr crtc,int x,int y)243 drmmode_set_cursor_position (xf86CrtcPtr crtc, int x, int y)
244 {
245     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
246     drmmode_ptr drmmode = drmmode_crtc->drmmode;
247 
248     drmModeMoveCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, x, y);
249 }
250 
251 static void
drmmode_hide_cursor(xf86CrtcPtr crtc)252 drmmode_hide_cursor (xf86CrtcPtr crtc)
253 {
254     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
255     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
256     xf86CursorInfoPtr cursor_info = xf86_config->cursor_info;
257     drmmode_ptr drmmode = drmmode_crtc->drmmode;
258 
259     drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, 0,
260                         cursor_info->MaxWidth, cursor_info->MaxHeight);
261 }
262 
263 static void
drmmode_show_cursor(xf86CrtcPtr crtc)264 drmmode_show_cursor (xf86CrtcPtr crtc)
265 {
266     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
267     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
268     xf86CursorInfoPtr cursor_info = xf86_config->cursor_info;
269     uint32_t handle = drmmode_crtc->cursor_bo->handle;
270     drmmode_ptr drmmode = drmmode_crtc->drmmode;
271 
272     drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, handle,
273                         cursor_info->MaxWidth, cursor_info->MaxHeight);
274 }
275 
276 static void
drmmode_load_cursor_argb(xf86CrtcPtr crtc,CARD32 * image)277 drmmode_load_cursor_argb (xf86CrtcPtr crtc, CARD32 *image)
278 {
279     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
280     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
281     xf86CursorInfoPtr cursor_info = xf86_config->cursor_info;
282     uint32_t handle = drmmode_crtc->cursor_bo->handle, *ptr;
283 
284     /* cursor should be mapped already */
285     ptr = drm_bo_map(crtc->scrn, drmmode_crtc->cursor_bo);
286     memset(ptr, 0x00, drmmode_crtc->cursor_bo->size);
287     memcpy(ptr, image, drmmode_crtc->cursor_bo->size);
288     drm_bo_unmap(crtc->scrn, drmmode_crtc->cursor_bo);
289 
290     if (drmModeSetCursor(drmmode_crtc->drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
291                             handle, cursor_info->MaxWidth, cursor_info->MaxHeight)) {
292         drmmode_ptr drmmode = drmmode_crtc->drmmode;
293 
294         cursor_info->MaxWidth = cursor_info->MaxHeight = 0;
295         drmmode->hwcursor = FALSE;
296     }
297 }
298 
299 static void
drmmode_crtc_gamma_set(xf86CrtcPtr crtc,uint16_t * red,uint16_t * green,uint16_t * blue,int size)300 drmmode_crtc_gamma_set(xf86CrtcPtr crtc, uint16_t *red, uint16_t *green,
301                       uint16_t *blue, int size)
302 {
303     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
304     drmmode_ptr drmmode = drmmode_crtc->drmmode;
305 
306     drmModeCrtcSetGamma(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
307                         size, red, green, blue);
308 }
309 
310 static const xf86CrtcFuncsRec drmmode_crtc_funcs = {
311     .dpms                   = drmmode_crtc_dpms,
312     .set_mode_major         = drmmode_set_mode_major,
313     .set_cursor_colors      = drmmode_set_cursor_colors,
314     .set_cursor_position    = drmmode_set_cursor_position,
315     .show_cursor            = drmmode_show_cursor,
316     .hide_cursor            = drmmode_hide_cursor,
317     .load_cursor_argb       = drmmode_load_cursor_argb,
318     .gamma_set              = drmmode_crtc_gamma_set,
319     .destroy                = NULL,
320 };
321 
322 static void
drmmode_crtc_init(ScrnInfoPtr pScrn,drmmode_ptr drmmode,int num)323 drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num)
324 {
325     drmmode_crtc_private_ptr drmmode_crtc;
326     xf86CrtcPtr crtc;
327 
328     crtc = xf86CrtcCreate(pScrn, &drmmode_crtc_funcs);
329     if (crtc == NULL)
330         return;
331 
332     drmmode_crtc = xnfcalloc(sizeof(drmmode_crtc_private_rec), 1);
333     drmmode_crtc->mode_crtc = drmModeGetCrtc(drmmode->fd, drmmode->mode_res->crtcs[num]);
334     drmmode_crtc->drmmode = drmmode;
335     crtc->driver_private = drmmode_crtc;
336 }
337 
338 /*
339  * Handle KMS xf86Outputs
340  */
341 static Bool
drmmode_property_ignore(drmModePropertyPtr prop)342 drmmode_property_ignore(drmModePropertyPtr prop)
343 {
344     if (!prop)
345         return TRUE;
346 
347     /* ignore blob prop */
348     if (prop->flags & DRM_MODE_PROP_BLOB)
349         return TRUE;
350 
351     /* ignore standard property */
352     if (!strcmp(prop->name, "EDID") ||
353         !strcmp(prop->name, "DPMS"))
354         return TRUE;
355 
356     return FALSE;
357 }
358 
359 static void
drmmode_output_dpms(xf86OutputPtr output,int mode)360 drmmode_output_dpms(xf86OutputPtr output, int mode)
361 {
362 	drmmode_output_private_ptr drmmode_output = output->driver_private;
363 	drmModeConnectorPtr koutput = drmmode_output->mode_output;
364 	drmmode_ptr drmmode = drmmode_output->drmmode;
365 
366 	drmModeConnectorSetProperty(drmmode->fd, koutput->connector_id,
367 	                            drmmode_output->dpms_enum_id, mode);
368 	return;
369 }
370 
371 static void
drmmode_output_create_resources(xf86OutputPtr output)372 drmmode_output_create_resources(xf86OutputPtr output)
373 {
374     drmmode_output_private_ptr drmmode_output = output->driver_private;
375     drmModeConnectorPtr mode_output = drmmode_output->mode_output;
376     drmmode_ptr drmmode = drmmode_output->drmmode;
377     drmModePropertyPtr drmmode_prop;
378     int i, j, err;
379 
380     drmmode_output->props = calloc(mode_output->count_props, sizeof(drmmode_prop_rec));
381     if (!drmmode_output->props)
382         return;
383 
384     drmmode_output->num_props = 0;
385     for (i = 0, j = 0; i < mode_output->count_props; i++) {
386 	    drmmode_prop = drmModeGetProperty(drmmode->fd, mode_output->props[i]);
387 
388         if (drmmode_property_ignore(drmmode_prop)) {
389 	        drmModeFreeProperty(drmmode_prop);
390             continue;
391         }
392         drmmode_output->props[j].mode_prop = drmmode_prop;
393         drmmode_output->props[j].value = mode_output->prop_values[i];
394         drmmode_output->num_props++;
395         j++;
396     }
397 
398     for (i = 0; i < drmmode_output->num_props; i++) {
399         drmmode_prop_ptr p = &drmmode_output->props[i];
400         drmmode_prop = p->mode_prop;
401 
402         if (drmmode_prop->flags & DRM_MODE_PROP_RANGE) {
403             INT32 range[2];
404             INT32 value = p->value;
405 
406             p->num_atoms = 1;
407             p->atoms = calloc(p->num_atoms, sizeof(Atom));
408             if (!p->atoms)
409                 continue;
410             p->atoms[0] = MakeAtom(drmmode_prop->name, strlen(drmmode_prop->name), TRUE);
411             range[0] = drmmode_prop->values[0];
412             range[1] = drmmode_prop->values[1];
413             err = RRConfigureOutputProperty(output->randr_output, p->atoms[0],
414                                             FALSE, TRUE,
415                                             drmmode_prop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE,
416                                             2, range);
417             if (err != 0) {
418                 xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
419                             "RRConfigureOutputProperty error, %d\n", err);
420             }
421             err = RRChangeOutputProperty(output->randr_output, p->atoms[0],
422                                         XA_INTEGER, 32, PropModeReplace, 1,
423                                         &value, FALSE, TRUE);
424             if (err != 0) {
425                 xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
426                             "RRChangeOutputProperty error, %d\n", err);
427             }
428 
429         } else if (drmmode_prop->flags & DRM_MODE_PROP_ENUM) {
430             p->num_atoms = drmmode_prop->count_enums + 1;
431             p->atoms = calloc(p->num_atoms, sizeof(Atom));
432             if (!p->atoms)
433                 continue;
434 
435             p->atoms[0] = MakeAtom(drmmode_prop->name, strlen(drmmode_prop->name), TRUE);
436             for (j = 1; j <= drmmode_prop->count_enums; j++) {
437                 struct drm_mode_property_enum *e = &drmmode_prop->enums[j-1];
438 
439                 p->atoms[j] = MakeAtom(e->name, strlen(e->name), TRUE);
440             }
441             err = RRConfigureOutputProperty(output->randr_output, p->atoms[0],
442                                             FALSE, FALSE,
443                                             drmmode_prop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE,
444                                             p->num_atoms - 1, (INT32 *)&p->atoms[1]);
445             if (err != 0) {
446                 xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
447                             "RRConfigureOutputProperty error, %d\n", err);
448             }
449 
450             for (j = 0; j < drmmode_prop->count_enums; j++)
451                 if (drmmode_prop->enums[j].value == p->value)
452                     break;
453 
454             /* there's always a matching value */
455             err = RRChangeOutputProperty(output->randr_output, p->atoms[0],
456                                         XA_ATOM, 32, PropModeReplace, 1,
457                                         &p->atoms[j+1], FALSE, TRUE);
458             if (err != 0) {
459                 xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
460                             "RRChangeOutputProperty error, %d\n", err);
461             }
462         }
463     }
464 }
465 
466 static Bool
drmmode_output_set_property(xf86OutputPtr output,Atom property,RRPropertyValuePtr value)467 drmmode_output_set_property(xf86OutputPtr output, Atom property,
468                             RRPropertyValuePtr value)
469 {
470     drmmode_output_private_ptr drmmode_output = output->driver_private;
471     drmmode_ptr drmmode = drmmode_output->drmmode;
472     int i;
473 
474     for (i = 0; i < drmmode_output->num_props; i++) {
475         drmmode_prop_ptr p = &drmmode_output->props[i];
476 
477         if (p->atoms[0] != property)
478             continue;
479 
480         if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
481             uint32_t val;
482 
483             if (value->type != XA_INTEGER || value->format != 32 ||
484                 value->size != 1)
485                 return FALSE;
486             val = *(uint32_t *)value->data;
487 
488             drmModeConnectorSetProperty(drmmode->fd, drmmode_output->output_id,
489                                         p->mode_prop->prop_id, (uint64_t)val);
490             return TRUE;
491         } else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
492             const char *name;
493             Atom atom;
494             int	j;
495 
496             if (value->type != XA_ATOM || value->format != 32 || value->size != 1)
497                 return FALSE;
498             memcpy(&atom, value->data, 4);
499             name = NameForAtom(atom);
500 
501             /* search for matching name string, then set its value down */
502             for (j = 0; j < p->mode_prop->count_enums; j++) {
503                 if (!strcmp(p->mode_prop->enums[j].name, name)) {
504                     drmModeConnectorSetProperty(drmmode->fd, drmmode_output->output_id,
505                     p->mode_prop->prop_id, p->mode_prop->enums[j].value);
506                     return TRUE;
507                 }
508             }
509         }
510     }
511     return TRUE;
512 }
513 
514 static Bool
drmmode_output_get_property(xf86OutputPtr output,Atom property)515 drmmode_output_get_property(xf86OutputPtr output, Atom property)
516 {
517     return TRUE;
518 }
519 
520 static xf86OutputStatus
drmmode_output_detect(xf86OutputPtr output)521 drmmode_output_detect(xf86OutputPtr output)
522 {
523     /* go to the hw and retrieve a new output struct */
524     drmmode_output_private_ptr drmmode_output = output->driver_private;
525     drmmode_ptr drmmode = drmmode_output->drmmode;
526     xf86OutputStatus status;
527 
528     drmModeFreeConnector(drmmode_output->mode_output);
529     drmmode_output->mode_output = drmModeGetConnector(drmmode->fd, drmmode_output->output_id);
530 
531     switch (drmmode_output->mode_output->connection) {
532     case DRM_MODE_CONNECTED:
533         status = XF86OutputStatusConnected;
534         break;
535     case DRM_MODE_DISCONNECTED:
536         status = XF86OutputStatusDisconnected;
537         break;
538     default:
539     case DRM_MODE_UNKNOWNCONNECTION:
540         status = XF86OutputStatusUnknown;
541         break;
542     }
543     return status;
544 }
545 
546 static Bool
drmmode_output_mode_valid(xf86OutputPtr output,DisplayModePtr pModes)547 drmmode_output_mode_valid(xf86OutputPtr output, DisplayModePtr pModes)
548 {
549     return MODE_OK;
550 }
551 
552 static DisplayModePtr
drmmode_output_get_modes(xf86OutputPtr output)553 drmmode_output_get_modes(xf86OutputPtr output)
554 {
555     drmmode_output_private_ptr drmmode_output = output->driver_private;
556     drmModeConnectorPtr koutput = drmmode_output->mode_output;
557     drmmode_ptr drmmode = drmmode_output->drmmode;
558     DisplayModePtr Modes = NULL, Mode;
559     drmModePropertyPtr props;
560     xf86MonPtr mon = NULL;
561     int i;
562 
563     /* look for an EDID property */
564     for (i = 0; i < koutput->count_props; i++) {
565         props = drmModeGetProperty(drmmode->fd, koutput->props[i]);
566         if (props && (props->flags & DRM_MODE_PROP_BLOB)) {
567             if (!strcmp(props->name, "EDID")) {
568                 if (drmmode_output->edid_blob)
569                     drmModeFreePropertyBlob(drmmode_output->edid_blob);
570                 drmmode_output->edid_blob = drmModeGetPropertyBlob(drmmode->fd, koutput->prop_values[i]);
571             }
572             drmModeFreeProperty(props);
573         }
574     }
575 
576     if (drmmode_output->edid_blob) {
577         mon = xf86InterpretEDID(output->scrn->scrnIndex,
578                                 drmmode_output->edid_blob->data);
579         if (mon && drmmode_output->edid_blob->length > 128)
580             mon->flags |= MONITOR_EDID_COMPLETE_RAWDATA;
581     }
582     xf86OutputSetEDID(output, mon);
583 
584     /* modes should already be available */
585     for (i = 0; i < koutput->count_modes; i++) {
586         Mode = xnfalloc(sizeof(DisplayModeRec));
587 
588         drmmode_ConvertFromKMode(output->scrn, &koutput->modes[i], Mode);
589         Modes = xf86ModesAdd(Modes, Mode);
590 
591     }
592     return Modes;
593 }
594 
595 static void
drmmode_output_destroy(xf86OutputPtr output)596 drmmode_output_destroy(xf86OutputPtr output)
597 {
598     drmmode_output_private_ptr drmmode_output = output->driver_private;
599     int i;
600 
601     if (drmmode_output->edid_blob)
602         drmModeFreePropertyBlob(drmmode_output->edid_blob);
603 
604     for (i = 0; i < drmmode_output->num_props; i++) {
605         drmModeFreeProperty(drmmode_output->props[i].mode_prop);
606         free(drmmode_output->props[i].atoms);
607     }
608 
609     for (i = 0; i < drmmode_output->mode_output->count_encoders; i++) {
610         drmModeFreeEncoder(drmmode_output->mode_encoders[i]);
611         free(drmmode_output->mode_encoders);
612     }
613     free(drmmode_output->props);
614     drmModeFreeConnector(drmmode_output->mode_output);
615     free(drmmode_output);
616     output->driver_private = NULL;
617 }
618 
619 static const xf86OutputFuncsRec drmmode_output_funcs = {
620     .dpms               = drmmode_output_dpms,
621     .create_resources   = drmmode_output_create_resources,
622 #ifdef RANDR_12_INTERFACE
623     .set_property       = drmmode_output_set_property,
624     .get_property       = drmmode_output_get_property,
625 #endif
626     .detect             = drmmode_output_detect,
627     .mode_valid         = drmmode_output_mode_valid,
628     .get_modes          = drmmode_output_get_modes,
629     .destroy            = drmmode_output_destroy
630 };
631 
632 static int subpixel_conv_table[7] = {
633     0,
634     SubPixelUnknown,
635     SubPixelHorizontalRGB,
636     SubPixelHorizontalBGR,
637     SubPixelVerticalRGB,
638     SubPixelVerticalBGR,
639     SubPixelNone
640 };
641 
642 const char *output_names[] = {
643     "None",
644     "VGA",
645     "DVI",
646     "DVI",
647     "DVI",
648     "Composite",
649     "S-video",
650     "LVDS",
651     "CTV",
652     "DIN",
653     "DisplayPort",
654     "HDMI",
655     "HDMI",
656     "TV",
657     "eDP"
658 };
659 
660 static void
drmmode_output_init(ScrnInfoPtr pScrn,drmmode_ptr drmmode,int num)661 drmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num)
662 {
663     xf86OutputPtr output;
664     drmModeConnectorPtr koutput;
665     drmModeEncoderPtr *kencoders = NULL;
666     drmmode_output_private_ptr drmmode_output;
667     drmModePropertyPtr props;
668     char name[32];
669     int i;
670 
671     koutput = drmModeGetConnector(drmmode->fd, drmmode->mode_res->connectors[num]);
672     if (!koutput)
673         return;
674 
675     kencoders = calloc(sizeof(drmModeEncoderPtr), koutput->count_encoders);
676     if (!kencoders) {
677         goto out_free_encoders;
678     }
679 
680     for (i = 0; i < koutput->count_encoders; i++) {
681         kencoders[i] = drmModeGetEncoder(drmmode->fd, koutput->encoders[i]);
682         if (!kencoders[i]) {
683             goto out_free_encoders;
684         }
685     }
686 
687     /* need to do smart conversion here for compat with non-kms ATI driver */
688     snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], koutput->connector_type_id - 1);
689 
690     output = xf86OutputCreate (pScrn, &drmmode_output_funcs, name);
691     if (!output) {
692         goto out_free_encoders;
693     }
694 
695     drmmode_output = calloc(sizeof(drmmode_output_private_rec), 1);
696     if (!drmmode_output) {
697         xf86OutputDestroy(output);
698         goto out_free_encoders;
699     }
700 
701     drmmode_output->output_id = drmmode->mode_res->connectors[num];
702     drmmode_output->mode_output = koutput;
703     drmmode_output->mode_encoders = kencoders;
704     drmmode_output->drmmode = drmmode;
705     output->mm_width = koutput->mmWidth;
706     output->mm_height = koutput->mmHeight;
707 
708     output->subpixel_order = subpixel_conv_table[koutput->subpixel];
709     output->interlaceAllowed = TRUE;
710     output->doubleScanAllowed = TRUE;
711     output->driver_private = drmmode_output;
712 
713     output->possible_crtcs = 0x7f;
714     for (i = 0; i < koutput->count_encoders; i++)
715         output->possible_crtcs &= kencoders[i]->possible_crtcs;
716 
717     /* work out the possible clones later */
718     output->possible_clones = 0;
719 
720     for (i = 0; i < koutput->count_props; i++) {
721         props = drmModeGetProperty(drmmode->fd, koutput->props[i]);
722         if (props && (props->flags & DRM_MODE_PROP_ENUM)) {
723             if (!strcmp(props->name, "DPMS")) {
724                 drmmode_output->dpms_enum_id = koutput->props[i];
725                 drmModeFreeProperty(props);
726                 break;
727             }
728             drmModeFreeProperty(props);
729         }
730     }
731 
732     return;
733 out_free_encoders:
734     if (kencoders){
735         for (i = 0; i < koutput->count_encoders; i++)
736             drmModeFreeEncoder(kencoders[i]);
737         free(kencoders);
738     }
739     drmModeFreeConnector(koutput);
740 }
741 
find_clones(ScrnInfoPtr scrn,xf86OutputPtr output)742 uint32_t find_clones(ScrnInfoPtr scrn, xf86OutputPtr output)
743 {
744     drmmode_output_private_ptr drmmode_output = output->driver_private, clone_drmout;
745     xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
746     xf86OutputPtr clone_output;
747     int index_mask = 0, i;
748 
749     if (drmmode_output->enc_clone_mask == 0)
750         return index_mask;
751 
752     for (i = 0; i < xf86_config->num_output; i++) {
753         clone_output = xf86_config->output[i];
754         clone_drmout = clone_output->driver_private;
755         if (output == clone_output)
756             continue;
757         if (clone_drmout->enc_mask == 0)
758             continue;
759         if (drmmode_output->enc_clone_mask == clone_drmout->enc_mask)
760             index_mask |= (1 << i);
761     }
762     return index_mask;
763 }
764 
765 static void
drmmode_clones_init(ScrnInfoPtr scrn,drmmode_ptr drmmode)766 drmmode_clones_init(ScrnInfoPtr scrn, drmmode_ptr drmmode)
767 {
768     xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
769     int i, j;
770 
771     for (i = 0; i < xf86_config->num_output; i++) {
772         xf86OutputPtr output = xf86_config->output[i];
773         drmmode_output_private_ptr drmmode_output;
774 
775         drmmode_output = output->driver_private;
776         drmmode_output->enc_clone_mask = 0xff;
777         /* and all the possible encoder clones for this output together */
778         for (j = 0; j < drmmode_output->mode_output->count_encoders; j++) {
779             int k;
780 
781             for (k = 0; k < drmmode->mode_res->count_encoders; k++) {
782                 if (drmmode->mode_res->encoders[k] == drmmode_output->mode_encoders[j]->encoder_id)
783                     drmmode_output->enc_mask |= (1 << k);
784             }
785 
786             drmmode_output->enc_clone_mask &= drmmode_output->mode_encoders[j]->possible_clones;
787         }
788     }
789 
790     for (i = 0; i < xf86_config->num_output; i++) {
791         xf86OutputPtr output = xf86_config->output[i];
792         output->possible_clones = find_clones(scrn, output);
793     }
794 }
795 
KMSCrtcInit(ScrnInfoPtr pScrn,drmmode_ptr drmmode)796 Bool KMSCrtcInit(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
797 {
798     int i;
799 
800     DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, "KMSCrtcInit\n"));
801 
802     drmmode->scrn = pScrn;
803     drmmode->mode_res = drmModeGetResources(drmmode->fd);
804     if (!drmmode->mode_res)
805         return FALSE;
806 
807     xf86CrtcSetSizeRange(pScrn, 320, 200, drmmode->mode_res->max_width, drmmode->mode_res->max_height);
808     for (i = 0; i < drmmode->mode_res->count_crtcs; i++)
809         if (!xf86IsEntityShared(pScrn->entityList[0]) || pScrn->confScreen->device->screen == i)
810             drmmode_crtc_init(pScrn, drmmode, i);
811 
812     for (i = 0; i < drmmode->mode_res->count_connectors; i++)
813         drmmode_output_init(pScrn, drmmode, i);
814 
815     /* workout clones */
816     drmmode_clones_init(pScrn, drmmode);
817     return TRUE;
818 }
819 
820 #ifdef HAVE_UDEV
821 static void
drmmode_handle_uevents(int fd,void * closure)822 drmmode_handle_uevents(int fd, void *closure)
823 {
824     drmmode_ptr drmmode = closure;
825     ScrnInfoPtr scrn = drmmode->scrn;
826     struct udev_device *dev;
827 
828     dev = udev_monitor_receive_device(drmmode->uevent_monitor);
829     if (!dev)
830         return;
831 
832     RRGetInfo(xf86ScrnToScreen(scrn), TRUE);
833     udev_device_unref(dev);
834 }
835 #endif
836 
drmmode_uevent_init(ScrnInfoPtr scrn,drmmode_ptr drmmode)837 void drmmode_uevent_init(ScrnInfoPtr scrn, drmmode_ptr drmmode)
838 {
839 #ifdef HAVE_UDEV
840     struct udev_monitor *mon;
841     struct udev *u;
842 
843     u = udev_new();
844     if (!u)
845         return;
846 
847     mon = udev_monitor_new_from_netlink(u, "udev");
848     if (!mon) {
849         udev_unref(u);
850         return;
851     }
852 
853     if (udev_monitor_filter_add_match_subsystem_devtype(mon, "drm", "drm_minor") < 0 ||
854             udev_monitor_enable_receiving(mon) < 0) {
855         udev_monitor_unref(mon);
856         udev_unref(u);
857         return;
858     }
859 
860     drmmode->uevent_handler = xf86AddGeneralHandler(udev_monitor_get_fd(mon),
861                                                     drmmode_handle_uevents,
862                                                     drmmode);
863     drmmode->uevent_monitor = mon;
864 #endif
865 }
866 
drmmode_uevent_fini(ScrnInfoPtr scrn,drmmode_ptr drmmode)867 void drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr drmmode)
868 {
869 #ifdef HAVE_UDEV
870     if (drmmode->uevent_handler) {
871         struct udev *u = udev_monitor_get_udev(drmmode->uevent_monitor);
872 
873         xf86RemoveGeneralHandler(drmmode->uevent_handler);
874 
875         udev_monitor_unref(drmmode->uevent_monitor);
876         udev_unref(u);
877     }
878 #endif
879 }
880