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