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 int output = getOutputIndex(device); 513 if (output < 0 ) { 514 return false; 515 } 516 517 if (mode != IDisplayDevice::DEVICE_DISPLAY_OFF && 518 mode != IDisplayDevice::DEVICE_DISPLAY_STANDBY && 519 mode != IDisplayDevice::DEVICE_DISPLAY_ON) { 520 ELOGTRACE("invalid mode %d", mode); 521 return false; 522 } 523 524 DrmOutput *out = &mOutputs[output]; 525 if (!out->connected) { 526 ELOGTRACE("device is not connected"); 527 return false; 528 } 529 530 drmModePropertyPtr props; 531 for (int i = 0; i < out->connector->count_props; i++) { 532 props = drmModeGetProperty(mDrmFd, out->connector->props[i]); 533 if (!props) { 534 continue; 535 } 536 537 if (strcmp(props->name, "DPMS") == 0) { 538 int ret = drmModeConnectorSetProperty( 539 mDrmFd, 540 out->connector->connector_id, 541 props->prop_id, 542 (mode == IDisplayDevice::DEVICE_DISPLAY_ON) ? DRM_MODE_DPMS_ON : 543 IDisplayDevice::DEVICE_DISPLAY_STANDBY == mode ? 544 DRM_MODE_DPMS_STANDBY : DRM_MODE_DPMS_OFF); 545 drmModeFreeProperty(props); 546 if (ret != 0) { 547 ELOGTRACE("unable to set DPMS %d", mode); 548 return false; 549 } else { 550 return true; 551 } 552 } 553 drmModeFreeProperty(props); 554 } 555 return false; 556 } 557 558 void Drm::resetOutput(int index) 559 { 560 DrmOutput *output = &mOutputs[index]; 561 562 output->connected = false; 563 memset(&output->mode, 0, sizeof(drmModeModeInfo)); 564 565 if (output->connector) { 566 drmModeFreeConnector(output->connector); 567 output->connector = 0; 568 } 569 if (output->encoder) { 570 drmModeFreeEncoder(output->encoder); 571 output->encoder = 0; 572 } 573 if (output->crtc) { 574 drmModeFreeCrtc(output->crtc); 575 output->crtc = 0; 576 } 577 if (output->fbId) { 578 drmModeRmFB(mDrmFd, output->fbId); 579 output->fbId = 0; 580 } 581 if (output->fbHandle) { 582 Hwcomposer::getInstance().getBufferManager()->freeFrameBuffer(output->fbHandle); 583 output->fbHandle = 0; 584 } 585 } 586 587 bool Drm::initDrmMode(int outputIndex) 588 { 589 DrmOutput *output= &mOutputs[outputIndex]; 590 if (output->connector->count_modes <= 0) { 591 ELOGTRACE("invalid count of modes"); 592 return false; 593 } 594 595 drmModeModeInfoPtr mode; 596 int index = 0; 597 for (int i = 0; i < output->connector->count_modes; i++) { 598 mode = &output->connector->modes[i]; 599 if (mode->type & DRM_MODE_TYPE_PREFERRED) { 600 index = i; 601 break; 602 } 603 } 604 605 return setDrmMode(outputIndex, &output->connector->modes[index]); 606 } 607 608 bool Drm::setDrmMode(int index, drmModeModeInfoPtr mode) 609 { 610 DrmOutput *output = &mOutputs[index]; 611 612 int oldFbId = 0; 613 int oldFbHandle = 0; 614 // reuse current frame buffer if there is no resolution change 615 int fbId = -1; 616 617 drmModeModeInfo currentMode; 618 memcpy(¤tMode, &output->mode, sizeof(drmModeModeInfo)); 619 620 if (isSameDrmMode(mode, ¤tMode)) 621 return true; 622 623 if (currentMode.hdisplay != mode->hdisplay || 624 currentMode.vdisplay != mode->vdisplay) { 625 626 oldFbId = output->fbId; 627 oldFbHandle = output->fbHandle; 628 629 // allocate frame buffer 630 int stride = 0; 631 #ifdef INTEL_SUPPORT_HDMI_PRIMARY 632 output->fbHandle = Hwcomposer::getInstance().getBufferManager()->allocFrameBuffer( 633 DEFAULT_DRM_FB_WIDTH, DEFAULT_DRM_FB_HEIGHT, &stride); 634 #else 635 output->fbHandle = Hwcomposer::getInstance().getBufferManager()->allocFrameBuffer( 636 mode->hdisplay, mode->vdisplay, &stride); 637 #endif 638 if (output->fbHandle == 0) { 639 ELOGTRACE("failed to allocate frame buffer"); 640 return false; 641 } 642 643 int ret = 0; 644 ret = drmModeAddFB( 645 mDrmFd, 646 #ifdef INTEL_SUPPORT_HDMI_PRIMARY 647 DEFAULT_DRM_FB_WIDTH, 648 DEFAULT_DRM_FB_HEIGHT, 649 #else 650 mode->hdisplay, 651 mode->vdisplay, 652 #endif 653 DrmConfig::getFrameBufferDepth(), 654 DrmConfig::getFrameBufferBpp(), 655 stride, 656 output->fbHandle, 657 &output->fbId); 658 if (ret != 0) { 659 ELOGTRACE("drmModeAddFB failed, error: %d", ret); 660 return false; 661 } 662 fbId = output->fbId; 663 } 664 665 ILOGTRACE("mode set: %dx%d@%dHz", mode->hdisplay, mode->vdisplay, mode->vrefresh); 666 667 int ret = drmModeSetCrtc(mDrmFd, output->crtc->crtc_id, fbId, 0, 0, 668 &output->connector->connector_id, 1, mode); 669 670 if (ret == 0) { 671 //save mode 672 memcpy(&output->mode, mode, sizeof(drmModeModeInfo)); 673 } else { 674 ELOGTRACE("drmModeSetCrtc failed. error: %d", ret); 675 } 676 677 if (oldFbId) { 678 drmModeRmFB(mDrmFd, oldFbId); 679 } 680 681 if (oldFbHandle) { 682 Hwcomposer::getInstance().getBufferManager()->freeFrameBuffer(oldFbHandle); 683 } 684 685 return ret == 0; 686 } 687 688 int Drm::getOutputIndex(int device) 689 { 690 switch (device) { 691 case IDisplayDevice::DEVICE_PRIMARY: 692 return OUTPUT_PRIMARY; 693 case IDisplayDevice::DEVICE_EXTERNAL: 694 return OUTPUT_EXTERNAL; 695 default: 696 ELOGTRACE("invalid display device"); 697 break; 698 } 699 700 return -1; 701 } 702 703 int Drm::getPanelOrientation(int device) 704 { 705 int outputIndex = getOutputIndex(device); 706 if (outputIndex < 0) { 707 ELOGTRACE("invalid device"); 708 return PANEL_ORIENTATION_0; 709 } 710 711 DrmOutput *output= &mOutputs[outputIndex]; 712 if (output->connected == false) { 713 ELOGTRACE("device is not connected"); 714 return PANEL_ORIENTATION_0; 715 } 716 717 return output->panelOrientation; 718 } 719 720 // HWC 1.4 requires that we return all of the compatible configs in getDisplayConfigs 721 // this is needed so getActiveConfig/setActiveConfig work correctly. It is up to the 722 // user space to decide what speed to send. 723 drmModeModeInfoPtr Drm::detectAllConfigs(int device, int *modeCount) 724 { 725 RETURN_NULL_IF_NOT_INIT(); 726 Mutex::Autolock _l(mLock); 727 728 if (modeCount != NULL) 729 *modeCount = 0; 730 else 731 return NULL; 732 733 int outputIndex = getOutputIndex(device); 734 if (outputIndex < 0) { 735 ELOGTRACE("invalid device"); 736 return NULL; 737 } 738 739 DrmOutput *output= &mOutputs[outputIndex]; 740 if (!output->connected) { 741 ELOGTRACE("device is not connected"); 742 return NULL; 743 } 744 745 if (output->connector->count_modes <= 0) { 746 ELOGTRACE("invalid count of modes"); 747 return NULL; 748 } 749 750 *modeCount = output->connector->count_modes; 751 return output->connector->modes; 752 } 753 754 } // namespace intel 755 } // namespace android 756 757