blob: f1f85ea159a37418166f9e09a7f98117dcea8d7b [file] [log] [blame]
/*
* Copyright (C) 2019 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 _EXYNOSDISPLAYDRMINTERFACE_H
#define _EXYNOSDISPLAYDRMINTERFACE_H
#include <drm/samsung_drm.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <xf86drmMode.h>
#include <list>
#include <unordered_map>
#include "ExynosDisplay.h"
#include "ExynosDisplayInterface.h"
#include "ExynosHWC.h"
#include "ExynosMPP.h"
#include "drmconnector.h"
#include "drmcrtc.h"
#include "vsyncworker.h"
/* Max plane number of buffer object */
#define HWC_DRM_BO_MAX_PLANES 4
#ifndef HWC_FORCE_PANIC_PATH
#define HWC_FORCE_PANIC_PATH "/d/dpu/panic"
#endif
using namespace android;
using DrmPropertyMap = std::unordered_map<uint32_t, uint64_t>;
class ExynosDevice;
using BufHandles = std::array<uint32_t, HWC_DRM_BO_MAX_PLANES>;
class FramebufferManager {
public:
FramebufferManager(){};
~FramebufferManager();
void init(int drmFd);
// get buffer for provided config, if a buffer with same config is already cached it will be
// reused otherwise one will be allocated. returns fbId that can be used to attach to the
// plane, any buffers allocated/reused with this call will be bound to the corresponding
// layer. Those fbIds will be cleaned up once the layer was destroyed.
int32_t getBuffer(const exynos_win_config_data &config, uint32_t &fbId);
bool checkShrink();
void cleanup(const ExynosLayer *layer);
// The flip function is to help clean up the cached fbIds of destroyed
// layers after the previous fdIds were update successfully on the
// screen.
// This should be called after the frame update.
void flip(bool hasSecureFrameBuffer);
// release all currently tracked buffers, this can be called for example when display is turned
// off
void releaseAll();
private:
// this struct should contain elements that can be used to identify framebuffer more easily
struct Framebuffer {
using BufferDesc = uint64_t;
struct SolidColorDesc {
uint32_t width;
uint32_t height;
bool operator==(const Framebuffer::SolidColorDesc &rhs) const {
return (width == rhs.width && height == rhs.height);
}
};
explicit Framebuffer(int fd, uint32_t fb, BufferDesc desc)
: drmFd(fd), fbId(fb), bufferDesc(desc){};
explicit Framebuffer(int fd, uint32_t fb, SolidColorDesc desc)
: drmFd(fd), fbId(fb), colorDesc(desc){};
~Framebuffer() { drmModeRmFB(drmFd, fbId); };
int drmFd;
uint32_t fbId;
union {
BufferDesc bufferDesc;
SolidColorDesc colorDesc;
};
};
using FBList = std::list<std::unique_ptr<Framebuffer>>;
template <class UnaryPredicate>
uint32_t findCachedFbId(const ExynosLayer *layer, UnaryPredicate predicate);
int addFB2WithModifiers(uint32_t width, uint32_t height, uint32_t pixel_format,
const BufHandles handles, const uint32_t pitches[4],
const uint32_t offsets[4], const uint64_t modifier[4], uint32_t *buf_id,
uint32_t flags);
uint32_t getBufHandleFromFd(int fd);
void freeBufHandle(uint32_t handle);
void removeFBsThreadRoutine();
void markInuseLayerLocked(const ExynosLayer *layer) REQUIRES(mMutex);
void destroyUnusedLayersLocked() REQUIRES(mMutex);
void destroyFramebufferLocked() REQUIRES(mMutex);
int mDrmFd = -1;
// mCachedLayerBuffers map keep the relationship between Layer and
// FBList. The map entry will be deleted once the layer is destroyed.
std::map<const ExynosLayer *, FBList> mCachedLayerBuffers;
// mCleanBuffers list keeps fbIds of destroyed layers. Those fbIds will
// be destroyed in mRmFBThread thread.
FBList mCleanBuffers;
// mCacheShrinkPending is set when we want to clean up unused layers
// in mCachedLayerBuffers. When the flag is set, mCachedLayersInuse will
// keep in-use layers in this frame update. Those unused layers will be
// freed at the end of the update.
bool mCacheShrinkPending = false;
bool mHasSecureFramebuffer = false;
std::set<const ExynosLayer *> mCachedLayersInuse;
std::thread mRmFBThread;
bool mRmFBThreadRunning = false;
Condition mFlipDone;
Mutex mMutex;
static constexpr size_t MAX_CACHED_LAYERS = 16;
static constexpr size_t MAX_CACHED_BUFFERS_PER_LAYER = 32;
};
inline bool isFramebuffer(const ExynosLayer *layer) {
return layer == nullptr;
}
template <class UnaryPredicate>
uint32_t FramebufferManager::findCachedFbId(const ExynosLayer *layer, UnaryPredicate predicate) {
Mutex::Autolock lock(mMutex);
markInuseLayerLocked(layer);
const auto &cachedBuffers = mCachedLayerBuffers[layer];
const auto it = std::find_if(cachedBuffers.begin(), cachedBuffers.end(), predicate);
return (it != cachedBuffers.end()) ? (*it)->fbId : 0;
}
class ExynosDisplayDrmInterface :
public ExynosDisplayInterface,
public VsyncCallback
{
public:
class DrmModeAtomicReq {
public:
DrmModeAtomicReq(ExynosDisplayDrmInterface *displayInterface);
~DrmModeAtomicReq();
DrmModeAtomicReq(const DrmModeAtomicReq&) = delete;
DrmModeAtomicReq& operator=(const DrmModeAtomicReq&) = delete;
drmModeAtomicReqPtr pset() { return mPset; };
void savePset() {
if (mSavedPset) {
drmModeAtomicFree(mSavedPset);
}
mSavedPset = drmModeAtomicDuplicate(mPset);
}
void restorePset() {
if (mPset) {
drmModeAtomicFree(mPset);
}
mPset = mSavedPset;
mSavedPset = NULL;
}
void setError(int err) { mError = err; };
int getError() { return mError; };
int32_t atomicAddProperty(const uint32_t id,
const DrmProperty &property,
uint64_t value, bool optional = false);
String8& dumpAtomicCommitInfo(String8 &result, bool debugPrint = false);
int commit(uint32_t flags, bool loggingForDebug = false);
void addOldBlob(uint32_t blob_id) {
mOldBlobs.push_back(blob_id);
};
int destroyOldBlobs() {
for (auto &blob : mOldBlobs) {
int ret = mDrmDisplayInterface->mDrmDevice->DestroyPropertyBlob(blob);
if (ret) {
HWC_LOGE(mDrmDisplayInterface->mExynosDisplay,
"Failed to destroy old blob after commit %d", ret);
return ret;
}
}
mOldBlobs.clear();
return NO_ERROR;
};
private:
drmModeAtomicReqPtr mPset;
drmModeAtomicReqPtr mSavedPset;
int mError = 0;
ExynosDisplayDrmInterface *mDrmDisplayInterface = NULL;
/* Destroy old blobs after commit */
std::vector<uint32_t> mOldBlobs;
int drmFd() const { return mDrmDisplayInterface->mDrmDevice->fd(); }
};
class ExynosVsyncCallback {
public:
void enableVSync(bool enable) {
mVsyncEnabled = enable;
resetVsyncTimeStamp();
};
bool getVSyncEnabled() { return mVsyncEnabled; };
void setDesiredVsyncPeriod(uint64_t period) {
mDesiredVsyncPeriod = period;
resetVsyncTimeStamp();
};
uint64_t getDesiredVsyncPeriod() { return mDesiredVsyncPeriod;};
uint64_t getVsyncTimeStamp() { return mVsyncTimeStamp; };
uint64_t getVsyncPeriod() { return mVsyncPeriod; };
bool Callback(int display, int64_t timestamp);
void resetVsyncTimeStamp() { mVsyncTimeStamp = 0; };
void resetDesiredVsyncPeriod() { mDesiredVsyncPeriod = 0;};
private:
bool mVsyncEnabled = false;
uint64_t mVsyncTimeStamp = 0;
uint64_t mVsyncPeriod = 0;
uint64_t mDesiredVsyncPeriod = 0;
};
void Callback(int display, int64_t timestamp) override;
ExynosDisplayDrmInterface(ExynosDisplay *exynosDisplay);
~ExynosDisplayDrmInterface();
virtual void init(ExynosDisplay *exynosDisplay);
virtual int32_t setPowerMode(int32_t mode);
virtual int32_t setLowPowerMode() override;
virtual bool isDozeModeAvailable() const {
return mDozeDrmMode.h_display() > 0 && mDozeDrmMode.v_display() > 0;
};
virtual int32_t setVsyncEnabled(uint32_t enabled);
virtual int32_t getDisplayConfigs(
uint32_t* outNumConfigs,
hwc2_config_t* outConfigs);
virtual void dumpDisplayConfigs();
virtual bool supportDataspace(int32_t dataspace);
virtual int32_t getColorModes(uint32_t* outNumModes, int32_t* outModes);
virtual int32_t setColorMode(int32_t mode);
virtual int32_t setActiveConfig(hwc2_config_t config);
virtual int32_t setCursorPositionAsync(uint32_t x_pos, uint32_t y_pos);
virtual int32_t updateHdrCapabilities();
virtual int32_t deliverWinConfigData();
virtual int32_t clearDisplay(bool needModeClear = false);
virtual int32_t disableSelfRefresh(uint32_t disable);
virtual int32_t setForcePanic();
virtual int getDisplayFd() { return mDrmDevice->fd(); };
virtual int32_t initDrmDevice(DrmDevice *drmDevice);
virtual uint32_t getDrmDisplayId(uint32_t type, uint32_t index);
virtual uint32_t getMaxWindowNum();
virtual int32_t getReadbackBufferAttributes(int32_t* /*android_pixel_format_t*/ outFormat,
int32_t* /*android_dataspace_t*/ outDataspace);
virtual int32_t getDisplayIdentificationData(uint8_t* outPort,
uint32_t* outDataSize, uint8_t* outData);
/* For HWC 2.4 APIs */
virtual int32_t getDisplayVsyncPeriod(
hwc2_vsync_period_t* outVsyncPeriod);
virtual int32_t getConfigChangeDuration();
virtual int32_t getVsyncAppliedTime(hwc2_config_t config,
int64_t* actualChangeTime);
virtual int32_t setActiveConfigWithConstraints(
hwc2_config_t config, bool test = false);
virtual int32_t setDisplayColorSetting(
ExynosDisplayDrmInterface::DrmModeAtomicReq &drmReq)
{ return NO_ERROR;};
virtual int32_t setPlaneColorSetting(
ExynosDisplayDrmInterface::DrmModeAtomicReq &drmReq,
const std::unique_ptr<DrmPlane> &plane,
const exynos_win_config_data& config)
{ return NO_ERROR;};
virtual int32_t updateBrightness(bool syncFrame);
virtual float getSdrDimRatio();
virtual void destroyLayer(ExynosLayer *layer) override;
virtual int32_t waitVBlank();
bool isHbmOn() { return mBrightnessCtrl.HbmOn.get(); }
uint32_t getDbv() { return mBrightnessLevel.get(); }
float getDesiredRefreshRate() { return mDesiredModeState.mode.v_refresh(); }
protected:
struct ModeState {
bool needs_modeset = false;
DrmMode mode;
uint32_t blob_id = 0;
uint32_t old_blob_id = 0;
void setMode(const DrmMode newMode, const uint32_t modeBlob,
DrmModeAtomicReq &drmReq) {
drmReq.addOldBlob(old_blob_id);
mode = newMode;
old_blob_id = blob_id;
blob_id = modeBlob;
};
void reset() {
*this = {};
};
void apply(ModeState &toModeState, DrmModeAtomicReq &drmReq) {
toModeState.setMode(mode, blob_id, drmReq);
drmReq.addOldBlob(old_blob_id);
reset();
};
};
int32_t createModeBlob(const DrmMode &mode, uint32_t &modeBlob);
int32_t setDisplayMode(DrmModeAtomicReq &drmReq, const uint32_t modeBlob);
int32_t clearDisplayMode(DrmModeAtomicReq &drmReq);
int32_t chosePreferredConfig();
int getDeconChannel(ExynosMPP *otfMPP);
static std::tuple<uint64_t, int> halToDrmEnum(
const int32_t halData, const DrmPropertyMap &drmEnums);
/*
* This function adds FB and gets new fb id if fbId is 0,
* if fbId is not 0, this reuses fbId.
*/
int32_t setupCommitFromDisplayConfig(DrmModeAtomicReq &drmReq,
const exynos_win_config_data &config,
const uint32_t configIndex,
const std::unique_ptr<DrmPlane> &plane,
uint32_t &fbId);
int32_t setupPartialRegion(DrmModeAtomicReq &drmReq);
static void parseEnums(const DrmProperty &property,
const std::vector<std::pair<uint32_t, const char *>> &enums,
DrmPropertyMap &out_enums);
void parseBlendEnums(const DrmProperty &property);
void parseStandardEnums(const DrmProperty &property);
void parseTransferEnums(const DrmProperty &property);
void parseRangeEnums(const DrmProperty &property);
void parseColorModeEnums(const DrmProperty &property);
int32_t setupWritebackCommit(DrmModeAtomicReq &drmReq);
int32_t clearWritebackCommit(DrmModeAtomicReq &drmReq);
private:
int32_t updateColorSettings(DrmModeAtomicReq &drmReq, uint64_t dqeEnabled);
int32_t getLowPowerDrmModeModeInfo();
int32_t setActiveDrmMode(DrmMode const &mode);
protected:
struct PartialRegionState {
struct drm_clip_rect partial_rect = {0, 0, 0, 0};
uint32_t blob_id = 0;
bool isUpdated(drm_clip_rect rect) {
return ((partial_rect.x1 != rect.x1) ||
(partial_rect.y1 != rect.y1) ||
(partial_rect.x2 != rect.x2) ||
(partial_rect.y2 != rect.y2));
};
};
class DrmReadbackInfo {
public:
void init(DrmDevice *drmDevice, uint32_t displayId);
~DrmReadbackInfo() {
if (mDrmDevice == NULL)
return;
if (mOldFbId > 0)
drmModeRmFB(mDrmDevice->fd(), mOldFbId);
if (mFbId > 0)
drmModeRmFB(mDrmDevice->fd(), mFbId);
}
DrmConnector* getWritebackConnector() { return mWritebackConnector; };
void setFbId(uint32_t fbId) {
if ((mDrmDevice != NULL) && (mOldFbId > 0))
drmModeRmFB(mDrmDevice->fd(), mOldFbId);
mOldFbId = mFbId;
mFbId = fbId;
}
void pickFormatDataspace();
static constexpr uint32_t PREFERRED_READBACK_FORMAT =
HAL_PIXEL_FORMAT_RGBA_8888;
uint32_t mReadbackFormat = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
bool mNeedClearReadbackCommit = false;
private:
DrmDevice *mDrmDevice = NULL;
DrmConnector *mWritebackConnector = NULL;
uint32_t mFbId = 0;
uint32_t mOldFbId = 0;
std::vector<uint32_t> mSupportedFormats;
};
DrmDevice *mDrmDevice;
DrmCrtc *mDrmCrtc;
DrmConnector *mDrmConnector;
VSyncWorker mDrmVSyncWorker;
ExynosVsyncCallback mVsyncCallback;
ModeState mActiveModeState;
ModeState mDesiredModeState;
PartialRegionState mPartialRegionState;
/* Mapping plane id to ExynosMPP, key is plane id */
std::unordered_map<uint32_t, ExynosMPP*> mExynosMPPsForPlane;
DrmPropertyMap mBlendEnums;
DrmPropertyMap mStandardEnums;
DrmPropertyMap mTransferEnums;
DrmPropertyMap mRangeEnums;
DrmPropertyMap mColorModeEnums;
DrmReadbackInfo mReadbackInfo;
private:
DrmMode mDozeDrmMode;
protected:
void getBrightnessInterfaceSupport();
void setupBrightnessConfig();
FILE *mHbmOnFd;
FILE *mDimmingOnFd;
bool mBrightntessIntfSupported = false;
float mBrightnessHbmMax = 1.0f;
enum class PanelHbmType {
ONE_STEP,
CONTINUOUS,
};
enum BrightnessRange {
NORMAL = 0,
HBM,
MAX,
};
PanelHbmType mPanelHbmType;
Mutex mBrightnessUpdateMutex;
brightnessState_t mBrightnessState;
CtrlValue<uint32_t> mBrightnessLevel;
float mScaledBrightness;
typedef struct brightnessCtrl {
static constexpr size_t kNumOfBrightnessCtrl = 3;
union {
std::array<CtrlValue<bool>, kNumOfBrightnessCtrl> mData;
struct {
CtrlValue<bool> DimmingOn;
CtrlValue<bool> HbmOn;
CtrlValue<bool> LhbmOn;
};
};
void reset() {
for (uint32_t i = 0; i < kNumOfBrightnessCtrl; i++) {
mData[i].store(false);
mData[i].clear_dirty();
};
}
} brightnessCtrl_t;
brightnessCtrl_t mBrightnessCtrl;
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) {}
};
struct BrightnessTable mBrightnessTable[BrightnessRange::MAX];
// TODO: hbm in dual display is not supported. It should support it in
// the furture.
static constexpr const char *kHbmOnFileNode =
"/sys/class/backlight/panel0-backlight/hbm_mode";
static constexpr const char *kDimmingOnFileNode =
"/sys/class/backlight/panel0-backlight/dimming_on";
static constexpr int32_t kHbmDimmingTimeUs = 5000000;
FramebufferManager mFBManager;
/*
* BrightnessDimmingUsage:
* NORMAL- enable dimming
* HBM- enable dimming only for hbm transition
* NONE- disable dimming
*/
enum class BrightnessDimmingUsage {
NORMAL = 0,
HBM,
NONE,
};
BrightnessDimmingUsage mBrightnessDimmingUsage;
bool mHbmDimming;
int32_t mHbmDimmingTimeUs;
struct timeval mHbmDimmingStart;
private:
int32_t getDisplayFakeEdid(uint8_t &outPort, uint32_t &outDataSize, uint8_t *outData);
};
#endif