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
BrightnessController(int32_t panelIndex,std::function<void (void)> refresh,std::function<void (void)> updateDcLhbm)25 BrightnessController::BrightnessController(int32_t panelIndex, std::function<void(void)> refresh,
26 std::function<void(void)> updateDcLhbm)
27 : mPanelIndex(panelIndex),
28 mEnhanceHbmReq(false),
29 mLhbmReq(false),
30 mBrightnessFloatReq(-1),
31 mBrightnessLevel(0),
32 mGhbm(HbmMode::OFF),
33 mDimming(false),
34 mLhbm(false),
35 mSdrDim(false),
36 mPrevSdrDim(false),
37 mDimBrightnessReq(false),
38 mFrameRefresh(refresh),
39 mHdrLayerState(HdrLayerState::kHdrNone),
40 mUpdateDcLhbm(updateDcLhbm) {
41 initBrightnessSysfs();
42 initCabcSysfs();
43 }
44
~BrightnessController()45 BrightnessController::~BrightnessController() {
46 if (mDimmingLooper) {
47 mDimmingLooper->removeMessages(mDimmingHandler);
48 }
49 if (mDimmingThreadRunning) {
50 mDimmingLooper->sendMessage(mDimmingHandler, DimmingMsgHandler::MSG_QUIT);
51 mDimmingThread.join();
52 }
53 }
54
initDrm(const DrmDevice & drmDevice,const DrmConnector & connector)55 int BrightnessController::initDrm(const DrmDevice& drmDevice,
56 const DrmConnector& connector) {
57 initBrightnessTable(drmDevice, connector);
58
59 initDimmingUsage();
60
61 mLhbmSupported = connector.lhbm_on().id() != 0;
62 mGhbmSupported = connector.hbm_mode().id() != 0;
63
64 /* allow the first brightness to apply */
65 mBrightnessFloatReq.set_dirty();
66 return NO_ERROR;
67 }
68
initDimmingUsage()69 void BrightnessController::initDimmingUsage() {
70 String8 propName;
71 propName.appendFormat(kDimmingUsagePropName, mPanelIndex);
72
73 mBrightnessDimmingUsage = static_cast<BrightnessDimmingUsage>(property_get_int32(propName, 0));
74
75 propName.clear();
76 propName.appendFormat(kDimmingHbmTimePropName, mPanelIndex);
77 mHbmDimmingTimeUs = property_get_int32(propName, kHbmDimmingTimeUs);
78
79 if (mBrightnessDimmingUsage == BrightnessDimmingUsage::NORMAL) {
80 mDimming.store(true);
81 }
82
83 if (mBrightnessDimmingUsage == BrightnessDimmingUsage::HBM) {
84 mDimmingHandler = new DimmingMsgHandler(this);
85 mDimmingThread = std::thread(&BrightnessController::dimmingThread, this);
86 }
87 }
88
initBrightnessSysfs()89 void BrightnessController::initBrightnessSysfs() {
90 String8 nodeName;
91 nodeName.appendFormat(BRIGHTNESS_SYSFS_NODE, mPanelIndex);
92 mBrightnessOfs.open(nodeName.string(), std::ofstream::out);
93 if (mBrightnessOfs.fail()) {
94 ALOGE("%s %s fail to open", __func__, nodeName.string());
95 mBrightnessOfs.close();
96 return;
97 }
98
99 nodeName.clear();
100 nodeName.appendFormat(MAX_BRIGHTNESS_SYSFS_NODE, mPanelIndex);
101
102 std::ifstream ifsMaxBrightness(nodeName.string());
103 if (ifsMaxBrightness.fail()) {
104 ALOGE("%s fail to open %s", __func__, nodeName.string());
105 return;
106 }
107
108 ifsMaxBrightness >> mMaxBrightness;
109 ifsMaxBrightness.close();
110 }
111
initCabcSysfs()112 void BrightnessController::initCabcSysfs() {
113 mCabcSupport = property_get_bool("vendor.display.cabc.supported", false);
114 if (!mCabcSupport) return;
115
116 String8 nodeName;
117 nodeName.appendFormat(kLocalCabcModeFileNode, mPanelIndex);
118
119 mCabcModeOfs.open(nodeName.string(), std::ofstream::out);
120 if (mCabcModeOfs.fail()) {
121 ALOGE("%s %s fail to open", __func__, nodeName.string());
122 mCabcModeOfs.close();
123 return;
124 }
125 }
126
initBrightnessTable(const DrmDevice & drmDevice,const DrmConnector & connector)127 void BrightnessController::initBrightnessTable(const DrmDevice& drmDevice,
128 const DrmConnector& connector) {
129 if (connector.brightness_cap().id() == 0) {
130 ALOGD("the brightness_cap is not supported");
131 return;
132 }
133
134 const auto [ret, blobId] = connector.brightness_cap().value();
135 if (ret) {
136 ALOGE("Fail to get brightness_cap (ret = %d)", ret);
137 return;
138 }
139
140 if (blobId == 0) {
141 ALOGE("the brightness_cap is supported but blob is not valid");
142 return;
143 }
144
145 drmModePropertyBlobPtr blob = drmModeGetPropertyBlob(drmDevice.fd(), blobId);
146 if (blob == nullptr) {
147 ALOGE("Fail to get brightness_cap blob");
148 return;
149 }
150
151 const struct brightness_capability *cap =
152 reinterpret_cast<struct brightness_capability *>(blob->data);
153 mBrightnessTable[toUnderlying(BrightnessRange::NORMAL)] = BrightnessTable(cap->normal);
154 mBrightnessTable[toUnderlying(BrightnessRange::HBM)] = BrightnessTable(cap->hbm);
155
156 parseHbmModeEnums(connector.hbm_mode());
157
158 // init to min before SF sets the brightness
159 mDisplayWhitePointNits = cap->normal.nits.min;
160 mPrevDisplayWhitePointNits = mDisplayWhitePointNits;
161 mBrightnessIntfSupported = true;
162
163 drmModeFreePropertyBlob(blob);
164
165 String8 nodeName;
166 nodeName.appendFormat(kDimBrightnessFileNode, mPanelIndex);
167
168 std::ifstream ifsDimBrightness(nodeName.string());
169 if (ifsDimBrightness.fail()) {
170 ALOGW("%s fail to open %s", __func__, nodeName.string());
171 } else {
172 ifsDimBrightness >> mDimBrightness;
173 ifsDimBrightness.close();
174 if (mDimBrightness >= mBrightnessTable[toUnderlying(BrightnessRange::NORMAL)].mBklStart)
175 mDimBrightness = 0;
176 }
177 mDbmSupported = !!mDimBrightness;
178 ALOGI("%s mDimBrightness=%d, mDbmSupported=%d", __func__, mDimBrightness, mDbmSupported);
179 }
180
processEnhancedHbm(bool on)181 int BrightnessController::processEnhancedHbm(bool on) {
182 if (!mGhbmSupported) {
183 return HWC2_ERROR_UNSUPPORTED;
184 }
185
186 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
187 mEnhanceHbmReq.store(on);
188 if (mEnhanceHbmReq.is_dirty()) {
189 updateStates();
190 }
191 return NO_ERROR;
192 }
193
processDimmingOff()194 void BrightnessController::processDimmingOff() {
195 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
196 if (mHbmDimming) {
197 mHbmDimming = false;
198 updateStates();
199 mFrameRefresh();
200 }
201 }
202
processDisplayBrightness(float brightness,const nsecs_t vsyncNs,bool waitPresent)203 int BrightnessController::processDisplayBrightness(float brightness, const nsecs_t vsyncNs,
204 bool waitPresent) {
205 uint32_t level;
206 bool ghbm;
207
208 if (brightness < -1.0f || brightness > 1.0f) {
209 return HWC2_ERROR_BAD_PARAMETER;
210 }
211
212 ATRACE_CALL();
213 if (!mBrightnessIntfSupported) {
214 level = brightness < 0 ? 0 : static_cast<uint32_t>(brightness * mMaxBrightness + 0.5f);
215 return applyBrightnessViaSysfs(level);
216 }
217
218 {
219 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
220 /* apply the first brightness */
221 if (mBrightnessFloatReq.is_dirty()) mBrightnessLevel.set_dirty();
222
223 mBrightnessFloatReq.store(brightness);
224 if (!mBrightnessFloatReq.is_dirty()) {
225 return NO_ERROR;
226 }
227
228 // check if it will go drm path for below cases.
229 // case 1: hbm state will change
230 // case 2: for hwc3, brightness command could apply at next present if possible
231 if (queryBrightness(brightness, &ghbm, &level) == NO_ERROR) {
232 // ghbm on/off always go drm path
233 // check if this will cause a hbm transition
234 if (mGhbmSupported && (mGhbm.get() != HbmMode::OFF) != ghbm) {
235 // this brightness change will go drm path
236 updateStates();
237 mFrameRefresh(); // force next frame to update brightness
238 return NO_ERROR;
239 }
240 // there will be a Present to apply this brightness change
241 if (waitPresent) {
242 // this brightness change will go drm path
243 updateStates();
244 return NO_ERROR;
245 }
246 } else {
247 level = brightness < 0 ? 0 : static_cast<uint32_t>(brightness * mMaxBrightness + 0.5f);
248 }
249 // go sysfs path
250 }
251
252 // Sysfs path is faster than drm path. If there is an unchecked drm path change, the sysfs
253 // path should check the sysfs content.
254 if (mUncheckedGbhmRequest) {
255 ATRACE_NAME("check_ghbm_mode");
256 checkSysfsStatus(kGlobalHbmModeFileNode,
257 {std::to_string(toUnderlying(mPendingGhbmStatus.load()))}, vsyncNs * 5);
258 mUncheckedGbhmRequest = false;
259 }
260
261 if (mUncheckedLhbmRequest) {
262 ATRACE_NAME("check_lhbm_mode");
263 checkSysfsStatus(kLocalHbmModeFileNode, {std::to_string(mPendingLhbmStatus)}, vsyncNs * 5);
264 mUncheckedLhbmRequest = false;
265 }
266
267 return applyBrightnessViaSysfs(level);
268 }
269
270 // In HWC3, brightness change could be applied via drm commit or sysfs path.
271 // If a brightness change command does not come with a frame update, this
272 // function wil be called to apply the brghtness change via sysfs path.
applyPendingChangeViaSysfs(const nsecs_t vsyncNs)273 int BrightnessController::applyPendingChangeViaSysfs(const nsecs_t vsyncNs) {
274 ATRACE_CALL();
275 uint32_t level;
276 {
277 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
278
279 if (!mBrightnessLevel.is_dirty()) {
280 return NO_ERROR;
281 }
282
283 // there will be a drm commit to apply this brightness change if a GHBM change is pending.
284 if (mGhbm.is_dirty()) {
285 ALOGI("%s standalone brightness change will be handled by next frame update for GHBM",
286 __func__);
287 return NO_ERROR;
288 }
289
290 // there will be a drm commit to apply this brightness change if a LHBM change is pending.
291 if (mLhbm.is_dirty()) {
292 ALOGI("%s standalone brightness change will be handled by next frame update for LHBM",
293 __func__);
294 return NO_ERROR;
295 }
296
297 level = mBrightnessLevel.get();
298 }
299
300 if (mUncheckedBlRequest) {
301 ATRACE_NAME("check_bl_value");
302 checkSysfsStatus(BRIGHTNESS_SYSFS_NODE, {std::to_string(mPendingBl)}, vsyncNs * 5);
303 mUncheckedBlRequest = false;
304 }
305
306 return applyBrightnessViaSysfs(level);
307 }
308
processLocalHbm(bool on)309 int BrightnessController::processLocalHbm(bool on) {
310 if (!mLhbmSupported) {
311 return HWC2_ERROR_UNSUPPORTED;
312 }
313
314 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
315 mLhbmReq.store(on);
316 if (mLhbmReq.is_dirty()) {
317 updateStates();
318 }
319
320 return NO_ERROR;
321 }
322
updateFrameStates(HdrLayerState hdrState,bool sdrDim)323 void BrightnessController::updateFrameStates(HdrLayerState hdrState, bool sdrDim) {
324 mHdrLayerState.store(hdrState);
325 if (!mGhbmSupported) {
326 return;
327 }
328
329 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
330 mPrevSdrDim.store(mSdrDim.get());
331 mSdrDim.store(sdrDim);
332 if (mSdrDim.is_dirty() || mPrevSdrDim.is_dirty()) {
333 updateStates();
334 }
335 }
336
processInstantHbm(bool on)337 int BrightnessController::processInstantHbm(bool on) {
338 if (!mGhbmSupported) {
339 return HWC2_ERROR_UNSUPPORTED;
340 }
341
342 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
343 mInstantHbmReq.store(on);
344 if (mInstantHbmReq.is_dirty()) {
345 updateStates();
346 }
347 return NO_ERROR;
348 }
349
processDimBrightness(bool on)350 int BrightnessController::processDimBrightness(bool on) {
351 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
352 mDimBrightnessReq.store(on);
353 if (mDimBrightnessReq.is_dirty()) {
354 updateStates();
355 ALOGI("%s request = %d", __func__, mDimBrightnessReq.get());
356 }
357 return NO_ERROR;
358 }
359
getSdrDimRatioForInstantHbm()360 float BrightnessController::getSdrDimRatioForInstantHbm() {
361 if (!mBrightnessIntfSupported || !mGhbmSupported) {
362 return 1.0f;
363 }
364
365 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
366 if (!mInstantHbmReq.get()) {
367 return 1.0f;
368 }
369
370 float sdr = 0;
371 if (queryBrightness(mBrightnessFloatReq.get(), nullptr, nullptr, &sdr) != NO_ERROR) {
372 return 1.0f;
373 }
374
375 float peak = mBrightnessTable[toUnderlying(BrightnessRange::MAX) - 1].mNitsEnd;
376 if (sdr == 0 || peak == 0) {
377 ALOGW("%s error luminance value sdr %f peak %f", __func__, sdr, peak);
378 return 1.0f;
379 }
380
381 float ratio = sdr / peak;
382 if (ratio < kGhbmMinDimRatio) {
383 ALOGW("%s sdr dim ratio %f too small", __func__, ratio);
384 ratio = kGhbmMinDimRatio;
385 }
386
387 return ratio;
388 }
389
onClearDisplay(bool needModeClear)390 void BrightnessController::onClearDisplay(bool needModeClear) {
391 resetLhbmState();
392 mInstantHbmReq.reset(false);
393
394 if (mBrightnessLevel.is_dirty()) applyBrightnessViaSysfs(mBrightnessLevel.get());
395
396 if (!needModeClear) return;
397
398 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
399 mEnhanceHbmReq.reset(false);
400 mBrightnessFloatReq.reset(-1);
401
402 mBrightnessLevel.reset(0);
403 mDisplayWhitePointNits = 0;
404 mPrevDisplayWhitePointNits = 0;
405 mGhbm.reset(HbmMode::OFF);
406 mDimming.reset(false);
407 mHbmDimming = false;
408 if (mBrightnessDimmingUsage == BrightnessDimmingUsage::NORMAL) {
409 mDimming.store(true);
410 }
411
412 std::lock_guard<std::recursive_mutex> lock1(mCabcModeMutex);
413 mCabcMode.reset(CabcMode::OFF);
414 }
415
prepareFrameCommit(ExynosDisplay & display,const DrmConnector & connector,ExynosDisplayDrmInterface::DrmModeAtomicReq & drmReq,const bool mixedComposition,bool & ghbmSync,bool & lhbmSync,bool & blSync)416 int BrightnessController::prepareFrameCommit(ExynosDisplay& display,
417 const DrmConnector& connector,
418 ExynosDisplayDrmInterface::DrmModeAtomicReq& drmReq,
419 const bool mixedComposition,
420 bool& ghbmSync, bool& lhbmSync, bool& blSync) {
421 int ret;
422
423 ghbmSync = false;
424 lhbmSync = false;
425 blSync = false;
426
427 ATRACE_CALL();
428 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
429
430 bool sync = false;
431 if (mixedComposition && mPrevDisplayWhitePointNits > 0 && mDisplayWhitePointNits > 0) {
432 float diff = std::abs(mPrevDisplayWhitePointNits - mDisplayWhitePointNits);
433 float min = std::min(mPrevDisplayWhitePointNits, mDisplayWhitePointNits);
434 if (diff / min > kBrightnessSyncThreshold) {
435 sync = true;
436 ALOGD("%s: enable brightness sync for change from %f to %f", __func__,
437 mPrevDisplayWhitePointNits, mDisplayWhitePointNits);
438 }
439 }
440
441 if (mDimming.is_dirty()) {
442 if ((ret = drmReq.atomicAddProperty(connector.id(), connector.dimming_on(),
443 mDimming.get())) < 0) {
444 ALOGE("%s: Fail to set dimming_on property", __func__);
445 }
446 mDimming.clear_dirty();
447 }
448
449 if (mLhbm.is_dirty() && mLhbmSupported) {
450 if ((ret = drmReq.atomicAddProperty(connector.id(), connector.lhbm_on(),
451 mLhbm.get())) < 0) {
452 ALOGE("%s: Fail to set lhbm_on property", __func__);
453 } else {
454 lhbmSync = true;
455 }
456
457 auto dbv = mBrightnessLevel.get();
458 auto old_dbv = dbv;
459 if (mLhbm.get()) {
460 mUpdateDcLhbm();
461 uint32_t dbv_adj = 0;
462 if (display.getColorAdjustedDbv(dbv_adj)) {
463 ALOGW("failed to get adjusted dbv");
464 } else if (dbv_adj != dbv && dbv_adj != 0) {
465 dbv_adj = std::clamp(dbv_adj,
466 mBrightnessTable[toUnderlying(BrightnessRange::NORMAL)].mBklStart,
467 mBrightnessTable[toUnderlying(BrightnessRange::NORMAL)].mBklEnd);
468
469 ALOGI("lhbm: adjust dbv from %d to %d", dbv, dbv_adj);
470 dbv = dbv_adj;
471 mLhbmBrightnessAdj = (dbv != old_dbv);
472 }
473 }
474
475 if (mLhbmBrightnessAdj) {
476 // case 1: lhbm on and dbv is changed, use the new dbv
477 // case 2: lhbm off and dbv was changed at lhbm on, use current dbv
478 if ((ret = drmReq.atomicAddProperty(connector.id(),
479 connector.brightness_level(), dbv)) < 0) {
480 ALOGE("%s: Fail to set brightness_level property", __func__);
481 } else {
482 blSync = true;
483 mUncheckedBlRequest = true;
484 mPendingBl = dbv;
485 }
486 }
487
488 // mLhbmBrightnessAdj will last from LHBM on to off
489 if (!mLhbm.get() && mLhbmBrightnessAdj) {
490 mLhbmBrightnessAdj = false;
491 }
492
493 mLhbm.clear_dirty();
494 }
495
496 if (mBrightnessLevel.is_dirty()) {
497 // skip if lhbm has updated bl
498 if (!blSync) {
499 if ((ret = drmReq.atomicAddProperty(connector.id(),
500 connector.brightness_level(),
501 mBrightnessLevel.get())) < 0) {
502 ALOGE("%s: Fail to set brightness_level property", __func__);
503 } else {
504 mUncheckedBlRequest = true;
505 mPendingBl = mBrightnessLevel.get();
506 blSync = sync;
507 }
508 }
509 mBrightnessLevel.clear_dirty();
510 mPrevDisplayWhitePointNits = mDisplayWhitePointNits;
511 }
512
513 if (mGhbm.is_dirty() && mGhbmSupported) {
514 HbmMode hbmMode = mGhbm.get();
515 auto [hbmEnum, ret] = DrmEnumParser::halToDrmEnum(static_cast<int32_t>(hbmMode),
516 mHbmModeEnums);
517 if (ret < 0) {
518 ALOGE("Fail to convert hbm mode(%d)", hbmMode);
519 return ret;
520 }
521
522 if ((ret = drmReq.atomicAddProperty(connector.id(), connector.hbm_mode(),
523 hbmEnum)) < 0) {
524 ALOGE("%s: Fail to set hbm_mode property", __func__);
525 } else {
526 ghbmSync = sync;
527 }
528 mGhbm.clear_dirty();
529 }
530
531 mHdrLayerState.clear_dirty();
532 return NO_ERROR;
533 }
534
handleMessage(const::android::Message & message)535 void BrightnessController::DimmingMsgHandler::handleMessage(const ::android::Message& message) {
536 ALOGI("%s %d", __func__, message.what);
537
538 switch (message.what) {
539 case MSG_DIMMING_OFF:
540 mBrightnessController->processDimmingOff();
541 break;
542
543 case MSG_QUIT:
544 mBrightnessController->mDimmingThreadRunning = false;
545 break;
546 }
547 }
548
dimmingThread()549 void BrightnessController::dimmingThread() {
550 mDimmingLooper = new Looper(false);
551 Looper::setForThread(mDimmingLooper);
552 mDimmingThreadRunning = true;
553 while (mDimmingThreadRunning.load(std::memory_order_relaxed)) {
554 mDimmingLooper->pollOnce(-1);
555 }
556 }
557
558 // Process all requests to update states for next commit
updateStates()559 int BrightnessController::updateStates() {
560 bool ghbm;
561 uint32_t level;
562 float brightness = mInstantHbmReq.get() ? 1.0f : mBrightnessFloatReq.get();
563 if (queryBrightness(brightness, &ghbm, &level, &mDisplayWhitePointNits)) {
564 ALOGW("%s failed to convert brightness %f", __func__, mBrightnessFloatReq.get());
565 return HWC2_ERROR_UNSUPPORTED;
566 }
567
568 mBrightnessLevel.store(level);
569 mLhbm.store(mLhbmReq.get());
570
571 // turn off irc for sun light visibility
572 bool irc = !mEnhanceHbmReq.get();
573 if (ghbm) {
574 mGhbm.store(irc ? HbmMode::ON_IRC_ON : HbmMode::ON_IRC_OFF);
575 } else {
576 mGhbm.store(HbmMode::OFF);
577 }
578
579 if (mLhbm.is_dirty()) {
580 // Next sysfs path should verify this change has been applied.
581 mUncheckedLhbmRequest = true;
582 mPendingLhbmStatus = mLhbm.get();
583 }
584 if (mGhbm.is_dirty()) {
585 // Next sysfs path should verify this change has been applied.
586 mUncheckedGbhmRequest = true;
587 mPendingGhbmStatus = mGhbm.get();
588 }
589
590 // no dimming for instant hbm
591 // no dimming if current or previous frame is mixed composition
592 // - frame N-1: no HDR, HBM off, no sdr dim
593 // - frame N: HDR visible HBM on, sdr dim is enabled
594 // - frame N+1, HDR gone, HBM off, no sdr dim.
595 // We don't need panel dimming for HBM on at frame N and HBM off at frame N+1
596 bool dimming = !mInstantHbmReq.get() && !mSdrDim.get() && !mPrevSdrDim.get();
597 switch (mBrightnessDimmingUsage) {
598 case BrightnessDimmingUsage::HBM:
599 // turn on dimming at HBM on/off
600 // turn off dimming after mHbmDimmingTimeUs or there is an instant hbm on/off
601 if (mGhbm.is_dirty() && dimming) {
602 mHbmDimming = true;
603 if (mDimmingLooper) {
604 mDimmingLooper->removeMessages(mDimmingHandler,
605 DimmingMsgHandler::MSG_DIMMING_OFF);
606 mDimmingLooper->sendMessageDelayed(us2ns(mHbmDimmingTimeUs), mDimmingHandler,
607 DimmingMsgHandler::MSG_DIMMING_OFF);
608 }
609 }
610
611 dimming = dimming && (mHbmDimming);
612 break;
613
614 case BrightnessDimmingUsage::NONE:
615 dimming = false;
616 break;
617
618 default:
619 break;
620 }
621 mDimming.store(dimming);
622
623 mEnhanceHbmReq.clear_dirty();
624 mLhbmReq.clear_dirty();
625 mBrightnessFloatReq.clear_dirty();
626 mInstantHbmReq.clear_dirty();
627 mSdrDim.clear_dirty();
628 mPrevSdrDim.clear_dirty();
629 mDimBrightnessReq.clear_dirty();
630
631 if (mBrightnessLevel.is_dirty() || mDimming.is_dirty() || mGhbm.is_dirty() ||
632 mLhbm.is_dirty()) {
633 printBrightnessStates("drm");
634 }
635 return NO_ERROR;
636 }
637
queryBrightness(float brightness,bool * ghbm,uint32_t * level,float * nits)638 int BrightnessController::queryBrightness(float brightness, bool *ghbm, uint32_t *level,
639 float *nits) {
640 if (!mBrightnessIntfSupported) {
641 return HWC2_ERROR_UNSUPPORTED;
642 }
643
644 if (mBrightnessTable[toUnderlying(BrightnessRange::NORMAL)].mBklStart == 0 &&
645 mBrightnessTable[toUnderlying(BrightnessRange::NORMAL)].mBklEnd == 0 &&
646 mBrightnessTable[toUnderlying(BrightnessRange::NORMAL)].mBriStart == 0 &&
647 mBrightnessTable[toUnderlying(BrightnessRange::NORMAL)].mBriEnd == 0 &&
648 mBrightnessTable[toUnderlying(BrightnessRange::NORMAL)].mNitsStart == 0 &&
649 mBrightnessTable[toUnderlying(BrightnessRange::NORMAL)].mNitsEnd == 0) {
650 return HWC2_ERROR_UNSUPPORTED;
651 }
652
653 if (brightness < 0) {
654 // screen off
655 if (ghbm) {
656 *ghbm = false;
657 }
658 if (level) {
659 *level = 0;
660 }
661 if (nits) {
662 *nits = 0;
663 }
664 return NO_ERROR;
665 }
666
667 for (uint32_t i = 0; i < toUnderlying(BrightnessRange::MAX); ++i) {
668 if (brightness <= mBrightnessTable[i].mBriEnd) {
669 if (ghbm) {
670 *ghbm = (i == toUnderlying(BrightnessRange::HBM));
671 }
672
673 if (level || nits) {
674 auto fSpan = mBrightnessTable[i].mBriEnd - mBrightnessTable[i].mBriStart;
675 auto norm = fSpan == 0 ? 1 : (brightness - mBrightnessTable[i].mBriStart) / fSpan;
676
677 if (level) {
678 auto iSpan = mBrightnessTable[i].mBklEnd - mBrightnessTable[i].mBklStart;
679 auto bl = norm * iSpan + mBrightnessTable[i].mBklStart;
680 *level = static_cast<uint32_t>(bl + 0.5);
681 }
682
683 if (nits) {
684 auto nSpan = mBrightnessTable[i].mNitsEnd - mBrightnessTable[i].mNitsStart;
685 *nits = norm * nSpan + mBrightnessTable[i].mNitsStart;
686 }
687 }
688 if ((i == toUnderlying(BrightnessRange::NORMAL)) && mDbmSupported &&
689 (mDimBrightnessReq.get() == true) && (*level == mBrightnessTable[i].mBklStart)) {
690 *level = mDimBrightness;
691 }
692 return NO_ERROR;
693 }
694 }
695
696 return -EINVAL;
697 }
698
699 // Return immediately if it's already in the status. Otherwise poll the status
checkSysfsStatus(const char * file,const std::vector<std::string> & expectedValue,const nsecs_t timeoutNs)700 int BrightnessController::checkSysfsStatus(const char* file,
701 const std::vector<std::string>& expectedValue,
702 const nsecs_t timeoutNs) {
703 ATRACE_CALL();
704
705 if (expectedValue.size() == 0) return false;
706
707 char buf[16];
708 String8 nodeName;
709 nodeName.appendFormat(file, mPanelIndex);
710 UniqueFd fd = open(nodeName.string(), O_RDONLY);
711
712 int size = read(fd.get(), buf, sizeof(buf));
713 if (size <= 0) {
714 ALOGE("%s failed to read from %s", __func__, kLocalHbmModeFileNode);
715 return false;
716 }
717
718 // '- 1' to remove trailing '\n'
719 std::string val = std::string(buf, size - 1);
720 if (std::find(expectedValue.begin(), expectedValue.end(), val) != expectedValue.end()) {
721 return true;
722 } else if (timeoutNs == 0) {
723 return false;
724 }
725
726 struct pollfd pfd;
727 int ret = EINVAL;
728
729 auto startTime = systemTime(SYSTEM_TIME_MONOTONIC);
730 pfd.fd = fd.get();
731 pfd.events = POLLPRI;
732 while (true) {
733 auto currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
734 // int64_t for nsecs_t
735 auto remainTimeNs = timeoutNs - (currentTime - startTime);
736 if (remainTimeNs <= 0) {
737 remainTimeNs = ms2ns(1);
738 }
739 int pollRet = poll(&pfd, 1, ns2ms(remainTimeNs));
740 if (pollRet == 0) {
741 ALOGW("%s poll timeout", __func__);
742 // time out
743 ret = ETIMEDOUT;
744 break;
745 } else if (pollRet > 0) {
746 if (!(pfd.revents & POLLPRI)) {
747 continue;
748 }
749
750 lseek(fd.get(), 0, SEEK_SET);
751 size = read(fd.get(), buf, sizeof(buf));
752 if (size > 0) {
753 val = std::string(buf, size - 1);
754 if (std::find(expectedValue.begin(), expectedValue.end(), val) !=
755 expectedValue.end()) {
756 ret = 0;
757 } else {
758 std::string values;
759 for (auto& s : expectedValue) {
760 values += s + std::string(" ");
761 }
762 if (values.size() > 0) {
763 values.resize(values.size() - 1);
764 }
765 ALOGE("%s read %s expected %s after notified", __func__, val.c_str(),
766 values.c_str());
767 ret = EINVAL;
768 }
769 } else {
770 ret = EIO;
771 ALOGE("%s failed to read after notified %d", __func__, errno);
772 }
773 break;
774 } else {
775 if (errno == EAGAIN || errno == EINTR) {
776 continue;
777 }
778
779 ALOGE("%s poll failed %d", __func__, errno);
780 ret = errno;
781 break;
782 }
783 };
784
785 return ret == NO_ERROR;
786 }
787
resetLhbmState()788 void BrightnessController::resetLhbmState() {
789 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
790 mLhbmReq.reset(false);
791 mLhbm.reset(false);
792 mLhbmBrightnessAdj = false;
793 }
794
setOutdoorVisibility(LbeState state)795 void BrightnessController::setOutdoorVisibility(LbeState state) {
796 std::lock_guard<std::recursive_mutex> lock(mCabcModeMutex);
797 mOutdoorVisibility = (state != LbeState::OFF);
798 }
799
updateCabcMode()800 int BrightnessController::updateCabcMode() {
801 if (!mCabcSupport || mCabcModeOfs.fail()) return HWC2_ERROR_UNSUPPORTED;
802
803 std::lock_guard<std::recursive_mutex> lock(mCabcModeMutex);
804 CabcMode mode;
805 if (mOutdoorVisibility)
806 mode = CabcMode::OFF;
807 else
808 mode = isHdrLayerOn() ? CabcMode::CABC_MOVIE_MODE : CabcMode::CABC_UI_MODE;
809 mCabcMode.store(mode);
810
811 if (mCabcMode.is_dirty()) {
812 applyCabcModeViaSysfs(static_cast<uint8_t>(mode));
813 ALOGD("%s, isHdrLayerOn: %d, mOutdoorVisibility: %d.", __func__, isHdrLayerOn(),
814 mOutdoorVisibility);
815 mCabcMode.clear_dirty();
816 }
817 return NO_ERROR;
818 }
819
applyBrightnessViaSysfs(uint32_t level)820 int BrightnessController::applyBrightnessViaSysfs(uint32_t level) {
821 if (mBrightnessOfs.is_open()) {
822 ATRACE_NAME("write_bl_sysfs");
823 mBrightnessOfs.seekp(std::ios_base::beg);
824 mBrightnessOfs << std::to_string(level);
825 mBrightnessOfs.flush();
826 if (mBrightnessOfs.fail()) {
827 ALOGE("%s fail to write brightness %d", __func__, level);
828 mBrightnessOfs.clear();
829 return HWC2_ERROR_NO_RESOURCES;
830 }
831
832 {
833 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
834 mBrightnessLevel.reset(level);
835 mPrevDisplayWhitePointNits = mDisplayWhitePointNits;
836 printBrightnessStates("sysfs");
837 }
838
839 return NO_ERROR;
840 }
841
842 return HWC2_ERROR_UNSUPPORTED;
843 }
844
applyCabcModeViaSysfs(uint8_t mode)845 int BrightnessController::applyCabcModeViaSysfs(uint8_t mode) {
846 if (!mCabcModeOfs.is_open()) return HWC2_ERROR_UNSUPPORTED;
847
848 ATRACE_NAME("write_cabc_mode_sysfs");
849 mCabcModeOfs.seekp(std::ios_base::beg);
850 mCabcModeOfs << std::to_string(mode);
851 mCabcModeOfs.flush();
852 if (mCabcModeOfs.fail()) {
853 ALOGE("%s fail to write CabcMode %d", __func__, mode);
854 mCabcModeOfs.clear();
855 return HWC2_ERROR_NO_RESOURCES;
856 }
857 ALOGI("%s Cabc_Mode=%d", __func__, mode);
858 return NO_ERROR;
859 }
860
861 // brightness is normalized to current display brightness
validateLayerBrightness(float brightness)862 bool BrightnessController::validateLayerBrightness(float brightness) {
863 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
864 if (!std::isfinite(brightness)) {
865 ALOGW("%s layer brightness %f is not a valid floating value", __func__, brightness);
866 return false;
867 }
868
869 if (brightness > 1.f || brightness < 0.f) {
870 ALOGW("%s Brightness is out of [0, 1] range: %f", __func__, brightness);
871 return false;
872 }
873
874 return true;
875 }
876
parseHbmModeEnums(const DrmProperty & property)877 void BrightnessController::parseHbmModeEnums(const DrmProperty& property) {
878 const std::vector<std::pair<uint32_t, const char *>> modeEnums = {
879 {static_cast<uint32_t>(HbmMode::OFF), "Off"},
880 {static_cast<uint32_t>(HbmMode::ON_IRC_ON), "On IRC On"},
881 {static_cast<uint32_t>(HbmMode::ON_IRC_OFF), "On IRC Off"},
882 };
883
884 DrmEnumParser::parseEnums(property, modeEnums, mHbmModeEnums);
885 for (auto &e : mHbmModeEnums) {
886 ALOGD("hbm mode [hal: %d, drm: %" PRId64 ", %s]", e.first, e.second,
887 modeEnums[e.first].second);
888 }
889 }
890
891 /*
892 * WARNING: This print is parsed by Battery Historian. Consult with the Battery
893 * Historian team before modifying (b/239640926).
894 */
printBrightnessStates(const char * path)895 void BrightnessController::printBrightnessStates(const char* path) {
896 ALOGI("path=%s, id=%d, level=%d, DimmingOn=%d, Hbm=%d, LhbmOn=%d", path ?: "unknown",
897 mPanelIndex, mBrightnessLevel.get(), mDimming.get(), mGhbm.get(), mLhbm.get());
898 }
899
dump(String8 & result)900 void BrightnessController::dump(String8& result) {
901 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
902
903 result.appendFormat("BrightnessController:\n");
904 result.appendFormat("\tsysfs support %d, max %d, valid brightness table %d, "
905 "lhbm supported %d, ghbm supported %d\n", mBrightnessOfs.is_open(),
906 mMaxBrightness, mBrightnessIntfSupported, mLhbmSupported, mGhbmSupported);
907 result.appendFormat("\trequests: enhance hbm %d, lhbm %d, "
908 "brightness %f, instant hbm %d, DimBrightness %d\n",
909 mEnhanceHbmReq.get(), mLhbmReq.get(), mBrightnessFloatReq.get(),
910 mInstantHbmReq.get(), mDimBrightnessReq.get());
911 result.appendFormat("\tstates: brighntess level %d, ghbm %d, dimming %d, lhbm %d",
912 mBrightnessLevel.get(), mGhbm.get(), mDimming.get(), mLhbm.get());
913 result.appendFormat("\thdr layer state %d, unchecked lhbm request %d(%d), "
914 "unchecked ghbm request %d(%d)\n",
915 mHdrLayerState.get(), mUncheckedLhbmRequest.load(),
916 mPendingLhbmStatus.load(), mUncheckedGbhmRequest.load(),
917 mPendingGhbmStatus.load());
918 result.appendFormat("\tdimming usage %d, hbm dimming %d, time us %d\n", mBrightnessDimmingUsage,
919 mHbmDimming, mHbmDimmingTimeUs);
920 result.appendFormat("\twhite point nits current %f, previous %f\n", mDisplayWhitePointNits,
921 mPrevDisplayWhitePointNits);
922 result.appendFormat("\tcabc supported %d, cabcMode %d\n", mCabcModeOfs.is_open(),
923 mCabcMode.get());
924
925 result.appendFormat("\n");
926 }
927