| /* |
| * 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 |