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