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 17 #include <common/utils/HwcTrace.h> 18 #include <common/base/Drm.h> 19 #include <DrmConfig.h> 20 #include <Hwcomposer.h> 21 #include <ExternalDevice.h> 22 23 namespace android { 24 namespace intel { 25 26 ExternalDevice::ExternalDevice(Hwcomposer& hwc, DisplayPlaneManager& dpm) 27 : PhysicalDevice(DEVICE_EXTERNAL, hwc, dpm), 28 mHdcpControl(NULL), 29 mAbortModeSettingCond(), 30 mPendingDrmMode(), 31 mHotplugEventPending(false), 32 mExpectedRefreshRate(0) 33 { 34 CTRACE(); 35 } 36 37 ExternalDevice::~ExternalDevice() 38 { 39 CTRACE(); 40 } 41 42 bool ExternalDevice::initialize() 43 { 44 if (!PhysicalDevice::initialize()) { 45 DEINIT_AND_RETURN_FALSE("failed to initialize physical device"); 46 } 47 48 mHdcpControl = createHdcpControl(); 49 if (!mHdcpControl) { 50 DEINIT_AND_RETURN_FALSE("failed to create HDCP control"); 51 } 52 53 mHotplugEventPending = false; 54 if (mConnected) { 55 mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this); 56 } 57 58 UeventObserver *observer = Hwcomposer::getInstance().getUeventObserver(); 59 if (observer) { 60 observer->registerListener( 61 DrmConfig::getHotplugString(), 62 hotplugEventListener, 63 this); 64 } else { 65 ELOGTRACE("Uevent observer is NULL"); 66 } 67 return true; 68 } 69 70 void ExternalDevice::deinitialize() 71 { 72 // abort mode settings if it is in the middle 73 mAbortModeSettingCond.signal(); 74 if (mThread.get()) { 75 mThread->join(); 76 mThread = NULL; 77 } 78 79 if (mHdcpControl) { 80 mHdcpControl->stopHdcp(); 81 delete mHdcpControl; 82 mHdcpControl = 0; 83 } 84 85 mHotplugEventPending = false; 86 PhysicalDevice::deinitialize(); 87 } 88 89 bool ExternalDevice::blank(bool blank) 90 { 91 if (!PhysicalDevice::blank(blank)) { 92 return false; 93 } 94 95 if (blank) { 96 mHdcpControl->stopHdcp(); 97 } else if (mConnected) { 98 mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this); 99 } 100 return true; 101 } 102 103 bool ExternalDevice::setDrmMode(drmModeModeInfo& value) 104 { 105 if (!mConnected) { 106 WLOGTRACE("external device is not connected"); 107 return false; 108 } 109 110 if (mThread.get()) { 111 mThread->join(); 112 mThread = NULL; 113 } 114 115 Drm *drm = Hwcomposer::getInstance().getDrm(); 116 drmModeModeInfo mode; 117 drm->getModeInfo(mType, mode); 118 if (drm->isSameDrmMode(&value, &mode)) 119 return true; 120 121 // any issue here by faking connection status? 122 mConnected = false; 123 mPendingDrmMode = value; 124 125 // setting mode in a working thread 126 mThread = new ModeSettingThread(this); 127 if (!mThread.get()) { 128 ELOGTRACE("failed to create mode settings thread"); 129 return false; 130 } 131 132 mThread->run("ModeSettingsThread", PRIORITY_URGENT_DISPLAY); 133 return true; 134 } 135 136 bool ExternalDevice::threadLoop() 137 { 138 // one-time execution 139 setDrmMode(); 140 return false; 141 } 142 143 void ExternalDevice::setDrmMode() 144 { 145 ILOGTRACE("start mode setting..."); 146 147 Drm *drm = Hwcomposer::getInstance().getDrm(); 148 149 mConnected = false; 150 mHwc.hotplug(mType, false); 151 152 { 153 Mutex::Autolock lock(mLock); 154 // TODO: make timeout value flexible, or wait until surface flinger 155 // acknowledges hot unplug event. 156 status_t err = mAbortModeSettingCond.waitRelative(mLock, milliseconds(20)); 157 if (err != -ETIMEDOUT) { 158 ILOGTRACE("Mode settings is interrupted"); 159 mHwc.hotplug(mType, true); 160 return; 161 } 162 } 163 164 // TODO: potential threading issue with onHotplug callback 165 mHdcpControl->stopHdcp(); 166 if (!drm->setDrmMode(mType, mPendingDrmMode)) { 167 ELOGTRACE("failed to set Drm mode"); 168 mHwc.hotplug(mType, true); 169 return; 170 } 171 172 if (!PhysicalDevice::updateDisplayConfigs()) { 173 ELOGTRACE("failed to update display configs"); 174 mHwc.hotplug(mType, true); 175 return; 176 } 177 mConnected = true; 178 mHotplugEventPending = true; 179 // delay sending hotplug event until HDCP is authenticated 180 if (mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this) == false) { 181 ELOGTRACE("startHdcpAsync() failed; HDCP is not enabled"); 182 mHotplugEventPending = false; 183 mHwc.hotplug(mType, true); 184 } 185 mExpectedRefreshRate = 0; 186 } 187 188 189 void ExternalDevice::HdcpLinkStatusListener(bool success, void *userData) 190 { 191 if (userData == NULL) { 192 return; 193 } 194 195 ExternalDevice *p = (ExternalDevice*)userData; 196 p->HdcpLinkStatusListener(success); 197 } 198 199 void ExternalDevice::HdcpLinkStatusListener(bool success) 200 { 201 if (mHotplugEventPending) { 202 DLOGTRACE("HDCP authentication status %d, sending hotplug event...", success); 203 mHwc.hotplug(mType, mConnected); 204 mHotplugEventPending = false; 205 } 206 } 207 208 void ExternalDevice::hotplugEventListener(void *data) 209 { 210 ExternalDevice *pThis = (ExternalDevice*)data; 211 if (pThis) { 212 pThis->hotplugListener(); 213 } 214 } 215 216 void ExternalDevice::hotplugListener() 217 { 218 bool ret; 219 220 CTRACE(); 221 222 // abort mode settings if it is in the middle 223 mAbortModeSettingCond.signal(); 224 225 // remember the current connection status before detection 226 bool connected = mConnected; 227 228 // detect display configs 229 ret = detectDisplayConfigs(); 230 if (ret == false) { 231 ELOGTRACE("failed to detect display config"); 232 return; 233 } 234 235 ILOGTRACE("hotpug event: %d", mConnected); 236 237 if (connected == mConnected) { 238 WLOGTRACE("same connection status detected, hotplug event ignored"); 239 return; 240 } 241 242 if (mConnected == false) { 243 mHotplugEventPending = false; 244 mHdcpControl->stopHdcp(); 245 mHwc.hotplug(mType, mConnected); 246 } else { 247 DLOGTRACE("start HDCP asynchronously..."); 248 // delay sending hotplug event till HDCP is authenticated. 249 mHotplugEventPending = true; 250 ret = mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this); 251 if (ret == false) { 252 ELOGTRACE("failed to start HDCP"); 253 mHotplugEventPending = false; 254 mHwc.hotplug(mType, mConnected); 255 } 256 } 257 mActiveDisplayConfig = 0; 258 } 259 260 void ExternalDevice::setRefreshRate(int hz) 261 { 262 RETURN_VOID_IF_NOT_INIT(); 263 264 ILOGTRACE("setting refresh rate to %d", hz); 265 266 if (mBlank) { 267 WLOGTRACE("external device is blank"); 268 return; 269 } 270 271 Drm *drm = Hwcomposer::getInstance().getDrm(); 272 drmModeModeInfo mode; 273 if (!drm->getModeInfo(IDisplayDevice::DEVICE_EXTERNAL, mode)) 274 return; 275 276 if (hz == 0 && (mode.type & DRM_MODE_TYPE_PREFERRED)) 277 return; 278 279 if (hz == (int)mode.vrefresh) 280 return; 281 282 if (mExpectedRefreshRate != 0 && 283 mExpectedRefreshRate == hz && mHotplugEventPending) { 284 ILOGTRACE("Ignore a new refresh setting event because there is a same event is handling"); 285 return; 286 } 287 mExpectedRefreshRate = hz; 288 289 ILOGTRACE("changing refresh rate from %d to %d", mode.vrefresh, hz); 290 291 mHdcpControl->stopHdcp(); 292 293 drm->setRefreshRate(IDisplayDevice::DEVICE_EXTERNAL, hz); 294 295 mHotplugEventPending = false; 296 mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this); 297 } 298 299 300 bool ExternalDevice::getDisplaySize(int *width, int *height) 301 { 302 #ifndef INTEL_SUPPORT_HDMI_PRIMARY 303 return PhysicalDevice::getDisplaySize(width, height); 304 #else 305 if (mConnected) 306 return PhysicalDevice::getDisplaySize(width, height); 307 308 if (!width || !height) 309 return false; 310 311 *width = 1920; 312 *height = 1080; 313 return true; 314 #endif 315 } 316 317 bool ExternalDevice::getDisplayConfigs(uint32_t *configs, size_t *numConfigs) 318 { 319 #ifndef INTEL_SUPPORT_HDMI_PRIMARY 320 return PhysicalDevice::getDisplayConfigs(configs, numConfigs); 321 #else 322 if (mConnected) 323 return PhysicalDevice::getDisplayConfigs(configs, numConfigs); 324 325 if (!configs || !numConfigs) 326 return false; 327 328 *configs = 0; 329 *numConfigs = 1; 330 return true; 331 #endif 332 } 333 334 bool ExternalDevice::getDisplayAttributes(uint32_t config, 335 const uint32_t *attributes, 336 int32_t *values) 337 { 338 #ifndef INTEL_SUPPORT_HDMI_PRIMARY 339 return PhysicalDevice::getDisplayAttributes(config, attributes, values); 340 #else 341 if (mConnected) 342 return PhysicalDevice::getDisplayAttributes(config, attributes, values); 343 if (!attributes || !values) 344 return false; 345 int i = 0; 346 while (attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE) { 347 switch (attributes[i]) { 348 case HWC_DISPLAY_VSYNC_PERIOD: 349 values[i] = 1e9 / 60; 350 break; 351 case HWC_DISPLAY_WIDTH: 352 values[i] = 1920; 353 break; 354 case HWC_DISPLAY_HEIGHT: 355 values[i] = 1080; 356 break; 357 case HWC_DISPLAY_DPI_X: 358 values[i] = 1; 359 break; 360 case HWC_DISPLAY_DPI_Y: 361 values[i] = 1; 362 break; 363 default: 364 ELOGTRACE("unknown attribute %d", attributes[i]); 365 break; 366 } 367 i++; 368 } 369 return true; 370 #endif 371 } 372 373 int ExternalDevice::getActiveConfig() 374 { 375 if (!mConnected) { 376 return 0; 377 } 378 return mActiveDisplayConfig; 379 } 380 381 bool ExternalDevice::setActiveConfig(int index) 382 { 383 if (!mConnected) { 384 if (index == 0) 385 return true; 386 else 387 return false; 388 } 389 390 // for now we will only permit the frequency change. In the future 391 // we may need to set mode as well. 392 if (index >= 0 && index < static_cast<int>(mDisplayConfigs.size())) { 393 DisplayConfig *config = mDisplayConfigs.itemAt(index); 394 setRefreshRate(config->getRefreshRate()); 395 mActiveDisplayConfig = index; 396 return true; 397 } else { 398 return false; 399 } 400 return true; 401 } 402 403 404 405 } // namespace intel 406 } // namespace android 407