1 /*
2 * Copyright (C) 2021 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 #define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL)
18
19 #include <cutils/properties.h>
20 #include <poll.h>
21
22 #include "BrightnessController.h"
23 #include "ExynosHWCModule.h"
24
Init(const struct brightness_capability * cap)25 void BrightnessController::LinearBrightnessTable::Init(const struct brightness_capability* cap) {
26 if (!cap) {
27 return;
28 }
29 setBrightnessRangeFromAttribute(cap->normal, mBrightnessRanges[BrightnessMode::BM_NOMINAL]);
30 setBrightnessRangeFromAttribute(cap->hbm, mBrightnessRanges[BrightnessMode::BM_HBM]);
31 if (mBrightnessRanges[BrightnessMode::BM_NOMINAL].brightness_max ==
32 mBrightnessRanges[BrightnessMode::BM_HBM].brightness_min) {
33 mBrightnessRanges[BrightnessMode::BM_HBM].brightness_min_exclusive = true;
34 }
35 if (!mBrightnessRanges.at(BrightnessMode::BM_NOMINAL).IsValid()) {
36 ALOGE("%s: brightness range for BM_NOMINAL is invalid!", __func__);
37 return;
38 }
39 // BM_HBM range is optional for some devices
40 if (mBrightnessRanges.count(BrightnessMode::BM_HBM) > 0) {
41 if (!mBrightnessRanges.at(BrightnessMode::BM_HBM).IsValid()) {
42 ALOGE("%s: brightness range for BM_HBM is invalid!", __func__);
43 return;
44 }
45 }
46 mIsValid = true;
47 }
48
NitsToBrightness(float nits) const49 std::optional<float> BrightnessController::LinearBrightnessTable::NitsToBrightness(
50 float nits) const {
51 BrightnessMode mode = GetBrightnessModeForNits(nits);
52 if (mode == BrightnessMode::BM_INVALID) {
53 return std::nullopt;
54 }
55
56 const DisplayBrightnessRange& range = mBrightnessRanges.at(mode);
57 const float brightness = LinearInterpolation(nits,
58 range.nits_min, range.nits_max,
59 range.brightness_min, range.brightness_max);
60 if (isnan(brightness)) {
61 return std::nullopt;
62 }
63
64 return brightness;
65 }
66
DbvToBrightness(uint32_t dbv) const67 std::optional<float> BrightnessController::LinearBrightnessTable::DbvToBrightness(
68 uint32_t dbv) const {
69 BrightnessMode bm = getBrightnessModeForDbv(dbv);
70 if (bm == BrightnessMode::BM_INVALID) {
71 return std::nullopt;
72 }
73
74 std::optional<float> nits = DbvToNits(bm, dbv);
75 if (nits == std::nullopt) {
76 return std::nullopt;
77 }
78
79 return NitsToBrightness(nits.value());
80 }
81
BrightnessToNits(float brightness,BrightnessMode & bm) const82 std::optional<float> BrightnessController::LinearBrightnessTable::BrightnessToNits(
83 float brightness, BrightnessMode& bm) const {
84 bm = GetBrightnessMode(brightness);
85 if (bm == BrightnessMode::BM_MAX) {
86 return std::nullopt;
87 }
88 const DisplayBrightnessRange& range = mBrightnessRanges.at(bm);
89 float nits = LinearInterpolation(brightness, range.brightness_min, range.brightness_max,
90 range.nits_min, range.nits_max);
91 if (isnan(nits)) {
92 return std::nullopt;
93 }
94
95 return nits;
96 }
97
NitsToDbv(BrightnessMode bm,float nits) const98 std::optional<uint32_t> BrightnessController::LinearBrightnessTable::NitsToDbv(BrightnessMode bm,
99 float nits) const {
100 if (mBrightnessRanges.count(bm) == 0) {
101 return std::nullopt;
102 }
103 const auto& range = mBrightnessRanges.at(bm);
104 float dbv = 0.0;
105
106 dbv = LinearInterpolation(nits, range.nits_min, range.nits_max, range.dbv_min, range.dbv_max);
107 if (isnan(dbv) || dbv < 0) {
108 return std::nullopt;
109 }
110 return lround(dbv);
111 }
112
DbvToNits(BrightnessMode bm,uint32_t dbv) const113 std::optional<float> BrightnessController::LinearBrightnessTable::DbvToNits(BrightnessMode bm,
114 uint32_t dbv) const {
115 if (mBrightnessRanges.count(bm) == 0) {
116 return std::nullopt;
117 }
118 const auto& range = mBrightnessRanges.at(bm);
119 float nits = 0.0;
120
121 nits = LinearInterpolation(dbv, range.dbv_min, range.dbv_max, range.nits_min, range.nits_max);
122 if (isnan(nits)) {
123 return std::nullopt;
124 }
125 return nits;
126 }
127
BrightnessController(int32_t panelIndex,std::function<void (void)> refresh,std::function<void (void)> updateDcLhbm)128 BrightnessController::BrightnessController(int32_t panelIndex, std::function<void(void)> refresh,
129 std::function<void(void)> updateDcLhbm)
130 : mPanelIndex(panelIndex),
131 mEnhanceHbmReq(false),
132 mLhbmReq(false),
133 mBrightnessFloatReq(-1),
134 mBrightnessLevel(0),
135 mGhbm(HbmMode::OFF),
136 mDimming(false),
137 mLhbm(false),
138 mSdrDim(false),
139 mPrevSdrDim(false),
140 mDimBrightnessReq(false),
141 mOperationRate(0),
142 mFrameRefresh(refresh),
143 mHdrLayerState(HdrLayerState::kHdrNone),
144 mUpdateDcLhbm(updateDcLhbm) {
145 initBrightnessSysfs();
146 initCabcSysfs();
147 }
148
~BrightnessController()149 BrightnessController::~BrightnessController() {
150 if (mDimmingLooper) {
151 mDimmingLooper->removeMessages(mDimmingHandler);
152 }
153 if (mDimmingThreadRunning) {
154 mDimmingLooper->sendMessage(mDimmingHandler, DimmingMsgHandler::MSG_QUIT);
155 mDimmingThread.join();
156 }
157 }
158
updateBrightnessTable(const IBrightnessTable * table)159 void BrightnessController::updateBrightnessTable(const IBrightnessTable* table) {
160 if (table && table->GetBrightnessRange(BrightnessMode::BM_NOMINAL)) {
161 ALOGI("%s: apply brightness table from libdisplaycolor", __func__);
162 mBrightnessTable = table;
163 } else {
164 ALOGW("%s: table is not valid!", __func__);
165 }
166 if (!mBrightnessTable) {
167 ALOGE("%s: brightness table is not available!", __func__);
168 return;
169 }
170 auto normal_range = mBrightnessTable->GetBrightnessRange(BrightnessMode::BM_NOMINAL);
171 if (!normal_range) {
172 ALOGE("%s: normal brightness range not available!", __func__);
173 return;
174 }
175
176 // init to min before SF sets the brightness
177 mDisplayWhitePointNits = normal_range.value().get().nits_min;
178 mPrevDisplayWhitePointNits = mDisplayWhitePointNits;
179 mBrightnessIntfSupported = true;
180
181 String8 nodeName;
182 nodeName.appendFormat(kDimBrightnessFileNode, mPanelIndex);
183
184 std::ifstream ifsDimBrightness(nodeName.string());
185 if (ifsDimBrightness.fail()) {
186 ALOGW("%s fail to open %s", __func__, nodeName.string());
187 } else {
188 ifsDimBrightness >> mDimBrightness;
189 ifsDimBrightness.close();
190 if (mDimBrightness >= normal_range.value().get().dbv_min) mDimBrightness = 0;
191 }
192 mDbmSupported = !!mDimBrightness;
193 ALOGI("%s mDimBrightness=%d, mDbmSupported=%d", __func__, mDimBrightness, mDbmSupported);
194 }
195
initDrm(const DrmDevice & drmDevice,const DrmConnector & connector)196 int BrightnessController::initDrm(const DrmDevice& drmDevice, const DrmConnector& connector) {
197 initBrightnessTable(drmDevice, connector);
198
199 initDimmingUsage();
200
201 mLhbmSupported = connector.lhbm_on().id() != 0;
202 mGhbmSupported = connector.hbm_mode().id() != 0;
203
204 /* allow the first brightness to apply */
205 mBrightnessFloatReq.set_dirty();
206 return NO_ERROR;
207 }
208
initDimmingUsage()209 void BrightnessController::initDimmingUsage() {
210 String8 propName;
211 propName.appendFormat(kDimmingUsagePropName, mPanelIndex);
212
213 mBrightnessDimmingUsage = static_cast<BrightnessDimmingUsage>(property_get_int32(propName, 0));
214
215 propName.clear();
216 propName.appendFormat(kDimmingHbmTimePropName, mPanelIndex);
217 mHbmDimmingTimeUs = property_get_int32(propName, kHbmDimmingTimeUs);
218
219 if (mBrightnessDimmingUsage == BrightnessDimmingUsage::NORMAL) {
220 mDimming.store(true);
221 }
222
223 if (mBrightnessDimmingUsage == BrightnessDimmingUsage::HBM) {
224 mDimmingHandler = new DimmingMsgHandler(this);
225 mDimmingThread = std::thread(&BrightnessController::dimmingThread, this);
226 }
227 }
228
initBrightnessSysfs()229 void BrightnessController::initBrightnessSysfs() {
230 String8 nodeName;
231 nodeName.appendFormat(BRIGHTNESS_SYSFS_NODE, mPanelIndex);
232 mBrightnessOfs.open(nodeName.string(), std::ofstream::out);
233 if (mBrightnessOfs.fail()) {
234 ALOGE("%s %s fail to open", __func__, nodeName.string());
235 return;
236 }
237
238 nodeName.clear();
239 nodeName.appendFormat(MAX_BRIGHTNESS_SYSFS_NODE, mPanelIndex);
240
241 std::ifstream ifsMaxBrightness(nodeName.string());
242 if (ifsMaxBrightness.fail()) {
243 ALOGE("%s fail to open %s", __func__, nodeName.string());
244 return;
245 }
246
247 ifsMaxBrightness >> mMaxBrightness;
248 ifsMaxBrightness.close();
249
250 nodeName.clear();
251 nodeName.appendFormat(kGlobalAclModeFileNode, mPanelIndex);
252 mAclModeOfs.open(nodeName.string(), std::ofstream::out);
253 if (mAclModeOfs.fail()) {
254 ALOGI("%s %s not supported", __func__, nodeName.string());
255 } else {
256 String8 propName;
257 propName.appendFormat(kAclModeDefaultPropName, mPanelIndex);
258
259 mAclModeDefault = static_cast<AclMode>(property_get_int32(propName, 0));
260 mAclMode.set_dirty();
261 }
262 }
263
initCabcSysfs()264 void BrightnessController::initCabcSysfs() {
265 mCabcSupport = property_get_bool("vendor.display.cabc.supported", false);
266 if (!mCabcSupport) return;
267
268 String8 nodeName;
269 nodeName.appendFormat(kLocalCabcModeFileNode, mPanelIndex);
270
271 mCabcModeOfs.open(nodeName.string(), std::ofstream::out);
272 if (mCabcModeOfs.fail()) {
273 ALOGE("%s %s fail to open", __func__, nodeName.string());
274 return;
275 }
276 }
277
initBrightnessTable(const DrmDevice & drmDevice,const DrmConnector & connector)278 void BrightnessController::initBrightnessTable(const DrmDevice& drmDevice,
279 const DrmConnector& connector) {
280 if (connector.brightness_cap().id() == 0) {
281 ALOGD("the brightness_cap is not supported");
282 return;
283 }
284
285 const auto [ret, blobId] = connector.brightness_cap().value();
286 if (ret) {
287 ALOGE("Fail to get brightness_cap (ret = %d)", ret);
288 return;
289 }
290
291 if (blobId == 0) {
292 ALOGE("the brightness_cap is supported but blob is not valid");
293 return;
294 }
295
296 drmModePropertyBlobPtr blob = drmModeGetPropertyBlob(drmDevice.fd(), blobId);
297 if (blob == nullptr) {
298 ALOGE("Fail to get brightness_cap blob");
299 return;
300 }
301
302 const struct brightness_capability *cap =
303 reinterpret_cast<struct brightness_capability *>(blob->data);
304 mKernelBrightnessTable.Init(cap);
305 if (mKernelBrightnessTable.IsValid()) {
306 mBrightnessTable = &mKernelBrightnessTable;
307 }
308
309 parseHbmModeEnums(connector.hbm_mode());
310
311 drmModeFreePropertyBlob(blob);
312 }
313
processEnhancedHbm(bool on)314 int BrightnessController::processEnhancedHbm(bool on) {
315 if (!mGhbmSupported) {
316 return HWC2_ERROR_UNSUPPORTED;
317 }
318
319 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
320 mEnhanceHbmReq.store(on);
321 if (mEnhanceHbmReq.is_dirty()) {
322 updateStates();
323 }
324 return NO_ERROR;
325 }
326
processDimmingOff()327 void BrightnessController::processDimmingOff() {
328 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
329 if (mHbmDimming) {
330 mHbmDimming = false;
331 updateStates();
332 mFrameRefresh();
333 }
334 }
335
updateAclMode()336 int BrightnessController::updateAclMode() {
337 if (!mAclModeOfs.is_open()) return HWC2_ERROR_UNSUPPORTED;
338
339 if (mColorRenderIntent.get() == ColorRenderIntent::COLORIMETRIC) {
340 mAclMode.store(AclMode::ACL_ENHANCED);
341 } else {
342 mAclMode.store(mAclModeDefault);
343 }
344
345 if (!mAclMode.is_dirty()) return NO_ERROR;
346
347 mAclModeOfs.seekp(std::ios_base::beg);
348 mAclModeOfs << std::to_string(static_cast<uint8_t>(mAclMode.get()));
349 mAclModeOfs.flush();
350 if (mAclModeOfs.fail()) {
351 ALOGW("%s write acl_mode to %d error = %s", __func__, mAclMode.get(), strerror(errno));
352 mAclModeOfs.clear();
353 return HWC2_ERROR_NO_RESOURCES;
354 }
355
356 mAclMode.clear_dirty();
357 ALOGI("%s acl_mode = %d", __func__, mAclMode.get());
358
359 return NO_ERROR;
360 }
361
processDisplayBrightness(float brightness,const nsecs_t vsyncNs,bool waitPresent)362 int BrightnessController::processDisplayBrightness(float brightness, const nsecs_t vsyncNs,
363 bool waitPresent) {
364 uint32_t level;
365 bool ghbm;
366
367 if (mIgnoreBrightnessUpdateRequests) {
368 ALOGI("%s: Brightness update is ignored. requested: %f, current: %f",
369 __func__, brightness, mBrightnessFloatReq.get());
370 return NO_ERROR;
371 }
372
373 if (brightness < -1.0f || brightness > 1.0f) {
374 return HWC2_ERROR_BAD_PARAMETER;
375 }
376
377 ATRACE_CALL();
378 if (!mBrightnessIntfSupported) {
379 level = brightness < 0 ? 0 : static_cast<uint32_t>(brightness * mMaxBrightness + 0.5f);
380 return applyBrightnessViaSysfs(level);
381 }
382
383 {
384 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
385 /* apply the first brightness */
386 if (mBrightnessFloatReq.is_dirty()) mBrightnessLevel.set_dirty();
387
388 mBrightnessFloatReq.store(brightness);
389 if (!mBrightnessFloatReq.is_dirty()) {
390 return NO_ERROR;
391 }
392
393 // check if it will go drm path for below cases.
394 // case 1: hbm state will change
395 // case 2: for hwc3, brightness command could apply at next present if possible
396 if (queryBrightness(brightness, &ghbm, &level) == NO_ERROR) {
397 // ghbm on/off always go drm path
398 // check if this will cause a hbm transition
399 if (mGhbmSupported && (mGhbm.get() != HbmMode::OFF) != ghbm) {
400 // this brightness change will go drm path
401 updateStates();
402 mFrameRefresh(); // force next frame to update brightness
403 return NO_ERROR;
404 }
405 // there will be a Present to apply this brightness change
406 if (waitPresent) {
407 // this brightness change will go drm path
408 updateStates();
409 return NO_ERROR;
410 }
411 } else {
412 level = brightness < 0 ? 0 : static_cast<uint32_t>(brightness * mMaxBrightness + 0.5f);
413 }
414 // clear dirty before go sysfs path
415 mBrightnessFloatReq.clear_dirty();
416 }
417
418 // Sysfs path is faster than drm path. If there is an unchecked drm path change, the sysfs
419 // path should check the sysfs content.
420 if (mUncheckedGbhmRequest) {
421 ATRACE_NAME("check_ghbm_mode");
422 checkSysfsStatus(GetPanelSysfileByIndex(kGlobalHbmModeFileNode),
423 {std::to_string(toUnderlying(mPendingGhbmStatus.load()))}, vsyncNs * 5);
424 mUncheckedGbhmRequest = false;
425 }
426
427 if (mUncheckedLhbmRequest) {
428 ATRACE_NAME("check_lhbm_mode");
429 checkSysfsStatus(GetPanelSysfileByIndex(kLocalHbmModeFileNode),
430 {std::to_string(mPendingLhbmStatus)}, vsyncNs * 5);
431 mUncheckedLhbmRequest = false;
432 }
433
434 return applyBrightnessViaSysfs(level);
435 }
436
ignoreBrightnessUpdateRequests(bool ignore)437 int BrightnessController::ignoreBrightnessUpdateRequests(bool ignore) {
438 mIgnoreBrightnessUpdateRequests = ignore;
439
440 return NO_ERROR;
441 }
442
setBrightnessNits(float nits,const nsecs_t vsyncNs)443 int BrightnessController::setBrightnessNits(float nits, const nsecs_t vsyncNs) {
444 ALOGI("%s set brightness to %f nits", __func__, nits);
445
446 std::optional<float> brightness = mBrightnessTable ?
447 mBrightnessTable->NitsToBrightness(nits) : std::nullopt;
448
449 if (brightness == std::nullopt) {
450 ALOGI("%s could not find brightness for %f nits", __func__, nits);
451 return -EINVAL;
452 }
453
454 return processDisplayBrightness(brightness.value(), vsyncNs);
455 }
456
setBrightnessDbv(uint32_t dbv,const nsecs_t vsyncNs)457 int BrightnessController::setBrightnessDbv(uint32_t dbv, const nsecs_t vsyncNs) {
458 ALOGI("%s set brightness to %u dbv", __func__, dbv);
459
460 std::optional<float> brightness =
461 mBrightnessTable ? mBrightnessTable->DbvToBrightness(dbv) : std::nullopt;
462
463 if (brightness == std::nullopt) {
464 ALOGI("%s could not find brightness for %d dbv", __func__, dbv);
465 return -EINVAL;
466 }
467
468 return processDisplayBrightness(brightness.value(), vsyncNs);
469 }
470
471 // In HWC3, brightness change could be applied via drm commit or sysfs path.
472 // If a brightness change command does not come with a frame update, this
473 // function wil be called to apply the brghtness change via sysfs path.
applyPendingChangeViaSysfs(const nsecs_t vsyncNs)474 int BrightnessController::applyPendingChangeViaSysfs(const nsecs_t vsyncNs) {
475 ATRACE_CALL();
476 uint32_t level;
477 {
478 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
479
480 if (!mBrightnessLevel.is_dirty()) {
481 return NO_ERROR;
482 }
483
484 // there will be a drm commit to apply this brightness change if a GHBM change is pending.
485 if (mGhbm.is_dirty()) {
486 ALOGI("%s standalone brightness change will be handled by next frame update for GHBM",
487 __func__);
488 return NO_ERROR;
489 }
490
491 // there will be a drm commit to apply this brightness change if a LHBM change is pending.
492 if (mLhbm.is_dirty()) {
493 ALOGI("%s standalone brightness change will be handled by next frame update for LHBM",
494 __func__);
495 return NO_ERROR;
496 }
497
498 // there will be a drm commit to apply this brightness change if a operation rate change is
499 // pending.
500 if (mOperationRate.is_dirty()) {
501 ALOGI("%s standalone brightness change will be handled by next frame update for "
502 "operation rate",
503 __func__);
504 return NO_ERROR;
505 }
506
507 level = mBrightnessLevel.get();
508 }
509
510 if (mUncheckedBlRequest) {
511 ATRACE_NAME("check_bl_value");
512 checkSysfsStatus(GetPanelSysfileByIndex(BRIGHTNESS_SYSFS_NODE),
513 {std::to_string(mPendingBl)}, vsyncNs * 5);
514 mUncheckedBlRequest = false;
515 }
516
517 return applyBrightnessViaSysfs(level);
518 }
519
processLocalHbm(bool on)520 int BrightnessController::processLocalHbm(bool on) {
521 if (!mLhbmSupported) {
522 return HWC2_ERROR_UNSUPPORTED;
523 }
524
525 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
526 mLhbmReq.store(on);
527 // As kernel timeout timer might disable LHBM without letting HWC know, enforce mLhbmReq and
528 // mLhbm dirty to ensure the enabling request can be passed through kernel unconditionally.
529 // TODO-b/260915350: move LHBM timeout mechanism from kernel to HWC for easier control and sync.
530 if (on) {
531 mLhbmReq.set_dirty();
532 mLhbm.set_dirty();
533 }
534 if (mLhbmReq.is_dirty()) {
535 updateStates();
536 }
537
538 return NO_ERROR;
539 }
540
updateFrameStates(HdrLayerState hdrState,bool sdrDim)541 void BrightnessController::updateFrameStates(HdrLayerState hdrState, bool sdrDim) {
542 mHdrLayerState.store(hdrState);
543 if (!mGhbmSupported) {
544 return;
545 }
546
547 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
548 mPrevSdrDim.store(mSdrDim.get());
549 mSdrDim.store(sdrDim);
550 if (mSdrDim.is_dirty() || mPrevSdrDim.is_dirty()) {
551 updateStates();
552 }
553 }
554
updateColorRenderIntent(int32_t intent)555 void BrightnessController::updateColorRenderIntent(int32_t intent) {
556 mColorRenderIntent.store(static_cast<ColorRenderIntent>(intent));
557 if (mColorRenderIntent.is_dirty()) {
558 updateAclMode();
559 ALOGI("%s Color Render Intent = %d", __func__, mColorRenderIntent.get());
560 mColorRenderIntent.clear_dirty();
561 }
562 }
563
processInstantHbm(bool on)564 int BrightnessController::processInstantHbm(bool on) {
565 if (!mGhbmSupported) {
566 return HWC2_ERROR_UNSUPPORTED;
567 }
568
569 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
570 mInstantHbmReq.store(on);
571 if (mInstantHbmReq.is_dirty()) {
572 updateStates();
573 }
574 return NO_ERROR;
575 }
576
processDimBrightness(bool on)577 int BrightnessController::processDimBrightness(bool on) {
578 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
579 mDimBrightnessReq.store(on);
580 if (mDimBrightnessReq.is_dirty()) {
581 updateStates();
582 ALOGI("%s request = %d", __func__, mDimBrightnessReq.get());
583 }
584 return NO_ERROR;
585 }
586
getSdrDimRatioForInstantHbm()587 float BrightnessController::getSdrDimRatioForInstantHbm() {
588 if (!mBrightnessIntfSupported || !mGhbmSupported) {
589 return 1.0f;
590 }
591
592 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
593 if (!mInstantHbmReq.get()) {
594 return 1.0f;
595 }
596
597 float sdr = 0;
598 if (queryBrightness(mBrightnessFloatReq.get(), nullptr, nullptr, &sdr) != NO_ERROR) {
599 return 1.0f;
600 }
601
602 auto hbm_range = mBrightnessTable->GetBrightnessRange(BrightnessMode::BM_HBM);
603 if (!hbm_range) {
604 ALOGE("%s error HBM brightness range not available!", __func__);
605 return 1.0f;
606 }
607 float peak = hbm_range.value().get().nits_max;
608 if (sdr == 0 || peak == 0) {
609 ALOGW("%s error luminance value sdr %f peak %f", __func__, sdr, peak);
610 return 1.0f;
611 }
612
613 float ratio = sdr / peak;
614 if (ratio < kGhbmMinDimRatio) {
615 ALOGW("%s sdr dim ratio %f too small", __func__, ratio);
616 ratio = kGhbmMinDimRatio;
617 }
618
619 return ratio;
620 }
621
processOperationRate(int32_t hz)622 int BrightnessController::processOperationRate(int32_t hz) {
623 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
624 if (mOperationRate.get() != hz) {
625 ATRACE_CALL();
626 ALOGI("%s: store operation rate %d", __func__, hz);
627 mOperationRate.set_dirty();
628 mOperationRate.store(hz);
629 updateStates();
630 }
631
632 return NO_ERROR;
633 }
634
onClearDisplay(bool needModeClear)635 void BrightnessController::onClearDisplay(bool needModeClear) {
636 resetLhbmState();
637 mInstantHbmReq.reset(false);
638
639 if (mBrightnessLevel.is_dirty()) applyBrightnessViaSysfs(mBrightnessLevel.get());
640
641 if (!needModeClear) return;
642
643 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
644 mEnhanceHbmReq.reset(false);
645 mBrightnessFloatReq.reset(-1);
646
647 mBrightnessLevel.reset(0);
648 mDisplayWhitePointNits = 0;
649 mPrevDisplayWhitePointNits = 0;
650 mGhbm.reset(HbmMode::OFF);
651 mDimming.reset(false);
652 mHbmDimming = false;
653 if (mBrightnessDimmingUsage == BrightnessDimmingUsage::NORMAL) {
654 mDimming.store(true);
655 }
656 mOperationRate.reset(0);
657
658 std::lock_guard<std::recursive_mutex> lock1(mCabcModeMutex);
659 mCabcMode.reset(CabcMode::OFF);
660 }
661
prepareFrameCommit(ExynosDisplay & display,const DrmConnector & connector,ExynosDisplayDrmInterface::DrmModeAtomicReq & drmReq,const bool mixedComposition,bool & ghbmSync,bool & lhbmSync,bool & blSync,bool & opRateSync)662 int BrightnessController::prepareFrameCommit(ExynosDisplay& display, const DrmConnector& connector,
663 ExynosDisplayDrmInterface::DrmModeAtomicReq& drmReq,
664 const bool mixedComposition, bool& ghbmSync,
665 bool& lhbmSync, bool& blSync, bool& opRateSync) {
666 int ret;
667
668 ghbmSync = false;
669 lhbmSync = false;
670 blSync = false;
671 opRateSync = false;
672
673 ATRACE_CALL();
674 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
675
676 bool sync = false;
677 if (mixedComposition && mPrevDisplayWhitePointNits > 0 && mDisplayWhitePointNits > 0) {
678 float diff = std::abs(mPrevDisplayWhitePointNits - mDisplayWhitePointNits);
679 float min = std::min(mPrevDisplayWhitePointNits, mDisplayWhitePointNits);
680 if (diff / min > kBrightnessSyncThreshold) {
681 sync = true;
682 ALOGD("%s: enable brightness sync for change from %f to %f", __func__,
683 mPrevDisplayWhitePointNits, mDisplayWhitePointNits);
684 }
685 }
686
687 if (mDimming.is_dirty()) {
688 if ((ret = drmReq.atomicAddProperty(connector.id(), connector.dimming_on(),
689 mDimming.get())) < 0) {
690 ALOGE("%s: Fail to set dimming_on property", __func__);
691 }
692 mDimming.clear_dirty();
693 }
694
695 if (mLhbm.is_dirty() && mLhbmSupported) {
696 if ((ret = drmReq.atomicAddProperty(connector.id(), connector.lhbm_on(),
697 mLhbm.get())) < 0) {
698 ALOGE("%s: Fail to set lhbm_on property", __func__);
699 } else {
700 lhbmSync = true;
701 }
702
703 auto dbv = mBrightnessLevel.get();
704 auto old_dbv = dbv;
705 if (mLhbm.get()) {
706 mUpdateDcLhbm();
707 uint32_t dbv_adj = 0;
708 if (display.getColorAdjustedDbv(dbv_adj)) {
709 ALOGW("failed to get adjusted dbv");
710 } else if (dbv_adj != dbv && dbv_adj != 0) {
711 if (mBrightnessTable) {
712 auto normal_range =
713 mBrightnessTable->GetBrightnessRange(BrightnessMode::BM_NOMINAL);
714 if (normal_range) {
715 dbv_adj = std::clamp(dbv_adj, normal_range.value().get().dbv_min,
716 normal_range.value().get().dbv_max);
717 }
718 }
719 ALOGI("lhbm: adjust dbv from %d to %d", dbv, dbv_adj);
720 dbv = dbv_adj;
721 mLhbmBrightnessAdj = (dbv != old_dbv);
722 }
723 }
724
725 if (mLhbmBrightnessAdj) {
726 // case 1: lhbm on and dbv is changed, use the new dbv
727 // case 2: lhbm off and dbv was changed at lhbm on, use current dbv
728 if ((ret = drmReq.atomicAddProperty(connector.id(),
729 connector.brightness_level(), dbv)) < 0) {
730 ALOGE("%s: Fail to set brightness_level property", __func__);
731 } else {
732 blSync = true;
733 mUncheckedBlRequest = true;
734 mPendingBl = dbv;
735 }
736 }
737
738 // mLhbmBrightnessAdj will last from LHBM on to off
739 if (!mLhbm.get() && mLhbmBrightnessAdj) {
740 mLhbmBrightnessAdj = false;
741 }
742
743 mLhbm.clear_dirty();
744 }
745
746 if (mBrightnessLevel.is_dirty()) {
747 // skip if lhbm has updated bl
748 if (!blSync) {
749 if ((ret = drmReq.atomicAddProperty(connector.id(),
750 connector.brightness_level(),
751 mBrightnessLevel.get())) < 0) {
752 ALOGE("%s: Fail to set brightness_level property", __func__);
753 } else {
754 mUncheckedBlRequest = true;
755 mPendingBl = mBrightnessLevel.get();
756 blSync = sync;
757 }
758 }
759 mBrightnessLevel.clear_dirty();
760 mPrevDisplayWhitePointNits = mDisplayWhitePointNits;
761 }
762
763 if (mGhbm.is_dirty() && mGhbmSupported) {
764 HbmMode hbmMode = mGhbm.get();
765 auto [hbmEnum, ret] = DrmEnumParser::halToDrmEnum(static_cast<int32_t>(hbmMode),
766 mHbmModeEnums);
767 if (ret < 0) {
768 ALOGE("Fail to convert hbm mode(%d)", hbmMode);
769 return ret;
770 }
771
772 if ((ret = drmReq.atomicAddProperty(connector.id(), connector.hbm_mode(),
773 hbmEnum)) < 0) {
774 ALOGE("%s: Fail to set hbm_mode property", __func__);
775 } else {
776 ghbmSync = sync;
777 }
778 mGhbm.clear_dirty();
779 }
780
781 mHdrLayerState.clear_dirty();
782
783 if (mOperationRate.is_dirty()) {
784 if ((ret = drmReq.atomicAddProperty(connector.id(), connector.operation_rate(),
785 mOperationRate.get())) < 0) {
786 ALOGE("%s: Fail to set operation_rate property", __func__);
787 } else {
788 opRateSync = sync;
789 }
790 mOperationRate.clear_dirty();
791 }
792
793 return NO_ERROR;
794 }
795
handleMessage(const::android::Message & message)796 void BrightnessController::DimmingMsgHandler::handleMessage(const ::android::Message& message) {
797 ALOGI("%s %d", __func__, message.what);
798
799 switch (message.what) {
800 case MSG_DIMMING_OFF:
801 mBrightnessController->processDimmingOff();
802 break;
803
804 case MSG_QUIT:
805 mBrightnessController->mDimmingThreadRunning = false;
806 break;
807 }
808 }
809
dimmingThread()810 void BrightnessController::dimmingThread() {
811 mDimmingLooper = new Looper(false);
812 Looper::setForThread(mDimmingLooper);
813 mDimmingThreadRunning = true;
814 while (mDimmingThreadRunning.load(std::memory_order_relaxed)) {
815 mDimmingLooper->pollOnce(-1);
816 }
817 }
818
819 // Process all requests to update states for next commit
updateStates()820 int BrightnessController::updateStates() {
821 bool ghbm;
822 uint32_t level;
823 float brightness = mInstantHbmReq.get() ? 1.0f : mBrightnessFloatReq.get();
824 if (queryBrightness(brightness, &ghbm, &level, &mDisplayWhitePointNits)) {
825 ALOGW("%s failed to convert brightness %f", __func__, mBrightnessFloatReq.get());
826 return HWC2_ERROR_UNSUPPORTED;
827 }
828
829 mBrightnessLevel.store(level);
830 mLhbm.store(mLhbmReq.get());
831
832 // turn off irc for sun light visibility
833 bool irc = !mEnhanceHbmReq.get();
834 if (ghbm) {
835 mGhbm.store(irc ? HbmMode::ON_IRC_ON : HbmMode::ON_IRC_OFF);
836 } else {
837 mGhbm.store(HbmMode::OFF);
838 }
839
840 if (mLhbm.is_dirty()) {
841 // Next sysfs path should verify this change has been applied.
842 mUncheckedLhbmRequest = true;
843 mPendingLhbmStatus = mLhbm.get();
844 }
845 if (mGhbm.is_dirty()) {
846 // Next sysfs path should verify this change has been applied.
847 mUncheckedGbhmRequest = true;
848 mPendingGhbmStatus = mGhbm.get();
849 }
850
851 // no dimming for instant hbm
852 // no dimming if current or previous frame is mixed composition
853 // - frame N-1: no HDR, HBM off, no sdr dim
854 // - frame N: HDR visible HBM on, sdr dim is enabled
855 // - frame N+1, HDR gone, HBM off, no sdr dim.
856 // We don't need panel dimming for HBM on at frame N and HBM off at frame N+1
857 bool dimming = !mInstantHbmReq.get() && !mSdrDim.get() && !mPrevSdrDim.get();
858 switch (mBrightnessDimmingUsage) {
859 case BrightnessDimmingUsage::HBM:
860 // turn on dimming at HBM on/off
861 // turn off dimming after mHbmDimmingTimeUs or there is an instant hbm on/off
862 if (mGhbm.is_dirty() && dimming) {
863 mHbmDimming = true;
864 if (mDimmingLooper) {
865 mDimmingLooper->removeMessages(mDimmingHandler,
866 DimmingMsgHandler::MSG_DIMMING_OFF);
867 mDimmingLooper->sendMessageDelayed(us2ns(mHbmDimmingTimeUs), mDimmingHandler,
868 DimmingMsgHandler::MSG_DIMMING_OFF);
869 }
870 }
871
872 dimming = dimming && (mHbmDimming);
873 break;
874
875 case BrightnessDimmingUsage::NONE:
876 dimming = false;
877 break;
878
879 default:
880 break;
881 }
882 mDimming.store(dimming);
883
884 mEnhanceHbmReq.clear_dirty();
885 mLhbmReq.clear_dirty();
886 mBrightnessFloatReq.clear_dirty();
887 mInstantHbmReq.clear_dirty();
888 mSdrDim.clear_dirty();
889 mPrevSdrDim.clear_dirty();
890 mDimBrightnessReq.clear_dirty();
891
892 if (mBrightnessLevel.is_dirty() || mDimming.is_dirty() || mGhbm.is_dirty() ||
893 mLhbm.is_dirty()) {
894 printBrightnessStates("drm");
895 }
896 return NO_ERROR;
897 }
898
queryBrightness(float brightness,bool * ghbm,uint32_t * level,float * nits)899 int BrightnessController::queryBrightness(float brightness, bool *ghbm, uint32_t *level,
900 float *nits) {
901 if (!mBrightnessIntfSupported) {
902 return HWC2_ERROR_UNSUPPORTED;
903 }
904
905 if (mBrightnessTable == nullptr) {
906 ALOGE("%s: brightness table is empty!", __func__);
907 return HWC2_ERROR_UNSUPPORTED;
908 }
909
910 auto normal_range = mBrightnessTable->GetBrightnessRange(BrightnessMode::BM_NOMINAL);
911 if (!normal_range) {
912 ALOGE("%s: normal brightness range not available!", __func__);
913 return HWC2_ERROR_UNSUPPORTED;
914 }
915
916 if (brightness < 0) {
917 // screen off
918 if (ghbm) {
919 *ghbm = false;
920 }
921 if (level) {
922 *level = 0;
923 }
924 if (nits) {
925 *nits = 0;
926 }
927 return NO_ERROR;
928 }
929
930 BrightnessMode bm = BrightnessMode::BM_MAX;
931 std::optional<float> nits_value = mBrightnessTable->BrightnessToNits(brightness, bm);
932 if (!nits_value) {
933 return -EINVAL;
934 }
935 if (ghbm) {
936 *ghbm = (bm == BrightnessMode::BM_HBM);
937 }
938 std::optional<uint32_t> dbv_value = mBrightnessTable->NitsToDbv(bm, nits_value.value());
939 if (!dbv_value) {
940 return -EINVAL;
941 }
942
943 if (level) {
944 if ((bm == BrightnessMode::BM_NOMINAL) && mDbmSupported &&
945 (mDimBrightnessReq.get() == true) &&
946 (dbv_value == normal_range.value().get().dbv_min)) {
947 *level = mDimBrightness;
948 } else {
949 *level = dbv_value.value();
950 }
951 }
952 if (nits) {
953 *nits = nits_value.value();
954 }
955
956 return NO_ERROR;
957 }
958
959 // Return immediately if it's already in the status. Otherwise poll the status
checkSysfsStatus(const std::string & file,const std::vector<std::string> & expectedValue,const nsecs_t timeoutNs)960 int BrightnessController::checkSysfsStatus(const std::string& file,
961 const std::vector<std::string>& expectedValue,
962 const nsecs_t timeoutNs) {
963 ATRACE_CALL();
964
965 if (expectedValue.size() == 0) {
966 return -EINVAL;
967 }
968
969 char buf[16];
970 UniqueFd fd = open(file.c_str(), O_RDONLY);
971 if (fd.get() < 0) {
972 ALOGE("%s failed to open sysfs %s: %s", __func__, file.c_str(), strerror(errno));
973 return -ENOENT;
974 }
975
976 int size = read(fd.get(), buf, sizeof(buf));
977 if (size <= 0) {
978 ALOGE("%s failed to read from %s: %s", __func__, file.c_str(), strerror(errno));
979 return -EIO;
980 }
981
982 // '- 1' to remove trailing '\n'
983 std::string val = std::string(buf, size - 1);
984 if (std::find(expectedValue.begin(), expectedValue.end(), val) != expectedValue.end()) {
985 return OK;
986 } else if (timeoutNs == 0) {
987 // not get the expected value and no intention to wait
988 return -EINVAL;
989 }
990
991 struct pollfd pfd;
992 int ret = -EINVAL;
993
994 auto startTime = systemTime(SYSTEM_TIME_MONOTONIC);
995 pfd.fd = fd.get();
996 pfd.events = POLLPRI;
997 while (true) {
998 auto currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
999 // int64_t for nsecs_t
1000 auto remainTimeNs = timeoutNs - (currentTime - startTime);
1001 if (remainTimeNs <= 0) {
1002 remainTimeNs = ms2ns(1);
1003 }
1004 int pollRet = poll(&pfd, 1, ns2ms(remainTimeNs));
1005 if (pollRet == 0) {
1006 ALOGW("%s poll %s timeout", __func__, file.c_str());
1007 // time out
1008 ret = -ETIMEDOUT;
1009 break;
1010 } else if (pollRet > 0) {
1011 if (!(pfd.revents & POLLPRI)) {
1012 continue;
1013 }
1014
1015 lseek(fd.get(), 0, SEEK_SET);
1016 size = read(fd.get(), buf, sizeof(buf));
1017 if (size > 0) {
1018 val = std::string(buf, size - 1);
1019 if (std::find(expectedValue.begin(), expectedValue.end(), val) !=
1020 expectedValue.end()) {
1021 ret = OK;
1022 break;
1023 } else {
1024 std::string values;
1025 for (auto& s : expectedValue) {
1026 values += s + std::string(" ");
1027 }
1028 if (values.size() > 0) {
1029 values.resize(values.size() - 1);
1030 }
1031 ALOGW("%s read %s expected %s after notified on file %s", __func__, val.c_str(),
1032 values.c_str(), file.c_str());
1033 }
1034 } else {
1035 ret = -EIO;
1036 ALOGE("%s failed to read after notified %d on file %s", __func__, errno,
1037 file.c_str());
1038 break;
1039 }
1040 } else {
1041 if (errno == EAGAIN || errno == EINTR) {
1042 continue;
1043 }
1044
1045 ALOGE("%s poll failed %d on file %s", __func__, errno, file.c_str());
1046 ret = -errno;
1047 break;
1048 }
1049 };
1050
1051 return ret;
1052 }
1053
resetLhbmState()1054 void BrightnessController::resetLhbmState() {
1055 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
1056 mLhbmReq.reset(false);
1057 mLhbm.reset(false);
1058 mLhbmBrightnessAdj = false;
1059 }
1060
setOutdoorVisibility(LbeState state)1061 void BrightnessController::setOutdoorVisibility(LbeState state) {
1062 std::lock_guard<std::recursive_mutex> lock(mCabcModeMutex);
1063 mOutdoorVisibility = (state != LbeState::OFF);
1064 }
1065
updateCabcMode()1066 int BrightnessController::updateCabcMode() {
1067 if (!mCabcSupport || mCabcModeOfs.fail()) return HWC2_ERROR_UNSUPPORTED;
1068
1069 std::lock_guard<std::recursive_mutex> lock(mCabcModeMutex);
1070 CabcMode mode;
1071 if (mOutdoorVisibility)
1072 mode = CabcMode::OFF;
1073 else
1074 mode = isHdrLayerOn() ? CabcMode::CABC_MOVIE_MODE : CabcMode::CABC_UI_MODE;
1075 mCabcMode.store(mode);
1076
1077 if (mCabcMode.is_dirty()) {
1078 applyCabcModeViaSysfs(static_cast<uint8_t>(mode));
1079 ALOGD("%s, isHdrLayerOn: %d, mOutdoorVisibility: %d.", __func__, isHdrLayerOn(),
1080 mOutdoorVisibility);
1081 mCabcMode.clear_dirty();
1082 }
1083 return NO_ERROR;
1084 }
1085
applyBrightnessViaSysfs(uint32_t level)1086 int BrightnessController::applyBrightnessViaSysfs(uint32_t level) {
1087 if (mBrightnessOfs.is_open()) {
1088 ATRACE_NAME("write_bl_sysfs");
1089 mBrightnessOfs.seekp(std::ios_base::beg);
1090 mBrightnessOfs << std::to_string(level);
1091 mBrightnessOfs.flush();
1092 if (mBrightnessOfs.fail()) {
1093 ALOGE("%s fail to write brightness %d", __func__, level);
1094 mBrightnessOfs.clear();
1095 return HWC2_ERROR_NO_RESOURCES;
1096 }
1097
1098 {
1099 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
1100 mBrightnessLevel.reset(level);
1101 mPrevDisplayWhitePointNits = mDisplayWhitePointNits;
1102 printBrightnessStates("sysfs");
1103 }
1104
1105 return NO_ERROR;
1106 }
1107
1108 return HWC2_ERROR_UNSUPPORTED;
1109 }
1110
applyCabcModeViaSysfs(uint8_t mode)1111 int BrightnessController::applyCabcModeViaSysfs(uint8_t mode) {
1112 if (!mCabcModeOfs.is_open()) return HWC2_ERROR_UNSUPPORTED;
1113
1114 ATRACE_NAME("write_cabc_mode_sysfs");
1115 mCabcModeOfs.seekp(std::ios_base::beg);
1116 mCabcModeOfs << std::to_string(mode);
1117 mCabcModeOfs.flush();
1118 if (mCabcModeOfs.fail()) {
1119 ALOGE("%s fail to write CabcMode %d", __func__, mode);
1120 mCabcModeOfs.clear();
1121 return HWC2_ERROR_NO_RESOURCES;
1122 }
1123 ALOGI("%s Cabc_Mode=%d", __func__, mode);
1124 return NO_ERROR;
1125 }
1126
1127 // brightness is normalized to current display brightness
validateLayerBrightness(float brightness)1128 bool BrightnessController::validateLayerBrightness(float brightness) {
1129 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
1130 if (!std::isfinite(brightness)) {
1131 ALOGW("%s layer brightness %f is not a valid floating value", __func__, brightness);
1132 return false;
1133 }
1134
1135 if (brightness > 1.f || brightness < 0.f) {
1136 ALOGW("%s Brightness is out of [0, 1] range: %f", __func__, brightness);
1137 return false;
1138 }
1139
1140 return true;
1141 }
1142
parseHbmModeEnums(const DrmProperty & property)1143 void BrightnessController::parseHbmModeEnums(const DrmProperty& property) {
1144 const std::vector<std::pair<uint32_t, const char *>> modeEnums = {
1145 {static_cast<uint32_t>(HbmMode::OFF), "Off"},
1146 {static_cast<uint32_t>(HbmMode::ON_IRC_ON), "On IRC On"},
1147 {static_cast<uint32_t>(HbmMode::ON_IRC_OFF), "On IRC Off"},
1148 };
1149
1150 DrmEnumParser::parseEnums(property, modeEnums, mHbmModeEnums);
1151 for (auto &e : mHbmModeEnums) {
1152 ALOGD("hbm mode [hal: %d, drm: %" PRId64 ", %s]", e.first, e.second,
1153 modeEnums[e.first].second);
1154 }
1155 }
1156
1157 /*
1158 * WARNING: This print is parsed by Battery Historian. Consult with the Battery
1159 * Historian team before modifying (b/239640926).
1160 */
printBrightnessStates(const char * path)1161 void BrightnessController::printBrightnessStates(const char* path) {
1162 ALOGI("path=%s, id=%d, level=%d, nits=%f, brightness=%f, DimmingOn=%d, Hbm=%d, LhbmOn=%d, "
1163 "OpRate=%d",
1164 path ?: "unknown", mPanelIndex, mBrightnessLevel.get(), mDisplayWhitePointNits,
1165 mBrightnessFloatReq.get(), mDimming.get(), mGhbm.get(), mLhbm.get(),
1166 mOperationRate.get());
1167 }
1168
dump(String8 & result)1169 void BrightnessController::dump(String8& result) {
1170 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
1171
1172 result.appendFormat("BrightnessController:\n");
1173 result.appendFormat("\tsysfs support %d, max %d, valid brightness table %d, "
1174 "lhbm supported %d, ghbm supported %d\n", mBrightnessOfs.is_open(),
1175 mMaxBrightness, mBrightnessIntfSupported, mLhbmSupported, mGhbmSupported);
1176 result.appendFormat("\trequests: enhance hbm %d, lhbm %d, "
1177 "brightness %f, instant hbm %d, DimBrightness %d\n",
1178 mEnhanceHbmReq.get(), mLhbmReq.get(), mBrightnessFloatReq.get(),
1179 mInstantHbmReq.get(), mDimBrightnessReq.get());
1180 result.appendFormat("\tstates: brighntess level %d, ghbm %d, dimming %d, lhbm %d",
1181 mBrightnessLevel.get(), mGhbm.get(), mDimming.get(), mLhbm.get());
1182 result.appendFormat("\thdr layer state %d, unchecked lhbm request %d(%d), "
1183 "unchecked ghbm request %d(%d)\n",
1184 mHdrLayerState.get(), mUncheckedLhbmRequest.load(),
1185 mPendingLhbmStatus.load(), mUncheckedGbhmRequest.load(),
1186 mPendingGhbmStatus.load());
1187 result.appendFormat("\tdimming usage %d, hbm dimming %d, time us %d\n", mBrightnessDimmingUsage,
1188 mHbmDimming, mHbmDimmingTimeUs);
1189 result.appendFormat("\twhite point nits current %f, previous %f\n", mDisplayWhitePointNits,
1190 mPrevDisplayWhitePointNits);
1191 result.appendFormat("\tcabc supported %d, cabcMode %d\n", mCabcModeOfs.is_open(),
1192 mCabcMode.get());
1193 result.appendFormat("\tignore brightness update request %d\n", mIgnoreBrightnessUpdateRequests);
1194 result.appendFormat("\tacl mode supported %d, acl mode %d\n", mAclModeOfs.is_open(),
1195 mAclMode.get());
1196 result.appendFormat("\toperation rate %d\n", mOperationRate.get());
1197
1198 result.appendFormat("\n");
1199 }
1200