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