1 /* 2 // Copyright(c)2014 IntelCorporation 3 // 4 // LicensedundertheApacheLicense,Version2.0(the"License"); 5 // youmaynotusethisfileexceptincompliancewiththeLicense. 6 // YoumayobtainacopyoftheLicenseat 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unlessrequiredbyapplicablelaworagreedtoinwriting,software 11 // distributedundertheLicenseisdistributedonan"ASIS"BASIS, 12 // WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied. 13 // SeetheLicenseforthespecificlanguagegoverningpermissionsand 14 // limitationsundertheLicense. 15 */ 16 #include <fcntl.h> 17 #include <errno.h> 18 #include <common/utils/HwcTrace.h> 19 #include <IDisplayDevice.h> 20 #include <DrmConfig.h> 21 #include <common/base/Drm.h> 22 #include <Hwcomposer.h> 23 24 namespace android { 25 namespace intel { 26 27 Drm::Drm() 28 : mDrmFd(0), 29 mLock(), 30 mInitialized(false) 31 { 32 memset(&mOutputs, 0, sizeof(mOutputs)); 33 } 34 35 Drm::~Drm() 36 { 37 WARN_IF_NOT_DEINIT(); 38 } 39 40 bool Drm::initialize() 41 { 42 if (mInitialized) { 43 WLOGTRACE("Drm object has been initialized"); 44 return true; 45 } 46 47 const char *path = DrmConfig::getDrmPath(); 48 mDrmFd = open(path, O_RDWR, 0); 49 if (mDrmFd < 0) { 50 ELOGTRACE("failed to open Drm, error: %s", strerror(errno)); 51 return false; 52 } 53 DLOGTRACE("mDrmFd = %d", mDrmFd); 54 55 memset(&mOutputs, 0, sizeof(mOutputs)); 56 mInitialized = true; 57 return true; 58 } 59 60 void Drm::deinitialize() 61 { 62 for (int i = 0; i < OUTPUT_MAX; i++) { 63 resetOutput(i); 64 } 65 66 if (mDrmFd) { 67 close(mDrmFd); 68 mDrmFd = 0; 69 } 70 mInitialized = false; 71 } 72 73 bool Drm::detect(int device) 74 { 75 RETURN_FALSE_IF_NOT_INIT(); 76 77 Mutex::Autolock _l(mLock); 78 int outputIndex = getOutputIndex(device); 79 if (outputIndex < 0 ) { 80 return false; 81 } 82 83 resetOutput(outputIndex); 84 85 // get drm resources 86 drmModeResPtr resources = drmModeGetResources(mDrmFd); 87 if (!resources) { 88 ELOGTRACE("fail to get drm resources, error: %s", strerror(errno)); 89 return false; 90 } 91 92 drmModeConnectorPtr connector = NULL; 93 DrmOutput *output = &mOutputs[outputIndex]; 94 bool ret = false; 95 96 // find connector for the given device 97 for (int i = 0; i < resources->count_connectors; i++) { 98 if (!resources->connectors || !resources->connectors[i]) { 99 ELOGTRACE("fail to get drm resources connectors, error: %s", strerror(errno)); 100 continue; 101 } 102 103 connector = drmModeGetConnector(mDrmFd, resources->connectors[i]); 104 if (!connector) { 105 ELOGTRACE("drmModeGetConnector failed"); 106 continue; 107 } 108 109 if (connector->connector_type != DrmConfig::getDrmConnector(device)) { 110 drmModeFreeConnector(connector); 111 continue; 112 } 113 114 if (connector->connection != DRM_MODE_CONNECTED) { 115 ILOGTRACE("device %d is not connected", device); 116 drmModeFreeConnector(connector); 117 ret = true; 118 break; 119 } 120 121 output->connector = connector; 122 output->connected = true; 123 124 // get proper encoder for the given connector 125 if (connector->encoder_id) { 126 ILOGTRACE("Drm connector has encoder attached on device %d", device); 127 output->encoder = drmModeGetEncoder(mDrmFd, connector->encoder_id); 128 if (!output->encoder) { 129 ELOGTRACE("failed to get encoder from a known encoder id"); 130 // fall through to get an encoder 131 } 132 } 133 if (!output->encoder) { 134 ILOGTRACE("getting encoder for device %d", device); 135 drmModeEncoderPtr encoder; 136 for (int j = 0; j < resources->count_encoders; j++) { 137 if (!resources->encoders || !resources->encoders[j]) { 138 ELOGTRACE("fail to get drm resources encoders, error: %s", strerror(errno)); 139 continue; 140 } 141 142 encoder = drmModeGetEncoder(mDrmFd, resources->encoders[i]); 143 if (!encoder) { 144 ELOGTRACE("drmModeGetEncoder failed"); 145 continue; 146 } 147 if (encoder->encoder_type == DrmConfig::getDrmEncoder(device)) { 148 output->encoder = encoder; 149 break; 150 } 151 drmModeFreeEncoder(encoder); 152 encoder = NULL; 153 } 154 } 155 if (!output->encoder) { 156 ELOGTRACE("failed to get drm encoder"); 157 break; 158 } 159 160 // get an attached crtc or spare crtc 161 if (output->encoder->crtc_id) { 162 ILOGTRACE("Drm encoder has crtc attached on device %d", device); 163 output->crtc = drmModeGetCrtc(mDrmFd, output->encoder->crtc_id); 164 if (!output->crtc) { 165 ELOGTRACE("failed to get crtc from a known crtc id"); 166 // fall through to get a spare crtc 167 } 168 } 169 if (!output->crtc) { 170 ILOGTRACE("getting crtc for device %d", device); 171 drmModeCrtcPtr crtc; 172 for (int j = 0; j < resources->count_crtcs; j++) { 173 if (!resources->crtcs || !resources->crtcs[j]) { 174 ELOGTRACE("fail to get drm resources crtcs, error: %s", strerror(errno)); 175 continue; 176 } 177 178 crtc = drmModeGetCrtc(mDrmFd, resources->crtcs[j]); 179 if (!crtc) { 180 ELOGTRACE("drmModeGetCrtc failed"); 181 continue; 182 } 183 // check if legal crtc to the encoder 184 if (output->encoder->possible_crtcs & (1<<j)) { 185 if (crtc->buffer_id == 0) { 186 output->crtc = crtc; 187 break; 188 } 189 } 190 drmModeFreeCrtc(crtc); 191 } 192 } 193 if (!output->crtc) { 194 ELOGTRACE("failed to get drm crtc"); 195 break; 196 } 197 198 // current mode 199 if (output->crtc->mode_valid) { 200 ILOGTRACE("mode is valid, kernel mode settings"); 201 memcpy(&output->mode, &output->crtc->mode, sizeof(drmModeModeInfo)); 202 ret = true; 203 } else { 204 ELOGTRACE("mode is invalid. Kernel mode setting is not completed"); 205 ret = false; 206 } 207 208 if (outputIndex == OUTPUT_PRIMARY) { 209 if (!readIoctl(DRM_PSB_PANEL_ORIENTATION, &output->panelOrientation, sizeof(int))) { 210 ELOGTRACE("failed to get device %d orientation", device); 211 output->panelOrientation = PANEL_ORIENTATION_0; 212 } 213 } else { 214 output->panelOrientation = PANEL_ORIENTATION_0; 215 } 216 break; 217 } 218 219 if (!ret) { 220 if (output->connector == NULL && outputIndex != OUTPUT_PRIMARY) { 221 // a fatal failure on primary device 222 // non fatal on secondary device 223 WLOGTRACE("device %d is disabled?", device); 224 ret = true; 225 } 226 resetOutput(outputIndex); 227 } else if (output->connected) { 228 ILOGTRACE("mode is: %dx%d@%dHz", output->mode.hdisplay, output->mode.vdisplay, output->mode.vrefresh); 229 } 230 231 drmModeFreeResources(resources); 232 return ret; 233 } 234 235 bool Drm::isSameDrmMode(drmModeModeInfoPtr value, 236 drmModeModeInfoPtr base) const 237 { 238 if (base->hdisplay == value->hdisplay && 239 base->vdisplay == value->vdisplay && 240 base->vrefresh == value->vrefresh && 241 (base->flags & value->flags) == value->flags) { 242 VLOGTRACE("Drm mode is not changed"); 243 return true; 244 } 245 246 return false; 247 } 248 249 bool Drm::setDrmMode(int device, drmModeModeInfo& value) 250 { 251 RETURN_FALSE_IF_NOT_INIT(); 252 Mutex::Autolock _l(mLock); 253 254 if (device != IDisplayDevice::DEVICE_EXTERNAL) { 255 WLOGTRACE("Setting mode on invalid device %d", device); 256 return false; 257 } 258 259 int outputIndex = getOutputIndex(device); 260 if (outputIndex < 0 ) { 261 ELOGTRACE("invalid device"); 262 return false; 263 } 264 265 DrmOutput *output= &mOutputs[outputIndex]; 266 if (!output->connected) { 267 ELOGTRACE("device is not connected"); 268 return false; 269 } 270 271 if (output->connector->count_modes <= 0) { 272 ELOGTRACE("invalid count of modes"); 273 return false; 274 } 275 276 drmModeModeInfoPtr mode; 277 int index = 0; 278 for (int i = 0; i < output->connector->count_modes; i++) { 279 mode = &output->connector->modes[i]; 280 if (mode->type & DRM_MODE_TYPE_PREFERRED) { 281 index = i; 282 } 283 if (isSameDrmMode(&value, mode)) { 284 index = i; 285 break; 286 } 287 } 288 289 mode = &output->connector->modes[index]; 290 return setDrmMode(outputIndex, mode); 291 } 292 293 bool Drm::setRefreshRate(int device, int hz) 294 { 295 RETURN_FALSE_IF_NOT_INIT(); 296 Mutex::Autolock _l(mLock); 297 298 if (device != IDisplayDevice::DEVICE_EXTERNAL) { 299 WLOGTRACE("Setting mode on invalid device %d", device); 300 return false; 301 } 302 303 int outputIndex = getOutputIndex(device); 304 if (outputIndex < 0 ) { 305 ELOGTRACE("invalid device"); 306 return false; 307 } 308 309 DrmOutput *output= &mOutputs[outputIndex]; 310 if (!output->connected) { 311 ELOGTRACE("device is not connected"); 312 return false; 313 } 314 315 if (output->connector->count_modes <= 0) { 316 ELOGTRACE("invalid count of modes"); 317 return false; 318 } 319 320 drmModeModeInfoPtr mode; 321 int index = 0; 322 for (int i = 0; i < output->connector->count_modes; i++) { 323 mode = &output->connector->modes[i]; 324 if (mode->type & DRM_MODE_TYPE_PREFERRED) { 325 index = i; 326 } 327 if (mode->hdisplay == output->mode.hdisplay && 328 mode->vdisplay == output->mode.vdisplay && 329 mode->vrefresh == (uint32_t)hz) { 330 index = i; 331 break; 332 } 333 } 334 335 mode = &output->connector->modes[index]; 336 return setDrmMode(outputIndex, mode); 337 } 338 339 bool Drm::writeReadIoctl(unsigned long cmd, void *data, 340 unsigned long size) 341 { 342 int err; 343 344 if (mDrmFd <= 0) { 345 ELOGTRACE("drm is not initialized"); 346 return false; 347 } 348 349 if (!data || !size) { 350 ELOGTRACE("invalid parameters"); 351 return false; 352 } 353 354 err = drmCommandWriteRead(mDrmFd, cmd, data, size); 355 if (err) { 356 WLOGTRACE("failed to call %ld ioctl with failure %d", cmd, err); 357 return false; 358 } 359 360 return true; 361 } 362 363 bool Drm::writeIoctl(unsigned long cmd, void *data, 364 unsigned long size) 365 { 366 int err; 367 368 if (mDrmFd <= 0) { 369 ELOGTRACE("drm is not initialized"); 370 return false; 371 } 372 373 if (!data || !size) { 374 ELOGTRACE("invalid parameters"); 375 return false; 376 } 377 378 err = drmCommandWrite(mDrmFd, cmd, data, size); 379 if (err) { 380 WLOGTRACE("failed to call %ld ioctl with failure %d", cmd, err); 381 return false; 382 } 383 384 return true; 385 } 386 387 388 bool Drm::readIoctl(unsigned long cmd, void *data, 389 unsigned long size) 390 { 391 int err; 392 393 if (mDrmFd <= 0) { 394 ELOGTRACE("drm is not initialized"); 395 return false; 396 } 397 398 if (!data || !size) { 399 ELOGTRACE("invalid parameters"); 400 return false; 401 } 402 403 err = drmCommandRead(mDrmFd, cmd, data, size); 404 if (err) { 405 WLOGTRACE("failed to call %ld ioctl with failure %d", cmd, err); 406 return false; 407 } 408 409 return true; 410 } 411 412 413 int Drm::getDrmFd() const 414 { 415 return mDrmFd; 416 } 417 418 bool Drm::getModeInfo(int device, drmModeModeInfo& mode) 419 { 420 Mutex::Autolock _l(mLock); 421 422 int outputIndex = getOutputIndex(device); 423 if (outputIndex < 0 ) { 424 return false; 425 } 426 427 DrmOutput *output= &mOutputs[outputIndex]; 428 if (output->connected == false) { 429 ELOGTRACE("device is not connected"); 430 return false; 431 } 432 433 if (output->mode.hdisplay == 0 || output->mode.vdisplay == 0) { 434 ELOGTRACE("invalid width or height"); 435 return false; 436 } 437 438 memcpy(&mode, &output->mode, sizeof(drmModeModeInfo)); 439 440 #ifdef INTEL_SUPPORT_HDMI_PRIMARY 441 // FIXME: use default fb size instead of hdmi mode, because to 442 // support hdmi primary, we cannot report dynamic mode to SF. 443 mode.hdisplay = DEFAULT_DRM_FB_WIDTH; 444 mode.vdisplay = DEFAULT_DRM_FB_HEIGHT; 445 #endif 446 447 return true; 448 } 449 450 bool Drm::getPhysicalSize(int device, uint32_t& width, uint32_t& height) 451 { 452 Mutex::Autolock _l(mLock); 453 454 int outputIndex = getOutputIndex(device); 455 if (outputIndex < 0 ) { 456 return false; 457 } 458 459 DrmOutput *output= &mOutputs[outputIndex]; 460 if (output->connected == false) { 461 ELOGTRACE("device is not connected"); 462 return false; 463 } 464 465 width = output->connector->mmWidth; 466 height = output->connector->mmHeight; 467 return true; 468 } 469 470 bool Drm::getDisplayResolution(int device, uint32_t& width, uint32_t& height) 471 { 472 Mutex::Autolock _l(mLock); 473 474 int outputIndex = getOutputIndex(device); 475 if (outputIndex < 0) { 476 return false; 477 } 478 479 DrmOutput *output= &mOutputs[outputIndex]; 480 if (output->connected == false) { 481 ELOGTRACE("device is not connected"); 482 return false; 483 } 484 485 width = output->mode.hdisplay; 486 height = output->mode.vdisplay; 487 488 if (!width || !height) { 489 ELOGTRACE("invalid width or height"); 490 return false; 491 } 492 return true; 493 } 494 495 bool Drm::isConnected(int device) 496 { 497 Mutex::Autolock _l(mLock); 498 499 int output = getOutputIndex(device); 500 if (output < 0 ) { 501 return false; 502 } 503 504 return mOutputs[output].connected; 505 } 506 507 bool Drm::setDpmsMode(int device, int mode) 508 { 509 Mutex::Autolock _l(mLock); 510 511 int output = getOutputIndex(device); 512 if (output < 0 ) { 513 return false; 514 } 515 516 if (mode != IDisplayDevice::DEVICE_DISPLAY_OFF && 517 mode != IDisplayDevice::DEVICE_DISPLAY_STANDBY && 518 mode != IDisplayDevice::DEVICE_DISPLAY_ON) { 519 ELOGTRACE("invalid mode %d", mode); 520 return false; 521 } 522 523 DrmOutput *out = &mOutputs[output]; 524 if (!out->connected) { 525 ELOGTRACE("device is not connected"); 526 return false; 527 } 528 529 drmModePropertyPtr props; 530 for (int i = 0; i < out->connector->count_props; i++) { 531 props = drmModeGetProperty(mDrmFd, out->connector->props[i]); 532 if (!props) { 533 continue; 534 } 535 536 if (strcmp(props->name, "DPMS") == 0) { 537 int ret = drmModeConnectorSetProperty( 538 mDrmFd, 539 out->connector->connector_id, 540 props->prop_id, 541 (mode == IDisplayDevice::DEVICE_DISPLAY_ON) ? DRM_MODE_DPMS_ON : 542 IDisplayDevice::DEVICE_DISPLAY_STANDBY == mode ? 543 DRM_MODE_DPMS_STANDBY : DRM_MODE_DPMS_OFF); 544 drmModeFreeProperty(props); 545 if (ret != 0) { 546 ELOGTRACE("unable to set DPMS %d", mode); 547 return false; 548 } else { 549 return true; 550 } 551 } 552 drmModeFreeProperty(props); 553 } 554 return false; 555 } 556 557 void Drm::resetOutput(int index) 558 { 559 DrmOutput *output = &mOutputs[index]; 560 561 output->connected = false; 562 memset(&output->mode, 0, sizeof(drmModeModeInfo)); 563 564 if (output->connector) { 565 drmModeFreeConnector(output->connector); 566 output->connector = 0; 567 } 568 if (output->encoder) { 569 drmModeFreeEncoder(output->encoder); 570 output->encoder = 0; 571 } 572 if (output->crtc) { 573 drmModeFreeCrtc(output->crtc); 574 output->crtc = 0; 575 } 576 if (output->fbId) { 577 drmModeRmFB(mDrmFd, output->fbId); 578 output->fbId = 0; 579 } 580 if (output->fbHandle) { 581 Hwcomposer::getInstance().getBufferManager()->freeFrameBuffer(output->fbHandle); 582 output->fbHandle = 0; 583 } 584 } 585 586 bool Drm::initDrmMode(int outputIndex) 587 { 588 DrmOutput *output= &mOutputs[outputIndex]; 589 if (output->connector->count_modes <= 0) { 590 ELOGTRACE("invalid count of modes"); 591 return false; 592 } 593 594 drmModeModeInfoPtr mode; 595 int index = 0; 596 for (int i = 0; i < output->connector->count_modes; i++) { 597 mode = &output->connector->modes[i]; 598 if (mode->type & DRM_MODE_TYPE_PREFERRED) { 599 index = i; 600 break; 601 } 602 } 603 604 return setDrmMode(outputIndex, &output->connector->modes[index]); 605 } 606 607 bool Drm::setDrmMode(int index, drmModeModeInfoPtr mode) 608 { 609 DrmOutput *output = &mOutputs[index]; 610 611 int oldFbId =0; 612 int oldFbHandle = 0; 613 614 drmModeModeInfo currentMode; 615 memcpy(¤tMode, &output->mode, sizeof(drmModeModeInfo)); 616 617 if (isSameDrmMode(mode, ¤tMode)) 618 return true; 619 620 621 if (output->fbId) { 622 oldFbId = output->fbId ; 623 output->fbId = 0; 624 } 625 626 if (output->fbHandle) { 627 oldFbHandle = output->fbHandle; 628 output->fbHandle = 0; 629 } 630 631 // allocate frame buffer 632 int stride = 0; 633 #ifdef INTEL_SUPPORT_HDMI_PRIMARY 634 output->fbHandle = Hwcomposer::getInstance().getBufferManager()->allocFrameBuffer( 635 DEFAULT_DRM_FB_WIDTH, DEFAULT_DRM_FB_HEIGHT, &stride); 636 #else 637 output->fbHandle = Hwcomposer::getInstance().getBufferManager()->allocFrameBuffer( 638 mode->hdisplay, mode->vdisplay, &stride); 639 #endif 640 if (output->fbHandle == 0) { 641 ELOGTRACE("failed to allocate frame buffer"); 642 return false; 643 } 644 645 int ret = 0; 646 ret = drmModeAddFB( 647 mDrmFd, 648 #ifdef INTEL_SUPPORT_HDMI_PRIMARY 649 DEFAULT_DRM_FB_WIDTH, 650 DEFAULT_DRM_FB_HEIGHT, 651 #else 652 mode->hdisplay, 653 mode->vdisplay, 654 #endif 655 DrmConfig::getFrameBufferDepth(), 656 DrmConfig::getFrameBufferBpp(), 657 stride, 658 output->fbHandle, 659 &output->fbId); 660 if (ret != 0) { 661 ELOGTRACE("drmModeAddFB failed, error: %d", ret); 662 return false; 663 } 664 665 ILOGTRACE("mode set: %dx%d@%dHz", mode->hdisplay, mode->vdisplay, mode->vrefresh); 666 667 ret = drmModeSetCrtc(mDrmFd, output->crtc->crtc_id, output->fbId, 0, 0, 668 &output->connector->connector_id, 1, mode); 669 if (ret == 0) { 670 //save mode 671 memcpy(&output->mode, mode, sizeof(drmModeModeInfo)); 672 } else { 673 ELOGTRACE("drmModeSetCrtc failed. error: %d", ret); 674 } 675 676 if (oldFbId) { 677 drmModeRmFB(mDrmFd, oldFbId); 678 } 679 680 if (oldFbHandle) { 681 Hwcomposer::getInstance().getBufferManager()->freeFrameBuffer(oldFbHandle); 682 } 683 684 return ret == 0; 685 } 686 687 int Drm::getOutputIndex(int device) 688 { 689 switch (device) { 690 case IDisplayDevice::DEVICE_PRIMARY: 691 return OUTPUT_PRIMARY; 692 case IDisplayDevice::DEVICE_EXTERNAL: 693 return OUTPUT_EXTERNAL; 694 default: 695 ELOGTRACE("invalid display device"); 696 break; 697 } 698 699 return -1; 700 } 701 702 int Drm::getPanelOrientation(int device) 703 { 704 int outputIndex = getOutputIndex(device); 705 if (outputIndex < 0) { 706 ELOGTRACE("invalid device"); 707 return PANEL_ORIENTATION_0; 708 } 709 710 DrmOutput *output= &mOutputs[outputIndex]; 711 if (output->connected == false) { 712 ELOGTRACE("device is not connected"); 713 return PANEL_ORIENTATION_0; 714 } 715 716 return output->panelOrientation; 717 } 718 719 // HWC 1.4 requires that we return all of the compatible configs in getDisplayConfigs 720 // this is needed so getActiveConfig/setActiveConfig work correctly. It is up to the 721 // user space to decide what speed to send. 722 drmModeModeInfoPtr Drm::detectAllConfigs(int device, int *modeCount) 723 { 724 RETURN_NULL_IF_NOT_INIT(); 725 Mutex::Autolock _l(mLock); 726 727 if (modeCount != NULL) 728 *modeCount = 0; 729 else 730 return NULL; 731 732 int outputIndex = getOutputIndex(device); 733 if (outputIndex < 0) { 734 ELOGTRACE("invalid device"); 735 return NULL; 736 } 737 738 DrmOutput *output= &mOutputs[outputIndex]; 739 if (!output->connected) { 740 ELOGTRACE("device is not connected"); 741 return NULL; 742 } 743 744 if (output->connector->count_modes <= 0) { 745 ELOGTRACE("invalid count of modes"); 746 return NULL; 747 } 748 749 *modeCount = output->connector->count_modes; 750 return output->connector->modes; 751 } 752 753 } // namespace intel 754 } // namespace android 755 756