1 /*
2 * Copyright (C) 2012 The Android Open Source Project
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 "ExynosExternalDisplay.h"
18 #include <errno.h>
19 #include <hardware/hwcomposer_defs.h>
20 #include <linux/fb.h>
21 #include "ExynosDevice.h"
22 #include "ExynosDisplayDrmInterface.h"
23 #include "ExynosDisplayDrmInterfaceModule.h"
24 #include "ExynosHWCDebug.h"
25 #include "ExynosHWCHelper.h"
26 #include "ExynosLayer.h"
27 #include "pixelstats-display.h"
28
29 #define SKIP_FRAME_COUNT 3
30 extern struct exynos_hwc_control exynosHWCControl;
31
32 using namespace SOC_VERSION;
33
ExynosExternalDisplay(uint32_t index,ExynosDevice * device,const std::string & displayName)34 ExynosExternalDisplay::ExynosExternalDisplay(uint32_t index, ExynosDevice* device,
35 const std::string& displayName)
36 : ExynosDisplay(HWC_DISPLAY_EXTERNAL, index, device, displayName) {
37 DISPLAY_LOGD(eDebugExternalDisplay, "");
38
39 mEnabled = false;
40 mBlanked = false;
41
42 mXres = 0;
43 mYres = 0;
44 mXdpi = 0;
45 mYdpi = 0;
46 mVsyncPeriod = 0;
47 mSkipStartFrame = 0;
48 mSkipFrameCount = -1;
49 mIsSkipFrame = false;
50 mVirtualDisplayState = 0;
51
52 mDRDefault = true;
53 mDREnable = false;
54
55 //TODO : Hard coded currently
56 mNumMaxPriorityAllowed = 1;
57 mPowerModeState = (hwc2_power_mode_t)HWC_POWER_MODE_OFF;
58 mDisplayControl.multiThreadedPresent = true;
59 }
60
~ExynosExternalDisplay()61 ExynosExternalDisplay::~ExynosExternalDisplay()
62 {
63
64 }
65
init()66 void ExynosExternalDisplay::init()
67 {
68
69 }
70
deInit()71 void ExynosExternalDisplay::deInit()
72 {
73
74 }
75
openExternalDisplay()76 int ExynosExternalDisplay::openExternalDisplay()
77 {
78 DISPLAY_LOGD(eDebugExternalDisplay, "");
79
80 int ret = 0;
81
82 mSkipFrameCount = SKIP_FRAME_COUNT;
83 mSkipStartFrame = 0;
84 mPlugState = true;
85 setGeometryChanged(GEOMETRY_DEVICE_DISPLAY_ADDED);
86
87 if (mLayers.size() != 0) {
88 mLayers.clear();
89 }
90
91 DISPLAY_LOGD(eDebugExternalDisplay, "open fd for External Display(%d)", ret);
92
93 return ret;
94 }
95
closeExternalDisplay()96 void ExynosExternalDisplay::closeExternalDisplay()
97 {
98 DISPLAY_LOGD(eDebugExternalDisplay, "");
99
100 setVsyncEnabledInternal(HWC2_VSYNC_DISABLE);
101
102 if (mPowerModeState.has_value() &&
103 (*mPowerModeState != (hwc2_power_mode_t)HWC_POWER_MODE_OFF)) {
104 if (mDisplayInterface->setPowerMode(HWC_POWER_MODE_OFF) < 0) {
105 DISPLAY_LOGE("%s: set powermode ioctl failed errno : %d", __func__, errno);
106 return;
107 }
108 }
109
110 mPowerModeState = (hwc2_power_mode_t)HWC_POWER_MODE_OFF;
111
112 DISPLAY_LOGD(eDebugExternalDisplay, "Close fd for External Display");
113
114 mPlugState = false;
115 setGeometryChanged(GEOMETRY_DEVICE_DISPLAY_REMOVED);
116 mEnabled = false;
117 mBlanked = false;
118 mSkipFrameCount = SKIP_FRAME_COUNT;
119
120 for (size_t i = 0; i < mLayers.size(); i++) {
121 ExynosLayer *layer = mLayers[i];
122 layer->mAcquireFence = fence_close(layer->mAcquireFence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_LAYER);
123 layer->mReleaseFence = -1;
124 layer->mLayerBuffer = NULL;
125 }
126
127 mClientCompositionInfo.initializeInfosComplete(this);
128 mExynosCompositionInfo.initializeInfosComplete(this);
129 }
130
getDisplayConfigs(uint32_t * outNumConfigs,hwc2_config_t * outConfigs)131 int ExynosExternalDisplay::getDisplayConfigs(uint32_t* outNumConfigs, hwc2_config_t* outConfigs)
132 {
133 DISPLAY_LOGD(eDebugExternalDisplay, "");
134
135 int32_t ret = mDisplayInterface->getDisplayConfigs(outNumConfigs, outConfigs);
136 if (ret)
137 DISPLAY_LOGE("%s: failed to getDisplayConfigs, ret(%d)", __func__, ret);
138
139 if (outConfigs) {
140 char modeStr[PROPERTY_VALUE_MAX] = "\0";
141 int32_t width, height, fps, config;
142 int32_t err = HWC2_ERROR_BAD_CONFIG;
143
144 if (property_get("vendor.display.external.preferred_mode", modeStr, "") > 0) {
145 if (sscanf(modeStr, "%dx%d@%d", &width, &height, &fps) == 3) {
146 err = lookupDisplayConfigs(width, height, fps, fps, &config);
147 if (err != HWC2_ERROR_NONE) {
148 DISPLAY_LOGW("%s: display does not support preferred mode %dx%d@%d",
149 __func__, width, height, fps);
150 }
151 } else {
152 DISPLAY_LOGW("%s: vendor.display.external.preferred_mode: bad format",
153 __func__);
154 }
155 }
156
157 if (err == HWC2_ERROR_NONE) {
158 mActiveConfig = config;
159 } else {
160 err = lookupDisplayConfigsRelaxed(1920, 1080, 60, &config);
161 if (err == HWC2_ERROR_NONE) {
162 mActiveConfig = config;
163 } else {
164 mActiveConfig = outConfigs[0];
165 }
166 }
167
168 displayConfigs_t displayConfig = mDisplayConfigs[mActiveConfig];
169 mXres = displayConfig.width;
170 mYres = displayConfig.height;
171 mVsyncPeriod = displayConfig.vsyncPeriod;
172 mRefreshRate = displayConfig.refreshRate;
173
174 if (mDisplayInterface->mType == INTERFACE_TYPE_DRM) {
175 ret = mDisplayInterface->setActiveConfig(mActiveConfig);
176 if (ret) {
177 DISPLAY_LOGE("%s: failed to setActiveConfigs, ret(%d)", __func__, ret);
178 return ret;
179 }
180 }
181 }
182
183 return ret;
184 }
185
getActiveConfig(hwc2_config_t * outConfig)186 int32_t ExynosExternalDisplay::getActiveConfig(hwc2_config_t* outConfig) {
187 DISPLAY_LOGD(eDebugExternalDisplay, "");
188
189 if (!mHpdStatus)
190 return HWC2_ERROR_BAD_DISPLAY;
191
192 if (mActiveConfig == UINT_MAX)
193 return HWC2_ERROR_BAD_CONFIG;
194
195 *outConfig = mActiveConfig;
196
197 return HWC2_ERROR_NONE;
198 }
199
handleRotate()200 bool ExynosExternalDisplay::handleRotate()
201 {
202 // FIXME: HWC2_COMPOSITION_SCREENSHOT is not dfeind in AOSP
203 // HWC guys should fix this.
204 if (mSkipStartFrame < SKIP_EXTERNAL_FRAME) {
205 #if 0
206 for (size_t i = 0; i < mLayers.size(); i++) {
207 ExynosLayer *layer = mLayers[i];
208 if (layer->mCompositionType == HWC2_COMPOSITION_SCREENSHOT)
209 layer->mCompositionType = HWC2_COMPOSITION_DEVICE;
210 }
211 #endif
212 mIsSkipFrame = false;
213 return false;
214 }
215
216 #if 0
217 for (size_t i = 0; i < mLayers.size(); i++) {
218 ExynosLayer *layer = mLayers[i];
219 if (layer->mCompositionType == HWC2_COMPOSITION_SCREENSHOT) {
220 DISPLAY_LOGD(eDebugExternalDisplay, "include rotation animation layer");
221 layer->mOverlayInfo = eSkipRotateAnim;
222 for (size_t j = 0; j < mLayers.size(); j++) {
223 ExynosLayer *skipLayer = mLayers[j];
224 skipLayer->updateValidateCompositionType(HWC2_COMPOSITION_DEVICE);
225 }
226 mIsSkipFrame = true;
227 return true;
228 }
229 }
230 #endif
231 mIsSkipFrame = false;
232 return false;
233 }
234
checkRotate()235 bool ExynosExternalDisplay::checkRotate()
236 {
237 // FIXME: HWC2_COMPOSITION_SCREENSHOT is not dfeind in AOSP
238 // HWC guys should fix this.
239 #if 0
240 for (size_t i = 0; i < mLayers.size(); i++) {
241 ExynosLayer *layer = mLayers[i];
242
243 if (layer->mCompositionType == HWC2_COMPOSITION_SCREENSHOT) {
244 return true;
245 }
246 }
247 #endif
248 return false;
249 }
250
validateDisplay(uint32_t * outNumTypes,uint32_t * outNumRequests)251 int32_t ExynosExternalDisplay::validateDisplay(
252 uint32_t* outNumTypes, uint32_t* outNumRequests) {
253 Mutex::Autolock lock(mExternalMutex);
254 DISPLAY_LOGD(eDebugExternalDisplay, "");
255
256 int32_t ret;
257 mSkipFrame = false;
258
259 if (mSkipStartFrame < SKIP_EXTERNAL_FRAME) {
260 ALOGI("[ExternalDisplay] %s : setGeometryChanged [%d/%d]", __func__, mSkipStartFrame, SKIP_EXTERNAL_FRAME);
261 initDisplay();
262 /*
263 * geometry should be set before ExynosDisplay::validateDisplay is called
264 * not to skip resource assignment
265 */
266 if (mPlugState)
267 setGeometryChanged(GEOMETRY_DEVICE_DISPLAY_ADDED);
268 else
269 setGeometryChanged(GEOMETRY_DEVICE_DISPLAY_REMOVED);
270 }
271
272 if (handleRotate()) {
273 if ((ret = mResourceManager->initResourcesState(this)) != NO_ERROR)
274 DISPLAY_LOGE("[ExternalDisplay] %s : initResourcesState fail, ret(%d)", __func__, ret);
275 mDevice->setGeometryChanged(GEOMETRY_LAYER_UNKNOWN_CHANGED);
276 mClientCompositionInfo.initializeInfos(this);
277 mExynosCompositionInfo.initializeInfos(this);
278 mRenderingState = RENDERING_STATE_VALIDATED;
279 return HWC2_ERROR_NONE;
280 }
281
282 if (mSkipStartFrame < SKIP_EXTERNAL_FRAME) {
283 /*
284 * Set mIsSkipFrame before calling ExynosDisplay::validateDisplay()
285 * startPostProcessing() that is called by ExynosDisplay::validateDisplay()
286 * checks mIsSkipFrame.
287 */
288 mIsSkipFrame = true;
289 }
290
291 ret = ExynosDisplay::validateDisplay(outNumTypes, outNumRequests);
292
293 if (mSkipStartFrame < SKIP_EXTERNAL_FRAME) {
294 initDisplay();
295 mRenderingState = RENDERING_STATE_VALIDATED;
296 uint32_t changed_count = 0;
297 for (size_t i = 0; i < mLayers.size(); i++) {
298 ExynosLayer *layer = mLayers[i];
299 if (layer &&
300 (layer->getValidateCompositionType() == HWC2_COMPOSITION_DEVICE ||
301 layer->getValidateCompositionType() == HWC2_COMPOSITION_EXYNOS)) {
302 layer->updateValidateCompositionType(HWC2_COMPOSITION_CLIENT, eSkipStartFrame);
303 layer->mReleaseFence = layer->mAcquireFence;
304 changed_count++;
305 }
306 }
307 mSkipStartFrame++;
308 *outNumTypes += changed_count;
309
310 ALOGI("[ExternalDisplay] %s : Skip start frame [%d/%d]", __func__, mSkipStartFrame, SKIP_EXTERNAL_FRAME);
311 }
312
313 return ret;
314 }
315
canSkipValidate()316 int32_t ExynosExternalDisplay::canSkipValidate() {
317
318 /*
319 * SurfaceFlinger may call vadlidate, present for a few frame
320 * even though external display is disconnected.
321 * Cammands for primary display can be discarded if validate is skipped
322 * in this case. HWC should return error not to skip validate.
323 */
324 if ((mHpdStatus == false) || (mBlanked == true))
325 return SKIP_ERR_DISP_NOT_CONNECTED;
326
327 if ((mSkipStartFrame > (SKIP_EXTERNAL_FRAME - 1)) && (mEnabled == false) &&
328 (mPowerModeState.has_value() &&
329 (*mPowerModeState == (hwc2_power_mode_t)HWC_POWER_MODE_NORMAL)))
330 return SKIP_ERR_DISP_NOT_POWER_ON;
331
332 if (checkRotate() || (mIsSkipFrame) ||
333 (mSkipStartFrame < SKIP_EXTERNAL_FRAME))
334 return SKIP_ERR_FORCE_VALIDATE;
335
336 return ExynosDisplay::canSkipValidate();
337 }
338
presentDisplay(int32_t * outRetireFence)339 int32_t ExynosExternalDisplay::presentDisplay(
340 int32_t* outRetireFence)
341 {
342 Mutex::Autolock lock(mExternalMutex);
343 DISPLAY_LOGD(eDebugExternalDisplay, "");
344 int32_t ret;
345
346 if (mSkipFrame) {
347 ALOGI("[%d] presentDisplay is skipped by mSkipFrame", mDisplayId);
348 closeFencesForSkipFrame(RENDERING_STATE_PRESENTED);
349 setGeometryChanged(GEOMETRY_DISPLAY_FORCE_VALIDATE);
350 *outRetireFence = -1;
351 for (size_t i=0; i < mLayers.size(); i++) {
352 mLayers[i]->mReleaseFence = -1;
353 }
354 if (mRenderingState == RENDERING_STATE_NONE) {
355 ALOGD("\tThis is the first frame after power on");
356 ret = HWC2_ERROR_NONE;
357 } else {
358 ALOGD("\tThis is the second frame after power on");
359 ret = HWC2_ERROR_NOT_VALIDATED;
360 }
361 mRenderingState = RENDERING_STATE_PRESENTED;
362 mDevice->onRefresh(mDisplayId);
363 return ret;
364 }
365
366 if ((mIsSkipFrame) || (mHpdStatus == false) || (mBlanked == true)) {
367 if ((exynosHWCControl.skipValidate == true) &&
368 ((mRenderingState == RENDERING_STATE_PRESENTED) ||
369 (mRenderingState == RENDERING_STATE_NONE))) {
370
371 if (mDevice->canSkipValidate() == false) {
372 mRenderingState = RENDERING_STATE_NONE;
373 return HWC2_ERROR_NOT_VALIDATED;
374 } else {
375 DISPLAY_LOGD(eDebugSkipValidate, "validate is skipped");
376 }
377 }
378
379 *outRetireFence = -1;
380 for (size_t i = 0; i < mLayers.size(); i++) {
381 ExynosLayer *layer = mLayers[i];
382 layer->mAcquireFence = fence_close(layer->mAcquireFence, this,
383 FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_LAYER);
384 layer->mReleaseFence = -1;
385 }
386 mClientCompositionInfo.mAcquireFence =
387 fence_close(mClientCompositionInfo.mAcquireFence, this,
388 FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_FB);
389 mClientCompositionInfo.mReleaseFence = -1;
390
391 /* this frame is not presented, but mRenderingState is updated to RENDERING_STATE_PRESENTED */
392 initDisplay();
393
394 /*
395 * Resource assignment information was initialized during skipping frames
396 * So resource assignment for the first displayed frame after skpping frames
397 * should not be skipped
398 */
399 setGeometryChanged(GEOMETRY_DISPLAY_FORCE_VALIDATE);
400
401 mDevice->onRefresh(mDisplayId);
402
403 return HWC2_ERROR_NONE;
404 }
405
406 ret = ExynosDisplay::presentDisplay(outRetireFence);
407
408 return ret;
409 }
setClientTarget(buffer_handle_t target,int32_t acquireFence,int32_t dataspace)410 int32_t ExynosExternalDisplay::setClientTarget(
411 buffer_handle_t target,
412 int32_t acquireFence, int32_t /*android_dataspace_t*/ dataspace) {
413 buffer_handle_t handle = NULL;
414 if (target != NULL)
415 handle = target;
416 if ((mClientCompositionInfo.mHasCompositionLayer == true) &&
417 (handle == NULL) &&
418 (mClientCompositionInfo.mSkipFlag == false)) {
419 /*
420 * openExternalDisplay() can be called between validateDisplay and getChangedCompositionTypes.
421 * Then getChangedCompositionTypes() returns no layer because openExternalDisplay() clears mLayers.
422 * SurfaceFlinger might not change compositionType to HWC2_COMPOSITION_CLIENT.
423 * Handle can be NULL in this case. It is not error case.
424 */
425 if (mSkipStartFrame == 0) {
426 if (acquireFence >= 0)
427 fence_close(acquireFence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_FB);
428 acquireFence = -1;
429 mClientCompositionInfo.setTargetBuffer(this, handle, acquireFence, (android_dataspace)dataspace);
430 return NO_ERROR;
431 }
432 }
433 return ExynosDisplay::setClientTarget(target, acquireFence, dataspace);
434 }
435
enable()436 int ExynosExternalDisplay::enable()
437 {
438 ALOGI("[ExternalDisplay] %s +", __func__);
439
440 if (mEnabled)
441 return HWC2_ERROR_NONE;
442
443 if (mHpdStatus == false) {
444 ALOGI("HPD is not connected");
445 return HWC2_ERROR_NONE;
446 }
447
448 if (openExternalDisplay() < 0)
449 return HWC2_ERROR_UNSUPPORTED;
450
451 if (mDisplayInterface->setPowerMode(HWC_POWER_MODE_NORMAL) < 0) {
452 DISPLAY_LOGE("set powermode ioctl failed errno : %d", errno);
453 return HWC2_ERROR_UNSUPPORTED;
454 }
455
456 mEnabled = true;
457 mPowerModeState = (hwc2_power_mode_t)HWC_POWER_MODE_NORMAL;
458 mDisplayInterface->triggerClearDisplayPlanes();
459
460 reportUsage(true);
461
462 ALOGI("[ExternalDisplay] %s -", __func__);
463
464 return HWC2_ERROR_NONE;
465 }
466
disable()467 int ExynosExternalDisplay::disable()
468 {
469 ALOGI("[ExternalDisplay] %s +", __func__);
470
471 if (mHpdStatus) {
472 /*
473 * DP cable is connected and link is up
474 *
475 * Currently, we don't power down here for two reasons:
476 * - power up would require DP link re-training (slow)
477 * - DP audio can continue playing while display is blank
478 */
479 if (mEnabled)
480 clearDisplay(false);
481 return HWC2_ERROR_NONE;
482 }
483
484 if (mSkipStartFrame > (SKIP_EXTERNAL_FRAME - 1)) {
485 clearDisplay(true);
486 } else {
487 ALOGI("Skip clearDisplay to avoid resource conflict");
488 }
489
490 if (mDisplayInterface->setPowerMode(HWC_POWER_MODE_OFF) < 0) {
491 DISPLAY_LOGE("set powermode ioctl failed errno : %d", errno);
492 return HWC2_ERROR_UNSUPPORTED;
493 }
494
495 if (mEnabled) reportUsage(false);
496
497 mEnabled = false;
498 mPowerModeState = (hwc2_power_mode_t)HWC_POWER_MODE_OFF;
499
500 ALOGI("[ExternalDisplay] %s -", __func__);
501
502 return HWC2_ERROR_NONE;
503 }
504
setPowerMode(int32_t mode)505 int32_t ExynosExternalDisplay::setPowerMode(
506 int32_t /*hwc2_power_mode_t*/ mode) {
507 Mutex::Autolock lock(mExternalMutex);
508 {
509 Mutex::Autolock lock(mDisplayMutex);
510
511 /* TODO state check routine should be added */
512
513 int fb_blank = 0;
514 int err = 0;
515 if (mode == HWC_POWER_MODE_OFF) {
516 fb_blank = FB_BLANK_POWERDOWN;
517 err = disable();
518 } else if (mode == HWC_POWER_MODE_NORMAL) {
519 fb_blank = FB_BLANK_UNBLANK;
520 err = enable();
521 } else {
522 DISPLAY_LOGE("unsupported powermode: %d", mode);
523 return HWC2_ERROR_UNSUPPORTED;
524 }
525
526 if (err != 0) {
527 DISPLAY_LOGE("set powermode ioctl failed errno : %d", errno);
528 return HWC2_ERROR_UNSUPPORTED;
529 }
530
531 if (fb_blank == FB_BLANK_POWERDOWN)
532 mDREnable = false;
533 else if (fb_blank == FB_BLANK_UNBLANK)
534 mDREnable = mDRDefault;
535
536 // check the dynamic recomposition thread by following display power status
537 mDevice->checkDynamicRecompositionThread();
538
539 DISPLAY_LOGD(eDebugExternalDisplay, "%s:: mode(%d), blank(%d)", __func__, mode, fb_blank);
540
541 if (mode == HWC_POWER_MODE_OFF) {
542 /* It should be called from validate() when the screen is on */
543 mSkipFrame = true;
544 setGeometryChanged(GEOMETRY_DISPLAY_POWER_OFF);
545 if ((mRenderingState >= RENDERING_STATE_VALIDATED) &&
546 (mRenderingState < RENDERING_STATE_PRESENTED))
547 closeFencesForSkipFrame(RENDERING_STATE_VALIDATED);
548 mRenderingState = RENDERING_STATE_NONE;
549 } else {
550 setGeometryChanged(GEOMETRY_DISPLAY_POWER_ON);
551 }
552 }
553 return HWC2_ERROR_NONE;
554 }
555
startPostProcessing()556 int32_t ExynosExternalDisplay::startPostProcessing() {
557 if ((mHpdStatus == false) || (mBlanked == true) || mIsSkipFrame) {
558 ALOGI("%s:: skip startPostProcessing display(%d) mHpdStatus(%d)",
559 __func__, mDisplayId, mHpdStatus);
560 return NO_ERROR;
561 }
562 return ExynosDisplay::startPostProcessing();
563 }
564
getHDRException(ExynosLayer * __unused layer)565 bool ExynosExternalDisplay::getHDRException(ExynosLayer* __unused layer)
566 {
567 bool ret = false;
568
569 if (mExternalHdrSupported) {
570 ret = true;
571 }
572 return ret;
573 }
574
handleHotplugEvent(bool hpdStatus)575 void ExynosExternalDisplay::handleHotplugEvent(bool hpdStatus)
576 {
577 mHpdStatus = hpdStatus;
578 if (mHpdStatus) {
579 if (openExternalDisplay() < 0) {
580 ALOGE("Failed to openExternalDisplay");
581 mHpdStatus = false;
582 return;
583 }
584 mDREnable = mDRDefault;
585 } else {
586 disable();
587 closeExternalDisplay();
588 mDREnable = false;
589 }
590 mDevice->checkDynamicRecompositionThread();
591
592 ALOGI("HPD status changed to %s, mDisplayId %d, mDisplayFd %d", mHpdStatus ? "enabled" : "disabled", mDisplayId, mDisplayInterface->getDisplayFd());
593 }
594
initDisplayInterface(uint32_t interfaceType)595 void ExynosExternalDisplay::initDisplayInterface(uint32_t interfaceType)
596 {
597 if (interfaceType == INTERFACE_TYPE_DRM)
598 mDisplayInterface = std::make_unique<ExynosExternalDisplayDrmInterfaceModule>((ExynosDisplay *)this);
599 else
600 LOG_ALWAYS_FATAL("%s::Unknown interface type(%d)",
601 __func__, interfaceType);
602 mDisplayInterface->init(this);
603 }
604
reportUsage(bool enabled)605 void ExynosExternalDisplay::reportUsage(bool enabled) {
606 auto refreshRate = nanoSec2Hz(mVsyncPeriod);
607 auto manufacturerInfo = mDisplayInterface->getManufacturerInfo();
608 auto productId = mDisplayInterface->getProductId();
609
610 ATRACE_NAME("report ext usage");
611 reportDisplayPortUsage(mXres, mYres, refreshRate, manufacturerInfo, productId, enabled);
612 }
613