/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _BRIGHTNESS_CONTROLLER_H_ #define _BRIGHTNESS_CONTROLLER_H_ #include #include #include #include #include #include "ExynosDisplayDrmInterface.h" /** * Brightness change requests come from binder calls or HWC itself. * The request could be applied via next drm commit or immeditely via sysfs. * * To make it simple, setDisplayBrightness from SF, if not triggering a HBM on/off, * will be applied immediately via sysfs path. All other requests will be applied via next * drm commit. * * Sysfs path is faster than drm path. So if there is a pending drm commit that may * change brightness level, sfsfs path task should wait until it has completed. */ class BrightnessController { public: using HdrLayerState = displaycolor::HdrLayerState; class DimmingMsgHandler : public virtual ::android::MessageHandler { public: enum { MSG_QUIT, MSG_DIMMING_OFF, }; DimmingMsgHandler(BrightnessController* bc) : mBrightnessController(bc) {} void handleMessage(const Message& message) override; private: BrightnessController* mBrightnessController; }; BrightnessController(int32_t panelIndex, std::function refresh, std::function updateDcLhbm); ~BrightnessController(); BrightnessController(int32_t panelIndex); int initDrm(const DrmDevice& drmDevice, const DrmConnector& connector); int processEnhancedHbm(bool on); int processDisplayBrightness(float bl, const nsecs_t vsyncNs, bool waitPresent = false); int processLocalHbm(bool on); int processDimBrightness(bool on); bool isDbmSupported() { return mDbmSupported; } int applyPendingChangeViaSysfs(const nsecs_t vsyncNs); bool validateLayerBrightness(float brightness); /** * processInstantHbm for GHBM UDFPS * - on true: turn on HBM at next frame with peak brightness * false: turn off HBM at next frame and use system display brightness * from processDisplayBrightness */ int processInstantHbm(bool on); /** * updateFrameStates * - hdrState: hdr layer size in this frame * - sdrDim: whether any dimmed sdr layer in this frame */ void updateFrameStates(HdrLayerState hdrState, bool sdrDim); /** * Dim ratio to keep the sdr brightness unchange after an instant hbm on with peak brightness. */ float getSdrDimRatioForInstantHbm(); void onClearDisplay(bool needModeClear); /** * apply brightness change on drm path. * Note: only this path can hold the lock for a long time */ int prepareFrameCommit(ExynosDisplay& display, const DrmConnector& connector, ExynosDisplayDrmInterface::DrmModeAtomicReq& drmReq, const bool mixedComposition, bool& ghbmSync, bool& lhbmSync, bool& blSync); bool isGhbmSupported() { return mGhbmSupported; } bool isLhbmSupported() { return mLhbmSupported; } bool isGhbmOn() { std::lock_guard lock(mBrightnessMutex); return mGhbm.get() != HbmMode::OFF; } bool isLhbmOn() { std::lock_guard lock(mBrightnessMutex); return mLhbm.get(); } int checkSysfsStatus(const char *file, const std::vector& expectedValue, const nsecs_t timeoutNs); void resetLhbmState(); uint32_t getBrightnessLevel() { std::lock_guard lock(mBrightnessMutex); return mBrightnessLevel.get(); } bool isDimSdr() { std::lock_guard lock(mBrightnessMutex); return mInstantHbmReq.get(); } HdrLayerState getHdrLayerState() { return mHdrLayerState.get(); } bool isSupported() { // valid mMaxBrightness means both brightness and max_brightness sysfs exist return mMaxBrightness > 0; } void dump(String8 &result); void setOutdoorVisibility(LbeState state); int updateCabcMode(); struct BrightnessTable { float mBriStart; float mBriEnd; uint32_t mBklStart; uint32_t mBklEnd; uint32_t mNitsStart; uint32_t mNitsEnd; BrightnessTable() {} BrightnessTable(const brightness_attribute &attr) : mBriStart(static_cast(attr.percentage.min) / 100.0f), mBriEnd(static_cast(attr.percentage.max) / 100.0f), mBklStart(attr.level.min), mBklEnd(attr.level.max), mNitsStart(attr.nits.min), mNitsEnd(attr.nits.max) {} }; const BrightnessTable *getBrightnessTable() { return mBrightnessTable; } /* * WARNING: This enum is parsed by Battery Historian. Add new values, but * do not modify/remove existing ones. Alternatively, consult with the * Battery Historian team (b/239640926). */ enum class BrightnessRange : uint32_t { NORMAL = 0, HBM = 1, MAX, }; /* * WARNING: This enum is parsed by Battery Historian. Add new values, but * do not modify/remove existing ones. Alternatively, consult with the * Battery Historian team (b/239640926). */ enum class HbmMode { OFF = 0, ON_IRC_ON = 1, ON_IRC_OFF = 2, }; /* * LHBM command need take a couple of frames to become effective * DISABLED - finish sending disabling command to panel * ENABLED - panel finishes boosting brightness to the peak value * ENABLING - finish sending enabling command to panel (panel begins boosting brightness) * Note: the definition should be consistent with kernel driver */ enum class LhbmMode { DISABLED = 0, ENABLED = 1, ENABLING = 2, }; /* * BrightnessDimmingUsage: * NORMAL- enable dimming * HBM- enable dimming only for hbm transition * NONE- disable dimming * * WARNING: This enum is parsed by Battery Historian. Add new values, but * do not modify/remove existing ones. Alternatively, consult with the * Battery Historian team (b/239640926). */ enum class BrightnessDimmingUsage { NORMAL = 0, HBM = 1, NONE, }; static constexpr const char *kLocalHbmModeFileNode = "/sys/class/backlight/panel%d-backlight/local_hbm_mode"; static constexpr const char* kDimBrightnessFileNode = "/sys/class/backlight/panel%d-backlight/dim_brightness"; private: // sync brightness change for mixed composition when there is more than 50% luminance change. // The percentage is calculated as: // (big_lumi - small_lumi) / small_lumi // For mixed composition, if remove brightness animations, the minimum brightness jump is // between nbm peak and hbm peak. 50% will cover known panels static constexpr float kBrightnessSyncThreshold = 0.5f; // Worst case for panel with brightness range 2 nits to 1000 nits. static constexpr float kGhbmMinDimRatio = 0.002; static constexpr int32_t kHbmDimmingTimeUs = 5000000; static constexpr const char *kGlobalHbmModeFileNode = "/sys/class/backlight/panel%d-backlight/hbm_mode"; static constexpr const char* kDimmingUsagePropName = "vendor.display.%d.brightness.dimming.usage"; static constexpr const char* kDimmingHbmTimePropName = "vendor.display.%d.brightness.dimming.hbm_time"; int queryBrightness(float brightness, bool* ghbm = nullptr, uint32_t* level = nullptr, float *nits = nullptr); void initBrightnessTable(const DrmDevice& device, const DrmConnector& connector); void initBrightnessSysfs(); void initCabcSysfs(); void initDimmingUsage(); int applyBrightnessViaSysfs(uint32_t level); int applyCabcModeViaSysfs(uint8_t mode); int updateStates() REQUIRES(mBrightnessMutex); void dimmingThread(); void processDimmingOff(); void parseHbmModeEnums(const DrmProperty& property); void printBrightnessStates(const char* path) REQUIRES(mBrightnessMutex); bool mLhbmSupported = false; bool mGhbmSupported = false; bool mDbmSupported = false; bool mBrightnessIntfSupported = false; BrightnessTable mBrightnessTable[toUnderlying(BrightnessRange::MAX)]; int32_t mPanelIndex; DrmEnumParser::MapHal2DrmEnum mHbmModeEnums; // brightness state std::recursive_mutex mBrightnessMutex; // requests CtrlValue mEnhanceHbmReq GUARDED_BY(mBrightnessMutex); CtrlValue mLhbmReq GUARDED_BY(mBrightnessMutex); CtrlValue mBrightnessFloatReq GUARDED_BY(mBrightnessMutex); CtrlValue mInstantHbmReq GUARDED_BY(mBrightnessMutex); // states to drm after updateStates call CtrlValue mBrightnessLevel GUARDED_BY(mBrightnessMutex); CtrlValue mGhbm GUARDED_BY(mBrightnessMutex); CtrlValue mDimming GUARDED_BY(mBrightnessMutex); CtrlValue mLhbm GUARDED_BY(mBrightnessMutex); CtrlValue mSdrDim GUARDED_BY(mBrightnessMutex); CtrlValue mPrevSdrDim GUARDED_BY(mBrightnessMutex); CtrlValue mDimBrightnessReq GUARDED_BY(mBrightnessMutex); // Indicating if the last LHBM on has changed the brightness level bool mLhbmBrightnessAdj = false; std::function mFrameRefresh; CtrlValue mHdrLayerState; // these are used by sysfs path to wait drm path bl change task // indicationg an unchecked LHBM change in drm path std::atomic mUncheckedLhbmRequest = false; std::atomic mPendingLhbmStatus = false; // indicationg an unchecked GHBM change in drm path std::atomic mUncheckedGbhmRequest = false; std::atomic mPendingGhbmStatus = HbmMode::OFF; // indicating an unchecked brightness change in drm path std::atomic mUncheckedBlRequest = false; std::atomic mPendingBl = 0; // these are dimming related BrightnessDimmingUsage mBrightnessDimmingUsage = BrightnessDimmingUsage::NORMAL; bool mHbmDimming GUARDED_BY(mBrightnessMutex) = false; int32_t mHbmDimmingTimeUs = 0; std::thread mDimmingThread; std::atomic mDimmingThreadRunning; ::android::sp<::android::Looper> mDimmingLooper; ::android::sp mDimmingHandler; // sysfs path std::ofstream mBrightnessOfs; uint32_t mMaxBrightness = 0; // read from sysfs std::ofstream mCabcModeOfs; bool mCabcSupport = false; uint32_t mDimBrightness = 0; // Note IRC or dimming is not in consideration for now. float mDisplayWhitePointNits = 0; float mPrevDisplayWhitePointNits = 0; std::function mUpdateDcLhbm; // state for control CABC state enum class CabcMode { OFF = 0, CABC_UI_MODE, CABC_STILL_MODE, CABC_MOVIE_MODE, }; static constexpr const char* kLocalCabcModeFileNode = "/sys/class/backlight/panel%d-backlight/cabc_mode"; std::recursive_mutex mCabcModeMutex; bool mOutdoorVisibility GUARDED_BY(mCabcModeMutex) = false; bool isHdrLayerOn() { return mHdrLayerState.get() == HdrLayerState::kHdrLarge; } CtrlValue mCabcMode GUARDED_BY(mCabcModeMutex); }; #endif // _BRIGHTNESS_CONTROLLER_H_