summaryrefslogtreecommitdiff
path: root/hwc2/common/devices/ExternalDevice.cpp (plain)
blob: 241b78804c8da531e0d282f85bb44d31c2552a00
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// This file is modified by Amlogic, Inc. 2017.01.17.
17*/
18
19#include <HwcTrace.h>
20#include <Drm.h>
21#include <DrmConfig.h>
22#include <Hwcomposer.h>
23#include <ExternalDevice.h>
24
25namespace android {
26namespace amlogic {
27
28ExternalDevice::ExternalDevice(Hwcomposer& hwc, DeviceControlFactory* controlFactory)
29 : PhysicalDevice(DEVICE_EXTERNAL, hwc, controlFactory),
30 mHdcpControl(NULL),
31 mAbortModeSettingCond(),
32 mPendingDrmMode(),
33 mHotplugEventPending(false),
34 mExpectedRefreshRate(0)
35{
36 CTRACE();
37}
38
39ExternalDevice::~ExternalDevice()
40{
41 CTRACE();
42}
43
44bool ExternalDevice::initialize()
45{
46 if (!PhysicalDevice::initialize()) {
47 DEINIT_AND_RETURN_FALSE("failed to initialize physical device");
48 }
49
50 mHdcpControl = mControlFactory->createHdcpControl();
51 if (!mHdcpControl) {
52 DEINIT_AND_RETURN_FALSE("failed to create HDCP control");
53 }
54
55 mHotplugEventPending = false;
56 if (mConnected) {
57 mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this);
58 }
59
60 UeventObserver *observer = Hwcomposer::getInstance().getUeventObserver();
61 if (observer) {
62 observer->registerListener(
63 DrmConfig::getHotplugString(),
64 hotplugEventListener,
65 this);
66 } else {
67 ETRACE("Uevent observer is NULL");
68 }
69
70 return true;
71}
72
73void ExternalDevice::deinitialize()
74{
75 // abort mode settings if it is in the middle
76 mAbortModeSettingCond.signal();
77 if (mThread.get()) {
78 mThread->join();
79 mThread = NULL;
80 }
81
82 if (mHdcpControl) {
83 mHdcpControl->stopHdcp();
84 delete mHdcpControl;
85 mHdcpControl = 0;
86 }
87
88 mHotplugEventPending = false;
89 PhysicalDevice::deinitialize();
90}
91
92bool ExternalDevice::setDrmMode(drmModeModeInfo& value)
93{
94 if (!mConnected) {
95 WTRACE("external device is not connected");
96 return false;
97 }
98
99 if (mThread.get()) {
100 mThread->join();
101 mThread = NULL;
102 }
103
104 Drm *drm = Hwcomposer::getInstance().getDrm();
105 drmModeModeInfo mode;
106 drm->getModeInfo(mType, mode);
107 if (drm->isSameDrmMode(&value, &mode))
108 return true;
109
110 // any issue here by faking connection status?
111 mConnected = false;
112 mPendingDrmMode = value;
113
114 // setting mode in a working thread
115 mThread = new ModeSettingThread(this);
116 if (!mThread.get()) {
117 ETRACE("failed to create mode settings thread");
118 return false;
119 }
120
121 mThread->run("ModeSettingsThread", PRIORITY_URGENT_DISPLAY);
122 return true;
123}
124
125bool ExternalDevice::threadLoop()
126{
127 // one-time execution
128 setDrmMode();
129 return false;
130}
131
132void ExternalDevice::setDrmMode()
133{
134 ITRACE("start mode setting...");
135
136 Drm *drm = Hwcomposer::getInstance().getDrm();
137
138 mConnected = false;
139 mHwc.hotplug(mType, false);
140
141 {
142 Mutex::Autolock lock(mLock);
143 // TODO: make timeout value flexible, or wait until surface flinger
144 // acknowledges hot unplug event.
145 status_t err = mAbortModeSettingCond.waitRelative(mLock, milliseconds(20));
146 if (err != -ETIMEDOUT) {
147 ETRACE("Mode settings is interrupted");
148 mHwc.hotplug(mType, true);
149 return;
150 }
151 }
152
153 // TODO: potential threading issue with onHotplug callback
154 mHdcpControl->stopHdcp();
155 if (!drm->setDrmMode(mType, mPendingDrmMode)) {
156 ETRACE("failed to set Drm mode");
157 mHwc.hotplug(mType, true);
158 return;
159 }
160
161 if (!PhysicalDevice::updateDisplayConfigs()) {
162 ETRACE("failed to update display configs");
163 mHwc.hotplug(mType, true);
164 return;
165 }
166 mConnected = true;
167 mHotplugEventPending = true;
168 // delay sending hotplug event until HDCP is authenticated
169 if (mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this) == false) {
170 ETRACE("startHdcpAsync() failed; HDCP is not enabled");
171 mHotplugEventPending = false;
172 mHwc.hotplug(mType, true);
173 }
174 mExpectedRefreshRate = 0;
175}
176
177
178void ExternalDevice::HdcpLinkStatusListener(bool success, void *userData)
179{
180 if (userData == NULL) {
181 return;
182 }
183
184 ExternalDevice *p = (ExternalDevice*)userData;
185 p->HdcpLinkStatusListener(success);
186}
187
188void ExternalDevice::HdcpLinkStatusListener(bool success)
189{
190 if (!success) {
191 ETRACE("HDCP is not authenticated, disabling dynamic vsync");
192 mHwc.getVsyncManager()->enableDynamicVsync(false);
193 }
194
195 if (mHotplugEventPending) {
196 DTRACE("HDCP authentication status %d, sending hotplug event...", success);
197 mHwc.hotplug(mType, mConnected);
198 mHotplugEventPending = false;
199 }
200
201 if (success) {
202 ITRACE("HDCP authenticated, enabling dynamic vsync");
203 mHwc.getVsyncManager()->enableDynamicVsync(true);
204 }
205}
206
207void ExternalDevice::hotplugEventListener(void *data)
208{
209 ExternalDevice *pThis = (ExternalDevice*)data;
210 if (pThis) {
211 pThis->hotplugListener();
212 }
213}
214
215void ExternalDevice::hotplugListener()
216{
217 bool ret;
218
219 CTRACE();
220
221 // abort mode settings if it is in the middle
222 mAbortModeSettingCond.signal();
223
224 // remember the current connection status before detection
225 bool connected = mConnected;
226
227 // detect display configs
228 ret = detectDisplayConfigs();
229 if (ret == false) {
230 ETRACE("failed to detect display config");
231 return;
232 }
233
234 ITRACE("hotpug event: %d", mConnected);
235
236 if (connected == mConnected) {
237 WTRACE("same connection status detected, hotplug event ignored");
238 return;
239 }
240
241 if (mConnected == false) {
242 mHotplugEventPending = false;
243 mHwc.getVsyncManager()->resetVsyncSource();
244 mHdcpControl->stopHdcp();
245 mHwc.hotplug(mType, mConnected);
246 } else {
247 DTRACE("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 ETRACE("failed to start HDCP");
253 mHotplugEventPending = false;
254 mHwc.hotplug(mType, mConnected);
255 }
256 }
257 mActiveDisplayConfig = 0;
258}
259
260int ExternalDevice::getRefreshRate()
261{
262 Drm *drm = Hwcomposer::getInstance().getDrm();
263 drmModeModeInfo mode;
264 if (!drm->getModeInfo(IDisplayDevice::DEVICE_EXTERNAL, mode))
265 return 0;
266 return mode.vrefresh;
267}
268
269void ExternalDevice::setRefreshRate(int hz)
270{
271 RETURN_VOID_IF_NOT_INIT();
272
273 ITRACE("setting refresh rate to %d", hz);
274
275 if (mBlank) {
276 WTRACE("external device is blank");
277 return;
278 }
279
280 Drm *drm = Hwcomposer::getInstance().getDrm();
281 drmModeModeInfo mode;
282 if (!drm->getModeInfo(IDisplayDevice::DEVICE_EXTERNAL, mode))
283 return;
284
285 if (hz == 0 && (mode.type & DRM_MODE_TYPE_PREFERRED))
286 return;
287
288 if (hz == (int)mode.vrefresh)
289 return;
290
291 if (mExpectedRefreshRate != 0 &&
292 mExpectedRefreshRate == hz && mHotplugEventPending) {
293 ITRACE("Ignore a new refresh setting event because there is a same event is handling");
294 return;
295 }
296 mExpectedRefreshRate = hz;
297
298 ITRACE("changing refresh rate from %d to %d", mode.vrefresh, hz);
299
300 mHwc.getVsyncManager()->enableDynamicVsync(false);
301
302 mHdcpControl->stopHdcp();
303
304 drm->setRefreshRate(IDisplayDevice::DEVICE_EXTERNAL, hz);
305
306 mHotplugEventPending = false;
307 mHdcpControl->startHdcpAsync(HdcpLinkStatusListener, this);
308 mHwc.getVsyncManager()->enableDynamicVsync(true);
309}
310
311int ExternalDevice::getActiveConfig()
312{
313 if (!mConnected) {
314 return 0;
315 }
316 return mActiveDisplayConfig;
317}
318
319bool ExternalDevice::setActiveConfig(int index)
320{
321 if (!mConnected) {
322 if (index == 0)
323 return true;
324 else
325 return false;
326 }
327
328 // for now we will only permit the frequency change. In the future
329 // we may need to set mode as well.
330 if (index >= 0 && index < static_cast<int>(mDisplayConfigs.size())) {
331 DisplayConfig *config = mDisplayConfigs.itemAt(index);
332 setRefreshRate(config->getRefreshRate());
333 mActiveDisplayConfig = index;
334 return true;
335 } else {
336 return false;
337 }
338}
339
340} // namespace amlogic
341} // namespace android
342