• 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 <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