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