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