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