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 #ifndef _BRIGHTNESS_CONTROLLER_H_ 18 #define _BRIGHTNESS_CONTROLLER_H_ 19 20 #include <drm/samsung_drm.h> 21 #include <utils/Looper.h> 22 #include <utils/Mutex.h> 23 24 #include <fstream> 25 #include <thread> 26 27 #include "ExynosDisplayDrmInterface.h" 28 29 /** 30 * Brightness change requests come from binder calls or HWC itself. 31 * The request could be applied via next drm commit or immeditely via sysfs. 32 * 33 * To make it simple, setDisplayBrightness from SF, if not triggering a HBM on/off, 34 * will be applied immediately via sysfs path. All other requests will be applied via next 35 * drm commit. 36 * 37 * Sysfs path is faster than drm path. So if there is a pending drm commit that may 38 * change brightness level, sfsfs path task should wait until it has completed. 39 */ 40 class BrightnessController { 41 public: 42 using HdrLayerState = displaycolor::HdrLayerState; 43 using DisplayBrightnessRange = displaycolor::DisplayBrightnessRange; 44 using BrightnessRangeMap = displaycolor::BrightnessRangeMap; 45 using IBrightnessTable = displaycolor::IBrightnessTable; 46 using BrightnessMode = displaycolor::BrightnessMode; 47 using ColorRenderIntent = displaycolor::hwc::RenderIntent; 48 49 class DimmingMsgHandler : public virtual ::android::MessageHandler { 50 public: 51 enum { 52 MSG_QUIT, 53 MSG_DIMMING_OFF, 54 }; DimmingMsgHandler(BrightnessController * bc)55 DimmingMsgHandler(BrightnessController* bc) : mBrightnessController(bc) {} 56 void handleMessage(const Message& message) override; 57 58 private: 59 BrightnessController* mBrightnessController; 60 }; 61 62 BrightnessController(int32_t panelIndex, std::function<void(void)> refresh, 63 std::function<void(void)> updateDcLhbm); 64 ~BrightnessController(); 65 66 BrightnessController(int32_t panelIndex); 67 int initDrm(const DrmDevice& drmDevice, 68 const DrmConnector& connector); 69 70 int processEnhancedHbm(bool on); 71 int processDisplayBrightness(float bl, const nsecs_t vsyncNs, bool waitPresent = false); 72 int ignoreBrightnessUpdateRequests(bool ignore); 73 int setBrightnessNits(float nits, const nsecs_t vsyncNs); 74 int setBrightnessDbv(uint32_t dbv, const nsecs_t vsyncNs); 75 int processLocalHbm(bool on); 76 int processDimBrightness(bool on); 77 int processOperationRate(int32_t hz); isDbmSupported()78 bool isDbmSupported() { return mDbmSupported; } 79 int applyPendingChangeViaSysfs(const nsecs_t vsyncNs); 80 bool validateLayerBrightness(float brightness); 81 82 /** 83 * processInstantHbm for GHBM UDFPS 84 * - on true: turn on HBM at next frame with peak brightness 85 * false: turn off HBM at next frame and use system display brightness 86 * from processDisplayBrightness 87 */ 88 int processInstantHbm(bool on); 89 90 /** 91 * updateFrameStates 92 * - hdrState: hdr layer size in this frame 93 * - sdrDim: whether any dimmed sdr layer in this frame 94 */ 95 void updateFrameStates(HdrLayerState hdrState, bool sdrDim); 96 97 /** 98 * updateColorRenderIntent 99 * - intent: color render intent 100 */ 101 void updateColorRenderIntent(int32_t intent); 102 103 /** 104 * Dim ratio to keep the sdr brightness unchange after an instant hbm on with peak brightness. 105 */ 106 float getSdrDimRatioForInstantHbm(); 107 108 void onClearDisplay(bool needModeClear); 109 110 /** 111 * apply brightness change on drm path. 112 * Note: only this path can hold the lock for a long time 113 */ 114 int prepareFrameCommit(ExynosDisplay& display, const DrmConnector& connector, 115 ExynosDisplayDrmInterface::DrmModeAtomicReq& drmReq, 116 const bool mixedComposition, bool& ghbmSync, bool& lhbmSync, 117 bool& blSync, bool& opRateSync); 118 isGhbmSupported()119 bool isGhbmSupported() { return mGhbmSupported; } isLhbmSupported()120 bool isLhbmSupported() { return mLhbmSupported; } 121 isGhbmOn()122 bool isGhbmOn() { 123 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex); 124 return mGhbm.get() != HbmMode::OFF; 125 } 126 isLhbmOn()127 bool isLhbmOn() { 128 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex); 129 return mLhbm.get(); 130 } 131 int checkSysfsStatus(const std::string& file, const std::vector<std::string>& expectedValue, 132 const nsecs_t timeoutNs); fileExists(const std::string & file)133 bool fileExists(const std::string& file) { 134 struct stat sb; 135 return stat(file.c_str(), &sb) == 0; 136 } 137 void resetLhbmState(); 138 getBrightnessLevel()139 uint32_t getBrightnessLevel() { 140 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex); 141 return mBrightnessLevel.get(); 142 } 143 isDimSdr()144 bool isDimSdr() { 145 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex); 146 return mInstantHbmReq.get(); 147 } 148 getHdrLayerState()149 HdrLayerState getHdrLayerState() { 150 return mHdrLayerState.get(); 151 } 152 getOperationRate()153 uint32_t getOperationRate() { 154 std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex); 155 return mOperationRate.get(); 156 } 157 isSupported()158 bool isSupported() { 159 // valid mMaxBrightness means both brightness and max_brightness sysfs exist 160 return mMaxBrightness > 0; 161 } 162 163 void dump(String8 &result); 164 165 void setOutdoorVisibility(LbeState state); 166 167 int updateCabcMode(); 168 GetPanelSysfileByIndex(const char * file_pattern)169 const std::string GetPanelSysfileByIndex(const char *file_pattern) { 170 String8 nodeName; 171 nodeName.appendFormat(file_pattern, mPanelIndex); 172 return nodeName.c_str(); 173 } 174 GetPanelRefreshRateSysfile()175 const std::string GetPanelRefreshRateSysfile() { 176 String8 nodeName; 177 nodeName.appendFormat(kRefreshrateFileNode, 178 mPanelIndex == 0 ? "primary" 179 : mPanelIndex == 1 ? "secondary" 180 : "unknown"); 181 return nodeName.c_str(); 182 } 183 184 void updateBrightnessTable(const IBrightnessTable* table); getBrightnessRanges()185 const BrightnessRangeMap& getBrightnessRanges() const { 186 return mKernelBrightnessTable.GetBrightnessRangeMap(); 187 } 188 189 /* 190 * WARNING: This enum is parsed by Battery Historian. Add new values, but 191 * do not modify/remove existing ones. Alternatively, consult with the 192 * Battery Historian team (b/239640926). 193 */ 194 enum class BrightnessRange : uint32_t { 195 NORMAL = 0, 196 HBM = 1, 197 MAX, 198 }; 199 200 /* 201 * WARNING: This enum is parsed by Battery Historian. Add new values, but 202 * do not modify/remove existing ones. Alternatively, consult with the 203 * Battery Historian team (b/239640926). 204 */ 205 enum class HbmMode { 206 OFF = 0, 207 ON_IRC_ON = 1, 208 ON_IRC_OFF = 2, 209 }; 210 211 /* 212 * LHBM command need take a couple of frames to become effective 213 * DISABLED - finish sending disabling command to panel 214 * ENABLED - panel finishes boosting brightness to the peak value 215 * ENABLING - finish sending enabling command to panel (panel begins boosting brightness) 216 * Note: the definition should be consistent with kernel driver 217 */ 218 enum class LhbmMode { 219 DISABLED = 0, 220 ENABLED = 1, 221 ENABLING = 2, 222 }; 223 224 /* 225 * BrightnessDimmingUsage: 226 * NORMAL- enable dimming 227 * HBM- enable dimming only for hbm transition 228 * NONE- disable dimming 229 * 230 * WARNING: This enum is parsed by Battery Historian. Add new values, but 231 * do not modify/remove existing ones. Alternatively, consult with the 232 * Battery Historian team (b/239640926). 233 */ 234 enum class BrightnessDimmingUsage { 235 NORMAL = 0, 236 HBM = 1, 237 NONE, 238 }; 239 240 static constexpr const char *kLocalHbmModeFileNode = 241 "/sys/class/backlight/panel%d-backlight/local_hbm_mode"; 242 static constexpr const char* kDimBrightnessFileNode = 243 "/sys/class/backlight/panel%d-backlight/dim_brightness"; 244 static constexpr const char* kRefreshrateFileNode = 245 "/sys/devices/platform/exynos-drm/%s-panel/refresh_rate"; 246 247 private: 248 // This is a backup implementation of brightness table. It would be applied only when the system 249 // failed to initiate libdisplaycolor. The complete implementation is class 250 // DisplayData::BrightnessTable 251 class LinearBrightnessTable : public IBrightnessTable { 252 public: LinearBrightnessTable()253 LinearBrightnessTable() : mIsValid(false) {} 254 void Init(const struct brightness_capability* cap); IsValid()255 bool IsValid() const { return mIsValid; } GetBrightnessRangeMap()256 const BrightnessRangeMap& GetBrightnessRangeMap() const { return mBrightnessRanges; } 257 258 /* IBrightnessTable functions */ GetBrightnessRange(BrightnessMode bm)259 std::optional<std::reference_wrapper<const DisplayBrightnessRange>> GetBrightnessRange( 260 BrightnessMode bm) const override { 261 if (mBrightnessRanges.count(bm) == 0) { 262 return std::nullopt; 263 } 264 return mBrightnessRanges.at(bm); 265 } 266 std::optional<float> BrightnessToNits(float brightness, BrightnessMode& bm) const override; 267 std::optional<float> NitsToBrightness(float nits) const override; 268 std::optional<float> DbvToBrightness(uint32_t dbv) const override; 269 std::optional<uint32_t> NitsToDbv(BrightnessMode bm, float nits) const override; 270 std::optional<float> DbvToNits(BrightnessMode bm, uint32_t dbv) const override; 271 GetBrightnessMode(float brightness)272 BrightnessMode GetBrightnessMode(float brightness) const { 273 for (const auto& [mode, range] : mBrightnessRanges) { 274 if (((!range.brightness_min_exclusive && brightness == range.brightness_min) || 275 brightness > range.brightness_min) && 276 brightness <= range.brightness_max) { 277 return mode; 278 } 279 } 280 // return BM_MAX if there is no matching range 281 return BrightnessMode::BM_MAX; 282 } 283 GetBrightnessModeForNits(float nits)284 BrightnessMode GetBrightnessModeForNits(float nits) const { 285 for (const auto& [mode, range] : mBrightnessRanges) { 286 if (nits >= range.nits_min && nits <= range.nits_max) { 287 return mode; 288 } 289 } 290 // return BM_INVALID if there is no matching range 291 return BrightnessMode::BM_INVALID; 292 } 293 getBrightnessModeForDbv(uint32_t dbv)294 BrightnessMode getBrightnessModeForDbv(uint32_t dbv) const { 295 for (const auto& [mode, range] : mBrightnessRanges) { 296 if (dbv >= range.dbv_min && dbv <= range.dbv_max) { 297 return mode; 298 } 299 } 300 // return BM_INVALID if there is no matching range 301 return BrightnessMode::BM_INVALID; 302 } 303 304 private: setBrightnessRangeFromAttribute(const struct brightness_attribute & attr,displaycolor::DisplayBrightnessRange & range)305 static void setBrightnessRangeFromAttribute(const struct brightness_attribute& attr, 306 displaycolor::DisplayBrightnessRange& range) { 307 range.nits_min = attr.nits.min; 308 range.nits_max = attr.nits.max; 309 range.dbv_min = attr.level.min; 310 range.dbv_max = attr.level.max; 311 range.brightness_min_exclusive = false; 312 range.brightness_min = static_cast<float>(attr.percentage.min) / 100.0f; 313 range.brightness_max = static_cast<float>(attr.percentage.max) / 100.0f; 314 } 315 /** 316 * Implement linear interpolation/extrapolation formula: 317 * y = y1+(y2-y1)*(x-x1)/(x2-x1) 318 * Return NAN for following cases: 319 * - Attempt to do extrapolation when x1==x2 320 * - Undefined output when (x2 == x1) and (y2 != y1) 321 */ LinearInterpolation(float x,float x1,float x2,float y1,float y2)322 static inline float LinearInterpolation(float x, float x1, float x2, float y1, float y2) { 323 if (x2 == x1) { 324 if (x != x1) { 325 ALOGE("%s: attempt to do extrapolation when x1==x2", __func__); 326 return NAN; 327 } 328 if (y2 == y1) { 329 // This is considered a normal case. (interpolation between a single point) 330 return y1; 331 } else { 332 // The output is undefined when (y1!=y2) 333 ALOGE("%s: undefined output when (x2 == x1) and (y2 != y1)", __func__); 334 return NAN; 335 } 336 } 337 float t = (x - x1) / (x2 - x1); 338 return y1 + (y2 - y1) * t; 339 } SupportHBM()340 inline bool SupportHBM() const { 341 return mBrightnessRanges.count(BrightnessMode::BM_HBM) > 0; 342 } 343 bool mIsValid; 344 BrightnessRangeMap mBrightnessRanges; 345 }; 346 // sync brightness change for mixed composition when there is more than 50% luminance change. 347 // The percentage is calculated as: 348 // (big_lumi - small_lumi) / small_lumi 349 // For mixed composition, if remove brightness animations, the minimum brightness jump is 350 // between nbm peak and hbm peak. 50% will cover known panels 351 static constexpr float kBrightnessSyncThreshold = 0.5f; 352 // Worst case for panel with brightness range 2 nits to 1000 nits. 353 static constexpr float kGhbmMinDimRatio = 0.002; 354 static constexpr int32_t kHbmDimmingTimeUs = 5000000; 355 static constexpr const char *kGlobalHbmModeFileNode = 356 "/sys/class/backlight/panel%d-backlight/hbm_mode"; 357 static constexpr const char* kDimmingUsagePropName = 358 "vendor.display.%d.brightness.dimming.usage"; 359 static constexpr const char* kDimmingHbmTimePropName = 360 "vendor.display.%d.brightness.dimming.hbm_time"; 361 static constexpr const char* kGlobalAclModeFileNode = 362 "/sys/class/backlight/panel%d-backlight/acl_mode"; 363 static constexpr const char* kAclModeDefaultPropName = 364 "vendor.display.%d.brightness.acl.default"; 365 366 int queryBrightness(float brightness, bool* ghbm = nullptr, uint32_t* level = nullptr, 367 float *nits = nullptr); 368 void initBrightnessTable(const DrmDevice& device, const DrmConnector& connector); 369 void initBrightnessSysfs(); 370 void initCabcSysfs(); 371 void initDimmingUsage(); 372 int applyBrightnessViaSysfs(uint32_t level); 373 int applyCabcModeViaSysfs(uint8_t mode); 374 int updateStates(); // REQUIRES(mBrightnessMutex) 375 void dimmingThread(); 376 void processDimmingOff(); 377 int updateAclMode(); 378 379 void parseHbmModeEnums(const DrmProperty& property); 380 381 void printBrightnessStates(const char* path); // REQUIRES(mBrightnessMutex) 382 383 bool mLhbmSupported = false; 384 bool mGhbmSupported = false; 385 bool mDbmSupported = false; 386 bool mBrightnessIntfSupported = false; 387 LinearBrightnessTable mKernelBrightnessTable; 388 // External object from libdisplaycolor 389 const IBrightnessTable* mBrightnessTable = nullptr; 390 391 int32_t mPanelIndex; 392 DrmEnumParser::MapHal2DrmEnum mHbmModeEnums; 393 394 // brightness state 395 std::recursive_mutex mBrightnessMutex; 396 // requests 397 CtrlValue<bool> mEnhanceHbmReq; // GUARDED_BY(mBrightnessMutex) 398 CtrlValue<bool> mLhbmReq; // GUARDED_BY(mBrightnessMutex) 399 CtrlValue<float> mBrightnessFloatReq; // GUARDED_BY(mBrightnessMutex) 400 CtrlValue<bool> mInstantHbmReq; // GUARDED_BY(mBrightnessMutex) 401 // states to drm after updateStates call 402 CtrlValue<uint32_t> mBrightnessLevel; // GUARDED_BY(mBrightnessMutex) 403 CtrlValue<HbmMode> mGhbm; // GUARDED_BY(mBrightnessMutex) 404 CtrlValue<bool> mDimming; // GUARDED_BY(mBrightnessMutex) 405 CtrlValue<bool> mLhbm; // GUARDED_BY(mBrightnessMutex) 406 CtrlValue<bool> mSdrDim; // GUARDED_BY(mBrightnessMutex) 407 CtrlValue<bool> mPrevSdrDim; // GUARDED_BY(mBrightnessMutex) 408 CtrlValue<bool> mDimBrightnessReq; // GUARDED_BY(mBrightnessMutex) 409 CtrlValue<uint32_t> mOperationRate; // GUARDED_BY(mBrightnessMutex) 410 411 // Indicating if the last LHBM on has changed the brightness level 412 bool mLhbmBrightnessAdj = false; 413 414 // Indicating if brightness updates are ignored 415 bool mIgnoreBrightnessUpdateRequests = false; 416 417 std::function<void(void)> mFrameRefresh; 418 CtrlValue<HdrLayerState> mHdrLayerState; 419 CtrlValue<ColorRenderIntent> mColorRenderIntent; 420 421 // these are used by sysfs path to wait drm path bl change task 422 // indicationg an unchecked LHBM change in drm path 423 std::atomic<bool> mUncheckedLhbmRequest = false; 424 std::atomic<bool> mPendingLhbmStatus = false; 425 // indicationg an unchecked GHBM change in drm path 426 std::atomic<bool> mUncheckedGbhmRequest = false; 427 std::atomic<HbmMode> mPendingGhbmStatus = HbmMode::OFF; 428 // indicating an unchecked brightness change in drm path 429 std::atomic<bool> mUncheckedBlRequest = false; 430 std::atomic<uint32_t> mPendingBl = 0; 431 432 // these are dimming related 433 BrightnessDimmingUsage mBrightnessDimmingUsage = BrightnessDimmingUsage::NORMAL; 434 bool mHbmDimming = false; // GUARDED_BY(mBrightnessMutex) 435 int32_t mHbmDimmingTimeUs = 0; 436 std::thread mDimmingThread; 437 std::atomic<bool> mDimmingThreadRunning; 438 ::android::sp<::android::Looper> mDimmingLooper; 439 ::android::sp<DimmingMsgHandler> mDimmingHandler; 440 441 // sysfs path 442 std::ofstream mBrightnessOfs; 443 uint32_t mMaxBrightness = 0; // read from sysfs 444 std::ofstream mCabcModeOfs; 445 bool mCabcSupport = false; 446 uint32_t mDimBrightness = 0; 447 448 // Note IRC or dimming is not in consideration for now. 449 float mDisplayWhitePointNits = 0; 450 float mPrevDisplayWhitePointNits = 0; 451 452 std::function<void(void)> mUpdateDcLhbm; 453 454 // state for control ACL state 455 enum class AclMode { 456 ACL_OFF = 0, 457 ACL_NORMAL, 458 ACL_ENHANCED, 459 }; 460 461 std::ofstream mAclModeOfs; 462 CtrlValue<AclMode> mAclMode; 463 AclMode mAclModeDefault = AclMode::ACL_OFF; 464 465 // state for control CABC state 466 enum class CabcMode { 467 OFF = 0, 468 CABC_UI_MODE, 469 CABC_STILL_MODE, 470 CABC_MOVIE_MODE, 471 }; 472 473 static constexpr const char* kLocalCabcModeFileNode = 474 "/sys/class/backlight/panel%d-backlight/cabc_mode"; 475 std::recursive_mutex mCabcModeMutex; 476 bool mOutdoorVisibility = false; // GUARDED_BY(mCabcModeMutex) isHdrLayerOn()477 bool isHdrLayerOn() { return mHdrLayerState.get() == HdrLayerState::kHdrLarge; } 478 CtrlValue<CabcMode> mCabcMode; // GUARDED_BY(mCabcModeMutex) 479 }; 480 481 #endif // _BRIGHTNESS_CONTROLLER_H_ 482