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