1/* 2// Copyright (c) 2014 Intel Corporation 3// 4// Licensed under the Apache License, Version 2.0 (the "License"); 5// you may not use this file except in compliance with the License. 6// You may obtain a copy of the License at 7// 8// http://www.apache.org/licenses/LICENSE-2.0 9// 10// Unless required by applicable law or agreed to in writing, software 11// distributed under the License is distributed on an "AS IS" BASIS, 12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13// See the License for the specific language governing permissions and 14// limitations under the License. 15*/ 16#ifdef TARGET_HAS_MULTIPLE_DISPLAY 17#include <HwcTrace.h> 18#include <binder/IServiceManager.h> 19#include <Hwcomposer.h> 20#include <DisplayAnalyzer.h> 21#include <ExternalDevice.h> 22#endif 23 24#include <MultiDisplayObserver.h> 25 26namespace android { 27namespace intel { 28 29#ifdef TARGET_HAS_MULTIPLE_DISPLAY 30 31////// MultiDisplayCallback 32 33MultiDisplayCallback::MultiDisplayCallback(MultiDisplayObserver *dispObserver) 34 : mDispObserver(dispObserver), 35 mVideoState(MDS_VIDEO_STATE_UNKNOWN) 36{ 37} 38 39MultiDisplayCallback::~MultiDisplayCallback() 40{ 41 CTRACE(); 42 mDispObserver = NULL; 43} 44 45status_t MultiDisplayCallback::blankSecondaryDisplay(bool blank) 46{ 47 ITRACE("blank: %d", blank); 48 mDispObserver->blankSecondaryDisplay(blank); 49 return NO_ERROR; 50} 51 52status_t MultiDisplayCallback::updateVideoState(int sessionId, MDS_VIDEO_STATE state) 53{ 54 mVideoState = state; 55 ITRACE("state: %d", state); 56 mDispObserver->updateVideoState(sessionId, state); 57 return NO_ERROR; 58} 59 60status_t MultiDisplayCallback::setHdmiTiming(const MDSHdmiTiming& timing) 61{ 62 mDispObserver->setHdmiTiming(timing); 63 return NO_ERROR; 64} 65 66status_t MultiDisplayCallback::updateInputState(bool state) 67{ 68 //ITRACE("input state: %d", state); 69 mDispObserver->updateInputState(state); 70 return NO_ERROR; 71} 72 73status_t MultiDisplayCallback::setHdmiScalingType(MDS_SCALING_TYPE type) 74{ 75 ITRACE("scaling type: %d", type); 76 // Merrifield doesn't implement this API 77 return INVALID_OPERATION; 78} 79 80status_t MultiDisplayCallback::setHdmiOverscan(int hValue, int vValue) 81{ 82 ITRACE("oversacn compensation, h: %d v: %d", hValue, vValue); 83 // Merrifield doesn't implement this API 84 return INVALID_OPERATION; 85} 86 87////// MultiDisplayObserver 88 89MultiDisplayObserver::MultiDisplayObserver() 90 : mMDSCbRegistrar(NULL), 91 mMDSInfoProvider(NULL), 92 mMDSConnObserver(NULL), 93 mMDSDecoderConfig(NULL), 94 mMDSCallback(NULL), 95 mLock(), 96 mCondition(), 97 mThreadLoopCount(0), 98 mDeviceConnected(false), 99 mExternalHdmiTiming(false), 100 mInitialized(false) 101{ 102 CTRACE(); 103} 104 105MultiDisplayObserver::~MultiDisplayObserver() 106{ 107 WARN_IF_NOT_DEINIT(); 108} 109 110bool MultiDisplayObserver::isMDSRunning() 111{ 112 // Check if Multi Display service is running 113 sp<IServiceManager> sm = defaultServiceManager(); 114 if (sm == NULL) { 115 ETRACE("fail to get service manager!"); 116 return false; 117 } 118 119 sp<IBinder> service = sm->checkService(String16(INTEL_MDS_SERVICE_NAME)); 120 if (service == NULL) { 121 VTRACE("fail to get MultiDisplay service!"); 122 return false; 123 } 124 125 return true; 126} 127 128bool MultiDisplayObserver::initMDSClient() 129{ 130 sp<IServiceManager> sm = defaultServiceManager(); 131 if (sm == NULL) { 132 ETRACE("Fail to get service manager"); 133 return false; 134 } 135 sp<IMDService> mds = interface_cast<IMDService>( 136 sm->getService(String16(INTEL_MDS_SERVICE_NAME))); 137 if (mds == NULL) { 138 ETRACE("Fail to get MDS service"); 139 return false; 140 } 141 mMDSCbRegistrar = mds->getCallbackRegistrar(); 142 if (mMDSCbRegistrar.get() == NULL) { 143 ETRACE("failed to create mds base Client"); 144 return false; 145 } 146 147 mMDSCallback = new MultiDisplayCallback(this); 148 if (mMDSCallback.get() == NULL) { 149 ETRACE("failed to create MultiDisplayCallback"); 150 deinitMDSClient(); 151 return false; 152 } 153 mMDSInfoProvider = mds->getInfoProvider(); 154 if (mMDSInfoProvider.get() == NULL) { 155 ETRACE("failed to create mds video Client"); 156 return false; 157 } 158 159 mMDSConnObserver = mds->getConnectionObserver(); 160 if (mMDSConnObserver.get() == NULL) { 161 ETRACE("failed to create mds video Client"); 162 return false; 163 } 164 mMDSDecoderConfig = mds->getDecoderConfig(); 165 if (mMDSDecoderConfig.get() == NULL) { 166 ETRACE("failed to create mds decoder Client"); 167 return false; 168 } 169 170 status_t ret = mMDSCbRegistrar->registerCallback(mMDSCallback); 171 if (ret != NO_ERROR) { 172 ETRACE("failed to register callback"); 173 deinitMDSClient(); 174 return false; 175 } 176 177 Drm *drm = Hwcomposer::getInstance().getDrm(); 178 mDeviceConnected = drm->isConnected(IDisplayDevice::DEVICE_EXTERNAL); 179 ITRACE("MDS client is initialized"); 180 return true; 181} 182 183void MultiDisplayObserver::deinitMDSClient() 184{ 185 if (mMDSCallback.get() && mMDSCbRegistrar.get()) { 186 mMDSCbRegistrar->unregisterCallback(mMDSCallback); 187 } 188 189 mDeviceConnected = false; 190 mMDSCbRegistrar = NULL; 191 mMDSInfoProvider = NULL; 192 mMDSCallback = NULL; 193 mMDSConnObserver = NULL; 194 mMDSDecoderConfig = NULL; 195} 196 197bool MultiDisplayObserver::initMDSClientAsync() 198{ 199 if (mThread.get()) { 200 WTRACE("working thread has been already created."); 201 return true; 202 } 203 204 mThread = new MDSClientInitThread(this); 205 if (mThread.get() == NULL) { 206 ETRACE("failed to create MDS client init thread"); 207 return false; 208 } 209 mThreadLoopCount = 0; 210 // TODO: check return value 211 mThread->run("MDSClientInitThread", PRIORITY_URGENT_DISPLAY); 212 return true; 213} 214 215bool MultiDisplayObserver::initialize() 216{ 217 bool ret = true; 218 Mutex::Autolock _l(mLock); 219 220 if (mInitialized) { 221 WTRACE("display observer has been initialized"); 222 return true; 223 } 224 225 // initialize MDS client once. This should succeed if MDS service starts 226 // before surfaceflinger service is started. 227 // if surface flinger runs first, MDS client will be initialized asynchronously in 228 // a working thread 229 if (isMDSRunning()) { 230 if (!initMDSClient()) { 231 ETRACE("failed to initialize MDS client"); 232 // FIXME: NOT a common case for system server crash. 233 // Start a working thread to initialize MDS client if exception happens 234 ret = initMDSClientAsync(); 235 } 236 } else { 237 ret = initMDSClientAsync(); 238 } 239 240 mInitialized = true; 241 return ret; 242} 243 244void MultiDisplayObserver::deinitialize() 245{ 246 sp<MDSClientInitThread> detachedThread; 247 do { 248 Mutex::Autolock _l(mLock); 249 250 if (mThread.get()) { 251 mCondition.signal(); 252 detachedThread = mThread; 253 mThread = NULL; 254 } 255 mThreadLoopCount = 0; 256 deinitMDSClient(); 257 mInitialized = false; 258 } while (0); 259 260 if (detachedThread.get()) { 261 detachedThread->requestExitAndWait(); 262 detachedThread = NULL; 263 } 264} 265 266bool MultiDisplayObserver::threadLoop() 267{ 268 Mutex::Autolock _l(mLock); 269 270 // try to create MDS client in the working thread 271 // multiple delayed attempts are made until MDS service starts. 272 273 // Return false if MDS service is running or loop limit is reached 274 // such that thread becomes inactive. 275 if (isMDSRunning()) { 276 if (!initMDSClient()) { 277 ETRACE("failed to initialize MDS client"); 278 } 279 return false; 280 } 281 282 if (mThreadLoopCount++ > THREAD_LOOP_BOUND) { 283 ETRACE("failed to initialize MDS client, loop limit reached"); 284 return false; 285 } 286 287 status_t err = mCondition.waitRelative(mLock, milliseconds(THREAD_LOOP_DELAY)); 288 if (err != -ETIMEDOUT) { 289 ITRACE("thread is interrupted"); 290 return false; 291 } 292 293 return true; // keep trying 294} 295 296 297status_t MultiDisplayObserver::blankSecondaryDisplay(bool blank) 298{ 299 // blank secondary display 300 Hwcomposer::getInstance().getDisplayAnalyzer()->postBlankEvent(blank); 301 return 0; 302} 303 304status_t MultiDisplayObserver::updateVideoState(int sessionId, MDS_VIDEO_STATE state) 305{ 306 Hwcomposer::getInstance().getDisplayAnalyzer()->postVideoEvent( 307 sessionId, (int)state); 308 return 0; 309} 310 311status_t MultiDisplayObserver::setHdmiTiming(const MDSHdmiTiming& timing) 312{ 313 drmModeModeInfo mode; 314 mode.hdisplay = timing.width; 315 mode.vdisplay = timing.height; 316 mode.vrefresh = timing.refresh; 317 mode.flags = timing.flags; 318 ITRACE("timing to set: %dx%d@%dHz", timing.width, timing.height, timing.refresh); 319 ExternalDevice *dev = 320 (ExternalDevice *)Hwcomposer::getInstance().getDisplayDevice(HWC_DISPLAY_EXTERNAL); 321 if (dev) { 322 dev->setDrmMode(mode); 323 } 324 325 mExternalHdmiTiming = true; 326 return 0; 327} 328 329status_t MultiDisplayObserver::updateInputState(bool active) 330{ 331 Hwcomposer::getInstance().getDisplayAnalyzer()->postInputEvent(active); 332 return 0; 333} 334 335 336/// Public interfaces 337 338status_t MultiDisplayObserver::notifyHotPlug( bool connected) 339{ 340 { 341 // lock scope 342 Mutex::Autolock _l(mLock); 343 if (mMDSConnObserver.get() == NULL) { 344 return NO_INIT; 345 } 346 347 if (connected == mDeviceConnected) { 348 WTRACE("hotplug event ignored"); 349 return NO_ERROR; 350 } 351 352 // clear it after externel device is disconnected 353 if (!connected) mExternalHdmiTiming = false; 354 355 mDeviceConnected = connected; 356 } 357 return mMDSConnObserver->updateHdmiConnectionStatus(connected); 358} 359 360status_t MultiDisplayObserver::getVideoSourceInfo(int sessionID, VideoSourceInfo* info) 361{ 362 Mutex::Autolock _l(mLock); 363 if (mMDSInfoProvider.get() == NULL) { 364 return NO_INIT; 365 } 366 367 if (info == NULL) { 368 ETRACE("invalid parameter"); 369 return UNKNOWN_ERROR; 370 } 371 372 MDSVideoSourceInfo videoInfo; 373 memset(&videoInfo, 0, sizeof(MDSVideoSourceInfo)); 374 status_t ret = mMDSInfoProvider->getVideoSourceInfo(sessionID, &videoInfo); 375 if (ret == NO_ERROR) { 376 info->width = videoInfo.displayW; 377 info->height = videoInfo.displayH; 378 info->frameRate = videoInfo.frameRate; 379 info->isProtected = videoInfo.isProtected; 380 VTRACE("Video Session[%d] source info: %dx%d@%d", sessionID, 381 info->width, info->height, info->frameRate); 382 } 383 return ret; 384} 385 386int MultiDisplayObserver::getVideoSessionNumber() 387{ 388 Mutex::Autolock _l(mLock); 389 if (mMDSInfoProvider.get() == NULL) { 390 return 0; 391 } 392 393 return mMDSInfoProvider->getVideoSessionNumber(); 394} 395 396bool MultiDisplayObserver::isExternalDeviceTimingFixed() const 397{ 398 Mutex::Autolock _l(mLock); 399 return mExternalHdmiTiming; 400} 401 402status_t MultiDisplayObserver::notifyWidiConnectionStatus( bool connected) 403{ 404 Mutex::Autolock _l(mLock); 405 if (mMDSConnObserver.get() == NULL) { 406 return NO_INIT; 407 } 408 return mMDSConnObserver->updateWidiConnectionStatus(connected); 409} 410 411status_t MultiDisplayObserver::setDecoderOutputResolution( 412 int sessionID, 413 int32_t width, int32_t height, 414 int32_t offX, int32_t offY, 415 int32_t bufWidth, int32_t bufHeight) 416{ 417 Mutex::Autolock _l(mLock); 418 if (mMDSDecoderConfig.get() == NULL) { 419 return NO_INIT; 420 } 421 if (width <= 0 || height <= 0 || 422 offX < 0 || offY < 0 || 423 bufWidth <= 0 || bufHeight <= 0) { 424 ETRACE(" Invalid parameter: %dx%d, %dx%d, %dx%d", width, height, offX, offY, bufWidth, bufHeight); 425 return UNKNOWN_ERROR; 426 } 427 428 status_t ret = mMDSDecoderConfig->setDecoderOutputResolution(sessionID, width, height, offX, offY, bufWidth, bufHeight); 429 if (ret == NO_ERROR) { 430 ITRACE("Video Session[%d] output resolution %dx%d ", sessionID, width, height); 431 } 432 return ret; 433} 434 435 436#endif //TARGET_HAS_MULTIPLE_DISPLAY 437 438} // namespace intel 439} // namespace android 440