| /* |
| // Copyright (c) 2014 Intel Corporation |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| */ |
| |
| #include <HwcTrace.h> |
| #include <Drm.h> |
| #include <DrmConfig.h> |
| #include <Hwcomposer.h> |
| #include <ExternalDevice.h> |
| |
| namespace android { |
| namespace intel { |
| |
| ExternalDevice::ExternalDevice(Hwcomposer& hwc, DeviceControlFactory* controlFactory) |
| : PhysicalDevice(DEVICE_EXTERNAL, hwc, controlFactory), |
| mHdcpControl(NULL), |
| mAbortModeSettingCond(), |
| mPendingDrmMode(), |
| mHotplugEventPending(false), |
| mExpectedRefreshRate(0) |
| { |
| CTRACE(); |
| } |
| |
| ExternalDevice::~ExternalDevice() |
| { |
| CTRACE(); |
| } |
| |
| bool ExternalDevice::initialize() |
| { |
| if (!PhysicalDevice::initialize()) { |
| DEINIT_AND_RETURN_FALSE("failed to initialize physical device"); |
| } |
| |
| mHdcpControl = mControlFactory->createHdcpControl(); |
| if (!mHdcpControl) { |
| DEINIT_AND_RETURN_FALSE("failed to create HDCP control"); |
| } |
| |
| mHotplugEventPending = false; |
| if (mConnected) { |
| mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this); |
| } |
| |
| UeventObserver *observer = Hwcomposer::getInstance().getUeventObserver(); |
| if (observer) { |
| observer->registerListener( |
| DrmConfig::getHotplugString(), |
| hotplugEventListener, |
| this); |
| } else { |
| ETRACE("Uevent observer is NULL"); |
| } |
| return true; |
| } |
| |
| void ExternalDevice::deinitialize() |
| { |
| // abort mode settings if it is in the middle |
| mAbortModeSettingCond.signal(); |
| if (mThread.get()) { |
| mThread->join(); |
| mThread = NULL; |
| } |
| |
| if (mHdcpControl) { |
| mHdcpControl->stopHdcp(); |
| delete mHdcpControl; |
| mHdcpControl = 0; |
| } |
| |
| mHotplugEventPending = false; |
| PhysicalDevice::deinitialize(); |
| } |
| |
| bool ExternalDevice::setDrmMode(drmModeModeInfo& value) |
| { |
| if (!mConnected) { |
| WTRACE("external device is not connected"); |
| return false; |
| } |
| |
| if (mThread.get()) { |
| mThread->join(); |
| mThread = NULL; |
| } |
| |
| Drm *drm = Hwcomposer::getInstance().getDrm(); |
| drmModeModeInfo mode; |
| drm->getModeInfo(mType, mode); |
| if (drm->isSameDrmMode(&value, &mode)) |
| return true; |
| |
| // any issue here by faking connection status? |
| mConnected = false; |
| mPendingDrmMode = value; |
| |
| // setting mode in a working thread |
| mThread = new ModeSettingThread(this); |
| if (!mThread.get()) { |
| ETRACE("failed to create mode settings thread"); |
| return false; |
| } |
| |
| mThread->run("ModeSettingsThread", PRIORITY_URGENT_DISPLAY); |
| return true; |
| } |
| |
| bool ExternalDevice::threadLoop() |
| { |
| // one-time execution |
| setDrmMode(); |
| return false; |
| } |
| |
| void ExternalDevice::setDrmMode() |
| { |
| ITRACE("start mode setting..."); |
| |
| Drm *drm = Hwcomposer::getInstance().getDrm(); |
| |
| mConnected = false; |
| mHwc.hotplug(mType, false); |
| |
| { |
| Mutex::Autolock lock(mLock); |
| // TODO: make timeout value flexible, or wait until surface flinger |
| // acknowledges hot unplug event. |
| status_t err = mAbortModeSettingCond.waitRelative(mLock, milliseconds(20)); |
| if (err != -ETIMEDOUT) { |
| ITRACE("Mode settings is interrupted"); |
| mHwc.hotplug(mType, true); |
| return; |
| } |
| } |
| |
| // TODO: potential threading issue with onHotplug callback |
| mHdcpControl->stopHdcp(); |
| if (!drm->setDrmMode(mType, mPendingDrmMode)) { |
| ETRACE("failed to set Drm mode"); |
| mHwc.hotplug(mType, true); |
| return; |
| } |
| |
| if (!PhysicalDevice::updateDisplayConfigs()) { |
| ETRACE("failed to update display configs"); |
| mHwc.hotplug(mType, true); |
| return; |
| } |
| mConnected = true; |
| mHotplugEventPending = true; |
| // delay sending hotplug event until HDCP is authenticated |
| if (mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this) == false) { |
| ETRACE("startHdcpAsync() failed; HDCP is not enabled"); |
| mHotplugEventPending = false; |
| mHwc.hotplug(mType, true); |
| } |
| mExpectedRefreshRate = 0; |
| } |
| |
| |
| void ExternalDevice::HdcpLinkStatusListener(bool success, void *userData) |
| { |
| if (userData == NULL) { |
| return; |
| } |
| |
| ExternalDevice *p = (ExternalDevice*)userData; |
| p->HdcpLinkStatusListener(success); |
| } |
| |
| void ExternalDevice::HdcpLinkStatusListener(bool success) |
| { |
| if (!success) { |
| ETRACE("HDCP is not authenticated, disabling dynamic vsync"); |
| mHwc.getVsyncManager()->enableDynamicVsync(false); |
| } |
| |
| if (mHotplugEventPending) { |
| DTRACE("HDCP authentication status %d, sending hotplug event...", success); |
| mHwc.hotplug(mType, mConnected); |
| mHotplugEventPending = false; |
| } |
| |
| if (success) { |
| ITRACE("HDCP authenticated, enabling dynamic vsync"); |
| mHwc.getVsyncManager()->enableDynamicVsync(true); |
| } |
| } |
| |
| void ExternalDevice::hotplugEventListener(void *data) |
| { |
| ExternalDevice *pThis = (ExternalDevice*)data; |
| if (pThis) { |
| pThis->hotplugListener(); |
| } |
| } |
| |
| void ExternalDevice::hotplugListener() |
| { |
| bool ret; |
| |
| CTRACE(); |
| |
| // abort mode settings if it is in the middle |
| mAbortModeSettingCond.signal(); |
| |
| // remember the current connection status before detection |
| bool connected = mConnected; |
| |
| // detect display configs |
| ret = detectDisplayConfigs(); |
| if (ret == false) { |
| ETRACE("failed to detect display config"); |
| return; |
| } |
| |
| ITRACE("hotpug event: %d", mConnected); |
| |
| if (connected == mConnected) { |
| WTRACE("same connection status detected, hotplug event ignored"); |
| return; |
| } |
| |
| if (mConnected == false) { |
| mHotplugEventPending = false; |
| mHwc.getVsyncManager()->resetVsyncSource(); |
| mHdcpControl->stopHdcp(); |
| mHwc.hotplug(mType, mConnected); |
| } else { |
| DTRACE("start HDCP asynchronously..."); |
| // delay sending hotplug event till HDCP is authenticated. |
| mHotplugEventPending = true; |
| ret = mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this); |
| if (ret == false) { |
| ETRACE("failed to start HDCP"); |
| mHotplugEventPending = false; |
| mHwc.hotplug(mType, mConnected); |
| } |
| } |
| mActiveDisplayConfig = 0; |
| } |
| |
| int ExternalDevice::getRefreshRate() |
| { |
| Drm *drm = Hwcomposer::getInstance().getDrm(); |
| drmModeModeInfo mode; |
| if (!drm->getModeInfo(IDisplayDevice::DEVICE_EXTERNAL, mode)) |
| return 0; |
| return mode.vrefresh; |
| } |
| |
| void ExternalDevice::setRefreshRate(int hz) |
| { |
| RETURN_VOID_IF_NOT_INIT(); |
| |
| ITRACE("setting refresh rate to %d", hz); |
| |
| if (mBlank) { |
| WTRACE("external device is blank"); |
| return; |
| } |
| |
| Drm *drm = Hwcomposer::getInstance().getDrm(); |
| drmModeModeInfo mode; |
| if (!drm->getModeInfo(IDisplayDevice::DEVICE_EXTERNAL, mode)) |
| return; |
| |
| if (hz == 0 && (mode.type & DRM_MODE_TYPE_PREFERRED)) |
| return; |
| |
| if (hz == (int)mode.vrefresh) |
| return; |
| |
| if (mExpectedRefreshRate != 0 && |
| mExpectedRefreshRate == hz && mHotplugEventPending) { |
| ITRACE("Ignore a new refresh setting event because there is a same event is handling"); |
| return; |
| } |
| mExpectedRefreshRate = hz; |
| |
| ITRACE("changing refresh rate from %d to %d", mode.vrefresh, hz); |
| |
| mHwc.getVsyncManager()->enableDynamicVsync(false); |
| |
| mHdcpControl->stopHdcp(); |
| |
| drm->setRefreshRate(IDisplayDevice::DEVICE_EXTERNAL, hz); |
| |
| mHotplugEventPending = false; |
| mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this); |
| mHwc.getVsyncManager()->enableDynamicVsync(true); |
| } |
| |
| int ExternalDevice::getActiveConfig() |
| { |
| if (!mConnected) { |
| return 0; |
| } |
| return mActiveDisplayConfig; |
| } |
| |
| bool ExternalDevice::setActiveConfig(int index) |
| { |
| if (!mConnected) { |
| if (index == 0) |
| return true; |
| else |
| return false; |
| } |
| |
| // for now we will only permit the frequency change. In the future |
| // we may need to set mode as well. |
| if (index >= 0 && index < static_cast<int>(mDisplayConfigs.size())) { |
| DisplayConfig *config = mDisplayConfigs.itemAt(index); |
| setRefreshRate(config->getRefreshRate()); |
| mActiveDisplayConfig = index; |
| return true; |
| } else { |
| return false; |
| } |
| return true; |
| } |
| |
| } // namespace intel |
| } // namespace android |