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