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