|
|
/* |
|
|
// 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. |
|
|
*/ |
|
|
#ifdef TARGET_HAS_MULTIPLE_DISPLAY |
|
|
#include <HwcTrace.h> |
|
|
#include <binder/IServiceManager.h> |
|
|
#include <Hwcomposer.h> |
|
|
#include <DisplayAnalyzer.h> |
|
|
#include <ExternalDevice.h> |
|
|
#endif |
|
|
|
|
|
#include <MultiDisplayObserver.h> |
|
|
|
|
|
namespace android { |
|
|
namespace intel { |
|
|
|
|
|
#ifdef TARGET_HAS_MULTIPLE_DISPLAY |
|
|
|
|
|
////// MultiDisplayCallback |
|
|
|
|
|
MultiDisplayCallback::MultiDisplayCallback(MultiDisplayObserver *dispObserver) |
|
|
: mDispObserver(dispObserver), |
|
|
mVideoState(MDS_VIDEO_STATE_UNKNOWN) |
|
|
{ |
|
|
} |
|
|
|
|
|
MultiDisplayCallback::~MultiDisplayCallback() |
|
|
{ |
|
|
CTRACE(); |
|
|
mDispObserver = NULL; |
|
|
} |
|
|
|
|
|
status_t MultiDisplayCallback::blankSecondaryDisplay(bool blank) |
|
|
{ |
|
|
ITRACE("blank: %d", blank); |
|
|
mDispObserver->blankSecondaryDisplay(blank); |
|
|
return NO_ERROR; |
|
|
} |
|
|
|
|
|
status_t MultiDisplayCallback::updateVideoState(int sessionId, MDS_VIDEO_STATE state) |
|
|
{ |
|
|
mVideoState = state; |
|
|
ITRACE("state: %d", state); |
|
|
mDispObserver->updateVideoState(sessionId, state); |
|
|
return NO_ERROR; |
|
|
} |
|
|
|
|
|
status_t MultiDisplayCallback::setHdmiTiming(const MDSHdmiTiming& timing) |
|
|
{ |
|
|
mDispObserver->setHdmiTiming(timing); |
|
|
return NO_ERROR; |
|
|
} |
|
|
|
|
|
status_t MultiDisplayCallback::updateInputState(bool state) |
|
|
{ |
|
|
//ITRACE("input state: %d", state); |
|
|
mDispObserver->updateInputState(state); |
|
|
return NO_ERROR; |
|
|
} |
|
|
|
|
|
status_t MultiDisplayCallback::setHdmiScalingType(MDS_SCALING_TYPE type) |
|
|
{ |
|
|
ITRACE("scaling type: %d", type); |
|
|
// Merrifield doesn't implement this API |
|
|
return INVALID_OPERATION; |
|
|
} |
|
|
|
|
|
status_t MultiDisplayCallback::setHdmiOverscan(int hValue, int vValue) |
|
|
{ |
|
|
ITRACE("oversacn compensation, h: %d v: %d", hValue, vValue); |
|
|
// Merrifield doesn't implement this API |
|
|
return INVALID_OPERATION; |
|
|
} |
|
|
|
|
|
////// MultiDisplayObserver |
|
|
|
|
|
MultiDisplayObserver::MultiDisplayObserver() |
|
|
: mMDSCbRegistrar(NULL), |
|
|
mMDSInfoProvider(NULL), |
|
|
mMDSConnObserver(NULL), |
|
|
mMDSDecoderConfig(NULL), |
|
|
mMDSCallback(NULL), |
|
|
mLock(), |
|
|
mCondition(), |
|
|
mThreadLoopCount(0), |
|
|
mDeviceConnected(false), |
|
|
mExternalHdmiTiming(false), |
|
|
mInitialized(false) |
|
|
{ |
|
|
CTRACE(); |
|
|
} |
|
|
|
|
|
MultiDisplayObserver::~MultiDisplayObserver() |
|
|
{ |
|
|
WARN_IF_NOT_DEINIT(); |
|
|
} |
|
|
|
|
|
bool MultiDisplayObserver::isMDSRunning() |
|
|
{ |
|
|
// Check if Multi Display service is running |
|
|
sp<IServiceManager> sm = defaultServiceManager(); |
|
|
if (sm == NULL) { |
|
|
ETRACE("fail to get service manager!"); |
|
|
return false; |
|
|
} |
|
|
|
|
|
sp<IBinder> service = sm->checkService(String16(INTEL_MDS_SERVICE_NAME)); |
|
|
if (service == NULL) { |
|
|
VTRACE("fail to get MultiDisplay service!"); |
|
|
return false; |
|
|
} |
|
|
|
|
|
return true; |
|
|
} |
|
|
|
|
|
bool MultiDisplayObserver::initMDSClient() |
|
|
{ |
|
|
sp<IServiceManager> sm = defaultServiceManager(); |
|
|
if (sm == NULL) { |
|
|
ETRACE("Fail to get service manager"); |
|
|
return false; |
|
|
} |
|
|
sp<IMDService> mds = interface_cast<IMDService>( |
|
|
sm->getService(String16(INTEL_MDS_SERVICE_NAME))); |
|
|
if (mds == NULL) { |
|
|
ETRACE("Fail to get MDS service"); |
|
|
return false; |
|
|
} |
|
|
mMDSCbRegistrar = mds->getCallbackRegistrar(); |
|
|
if (mMDSCbRegistrar.get() == NULL) { |
|
|
ETRACE("failed to create mds base Client"); |
|
|
return false; |
|
|
} |
|
|
|
|
|
mMDSCallback = new MultiDisplayCallback(this); |
|
|
if (mMDSCallback.get() == NULL) { |
|
|
ETRACE("failed to create MultiDisplayCallback"); |
|
|
deinitMDSClient(); |
|
|
return false; |
|
|
} |
|
|
mMDSInfoProvider = mds->getInfoProvider(); |
|
|
if (mMDSInfoProvider.get() == NULL) { |
|
|
ETRACE("failed to create mds video Client"); |
|
|
return false; |
|
|
} |
|
|
|
|
|
mMDSConnObserver = mds->getConnectionObserver(); |
|
|
if (mMDSConnObserver.get() == NULL) { |
|
|
ETRACE("failed to create mds video Client"); |
|
|
return false; |
|
|
} |
|
|
mMDSDecoderConfig = mds->getDecoderConfig(); |
|
|
if (mMDSDecoderConfig.get() == NULL) { |
|
|
ETRACE("failed to create mds decoder Client"); |
|
|
return false; |
|
|
} |
|
|
|
|
|
status_t ret = mMDSCbRegistrar->registerCallback(mMDSCallback); |
|
|
if (ret != NO_ERROR) { |
|
|
ETRACE("failed to register callback"); |
|
|
deinitMDSClient(); |
|
|
return false; |
|
|
} |
|
|
|
|
|
Drm *drm = Hwcomposer::getInstance().getDrm(); |
|
|
mDeviceConnected = drm->isConnected(IDisplayDevice::DEVICE_EXTERNAL); |
|
|
ITRACE("MDS client is initialized"); |
|
|
return true; |
|
|
} |
|
|
|
|
|
void MultiDisplayObserver::deinitMDSClient() |
|
|
{ |
|
|
if (mMDSCallback.get() && mMDSCbRegistrar.get()) { |
|
|
mMDSCbRegistrar->unregisterCallback(mMDSCallback); |
|
|
} |
|
|
|
|
|
mDeviceConnected = false; |
|
|
mMDSCbRegistrar = NULL; |
|
|
mMDSInfoProvider = NULL; |
|
|
mMDSCallback = NULL; |
|
|
mMDSConnObserver = NULL; |
|
|
mMDSDecoderConfig = NULL; |
|
|
} |
|
|
|
|
|
bool MultiDisplayObserver::initMDSClientAsync() |
|
|
{ |
|
|
if (mThread.get()) { |
|
|
WTRACE("working thread has been already created."); |
|
|
return true; |
|
|
} |
|
|
|
|
|
mThread = new MDSClientInitThread(this); |
|
|
if (mThread.get() == NULL) { |
|
|
ETRACE("failed to create MDS client init thread"); |
|
|
return false; |
|
|
} |
|
|
mThreadLoopCount = 0; |
|
|
// TODO: check return value |
|
|
mThread->run("MDSClientInitThread", PRIORITY_URGENT_DISPLAY); |
|
|
return true; |
|
|
} |
|
|
|
|
|
bool MultiDisplayObserver::initialize() |
|
|
{ |
|
|
bool ret = true; |
|
|
Mutex::Autolock _l(mLock); |
|
|
|
|
|
if (mInitialized) { |
|
|
WTRACE("display observer has been initialized"); |
|
|
return true; |
|
|
} |
|
|
|
|
|
// initialize MDS client once. This should succeed if MDS service starts |
|
|
// before surfaceflinger service is started. |
|
|
// if surface flinger runs first, MDS client will be initialized asynchronously in |
|
|
// a working thread |
|
|
if (isMDSRunning()) { |
|
|
if (!initMDSClient()) { |
|
|
ETRACE("failed to initialize MDS client"); |
|
|
// FIXME: NOT a common case for system server crash. |
|
|
// Start a working thread to initialize MDS client if exception happens |
|
|
ret = initMDSClientAsync(); |
|
|
} |
|
|
} else { |
|
|
ret = initMDSClientAsync(); |
|
|
} |
|
|
|
|
|
mInitialized = true; |
|
|
return ret; |
|
|
} |
|
|
|
|
|
void MultiDisplayObserver::deinitialize() |
|
|
{ |
|
|
sp<MDSClientInitThread> detachedThread; |
|
|
do { |
|
|
Mutex::Autolock _l(mLock); |
|
|
|
|
|
if (mThread.get()) { |
|
|
mCondition.signal(); |
|
|
detachedThread = mThread; |
|
|
mThread = NULL; |
|
|
} |
|
|
mThreadLoopCount = 0; |
|
|
deinitMDSClient(); |
|
|
mInitialized = false; |
|
|
} while (0); |
|
|
|
|
|
if (detachedThread.get()) { |
|
|
detachedThread->requestExitAndWait(); |
|
|
detachedThread = NULL; |
|
|
} |
|
|
} |
|
|
|
|
|
bool MultiDisplayObserver::threadLoop() |
|
|
{ |
|
|
Mutex::Autolock _l(mLock); |
|
|
|
|
|
// try to create MDS client in the working thread |
|
|
// multiple delayed attempts are made until MDS service starts. |
|
|
|
|
|
// Return false if MDS service is running or loop limit is reached |
|
|
// such that thread becomes inactive. |
|
|
if (isMDSRunning()) { |
|
|
if (!initMDSClient()) { |
|
|
ETRACE("failed to initialize MDS client"); |
|
|
} |
|
|
return false; |
|
|
} |
|
|
|
|
|
if (mThreadLoopCount++ > THREAD_LOOP_BOUND) { |
|
|
ETRACE("failed to initialize MDS client, loop limit reached"); |
|
|
return false; |
|
|
} |
|
|
|
|
|
status_t err = mCondition.waitRelative(mLock, milliseconds(THREAD_LOOP_DELAY)); |
|
|
if (err != -ETIMEDOUT) { |
|
|
ITRACE("thread is interrupted"); |
|
|
return false; |
|
|
} |
|
|
|
|
|
return true; // keep trying |
|
|
} |
|
|
|
|
|
|
|
|
status_t MultiDisplayObserver::blankSecondaryDisplay(bool blank) |
|
|
{ |
|
|
// blank secondary display |
|
|
Hwcomposer::getInstance().getDisplayAnalyzer()->postBlankEvent(blank); |
|
|
return 0; |
|
|
} |
|
|
|
|
|
status_t MultiDisplayObserver::updateVideoState(int sessionId, MDS_VIDEO_STATE state) |
|
|
{ |
|
|
Hwcomposer::getInstance().getDisplayAnalyzer()->postVideoEvent( |
|
|
sessionId, (int)state); |
|
|
return 0; |
|
|
} |
|
|
|
|
|
status_t MultiDisplayObserver::setHdmiTiming(const MDSHdmiTiming& timing) |
|
|
{ |
|
|
drmModeModeInfo mode; |
|
|
mode.hdisplay = timing.width; |
|
|
mode.vdisplay = timing.height; |
|
|
mode.vrefresh = timing.refresh; |
|
|
mode.flags = timing.flags; |
|
|
ITRACE("timing to set: %dx%d@%dHz", timing.width, timing.height, timing.refresh); |
|
|
ExternalDevice *dev = |
|
|
(ExternalDevice *)Hwcomposer::getInstance().getDisplayDevice(HWC_DISPLAY_EXTERNAL); |
|
|
if (dev) { |
|
|
dev->setDrmMode(mode); |
|
|
} |
|
|
|
|
|
mExternalHdmiTiming = true; |
|
|
return 0; |
|
|
} |
|
|
|
|
|
status_t MultiDisplayObserver::updateInputState(bool active) |
|
|
{ |
|
|
Hwcomposer::getInstance().getDisplayAnalyzer()->postInputEvent(active); |
|
|
return 0; |
|
|
} |
|
|
|
|
|
|
|
|
/// Public interfaces |
|
|
|
|
|
status_t MultiDisplayObserver::notifyHotPlug( bool connected) |
|
|
{ |
|
|
{ |
|
|
// lock scope |
|
|
Mutex::Autolock _l(mLock); |
|
|
if (mMDSConnObserver.get() == NULL) { |
|
|
return NO_INIT; |
|
|
} |
|
|
|
|
|
if (connected == mDeviceConnected) { |
|
|
WTRACE("hotplug event ignored"); |
|
|
return NO_ERROR; |
|
|
} |
|
|
|
|
|
// clear it after externel device is disconnected |
|
|
if (!connected) mExternalHdmiTiming = false; |
|
|
|
|
|
mDeviceConnected = connected; |
|
|
} |
|
|
return mMDSConnObserver->updateHdmiConnectionStatus(connected); |
|
|
} |
|
|
|
|
|
status_t MultiDisplayObserver::getVideoSourceInfo(int sessionID, VideoSourceInfo* info) |
|
|
{ |
|
|
Mutex::Autolock _l(mLock); |
|
|
if (mMDSInfoProvider.get() == NULL) { |
|
|
return NO_INIT; |
|
|
} |
|
|
|
|
|
if (info == NULL) { |
|
|
ETRACE("invalid parameter"); |
|
|
return UNKNOWN_ERROR; |
|
|
} |
|
|
|
|
|
MDSVideoSourceInfo videoInfo; |
|
|
memset(&videoInfo, 0, sizeof(MDSVideoSourceInfo)); |
|
|
status_t ret = mMDSInfoProvider->getVideoSourceInfo(sessionID, &videoInfo); |
|
|
if (ret == NO_ERROR) { |
|
|
info->width = videoInfo.displayW; |
|
|
info->height = videoInfo.displayH; |
|
|
info->frameRate = videoInfo.frameRate; |
|
|
info->isProtected = videoInfo.isProtected; |
|
|
VTRACE("Video Session[%d] source info: %dx%d@%d", sessionID, |
|
|
info->width, info->height, info->frameRate); |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
int MultiDisplayObserver::getVideoSessionNumber() |
|
|
{ |
|
|
Mutex::Autolock _l(mLock); |
|
|
if (mMDSInfoProvider.get() == NULL) { |
|
|
return 0; |
|
|
} |
|
|
|
|
|
return mMDSInfoProvider->getVideoSessionNumber(); |
|
|
} |
|
|
|
|
|
bool MultiDisplayObserver::isExternalDeviceTimingFixed() const |
|
|
{ |
|
|
Mutex::Autolock _l(mLock); |
|
|
return mExternalHdmiTiming; |
|
|
} |
|
|
|
|
|
status_t MultiDisplayObserver::notifyWidiConnectionStatus( bool connected) |
|
|
{ |
|
|
Mutex::Autolock _l(mLock); |
|
|
if (mMDSConnObserver.get() == NULL) { |
|
|
return NO_INIT; |
|
|
} |
|
|
return mMDSConnObserver->updateWidiConnectionStatus(connected); |
|
|
} |
|
|
|
|
|
status_t MultiDisplayObserver::setDecoderOutputResolution( |
|
|
int sessionID, |
|
|
int32_t width, int32_t height, |
|
|
int32_t offX, int32_t offY, |
|
|
int32_t bufWidth, int32_t bufHeight) |
|
|
{ |
|
|
Mutex::Autolock _l(mLock); |
|
|
if (mMDSDecoderConfig.get() == NULL) { |
|
|
return NO_INIT; |
|
|
} |
|
|
if (width <= 0 || height <= 0 || |
|
|
offX < 0 || offY < 0 || |
|
|
bufWidth <= 0 || bufHeight <= 0) { |
|
|
ETRACE(" Invalid parameter: %dx%d, %dx%d, %dx%d", width, height, offX, offY, bufWidth, bufHeight); |
|
|
return UNKNOWN_ERROR; |
|
|
} |
|
|
|
|
|
status_t ret = mMDSDecoderConfig->setDecoderOutputResolution(sessionID, width, height, offX, offY, bufWidth, bufHeight); |
|
|
if (ret == NO_ERROR) { |
|
|
ITRACE("Video Session[%d] output resolution %dx%d ", sessionID, width, height); |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
|
|
|
#endif //TARGET_HAS_MULTIPLE_DISPLAY |
|
|
|
|
|
} // namespace intel |
|
|
} // namespace android
|
|
|
|