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 <HwcTrace.h> 18 #include <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, DeviceControlFactory* controlFactory) 27 : PhysicalDevice(DEVICE_EXTERNAL, hwc, controlFactory), 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 = mControlFactory->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 ETRACE("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::setDrmMode(drmModeModeInfo& value) 90 { 91 if (!mConnected) { 92 WTRACE("external device is not connected"); 93 return false; 94 } 95 96 if (mThread.get()) { 97 mThread->join(); 98 mThread = NULL; 99 } 100 101 Drm *drm = Hwcomposer::getInstance().getDrm(); 102 drmModeModeInfo mode; 103 drm->getModeInfo(mType, mode); 104 if (drm->isSameDrmMode(&value, &mode)) 105 return true; 106 107 // any issue here by faking connection status? 108 mConnected = false; 109 mPendingDrmMode = value; 110 111 // setting mode in a working thread 112 mThread = new ModeSettingThread(this); 113 if (!mThread.get()) { 114 ETRACE("failed to create mode settings thread"); 115 return false; 116 } 117 118 mThread->run("ModeSettingsThread", PRIORITY_URGENT_DISPLAY); 119 return true; 120 } 121 122 bool ExternalDevice::threadLoop() 123 { 124 // one-time execution 125 setDrmMode(); 126 return false; 127 } 128 129 void ExternalDevice::setDrmMode() 130 { 131 ITRACE("start mode setting..."); 132 133 Drm *drm = Hwcomposer::getInstance().getDrm(); 134 135 mConnected = false; 136 mHwc.hotplug(mType, false); 137 138 { 139 Mutex::Autolock lock(mLock); 140 // TODO: make timeout value flexible, or wait until surface flinger 141 // acknowledges hot unplug event. 142 status_t err = mAbortModeSettingCond.waitRelative(mLock, milliseconds(20)); 143 if (err != -ETIMEDOUT) { 144 ITRACE("Mode settings is interrupted"); 145 mHwc.hotplug(mType, true); 146 return; 147 } 148 } 149 150 // TODO: potential threading issue with onHotplug callback 151 mHdcpControl->stopHdcp(); 152 if (!drm->setDrmMode(mType, mPendingDrmMode)) { 153 ETRACE("failed to set Drm mode"); 154 mHwc.hotplug(mType, true); 155 return; 156 } 157 158 if (!PhysicalDevice::updateDisplayConfigs()) { 159 ETRACE("failed to update display configs"); 160 mHwc.hotplug(mType, true); 161 return; 162 } 163 mConnected = true; 164 mHotplugEventPending = true; 165 // delay sending hotplug event until HDCP is authenticated 166 if (mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this) == false) { 167 ETRACE("startHdcpAsync() failed; HDCP is not enabled"); 168 mHotplugEventPending = false; 169 mHwc.hotplug(mType, true); 170 } 171 mExpectedRefreshRate = 0; 172 } 173 174 175 void ExternalDevice::HdcpLinkStatusListener(bool success, void *userData) 176 { 177 if (userData == NULL) { 178 return; 179 } 180 181 ExternalDevice *p = (ExternalDevice*)userData; 182 p->HdcpLinkStatusListener(success); 183 } 184 185 void ExternalDevice::HdcpLinkStatusListener(bool success) 186 { 187 if (!success) { 188 ETRACE("HDCP is not authenticated, disabling dynamic vsync"); 189 mHwc.getVsyncManager()->enableDynamicVsync(false); 190 } 191 192 if (mHotplugEventPending) { 193 DTRACE("HDCP authentication status %d, sending hotplug event...", success); 194 mHwc.hotplug(mType, mConnected); 195 mHotplugEventPending = false; 196 } 197 198 if (success) { 199 ITRACE("HDCP authenticated, enabling dynamic vsync"); 200 mHwc.getVsyncManager()->enableDynamicVsync(true); 201 } 202 } 203 204 void ExternalDevice::hotplugEventListener(void *data) 205 { 206 ExternalDevice *pThis = (ExternalDevice*)data; 207 if (pThis) { 208 pThis->hotplugListener(); 209 } 210 } 211 212 void ExternalDevice::hotplugListener() 213 { 214 bool ret; 215 216 CTRACE(); 217 218 // abort mode settings if it is in the middle 219 mAbortModeSettingCond.signal(); 220 221 // remember the current connection status before detection 222 bool connected = mConnected; 223 224 // detect display configs 225 ret = detectDisplayConfigs(); 226 if (ret == false) { 227 ETRACE("failed to detect display config"); 228 return; 229 } 230 231 ITRACE("hotpug event: %d", mConnected); 232 233 if (connected == mConnected) { 234 WTRACE("same connection status detected, hotplug event ignored"); 235 return; 236 } 237 238 if (mConnected == false) { 239 mHotplugEventPending = false; 240 mHwc.getVsyncManager()->resetVsyncSource(); 241 mHdcpControl->stopHdcp(); 242 mHwc.hotplug(mType, mConnected); 243 } else { 244 DTRACE("start HDCP asynchronously..."); 245 // delay sending hotplug event till HDCP is authenticated. 246 mHotplugEventPending = true; 247 ret = mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this); 248 if (ret == false) { 249 ETRACE("failed to start HDCP"); 250 mHotplugEventPending = false; 251 mHwc.hotplug(mType, mConnected); 252 } 253 } 254 mActiveDisplayConfig = 0; 255 } 256 257 int ExternalDevice::getRefreshRate() 258 { 259 Drm *drm = Hwcomposer::getInstance().getDrm(); 260 drmModeModeInfo mode; 261 if (!drm->getModeInfo(IDisplayDevice::DEVICE_EXTERNAL, mode)) 262 return 0; 263 return mode.vrefresh; 264 } 265 266 void ExternalDevice::setRefreshRate(int hz) 267 { 268 RETURN_VOID_IF_NOT_INIT(); 269 270 ITRACE("setting refresh rate to %d", hz); 271 272 if (mBlank) { 273 WTRACE("external device is blank"); 274 return; 275 } 276 277 Drm *drm = Hwcomposer::getInstance().getDrm(); 278 drmModeModeInfo mode; 279 if (!drm->getModeInfo(IDisplayDevice::DEVICE_EXTERNAL, mode)) 280 return; 281 282 if (hz == 0 && (mode.type & DRM_MODE_TYPE_PREFERRED)) 283 return; 284 285 if (hz == (int)mode.vrefresh) 286 return; 287 288 if (mExpectedRefreshRate != 0 && 289 mExpectedRefreshRate == hz && mHotplugEventPending) { 290 ITRACE("Ignore a new refresh setting event because there is a same event is handling"); 291 return; 292 } 293 mExpectedRefreshRate = hz; 294 295 ITRACE("changing refresh rate from %d to %d", mode.vrefresh, hz); 296 297 mHwc.getVsyncManager()->enableDynamicVsync(false); 298 299 mHdcpControl->stopHdcp(); 300 301 drm->setRefreshRate(IDisplayDevice::DEVICE_EXTERNAL, hz); 302 303 mHotplugEventPending = false; 304 mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this); 305 mHwc.getVsyncManager()->enableDynamicVsync(true); 306 } 307 308 int ExternalDevice::getActiveConfig() 309 { 310 if (!mConnected) { 311 return 0; 312 } 313 return mActiveDisplayConfig; 314 } 315 316 bool ExternalDevice::setActiveConfig(int index) 317 { 318 if (!mConnected) { 319 if (index == 0) 320 return true; 321 else 322 return false; 323 } 324 325 // for now we will only permit the frequency change. In the future 326 // we may need to set mode as well. 327 if (index >= 0 && index < static_cast<int>(mDisplayConfigs.size())) { 328 DisplayConfig *config = mDisplayConfigs.itemAt(index); 329 setRefreshRate(config->getRefreshRate()); 330 mActiveDisplayConfig = index; 331 return true; 332 } else { 333 return false; 334 } 335 return true; 336 } 337 338 } // namespace intel 339 } // namespace android 340