|
|
/* |
|
|
// 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
|
|
|
|