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
26 namespace android {
27 namespace intel {
28
29 #ifdef TARGET_HAS_MULTIPLE_DISPLAY
30
31 ////// MultiDisplayCallback
32
MultiDisplayCallback(MultiDisplayObserver * dispObserver)33 MultiDisplayCallback::MultiDisplayCallback(MultiDisplayObserver *dispObserver)
34 : mDispObserver(dispObserver),
35 mVideoState(MDS_VIDEO_STATE_UNKNOWN)
36 {
37 }
38
~MultiDisplayCallback()39 MultiDisplayCallback::~MultiDisplayCallback()
40 {
41 CTRACE();
42 mDispObserver = NULL;
43 }
44
blankSecondaryDisplay(bool blank)45 status_t MultiDisplayCallback::blankSecondaryDisplay(bool blank)
46 {
47 ITRACE("blank: %d", blank);
48 mDispObserver->blankSecondaryDisplay(blank);
49 return NO_ERROR;
50 }
51
updateVideoState(int sessionId,MDS_VIDEO_STATE state)52 status_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
setHdmiTiming(const MDSHdmiTiming & timing)60 status_t MultiDisplayCallback::setHdmiTiming(const MDSHdmiTiming& timing)
61 {
62 mDispObserver->setHdmiTiming(timing);
63 return NO_ERROR;
64 }
65
updateInputState(bool state)66 status_t MultiDisplayCallback::updateInputState(bool state)
67 {
68 //ITRACE("input state: %d", state);
69 mDispObserver->updateInputState(state);
70 return NO_ERROR;
71 }
72
setHdmiScalingType(MDS_SCALING_TYPE type)73 status_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
setHdmiOverscan(int hValue,int vValue)80 status_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
MultiDisplayObserver()89 MultiDisplayObserver::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
~MultiDisplayObserver()105 MultiDisplayObserver::~MultiDisplayObserver()
106 {
107 WARN_IF_NOT_DEINIT();
108 }
109
isMDSRunning()110 bool 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
initMDSClient()128 bool 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
deinitMDSClient()183 void 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
initMDSClientAsync()197 bool 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
initialize()215 bool 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
deinitialize()244 void 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
threadLoop()266 bool 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
blankSecondaryDisplay(bool blank)297 status_t MultiDisplayObserver::blankSecondaryDisplay(bool blank)
298 {
299 // blank secondary display
300 Hwcomposer::getInstance().getDisplayAnalyzer()->postBlankEvent(blank);
301 return 0;
302 }
303
updateVideoState(int sessionId,MDS_VIDEO_STATE state)304 status_t MultiDisplayObserver::updateVideoState(int sessionId, MDS_VIDEO_STATE state)
305 {
306 Hwcomposer::getInstance().getDisplayAnalyzer()->postVideoEvent(
307 sessionId, (int)state);
308 return 0;
309 }
310
setHdmiTiming(const MDSHdmiTiming & timing)311 status_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
updateInputState(bool active)329 status_t MultiDisplayObserver::updateInputState(bool active)
330 {
331 Hwcomposer::getInstance().getDisplayAnalyzer()->postInputEvent(active);
332 return 0;
333 }
334
335
336 /// Public interfaces
337
notifyHotPlug(bool connected)338 status_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
getVideoSourceInfo(int sessionID,VideoSourceInfo * info)360 status_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
getVideoSessionNumber()386 int MultiDisplayObserver::getVideoSessionNumber()
387 {
388 Mutex::Autolock _l(mLock);
389 if (mMDSInfoProvider.get() == NULL) {
390 return 0;
391 }
392
393 return mMDSInfoProvider->getVideoSessionNumber();
394 }
395
isExternalDeviceTimingFixed() const396 bool MultiDisplayObserver::isExternalDeviceTimingFixed() const
397 {
398 Mutex::Autolock _l(mLock);
399 return mExternalHdmiTiming;
400 }
401
notifyWidiConnectionStatus(bool connected)402 status_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
setDecoderOutputResolution(int sessionID,int32_t width,int32_t height,int32_t offX,int32_t offY,int32_t bufWidth,int32_t bufHeight)411 status_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