blob: 1a19758d63ab1b4546fd30fd24780c9933660d9e [file] [log] [blame]
/*
* 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 <drm/samsung_drm.h>
#include <utils/Looper.h>
#include <utils/Mutex.h>
#include <fstream>
#include <thread>
#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<void(void)> refresh,
std::function<void(void)> 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();
/**
* 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<std::recursive_mutex> lock(mBrightnessMutex);
return mGhbm.get() != HbmMode::OFF;
}
bool isLhbmOn() {
std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
return mLhbm.get();
}
int checkSysfsStatus(const char *file, const std::vector<std::string>& expectedValue,
const nsecs_t timeoutNs);
void resetLhbmState();
uint32_t getBrightnessLevel() {
std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex);
return mBrightnessLevel.get();
}
bool isDimSdr() {
std::lock_guard<std::recursive_mutex> 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<float>(attr.percentage.min) / 100.0f),
mBriEnd(static_cast<float>(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<bool> mEnhanceHbmReq GUARDED_BY(mBrightnessMutex);
CtrlValue<bool> mLhbmReq GUARDED_BY(mBrightnessMutex);
CtrlValue<float> mBrightnessFloatReq GUARDED_BY(mBrightnessMutex);
CtrlValue<bool> mInstantHbmReq GUARDED_BY(mBrightnessMutex);
// states to drm after updateStates call
CtrlValue<uint32_t> mBrightnessLevel GUARDED_BY(mBrightnessMutex);
CtrlValue<HbmMode> mGhbm GUARDED_BY(mBrightnessMutex);
CtrlValue<bool> mDimming GUARDED_BY(mBrightnessMutex);
CtrlValue<bool> mLhbm GUARDED_BY(mBrightnessMutex);
CtrlValue<bool> mSdrDim GUARDED_BY(mBrightnessMutex);
CtrlValue<bool> mPrevSdrDim GUARDED_BY(mBrightnessMutex);
CtrlValue<bool> mDimBrightnessReq GUARDED_BY(mBrightnessMutex);
// Indicating if the last LHBM on has changed the brightness level
bool mLhbmBrightnessAdj = false;
std::function<void(void)> mFrameRefresh;
CtrlValue<HdrLayerState> mHdrLayerState;
// these are used by sysfs path to wait drm path bl change task
// indicationg an unchecked LHBM change in drm path
std::atomic<bool> mUncheckedLhbmRequest = false;
std::atomic<bool> mPendingLhbmStatus = false;
// indicationg an unchecked GHBM change in drm path
std::atomic<bool> mUncheckedGbhmRequest = false;
std::atomic<HbmMode> mPendingGhbmStatus = HbmMode::OFF;
// indicating an unchecked brightness change in drm path
std::atomic<bool> mUncheckedBlRequest = false;
std::atomic<uint32_t> 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<bool> mDimmingThreadRunning;
::android::sp<::android::Looper> mDimmingLooper;
::android::sp<DimmingMsgHandler> 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<void(void)> mUpdateDcLhbm;
// state for control CABC state
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::kHdrNone; }
CtrlValue<bool> mCabcMode GUARDED_BY(mCabcModeMutex);
};
#endif // _BRIGHTNESS_CONTROLLER_H_