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