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