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 //output->fbId = output->crtc->buffer_id; 203 ret = true; 204 } else { 205 ELOGTRACE("mode is invalid. Kernel mode setting is not completed"); 206 ret = false; 207 } 208 209 if (outputIndex == OUTPUT_PRIMARY) { 210 if (!readIoctl(DRM_PSB_PANEL_ORIENTATION, &output->panelOrientation, sizeof(int))) { 211 ELOGTRACE("failed to get device %d orientation", device); 212 output->panelOrientation = PANEL_ORIENTATION_0; 213 } 214 } else { 215 output->panelOrientation = PANEL_ORIENTATION_0; 216 } 217 break; 218 } 219 220 if (!ret) { 221 if (output->connector == NULL && outputIndex != OUTPUT_PRIMARY) { 222 // a fatal failure on primary device 223 // non fatal on secondary device 224 WLOGTRACE("device %d is disabled?", device); 225 ret = true; 226 } 227 resetOutput(outputIndex); 228 } else if (output->connected) { 229 ILOGTRACE("mode is: %dx%d@%dHz", output->mode.hdisplay, output->mode.vdisplay, output->mode.vrefresh); 230 } 231 232 drmModeFreeResources(resources); 233 return ret; 234 } 235 236 bool Drm::isSameDrmMode(drmModeModeInfoPtr value, 237 drmModeModeInfoPtr base) const 238 { 239 if (base->hdisplay == value->hdisplay && 240 base->vdisplay == value->vdisplay && 241 base->vrefresh == value->vrefresh && 242 (base->flags & value->flags) == value->flags) { 243 VLOGTRACE("Drm mode is not changed"); 244 return true; 245 } 246 247 return false; 248 } 249 250 bool Drm::setDrmMode(int device, drmModeModeInfo& value) 251 { 252 RETURN_FALSE_IF_NOT_INIT(); 253 Mutex::Autolock _l(mLock); 254 255 if (device != IDisplayDevice::DEVICE_EXTERNAL) { 256 WLOGTRACE("Setting mode on invalid device %d", device); 257 return false; 258 } 259 260 int outputIndex = getOutputIndex(device); 261 if (outputIndex < 0 ) { 262 ELOGTRACE("invalid device"); 263 return false; 264 } 265 266 DrmOutput *output= &mOutputs[outputIndex]; 267 if (!output->connected) { 268 ELOGTRACE("device is not connected"); 269 return false; 270 } 271 272 if (output->connector->count_modes <= 0) { 273 ELOGTRACE("invalid count of modes"); 274 return false; 275 } 276 277 drmModeModeInfoPtr mode; 278 int index = 0; 279 for (int i = 0; i < output->connector->count_modes; i++) { 280 mode = &output->connector->modes[i]; 281 if (mode->type & DRM_MODE_TYPE_PREFERRED) { 282 index = i; 283 } 284 if (isSameDrmMode(&value, mode)) { 285 index = i; 286 break; 287 } 288 } 289 290 mode = &output->connector->modes[index]; 291 return setDrmMode(outputIndex, mode); 292 } 293 294 bool Drm::setRefreshRate(int device, int hz) 295 { 296 RETURN_FALSE_IF_NOT_INIT(); 297 Mutex::Autolock _l(mLock); 298 299 if (device != IDisplayDevice::DEVICE_EXTERNAL) { 300 WLOGTRACE("Setting mode on invalid device %d", device); 301 return false; 302 } 303 304 int outputIndex = getOutputIndex(device); 305 if (outputIndex < 0 ) { 306 ELOGTRACE("invalid device"); 307 return false; 308 } 309 310 DrmOutput *output= &mOutputs[outputIndex]; 311 if (!output->connected) { 312 ELOGTRACE("device is not connected"); 313 return false; 314 } 315 316 if (output->connector->count_modes <= 0) { 317 ELOGTRACE("invalid count of modes"); 318 return false; 319 } 320 321 drmModeModeInfoPtr mode; 322 int index = 0; 323 for (int i = 0; i < output->connector->count_modes; i++) { 324 mode = &output->connector->modes[i]; 325 if (mode->type & DRM_MODE_TYPE_PREFERRED) { 326 index = i; 327 } 328 if (mode->hdisplay == output->mode.hdisplay && 329 mode->vdisplay == output->mode.vdisplay && 330 mode->vrefresh == (uint32_t)hz) { 331 index = i; 332 break; 333 } 334 } 335 336 mode = &output->connector->modes[index]; 337 return setDrmMode(outputIndex, mode); 338 } 339 340 bool Drm::writeReadIoctl(unsigned long cmd, void *data, 341 unsigned long size) 342 { 343 int err; 344 345 if (mDrmFd <= 0) { 346 ELOGTRACE("drm is not initialized"); 347 return false; 348 } 349 350 if (!data || !size) { 351 ELOGTRACE("invalid parameters"); 352 return false; 353 } 354 355 err = drmCommandWriteRead(mDrmFd, cmd, data, size); 356 if (err) { 357 WLOGTRACE("failed to call %ld ioctl with failure %d", cmd, err); 358 return false; 359 } 360 361 return true; 362 } 363 364 bool Drm::writeIoctl(unsigned long cmd, void *data, 365 unsigned long size) 366 { 367 int err; 368 369 if (mDrmFd <= 0) { 370 ELOGTRACE("drm is not initialized"); 371 return false; 372 } 373 374 if (!data || !size) { 375 ELOGTRACE("invalid parameters"); 376 return false; 377 } 378 379 err = drmCommandWrite(mDrmFd, cmd, data, size); 380 if (err) { 381 WLOGTRACE("failed to call %ld ioctl with failure %d", cmd, err); 382 return false; 383 } 384 385 return true; 386 } 387 388 389 bool Drm::readIoctl(unsigned long cmd, void *data, 390 unsigned long size) 391 { 392 int err; 393 394 if (mDrmFd <= 0) { 395 ELOGTRACE("drm is not initialized"); 396 return false; 397 } 398 399 if (!data || !size) { 400 ELOGTRACE("invalid parameters"); 401 return false; 402 } 403 404 err = drmCommandRead(mDrmFd, cmd, data, size); 405 if (err) { 406 WLOGTRACE("failed to call %ld ioctl with failure %d", cmd, err); 407 return false; 408 } 409 410 return true; 411 } 412 413 414 int Drm::getDrmFd() const 415 { 416 return mDrmFd; 417 } 418 419 bool Drm::getModeInfo(int device, drmModeModeInfo& mode) 420 { 421 Mutex::Autolock _l(mLock); 422 423 int outputIndex = getOutputIndex(device); 424 if (outputIndex < 0 ) { 425 return false; 426 } 427 428 DrmOutput *output= &mOutputs[outputIndex]; 429 if (output->connected == false) { 430 ELOGTRACE("device is not connected"); 431 return false; 432 } 433 434 if (output->mode.hdisplay == 0 || output->mode.vdisplay == 0) { 435 ELOGTRACE("invalid width or height"); 436 return false; 437 } 438 439 memcpy(&mode, &output->mode, sizeof(drmModeModeInfo)); 440 441 #ifdef INTEL_SUPPORT_HDMI_PRIMARY 442 // FIXME: use default fb size instead of hdmi mode, because to 443 // support hdmi primary, we cannot report dynamic mode to SF. 444 mode.hdisplay = DEFAULT_DRM_FB_WIDTH; 445 mode.vdisplay = DEFAULT_DRM_FB_HEIGHT; 446 #endif 447 448 return true; 449 } 450 451 bool Drm::getPhysicalSize(int device, uint32_t& width, uint32_t& height) 452 { 453 Mutex::Autolock _l(mLock); 454 455 int outputIndex = getOutputIndex(device); 456 if (outputIndex < 0 ) { 457 return false; 458 } 459 460 DrmOutput *output= &mOutputs[outputIndex]; 461 if (output->connected == false) { 462 ELOGTRACE("device is not connected"); 463 return false; 464 } 465 466 width = output->connector->mmWidth; 467 height = output->connector->mmHeight; 468 return true; 469 } 470 471 bool Drm::getDisplayResolution(int device, uint32_t& width, uint32_t& height) 472 { 473 Mutex::Autolock _l(mLock); 474 475 int outputIndex = getOutputIndex(device); 476 if (outputIndex < 0) { 477 return false; 478 } 479 480 DrmOutput *output= &mOutputs[outputIndex]; 481 if (output->connected == false) { 482 ELOGTRACE("device is not connected"); 483 return false; 484 } 485 486 width = output->mode.hdisplay; 487 height = output->mode.vdisplay; 488 489 if (!width || !height) { 490 ELOGTRACE("invalid width or height"); 491 return false; 492 } 493 return true; 494 } 495 496 bool Drm::isConnected(int device) 497 { 498 Mutex::Autolock _l(mLock); 499 500 int output = getOutputIndex(device); 501 if (output < 0 ) { 502 return false; 503 } 504 505 return mOutputs[output].connected; 506 } 507 508 bool Drm::setDpmsMode(int device, int mode) 509 { 510 Mutex::Autolock _l(mLock); 511 512 #ifdef INTEL_SUPPORT_HDMI_PRIMARY 513 device = IDisplayDevice::DEVICE_EXTERNAL; 514 #endif 515 516 int output = getOutputIndex(device); 517 if (output < 0 ) { 518 return false; 519 } 520 521 if (mode != IDisplayDevice::DEVICE_DISPLAY_OFF && 522 mode != IDisplayDevice::DEVICE_DISPLAY_STANDBY && 523 mode != IDisplayDevice::DEVICE_DISPLAY_ON) { 524 ELOGTRACE("invalid mode %d", mode); 525 return false; 526 } 527 528 DrmOutput *out = &mOutputs[output]; 529 if (!out->connected) { 530 ELOGTRACE("device is not connected"); 531 return false; 532 } 533 534 drmModePropertyPtr props; 535 for (int i = 0; i < out->connector->count_props; i++) { 536 props = drmModeGetProperty(mDrmFd, out->connector->props[i]); 537 if (!props) { 538 continue; 539 } 540 541 if (strcmp(props->name, "DPMS") == 0) { 542 int ret = drmModeConnectorSetProperty( 543 mDrmFd, 544 out->connector->connector_id, 545 props->prop_id, 546 (mode == IDisplayDevice::DEVICE_DISPLAY_ON) ? DRM_MODE_DPMS_ON : 547 IDisplayDevice::DEVICE_DISPLAY_STANDBY == mode ? 548 DRM_MODE_DPMS_STANDBY : DRM_MODE_DPMS_OFF); 549 drmModeFreeProperty(props); 550 if (ret != 0) { 551 ELOGTRACE("unable to set DPMS %d", mode); 552 return false; 553 } else { 554 return true; 555 } 556 } 557 drmModeFreeProperty(props); 558 } 559 return false; 560 } 561 562 void Drm::resetOutput(int index) 563 { 564 DrmOutput *output = &mOutputs[index]; 565 566 output->connected = false; 567 memset(&output->mode, 0, sizeof(drmModeModeInfo)); 568 569 if (output->connector) { 570 drmModeFreeConnector(output->connector); 571 output->connector = 0; 572 } 573 if (output->encoder) { 574 drmModeFreeEncoder(output->encoder); 575 output->encoder = 0; 576 } 577 if (output->crtc) { 578 drmModeFreeCrtc(output->crtc); 579 output->crtc = 0; 580 } 581 if (output->fbId) { 582 drmModeRmFB(mDrmFd, output->fbId); 583 output->fbId = 0; 584 } 585 if (output->fbHandle) { 586 Hwcomposer::getInstance().getBufferManager()->freeFrameBuffer(output->fbHandle); 587 output->fbHandle = 0; 588 } 589 } 590 591 bool Drm::initDrmMode(int outputIndex) 592 { 593 DrmOutput *output= &mOutputs[outputIndex]; 594 if (output->connector->count_modes <= 0) { 595 ELOGTRACE("invalid count of modes"); 596 return false; 597 } 598 599 drmModeModeInfoPtr mode; 600 int index = 0; 601 for (int i = 0; i < output->connector->count_modes; i++) { 602 mode = &output->connector->modes[i]; 603 if (mode->type & DRM_MODE_TYPE_PREFERRED) { 604 index = i; 605 break; 606 } 607 } 608 609 return setDrmMode(outputIndex, &output->connector->modes[index]); 610 } 611 612 bool Drm::setDrmMode(int index, drmModeModeInfoPtr mode) 613 { 614 DrmOutput *output = &mOutputs[index]; 615 616 int oldFbId = 0; 617 int oldFbHandle = 0; 618 // reuse current frame buffer if there is no resolution change 619 int fbId = -1; 620 621 drmModeModeInfo currentMode; 622 memcpy(¤tMode, &output->mode, sizeof(drmModeModeInfo)); 623 624 if (isSameDrmMode(mode, ¤tMode)) 625 return true; 626 627 if (currentMode.hdisplay != mode->hdisplay || 628 currentMode.vdisplay != mode->vdisplay) { 629 630 oldFbId = output->fbId; 631 oldFbHandle = output->fbHandle; 632 633 // allocate frame buffer 634 int stride = 0; 635 #ifdef INTEL_SUPPORT_HDMI_PRIMARY 636 output->fbHandle = Hwcomposer::getInstance().getBufferManager()->allocFrameBuffer( 637 DEFAULT_DRM_FB_WIDTH, DEFAULT_DRM_FB_HEIGHT, &stride); 638 #else 639 output->fbHandle = Hwcomposer::getInstance().getBufferManager()->allocFrameBuffer( 640 mode->hdisplay, mode->vdisplay, &stride); 641 #endif 642 if (output->fbHandle == 0) { 643 ELOGTRACE("failed to allocate frame buffer"); 644 return false; 645 } 646 647 int ret = 0; 648 ret = drmModeAddFB( 649 mDrmFd, 650 #ifdef INTEL_SUPPORT_HDMI_PRIMARY 651 DEFAULT_DRM_FB_WIDTH, 652 DEFAULT_DRM_FB_HEIGHT, 653 #else 654 mode->hdisplay, 655 mode->vdisplay, 656 #endif 657 DrmConfig::getFrameBufferDepth(), 658 DrmConfig::getFrameBufferBpp(), 659 stride, 660 output->fbHandle, 661 &output->fbId); 662 if (ret != 0) { 663 ELOGTRACE("drmModeAddFB failed, error: %d", ret); 664 return false; 665 } 666 fbId = output->fbId; 667 } 668 669 ILOGTRACE("mode set: %dx%d@%dHz", mode->hdisplay, mode->vdisplay, mode->vrefresh); 670 671 int ret = drmModeSetCrtc(mDrmFd, output->crtc->crtc_id, fbId, 0, 0, 672 &output->connector->connector_id, 1, mode); 673 674 if (ret == 0) { 675 //save mode 676 memcpy(&output->mode, mode, sizeof(drmModeModeInfo)); 677 } else { 678 ELOGTRACE("drmModeSetCrtc failed. error: %d", ret); 679 } 680 681 if (oldFbId) { 682 drmModeRmFB(mDrmFd, oldFbId); 683 } 684 685 if (oldFbHandle) { 686 Hwcomposer::getInstance().getBufferManager()->freeFrameBuffer(oldFbHandle); 687 } 688 689 return ret == 0; 690 } 691 692 int Drm::getOutputIndex(int device) 693 { 694 switch (device) { 695 case IDisplayDevice::DEVICE_PRIMARY: 696 return OUTPUT_PRIMARY; 697 case IDisplayDevice::DEVICE_EXTERNAL: 698 return OUTPUT_EXTERNAL; 699 default: 700 ELOGTRACE("invalid display device"); 701 break; 702 } 703 704 return -1; 705 } 706 707 int Drm::getPanelOrientation(int device) 708 { 709 int outputIndex = getOutputIndex(device); 710 if (outputIndex < 0) { 711 ELOGTRACE("invalid device"); 712 return PANEL_ORIENTATION_0; 713 } 714 715 DrmOutput *output= &mOutputs[outputIndex]; 716 if (output->connected == false) { 717 ELOGTRACE("device is not connected"); 718 return PANEL_ORIENTATION_0; 719 } 720 721 return output->panelOrientation; 722 } 723 724 // HWC 1.4 requires that we return all of the compatible configs in getDisplayConfigs 725 // this is needed so getActiveConfig/setActiveConfig work correctly. It is up to the 726 // user space to decide what speed to send. 727 drmModeModeInfoPtr Drm::detectAllConfigs(int device, int *modeCount) 728 { 729 RETURN_NULL_IF_NOT_INIT(); 730 Mutex::Autolock _l(mLock); 731 732 if (modeCount != NULL) 733 *modeCount = 0; 734 else 735 return NULL; 736 737 int outputIndex = getOutputIndex(device); 738 if (outputIndex < 0) { 739 ELOGTRACE("invalid device"); 740 return NULL; 741 } 742 743 DrmOutput *output= &mOutputs[outputIndex]; 744 if (!output->connected) { 745 ELOGTRACE("device is not connected"); 746 return NULL; 747 } 748 749 if (output->connector->count_modes <= 0) { 750 ELOGTRACE("invalid count of modes"); 751 return NULL; 752 } 753 754 *modeCount = output->connector->count_modes; 755 return output->connector->modes; 756 } 757 758 } // namespace intel 759 } // namespace android 760 761