• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 #include <HwcTrace.h>
18 #include <Drm.h>
19 #include <DrmConfig.h>
20 #include <Hwcomposer.h>
21 #include <ExternalDevice.h>
22 
23 namespace android {
24 namespace intel {
25 
ExternalDevice(Hwcomposer & hwc,DeviceControlFactory * controlFactory)26 ExternalDevice::ExternalDevice(Hwcomposer& hwc, DeviceControlFactory* controlFactory)
27     : PhysicalDevice(DEVICE_EXTERNAL, hwc, controlFactory),
28       mHdcpControl(NULL),
29       mAbortModeSettingCond(),
30       mPendingDrmMode(),
31       mHotplugEventPending(false),
32       mExpectedRefreshRate(0)
33 {
34     CTRACE();
35 }
36 
~ExternalDevice()37 ExternalDevice::~ExternalDevice()
38 {
39     CTRACE();
40 }
41 
initialize()42 bool ExternalDevice::initialize()
43 {
44     if (!PhysicalDevice::initialize()) {
45         DEINIT_AND_RETURN_FALSE("failed to initialize physical device");
46     }
47 
48     mHdcpControl = mControlFactory->createHdcpControl();
49     if (!mHdcpControl) {
50         DEINIT_AND_RETURN_FALSE("failed to create HDCP control");
51     }
52 
53     mHotplugEventPending = false;
54     if (mConnected) {
55         mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this);
56     }
57 
58     UeventObserver *observer = Hwcomposer::getInstance().getUeventObserver();
59     if (observer) {
60         observer->registerListener(
61             DrmConfig::getHotplugString(),
62             hotplugEventListener,
63             this);
64     } else {
65         ETRACE("Uevent observer is NULL");
66     }
67     return true;
68 }
69 
deinitialize()70 void ExternalDevice::deinitialize()
71 {
72     // abort mode settings if it is in the middle
73     mAbortModeSettingCond.signal();
74     if (mThread.get()) {
75         mThread->join();
76         mThread = NULL;
77     }
78 
79     if (mHdcpControl) {
80         mHdcpControl->stopHdcp();
81         delete mHdcpControl;
82         mHdcpControl = 0;
83     }
84 
85     mHotplugEventPending = false;
86     PhysicalDevice::deinitialize();
87 }
88 
setDrmMode(drmModeModeInfo & value)89 bool ExternalDevice::setDrmMode(drmModeModeInfo& value)
90 {
91     if (!mConnected) {
92         WTRACE("external device is not connected");
93         return false;
94     }
95 
96     if (mThread.get()) {
97         mThread->join();
98         mThread = NULL;
99     }
100 
101     Drm *drm = Hwcomposer::getInstance().getDrm();
102     drmModeModeInfo mode;
103     drm->getModeInfo(mType, mode);
104     if (drm->isSameDrmMode(&value, &mode))
105         return true;
106 
107     // any issue here by faking connection status?
108     mConnected = false;
109     mPendingDrmMode = value;
110 
111     // setting mode in a working thread
112     mThread = new ModeSettingThread(this);
113     if (!mThread.get()) {
114         ETRACE("failed to create mode settings thread");
115         return false;
116     }
117 
118     mThread->run("ModeSettingsThread", PRIORITY_URGENT_DISPLAY);
119     return true;
120 }
121 
threadLoop()122 bool ExternalDevice::threadLoop()
123 {
124     // one-time execution
125     setDrmMode();
126     return false;
127 }
128 
setDrmMode()129 void ExternalDevice::setDrmMode()
130 {
131     ITRACE("start mode setting...");
132 
133     Drm *drm = Hwcomposer::getInstance().getDrm();
134 
135     mConnected = false;
136     mHwc.hotplug(mType, false);
137 
138     {
139         Mutex::Autolock lock(mLock);
140         // TODO: make timeout value flexible, or wait until surface flinger
141         // acknowledges hot unplug event.
142         status_t err = mAbortModeSettingCond.waitRelative(mLock, milliseconds(20));
143         if (err != -ETIMEDOUT) {
144             ITRACE("Mode settings is interrupted");
145             mHwc.hotplug(mType, true);
146             return;
147         }
148     }
149 
150     // TODO: potential threading issue with onHotplug callback
151     mHdcpControl->stopHdcp();
152     if (!drm->setDrmMode(mType, mPendingDrmMode)) {
153         ETRACE("failed to set Drm mode");
154         mHwc.hotplug(mType, true);
155         return;
156     }
157 
158     if (!PhysicalDevice::updateDisplayConfigs()) {
159         ETRACE("failed to update display configs");
160         mHwc.hotplug(mType, true);
161         return;
162     }
163     mConnected = true;
164     mHotplugEventPending = true;
165     // delay sending hotplug event until HDCP is authenticated
166     if (mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this) == false) {
167         ETRACE("startHdcpAsync() failed; HDCP is not enabled");
168         mHotplugEventPending = false;
169         mHwc.hotplug(mType, true);
170     }
171     mExpectedRefreshRate = 0;
172 }
173 
174 
HdcpLinkStatusListener(bool success,void * userData)175 void ExternalDevice::HdcpLinkStatusListener(bool success, void *userData)
176 {
177     if (userData == NULL) {
178         return;
179     }
180 
181     ExternalDevice *p = (ExternalDevice*)userData;
182     p->HdcpLinkStatusListener(success);
183 }
184 
HdcpLinkStatusListener(bool success)185 void ExternalDevice::HdcpLinkStatusListener(bool success)
186 {
187     if (!success) {
188         ETRACE("HDCP is not authenticated, disabling dynamic vsync");
189         mHwc.getVsyncManager()->enableDynamicVsync(false);
190     }
191 
192     if (mHotplugEventPending) {
193         DTRACE("HDCP authentication status %d, sending hotplug event...", success);
194         mHwc.hotplug(mType, mConnected);
195         mHotplugEventPending = false;
196     }
197 
198     if (success) {
199         ITRACE("HDCP authenticated, enabling dynamic vsync");
200         mHwc.getVsyncManager()->enableDynamicVsync(true);
201     }
202 }
203 
hotplugEventListener(void * data)204 void ExternalDevice::hotplugEventListener(void *data)
205 {
206     ExternalDevice *pThis = (ExternalDevice*)data;
207     if (pThis) {
208         pThis->hotplugListener();
209     }
210 }
211 
hotplugListener()212 void ExternalDevice::hotplugListener()
213 {
214     bool ret;
215 
216     CTRACE();
217 
218     // abort mode settings if it is in the middle
219     mAbortModeSettingCond.signal();
220 
221     // remember the current connection status before detection
222     bool connected = mConnected;
223 
224     // detect display configs
225     ret = detectDisplayConfigs();
226     if (ret == false) {
227         ETRACE("failed to detect display config");
228         return;
229     }
230 
231     ITRACE("hotpug event: %d", mConnected);
232 
233     if (connected == mConnected) {
234         WTRACE("same connection status detected, hotplug event ignored");
235         return;
236     }
237 
238     if (mConnected == false) {
239         mHotplugEventPending = false;
240         mHwc.getVsyncManager()->resetVsyncSource();
241         mHdcpControl->stopHdcp();
242         mHwc.hotplug(mType, mConnected);
243     } else {
244         DTRACE("start HDCP asynchronously...");
245          // delay sending hotplug event till HDCP is authenticated.
246         mHotplugEventPending = true;
247         ret = mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this);
248         if (ret == false) {
249             ETRACE("failed to start HDCP");
250             mHotplugEventPending = false;
251             mHwc.hotplug(mType, mConnected);
252         }
253     }
254     mActiveDisplayConfig = 0;
255 }
256 
getRefreshRate()257 int ExternalDevice::getRefreshRate()
258 {
259     Drm *drm = Hwcomposer::getInstance().getDrm();
260     drmModeModeInfo mode;
261     if (!drm->getModeInfo(IDisplayDevice::DEVICE_EXTERNAL, mode))
262         return 0;
263     return mode.vrefresh;
264 }
265 
setRefreshRate(int hz)266 void ExternalDevice::setRefreshRate(int hz)
267 {
268     RETURN_VOID_IF_NOT_INIT();
269 
270     ITRACE("setting refresh rate to %d", hz);
271 
272     if (mBlank) {
273         WTRACE("external device is blank");
274         return;
275     }
276 
277     Drm *drm = Hwcomposer::getInstance().getDrm();
278     drmModeModeInfo mode;
279     if (!drm->getModeInfo(IDisplayDevice::DEVICE_EXTERNAL, mode))
280         return;
281 
282     if (hz == 0 && (mode.type & DRM_MODE_TYPE_PREFERRED))
283         return;
284 
285     if (hz == (int)mode.vrefresh)
286         return;
287 
288     if (mExpectedRefreshRate != 0 &&
289             mExpectedRefreshRate == hz && mHotplugEventPending) {
290         ITRACE("Ignore a new refresh setting event because there is a same event is handling");
291         return;
292     }
293     mExpectedRefreshRate = hz;
294 
295     ITRACE("changing refresh rate from %d to %d", mode.vrefresh, hz);
296 
297     mHwc.getVsyncManager()->enableDynamicVsync(false);
298 
299     mHdcpControl->stopHdcp();
300 
301     drm->setRefreshRate(IDisplayDevice::DEVICE_EXTERNAL, hz);
302 
303     mHotplugEventPending = false;
304     mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this);
305     mHwc.getVsyncManager()->enableDynamicVsync(true);
306 }
307 
getActiveConfig()308 int ExternalDevice::getActiveConfig()
309 {
310     if (!mConnected) {
311         return 0;
312     }
313     return mActiveDisplayConfig;
314 }
315 
setActiveConfig(int index)316 bool ExternalDevice::setActiveConfig(int index)
317 {
318     if (!mConnected) {
319         if (index == 0)
320             return true;
321         else
322             return false;
323     }
324 
325     // for now we will only permit the frequency change.  In the future
326     // we may need to set mode as well.
327     if (index >= 0 && index < static_cast<int>(mDisplayConfigs.size())) {
328         DisplayConfig *config = mDisplayConfigs.itemAt(index);
329         setRefreshRate(config->getRefreshRate());
330         mActiveDisplayConfig = index;
331         return true;
332     } else {
333         return false;
334     }
335     return true;
336 }
337 
338 } // namespace intel
339 } // namespace android
340