| /* |
| * 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. |
| */ |
| |
| #define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL) |
| |
| #include "ExynosDisplayDrmInterface.h" |
| |
| #include <cutils/properties.h> |
| #include <drm.h> |
| #include <drm/drm_fourcc.h> |
| #include <sys/types.h> |
| #include <xf86drm.h> |
| |
| #include <algorithm> |
| #include <numeric> |
| |
| #include "BrightnessController.h" |
| #include "ExynosHWCDebug.h" |
| #include "ExynosHWCHelper.h" |
| #include "ExynosLayer.h" |
| #include "ExynosPrimaryDisplay.h" |
| #include "HistogramController.h" |
| |
| using namespace std::chrono_literals; |
| using namespace SOC_VERSION; |
| |
| constexpr uint32_t MAX_PLANE_NUM = 3; |
| constexpr uint32_t CBCR_INDEX = 1; |
| constexpr float DISPLAY_LUMINANCE_UNIT = 10000; |
| constexpr auto vsyncPeriodTag = "VsyncPeriod"; |
| |
| typedef struct _drmModeAtomicReqItem drmModeAtomicReqItem, *drmModeAtomicReqItemPtr; |
| |
| struct _drmModeAtomicReqItem { |
| uint32_t object_id; |
| uint32_t property_id; |
| uint64_t value; |
| }; |
| |
| struct _drmModeAtomicReq { |
| uint32_t cursor; |
| uint32_t size_items; |
| drmModeAtomicReqItemPtr items; |
| }; |
| |
| using namespace vendor::graphics; |
| |
| extern struct exynos_hwc_control exynosHWCControl; |
| static const int32_t kUmPerInch = 25400; |
| |
| int writeIntToKernelFile(const char* path, const int value) { |
| std::ofstream ofs(path); |
| |
| if (!ofs.is_open()) { |
| ALOGW("%s(): unable to open %s (%s)", __func__, path, strerror(errno)); |
| return -1; |
| } |
| |
| ofs << value << std::endl; |
| |
| return 0; |
| } |
| |
| FramebufferManager::~FramebufferManager() |
| { |
| { |
| Mutex::Autolock lock(mMutex); |
| mRmFBThreadRunning = false; |
| } |
| mFlipDone.signal(); |
| mRmFBThread.join(); |
| } |
| |
| void FramebufferManager::init(int drmFd) |
| { |
| mDrmFd = drmFd; |
| mRmFBThreadRunning = true; |
| mRmFBThread = std::thread(&FramebufferManager::removeFBsThreadRoutine, this); |
| pthread_setname_np(mRmFBThread.native_handle(), "RemoveFBsThread"); |
| } |
| |
| uint32_t FramebufferManager::getBufHandleFromFd(int fd) |
| { |
| uint32_t gem_handle = 0; |
| |
| int ret = drmPrimeFDToHandle(mDrmFd, fd, &gem_handle); |
| if (ret) { |
| ALOGE("drmPrimeFDToHandle failed with fd %d error %d (%s)", fd, ret, strerror(errno)); |
| } |
| return gem_handle; |
| } |
| |
| int FramebufferManager::addFB2WithModifiers(uint32_t state, uint32_t width, uint32_t height, |
| uint32_t drmFormat, const DrmArray<uint32_t> &handles, |
| const DrmArray<uint32_t> &pitches, |
| const DrmArray<uint32_t> &offsets, |
| const DrmArray<uint64_t> &modifier, uint32_t *buf_id, |
| uint32_t flags) { |
| if (CC_UNLIKELY(!validateLayerInfo(state, drmFormat, handles, modifier))) { |
| return -EINVAL; |
| } |
| |
| int ret = drmModeAddFB2WithModifiers(mDrmFd, width, height, drmFormat, handles.data(), |
| pitches.data(), offsets.data(), modifier.data(), buf_id, |
| flags); |
| if (ret) ALOGE("Failed to add fb error %d\n", ret); |
| |
| return ret; |
| } |
| |
| bool FramebufferManager::validateLayerInfo(uint32_t state, uint32_t drmFormat, |
| const DrmArray<uint32_t> &handles, |
| const DrmArray<uint64_t> &modifier) { |
| switch (state) { |
| case exynos_win_config_data::WIN_STATE_RCD: |
| return drmFormat == DRM_FORMAT_C8 && handles[0] != 0 && handles[1] == 0 && |
| modifier[0] == 0; |
| } |
| |
| return true; |
| } |
| |
| void FramebufferManager::checkShrink() { |
| Mutex::Autolock lock(mMutex); |
| |
| mCacheShrinkPending = mCachedLayerBuffers.size() > MAX_CACHED_LAYERS; |
| mCacheM2mSecureShrinkPending = |
| mCachedM2mSecureLayerBuffers.size() > MAX_CACHED_M2M_SECURE_LAYERS; |
| } |
| |
| void FramebufferManager::cleanup(const ExynosLayer *layer) { |
| ATRACE_CALL(); |
| |
| Mutex::Autolock lock(mMutex); |
| auto clean = [&](std::map<const ExynosLayer *, FBList> &layerBuffs) { |
| if (auto it = layerBuffs.find(layer); it != layerBuffs.end()) { |
| mCleanBuffers.splice(mCleanBuffers.end(), std::move(it->second)); |
| layerBuffs.erase(it); |
| } |
| }; |
| clean(mCachedLayerBuffers); |
| clean(mCachedM2mSecureLayerBuffers); |
| } |
| |
| void FramebufferManager::removeFBsThreadRoutine() |
| { |
| FBList cleanupBuffers; |
| while (true) { |
| { |
| Mutex::Autolock lock(mMutex); |
| if (!mRmFBThreadRunning) { |
| break; |
| } |
| mFlipDone.wait(mMutex); |
| cleanupBuffers.splice(cleanupBuffers.end(), mCleanBuffers); |
| } |
| ATRACE_NAME("cleanup framebuffers"); |
| cleanupBuffers.clear(); |
| } |
| } |
| |
| int32_t FramebufferManager::getBuffer(const exynos_win_config_data &config, uint32_t &fbId) { |
| ATRACE_CALL(); |
| int ret = NO_ERROR; |
| int drmFormat = DRM_FORMAT_UNDEFINED; |
| uint32_t bpp = 0; |
| uint32_t bufferNum, planeNum = 0; |
| uint32_t bufWidth, bufHeight = 0; |
| bool isM2mSecureLayer = (config.protection && config.layer && config.layer->mM2mMPP); |
| DrmArray<uint32_t> pitches = {0}; |
| DrmArray<uint32_t> offsets = {0}; |
| DrmArray<uint64_t> modifiers = {0}; |
| DrmArray<uint32_t> handles = {0}; |
| |
| if (config.protection) modifiers[0] |= DRM_FORMAT_MOD_PROTECTION; |
| |
| if (config.state == config.WIN_STATE_BUFFER || config.state == config.WIN_STATE_RCD) { |
| bufWidth = config.src.f_w; |
| bufHeight = config.src.f_h; |
| |
| auto exynosFormat = halFormatToExynosFormat(config.format, config.compressionInfo.type); |
| if (exynosFormat == nullptr) { |
| ALOGE("%s:: unknown HAL format (%d)", __func__, config.format); |
| return -EINVAL; |
| } |
| |
| drmFormat = exynosFormat->drmFormat; |
| if (drmFormat == DRM_FORMAT_UNDEFINED) { |
| ALOGE("%s:: unknown drm format (%d)", __func__, config.format); |
| return -EINVAL; |
| } |
| |
| bpp = getBytePerPixelOfPrimaryPlane(config.format); |
| if ((bufferNum = exynosFormat->bufferNum) == 0) { |
| ALOGE("%s:: getBufferNumOfFormat(%d) error", __func__, config.format); |
| return -EINVAL; |
| } |
| if (((planeNum = exynosFormat->planeNum) == 0) || (planeNum > MAX_PLANE_NUM)) { |
| ALOGE("%s:: getPlaneNumOfFormat(%d) error, planeNum(%d)", __func__, config.format, |
| planeNum); |
| return -EINVAL; |
| } |
| |
| fbId = findCachedFbId(config.layer, isM2mSecureLayer, |
| [bufferDesc = Framebuffer::BufferDesc{config.buffer_id, drmFormat, |
| config.protection}]( |
| auto &buffer) { return buffer->bufferDesc == bufferDesc; }); |
| if (fbId != 0) { |
| return NO_ERROR; |
| } |
| |
| if (config.compressionInfo.type == COMP_TYPE_AFBC) { |
| uint64_t compressed_modifier = config.compressionInfo.modifier; |
| switch (config.comp_src) { |
| case DPP_COMP_SRC_G2D: |
| compressed_modifier |= AFBC_FORMAT_MOD_SOURCE_G2D; |
| break; |
| case DPP_COMP_SRC_GPU: |
| compressed_modifier |= AFBC_FORMAT_MOD_SOURCE_GPU; |
| break; |
| default: |
| break; |
| } |
| modifiers[0] |= DRM_FORMAT_MOD_ARM_AFBC(compressed_modifier); |
| } else if (config.compressionInfo.type == COMP_TYPE_SBWC) { |
| modifiers[0] |= DRM_FORMAT_MOD_SAMSUNG_SBWC(config.compressionInfo.modifier); |
| } |
| |
| for (uint32_t bufferIndex = 0; bufferIndex < bufferNum; bufferIndex++) { |
| pitches[bufferIndex] = config.src.f_w * bpp; |
| modifiers[bufferIndex] = modifiers[0]; |
| handles[bufferIndex] = getBufHandleFromFd(config.fd_idma[bufferIndex]); |
| if (handles[bufferIndex] == 0) { |
| return -ENOMEM; |
| } |
| } |
| |
| if ((bufferNum == 1) && (planeNum > bufferNum)) { |
| /* offset for cbcr */ |
| offsets[CBCR_INDEX] = |
| getExynosBufferYLength(config.src.f_w, config.src.f_h, config.format); |
| for (uint32_t planeIndex = 1; planeIndex < planeNum; planeIndex++) { |
| handles[planeIndex] = handles[0]; |
| pitches[planeIndex] = pitches[0]; |
| modifiers[planeIndex] = modifiers[0]; |
| } |
| } |
| } else if (config.state == config.WIN_STATE_COLOR) { |
| bufWidth = config.dst.w; |
| bufHeight = config.dst.h; |
| modifiers[0] |= DRM_FORMAT_MOD_SAMSUNG_COLORMAP; |
| drmFormat = DRM_FORMAT_BGRA8888; |
| bufferNum = 0; |
| handles[0] = 0xff000000; |
| bpp = getBytePerPixelOfPrimaryPlane(HAL_PIXEL_FORMAT_BGRA_8888); |
| pitches[0] = config.dst.w * bpp; |
| fbId = findCachedFbId(config.layer, isM2mSecureLayer, |
| [colorDesc = Framebuffer::SolidColorDesc{bufWidth, bufHeight}]( |
| auto &buffer) { return buffer->colorDesc == colorDesc; }); |
| if (fbId != 0) { |
| return NO_ERROR; |
| } |
| } else { |
| ALOGE("%s:: unknown config state(%d)", __func__, config.state); |
| return -EINVAL; |
| } |
| |
| ret = addFB2WithModifiers(config.state, bufWidth, bufHeight, drmFormat, handles, pitches, |
| offsets, modifiers, &fbId, modifiers[0] ? DRM_MODE_FB_MODIFIERS : 0); |
| |
| for (uint32_t bufferIndex = 0; bufferIndex < bufferNum; bufferIndex++) { |
| freeBufHandle(handles[bufferIndex]); |
| } |
| |
| if (ret) { |
| ALOGE("%s:: Failed to add FB, fb_id(%d), ret(%d), f_w: %d, f_h: %d, dst.w: %d, dst.h: %d, " |
| "format: %d %4.4s, buf_handles[%d, %d, %d, %d], " |
| "pitches[%d, %d, %d, %d], offsets[%d, %d, %d, %d], modifiers[%#" PRIx64 ", %#" PRIx64 |
| ", %#" PRIx64 ", %#" PRIx64 "]", |
| __func__, fbId, ret, config.src.f_w, config.src.f_h, config.dst.w, config.dst.h, |
| drmFormat, (char *)&drmFormat, handles[0], handles[1], handles[2], handles[3], |
| pitches[0], pitches[1], pitches[2], pitches[3], offsets[0], offsets[1], offsets[2], |
| offsets[3], modifiers[0], modifiers[1], modifiers[2], modifiers[3]); |
| return ret; |
| } |
| |
| if (config.layer || config.buffer_id) { |
| Mutex::Autolock lock(mMutex); |
| auto &cachedBuffers = (!isM2mSecureLayer) ? mCachedLayerBuffers[config.layer] |
| : mCachedM2mSecureLayerBuffers[config.layer]; |
| auto maxCachedBufferSize = (!isM2mSecureLayer) ? MAX_CACHED_BUFFERS_PER_LAYER |
| : MAX_CACHED_M2M_SECURE_BUFFERS_PER_LAYER; |
| |
| if (cachedBuffers.size() > maxCachedBufferSize) { |
| ALOGW("FBManager: cached buffers size %zu exceeds limitation(%zu) while adding fbId %d", |
| cachedBuffers.size(), maxCachedBufferSize, fbId); |
| mCleanBuffers.splice(mCleanBuffers.end(), cachedBuffers); |
| } |
| |
| if (config.state == config.WIN_STATE_COLOR) { |
| cachedBuffers.emplace_front( |
| new Framebuffer(mDrmFd, fbId, |
| Framebuffer::SolidColorDesc{bufWidth, bufHeight})); |
| } else { |
| cachedBuffers.emplace_front( |
| new Framebuffer(mDrmFd, fbId, |
| Framebuffer::BufferDesc{config.buffer_id, drmFormat, |
| config.protection})); |
| mHasSecureFramebuffer |= (isFramebuffer(config.layer) && config.protection); |
| mHasM2mSecureLayerBuffer |= isM2mSecureLayer; |
| } |
| } else { |
| ALOGW("FBManager: possible leakage fbId %d was created", fbId); |
| } |
| |
| return 0; |
| } |
| |
| void FramebufferManager::flip(const bool hasSecureFrameBuffer, const bool hasM2mSecureLayerBuffer) { |
| bool needCleanup = false; |
| { |
| Mutex::Autolock lock(mMutex); |
| destroyUnusedLayersLocked(); |
| if (!hasSecureFrameBuffer) { |
| destroySecureFramebufferLocked(); |
| } |
| |
| if (!hasM2mSecureLayerBuffer) { |
| destroyM2mSecureLayerBufferLocked(); |
| } |
| needCleanup = mCleanBuffers.size() > 0; |
| } |
| |
| if (needCleanup) { |
| mFlipDone.signal(); |
| } |
| } |
| |
| void FramebufferManager::releaseAll() |
| { |
| Mutex::Autolock lock(mMutex); |
| mCachedLayerBuffers.clear(); |
| mCachedM2mSecureLayerBuffers.clear(); |
| mCleanBuffers.clear(); |
| } |
| |
| void FramebufferManager::freeBufHandle(uint32_t handle) { |
| if (handle == 0) { |
| return; |
| } |
| |
| struct drm_gem_close gem_close { |
| .handle = handle |
| }; |
| int ret = drmIoctl(mDrmFd, DRM_IOCTL_GEM_CLOSE, &gem_close); |
| if (ret) { |
| ALOGE("Failed to close gem handle 0x%x with error %d\n", handle, ret); |
| } |
| } |
| |
| void FramebufferManager::markInuseLayerLocked(const ExynosLayer *layer, |
| const bool isM2mSecureLayer) { |
| if (!isM2mSecureLayer && mCacheShrinkPending) { |
| mCachedLayersInuse.insert(layer); |
| } |
| |
| if (isM2mSecureLayer && mCacheM2mSecureShrinkPending) { |
| mCachedM2mSecureLayersInuse.insert(layer); |
| } |
| } |
| |
| void FramebufferManager::destroyUnusedLayersLocked() { |
| auto destroyUnusedLayers = |
| [&](const bool &cacheShrinkPending, std::set<const ExynosLayer *> &cachedLayersInuse, |
| std::map<const ExynosLayer *, FBList> &cachedLayerBuffers) -> bool { |
| if (!cacheShrinkPending || cachedLayersInuse.size() == cachedLayerBuffers.size()) { |
| cachedLayersInuse.clear(); |
| return false; |
| } |
| |
| for (auto layer = cachedLayerBuffers.begin(); layer != cachedLayerBuffers.end();) { |
| if (cachedLayersInuse.find(layer->first) == cachedLayersInuse.end()) { |
| mCleanBuffers.splice(mCleanBuffers.end(), std::move(layer->second)); |
| layer = cachedLayerBuffers.erase(layer); |
| } else { |
| ++layer; |
| } |
| } |
| cachedLayersInuse.clear(); |
| return true; |
| }; |
| |
| auto cachedLayerSize = mCachedLayerBuffers.size(); |
| if (destroyUnusedLayers(mCacheShrinkPending, mCachedLayersInuse, mCachedLayerBuffers)) { |
| ALOGW("FBManager: shrink cached layers from %zu to %zu", cachedLayerSize, |
| mCachedLayerBuffers.size()); |
| } |
| |
| cachedLayerSize = mCachedM2mSecureLayerBuffers.size(); |
| if (destroyUnusedLayers(mCacheM2mSecureShrinkPending, mCachedM2mSecureLayersInuse, |
| mCachedM2mSecureLayerBuffers)) { |
| ALOGW("FBManager: shrink cached M2M secure layers from %zu to %zu", cachedLayerSize, |
| mCachedM2mSecureLayerBuffers.size()); |
| } |
| } |
| |
| void FramebufferManager::destroySecureFramebufferLocked() { |
| if (!mHasSecureFramebuffer) { |
| return; |
| } |
| |
| mHasSecureFramebuffer = false; |
| |
| for (auto &layer : mCachedLayerBuffers) { |
| if (isFramebuffer(layer.first)) { |
| auto &bufferList = layer.second; |
| for (auto it = bufferList.begin(); it != bufferList.end(); ++it) { |
| auto &buffer = *it; |
| if (buffer->bufferDesc.isSecure) { |
| // Assume the latest non-secure buffer in the front |
| // TODO: have a better way to keep in-used buffers |
| mCleanBuffers.splice(mCleanBuffers.end(), bufferList, it, bufferList.end()); |
| return; |
| } |
| } |
| } |
| } |
| } |
| |
| void FramebufferManager::destroyM2mSecureLayerBufferLocked() { |
| if (!mHasM2mSecureLayerBuffer) { |
| return; |
| } |
| |
| mHasM2mSecureLayerBuffer = false; |
| |
| for (auto &layer : mCachedM2mSecureLayerBuffers) { |
| auto &bufferList = layer.second; |
| if (bufferList.size()) { |
| mCleanBuffers.splice(mCleanBuffers.end(), bufferList, bufferList.begin(), |
| bufferList.end()); |
| } |
| } |
| } |
| |
| void ExynosDisplayDrmInterface::destroyLayer(ExynosLayer *layer) { |
| mFBManager.cleanup(layer); |
| } |
| |
| int32_t ExynosDisplayDrmInterface::getDisplayIdleTimerSupport(bool &outSupport) { |
| if (mIsVrrModeSupported) { |
| outSupport = false; |
| return NO_ERROR; |
| } |
| |
| auto [ret, support] = mDrmConnector->panel_idle_support().value(); |
| if (ret) { |
| ALOGI("no panel_idle_support drm property or invalid value (%d)", ret); |
| outSupport = false; |
| } else { |
| outSupport = (support > 0); |
| } |
| |
| return NO_ERROR; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::getDefaultModeId(int32_t *modeId) { |
| if (modeId == nullptr) return HWC2_ERROR_BAD_PARAMETER; |
| |
| *modeId = mDrmConnector->get_preferred_mode_id(); |
| return NO_ERROR; |
| } |
| |
| ExynosDisplayDrmInterface::ExynosDisplayDrmInterface(ExynosDisplay *exynosDisplay): |
| mMonitorDescription{0} |
| { |
| mType = INTERFACE_TYPE_DRM; |
| init(exynosDisplay); |
| } |
| |
| ExynosDisplayDrmInterface::~ExynosDisplayDrmInterface() |
| { |
| if (mActiveModeState.blob_id) |
| mDrmDevice->DestroyPropertyBlob(mActiveModeState.blob_id); |
| if (mActiveModeState.old_blob_id) |
| mDrmDevice->DestroyPropertyBlob(mActiveModeState.old_blob_id); |
| if (mDesiredModeState.blob_id) |
| mDrmDevice->DestroyPropertyBlob(mDesiredModeState.blob_id); |
| if (mDesiredModeState.old_blob_id) |
| mDrmDevice->DestroyPropertyBlob(mDesiredModeState.old_blob_id); |
| if (mPartialRegionState.blob_id) |
| mDrmDevice->DestroyPropertyBlob(mPartialRegionState.blob_id); |
| } |
| |
| void ExynosDisplayDrmInterface::init(ExynosDisplay *exynosDisplay) |
| { |
| mExynosDisplay = exynosDisplay; |
| mDisplayTraceName = mExynosDisplay->mDisplayTraceName; |
| mDrmDevice = NULL; |
| mDrmCrtc = NULL; |
| mDrmConnector = NULL; |
| } |
| |
| void ExynosDisplayDrmInterface::parseBlendEnums(const DrmProperty &property) |
| { |
| const std::vector<std::pair<uint32_t, const char *>> blendEnums = { |
| {HWC2_BLEND_MODE_NONE, "None"}, |
| {HWC2_BLEND_MODE_PREMULTIPLIED, "Pre-multiplied"}, |
| {HWC2_BLEND_MODE_COVERAGE, "Coverage"}, |
| }; |
| |
| ALOGD("Init blend enums"); |
| DrmEnumParser::parseEnums(property, blendEnums, mBlendEnums); |
| for (auto &e : mBlendEnums) { |
| ALOGD("blend [hal: %d, drm: %" PRId64 "]", e.first, e.second); |
| } |
| } |
| |
| void ExynosDisplayDrmInterface::parseStandardEnums(const DrmProperty &property) |
| { |
| const std::vector<std::pair<uint32_t, const char *>> standardEnums = { |
| {HAL_DATASPACE_STANDARD_UNSPECIFIED, "Unspecified"}, |
| {HAL_DATASPACE_STANDARD_BT709, "BT709"}, |
| {HAL_DATASPACE_STANDARD_BT601_625, "BT601_625"}, |
| {HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED, "BT601_625_UNADJUSTED"}, |
| {HAL_DATASPACE_STANDARD_BT601_525, "BT601_525"}, |
| {HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED, "BT601_525_UNADJUSTED"}, |
| {HAL_DATASPACE_STANDARD_BT2020, "BT2020"}, |
| {HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE, "BT2020_CONSTANT_LUMINANCE"}, |
| {HAL_DATASPACE_STANDARD_BT470M, "BT470M"}, |
| {HAL_DATASPACE_STANDARD_FILM, "FILM"}, |
| {HAL_DATASPACE_STANDARD_DCI_P3, "DCI-P3"}, |
| {HAL_DATASPACE_STANDARD_ADOBE_RGB, "Adobe RGB"}, |
| }; |
| |
| ALOGD("Init standard enums"); |
| DrmEnumParser::parseEnums(property, standardEnums, mStandardEnums); |
| for (auto &e : mStandardEnums) { |
| ALOGD("standard [hal: %d, drm: %" PRId64 "]", |
| e.first >> HAL_DATASPACE_STANDARD_SHIFT, e.second); |
| } |
| } |
| |
| void ExynosDisplayDrmInterface::parseTransferEnums(const DrmProperty &property) |
| { |
| const std::vector<std::pair<uint32_t, const char *>> transferEnums = { |
| {HAL_DATASPACE_TRANSFER_UNSPECIFIED, "Unspecified"}, |
| {HAL_DATASPACE_TRANSFER_LINEAR, "Linear"}, |
| {HAL_DATASPACE_TRANSFER_SRGB, "sRGB"}, |
| {HAL_DATASPACE_TRANSFER_SMPTE_170M, "SMPTE 170M"}, |
| {HAL_DATASPACE_TRANSFER_GAMMA2_2, "Gamma 2.2"}, |
| {HAL_DATASPACE_TRANSFER_GAMMA2_6, "Gamma 2.6"}, |
| {HAL_DATASPACE_TRANSFER_GAMMA2_8, "Gamma 2.8"}, |
| {HAL_DATASPACE_TRANSFER_ST2084, "ST2084"}, |
| {HAL_DATASPACE_TRANSFER_HLG, "HLG"}, |
| }; |
| |
| ALOGD("Init transfer enums"); |
| DrmEnumParser::parseEnums(property, transferEnums, mTransferEnums); |
| for (auto &e : mTransferEnums) { |
| ALOGD("transfer [hal: %d, drm: %" PRId64 "]", |
| e.first >> HAL_DATASPACE_TRANSFER_SHIFT, e.second); |
| } |
| } |
| |
| void ExynosDisplayDrmInterface::parseRangeEnums(const DrmProperty &property) |
| { |
| const std::vector<std::pair<uint32_t, const char *>> rangeEnums = { |
| {HAL_DATASPACE_RANGE_UNSPECIFIED, "Unspecified"}, |
| {HAL_DATASPACE_RANGE_FULL, "Full"}, |
| {HAL_DATASPACE_RANGE_LIMITED, "Limited"}, |
| {HAL_DATASPACE_RANGE_EXTENDED, "Extended"}, |
| }; |
| |
| ALOGD("Init range enums"); |
| DrmEnumParser::parseEnums(property, rangeEnums, mRangeEnums); |
| for (auto &e : mRangeEnums) { |
| ALOGD("range [hal: %d, drm: %" PRId64 "]", |
| e.first >> HAL_DATASPACE_RANGE_SHIFT, e.second); |
| } |
| } |
| |
| void ExynosDisplayDrmInterface::parseColorModeEnums(const DrmProperty &property) |
| { |
| const std::vector<std::pair<uint32_t, const char *>> colorModeEnums = { |
| {HAL_COLOR_MODE_NATIVE, "Native"}, |
| {HAL_COLOR_MODE_DCI_P3, "DCI-P3"}, |
| {HAL_COLOR_MODE_SRGB, "sRGB"}, |
| }; |
| |
| ALOGD("Init color mode enums"); |
| DrmEnumParser::parseEnums(property, colorModeEnums, mColorModeEnums); |
| for (auto &e : mColorModeEnums) { |
| ALOGD("Colormode [hal: %d, drm: %" PRId64 "]", e.first, e.second); |
| } |
| } |
| |
| void ExynosDisplayDrmInterface::parseMipiSyncEnums(const DrmProperty &property) { |
| const std::vector<std::pair<uint32_t, const char *>> modeEnums = { |
| {toUnderlying(HalMipiSyncType::HAL_MIPI_CMD_SYNC_REFRESH_RATE), "sync_refresh_rate"}, |
| {toUnderlying(HalMipiSyncType::HAL_MIPI_CMD_SYNC_LHBM), "sync_lhbm"}, |
| {toUnderlying(HalMipiSyncType::HAL_MIPI_CMD_SYNC_GHBM), "sync_ghbm"}, |
| {toUnderlying(HalMipiSyncType::HAL_MIPI_CMD_SYNC_BL), "sync_bl"}, |
| {toUnderlying(HalMipiSyncType::HAL_MIPI_CMD_SYNC_OP_RATE), "sync_op_rate"}, |
| }; |
| DrmEnumParser::parseEnums(property, modeEnums, mMipiSyncEnums); |
| for (auto &e : mMipiSyncEnums) { |
| ALOGD("mipi sync [hal 0x%x, drm: %" PRId64 ", %s]", e.first, e.second, |
| modeEnums[e.first].second); |
| } |
| } |
| |
| void ExynosDisplayDrmInterface::updateMountOrientation() |
| { |
| const std::vector<std::pair<HwcMountOrientation, const char*>> orientationEnums = { |
| { HwcMountOrientation::ROT_0, "Normal" }, |
| { HwcMountOrientation::ROT_90, "Left Side Up" }, |
| { HwcMountOrientation::ROT_180, "Upside Down" }, |
| { HwcMountOrientation::ROT_270, "Right Side Up" }, |
| }; |
| |
| mExynosDisplay->mMountOrientation = HwcMountOrientation::ROT_0; |
| const DrmProperty &orientation = mDrmConnector->orientation(); |
| if (orientation.id() == 0) |
| return; |
| |
| auto [err, drmOrientation] = orientation.value(); |
| if (err) { |
| ALOGW("%s failed to get drm prop value, err: %d", __func__, err); |
| return; |
| } |
| |
| for (auto &e : orientationEnums) { |
| uint64_t enumValue; |
| std::tie(enumValue, err) = orientation.getEnumValueWithName(e.second); |
| if (!err && enumValue == drmOrientation) { |
| mExynosDisplay->mMountOrientation = e.first; |
| return; |
| } |
| } |
| |
| ALOGW("%s ignore unrecoganized orientation %" PRId64, __func__, drmOrientation); |
| } |
| |
| void ExynosDisplayDrmInterface::parseRCDId(const DrmProperty &property) { |
| if (mExynosDisplay->mType != HWC_DISPLAY_PRIMARY) { |
| ALOGW("%s invalid display type: %d", __func__, mExynosDisplay->mType); |
| return; |
| } |
| |
| if (property.id() == 0) { |
| static_cast<ExynosPrimaryDisplay *>(mExynosDisplay)->mRcdId = -1; |
| return; |
| } |
| |
| auto [err, rcd_id] = property.value(); |
| if (err < 0) { |
| ALOGW("%s failed to get drm prop value", __func__); |
| return; |
| } |
| |
| if (getSpecialChannelId(rcd_id) >= 0) |
| static_cast<ExynosPrimaryDisplay *>(mExynosDisplay)->mRcdId = rcd_id; |
| } |
| |
| int ExynosDisplayDrmInterface::getDrmDisplayId(uint32_t type, uint32_t index) |
| { |
| for (auto &conn: mDrmDevice->connectors()) { |
| if ((((type == HWC_DISPLAY_PRIMARY) && conn->internal()) && (index == conn->display())) || |
| ((type == HWC_DISPLAY_EXTERNAL) && conn->external())) |
| return conn->display(); |
| } |
| |
| return -1; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::initDrmDevice(DrmDevice *drmDevice) |
| { |
| if (mExynosDisplay == NULL) { |
| ALOGE("mExynosDisplay is not set"); |
| return -EINVAL; |
| } |
| if ((mDrmDevice = drmDevice) == NULL) { |
| ALOGE("drmDevice is NULL"); |
| return -EINVAL; |
| } |
| |
| mFBManager.init(mDrmDevice->fd()); |
| |
| int drmDisplayId = getDrmDisplayId(mExynosDisplay->mType, mExynosDisplay->mIndex); |
| if (drmDisplayId < 0) { |
| ALOGE("getDrmDisplayId is failed"); |
| return -EINVAL; |
| } |
| |
| if (mExynosDisplay->mType != HWC_DISPLAY_EXTERNAL) |
| mReadbackInfo.init(mDrmDevice, drmDisplayId); |
| |
| if ((mDrmCrtc = mDrmDevice->GetCrtcForDisplay(drmDisplayId)) == NULL) { |
| ALOGE("%s:: GetCrtcForDisplay is NULL (id: %d)", |
| mExynosDisplay->mDisplayName.c_str(), drmDisplayId); |
| return -EINVAL; |
| } |
| |
| if ((mDrmConnector = mDrmDevice->GetConnectorForDisplay(drmDisplayId)) == NULL) { |
| ALOGE("%s:: GetConnectorForDisplay is NULL (id: %d)", |
| mExynosDisplay->mDisplayName.c_str(), drmDisplayId); |
| return -EINVAL; |
| } |
| |
| /* Check CRTC and Connector are matched with Display Type */ |
| if (((mExynosDisplay->mType == HWC_DISPLAY_PRIMARY) && mDrmConnector->external()) || |
| ((mExynosDisplay->mType == HWC_DISPLAY_EXTERNAL) && mDrmConnector->internal())) { |
| ALOGE("%s:: Display(id: %u) is not matched with Connector(id: %u)", |
| mExynosDisplay->mDisplayName.c_str(), drmDisplayId, mDrmConnector->id()); |
| return -EINVAL; |
| } |
| |
| ALOGD("%s:: display type: %d, index: %d, drmDisplayId: %d, " |
| "crtc id: %d, connector id: %d", |
| __func__, mExynosDisplay->mType, mExynosDisplay->mIndex, |
| drmDisplayId, mDrmCrtc->id(), mDrmConnector->id()); |
| |
| /* Mapping ExynosMPP resource with DPP Planes */ |
| uint32_t numWindow = 0; |
| for (uint32_t i = 0; i < mDrmDevice->planes().size(); i++) { |
| auto &plane = mDrmDevice->planes().at(i); |
| uint32_t plane_id = plane->id(); |
| |
| if (!plane->zpos_property().isImmutable()) { |
| /* Plane can be used for composition */ |
| ExynosMPP *exynosMPP = |
| mExynosDisplay->mResourceManager->getOtfMPPWithChannel(i); |
| if (exynosMPP == NULL) |
| HWC_LOGE(mExynosDisplay, "getOtfMPPWithChannel fail, ch(%d)", plane_id); |
| mExynosMPPsForPlane[plane_id] = exynosMPP; |
| numWindow++; |
| } else { |
| /* |
| * Plane is special purpose plane which cannot be used for compositon. |
| * It's zpos property is immutable. |
| */ |
| mExynosMPPsForPlane[plane_id] = NULL; |
| } |
| } |
| setMaxWindowNum(numWindow); |
| |
| if (mExynosDisplay->mMaxWindowNum != getMaxWindowNum()) { |
| ALOGE("%s:: Invalid max window number (mMaxWindowNum: %d, getMaxWindowNum(): %d", |
| __func__, mExynosDisplay->mMaxWindowNum, getMaxWindowNum()); |
| return -EINVAL; |
| } |
| |
| getLowPowerDrmModeModeInfo(); |
| |
| mDrmVSyncWorker.Init(mDrmDevice, drmDisplayId, mDisplayTraceName); |
| mDrmVSyncWorker.RegisterCallback(std::shared_ptr<VsyncCallback>(this)); |
| |
| if (!mDrmDevice->planes().empty()) { |
| auto &plane = mDrmDevice->planes().front(); |
| parseBlendEnums(plane->blend_property()); |
| parseStandardEnums(plane->standard_property()); |
| parseTransferEnums(plane->transfer_property()); |
| parseRangeEnums(plane->range_property()); |
| } |
| |
| choosePreferredConfig(); |
| |
| // After choosePreferredConfig, the mDrmConnector->modes array is initialized, get the panel |
| // full resolution information here. |
| if (mExynosDisplay->mType == HWC_DISPLAY_PRIMARY) { |
| retrievePanelFullResolution(); |
| } |
| |
| parseColorModeEnums(mDrmCrtc->color_mode_property()); |
| parseMipiSyncEnums(mDrmConnector->mipi_sync()); |
| updateMountOrientation(); |
| |
| if (mExynosDisplay->mType == HWC_DISPLAY_PRIMARY) parseRCDId(mDrmCrtc->rcd_plane_id_property()); |
| |
| if (mExynosDisplay->mBrightnessController && |
| mExynosDisplay->mBrightnessController->initDrm(*mDrmDevice, *mDrmConnector)) { |
| ALOGW("%s failed to init brightness controller", __func__); |
| } |
| |
| if (mExynosDisplay->mHistogramController) { |
| mExynosDisplay->mHistogramController->initDrm(*mDrmCrtc); |
| } |
| |
| return NO_ERROR; |
| } |
| |
| |
| void ExynosDisplayDrmInterface::Callback( |
| int display, int64_t timestamp) |
| { |
| { |
| Mutex::Autolock lock(mExynosDisplay->getDisplayMutex()); |
| bool configApplied = mVsyncCallback.Callback(display, timestamp); |
| |
| if (configApplied) { |
| if (mVsyncCallback.getDesiredVsyncPeriod()) { |
| mExynosDisplay->resetConfigRequestStateLocked(mActiveModeState.mode.id()); |
| mDrmConnector->set_active_mode(mActiveModeState.mode); |
| mVsyncCallback.resetDesiredVsyncPeriod(); |
| } |
| |
| /* |
| * Disable vsync if vsync config change is done |
| */ |
| if (!mVsyncCallback.getVSyncEnabled()) { |
| mDrmVSyncWorker.VSyncControl(false); |
| mVsyncCallback.resetVsyncTimeStamp(); |
| } |
| } else { |
| mExynosDisplay->updateConfigRequestAppliedTime(); |
| } |
| |
| if (!mExynosDisplay->mPlugState || !mVsyncCallback.getVSyncEnabled()) { |
| return; |
| } |
| |
| // Refresh rate during enabling LHBM might be different from the one SF expects. |
| // HWC just reports the SF expected Vsync to make UI smoothness consistent even if |
| // HWC runs at different refresh rate temporarily. |
| if (!mExynosDisplay->isConfigSettingEnabled()) { |
| int64_t pendingPeriodNs = |
| mExynosDisplay->getVsyncPeriod(mExynosDisplay->mPendingConfig); |
| int64_t activePeriodNs = mExynosDisplay->getVsyncPeriod(mExynosDisplay->mActiveConfig); |
| if (pendingPeriodNs && mExynosDisplay->mLastVsyncTimestamp) { |
| if (activePeriodNs > pendingPeriodNs) { |
| DISPLAY_DRM_LOGW("wrong vsync period: %" PRId64 "us (active), %" PRId64 |
| "us (pending)", |
| activePeriodNs / 1000, pendingPeriodNs / 1000); |
| } else if (activePeriodNs != pendingPeriodNs) { |
| int64_t deltaNs = timestamp - mExynosDisplay->mLastVsyncTimestamp; |
| if (deltaNs < (pendingPeriodNs - ms2ns(2))) { |
| DISPLAY_DRM_LOGI("skip mismatching Vsync callback, delta=%" PRId64 "us", |
| deltaNs / 1000); |
| return; |
| } |
| } |
| } |
| } |
| mExynosDisplay->mLastVsyncTimestamp = timestamp; |
| } |
| |
| mExynosDisplay->onVsync(timestamp); |
| |
| ExynosDevice *exynosDevice = mExynosDisplay->mDevice; |
| |
| if (exynosDevice->onVsync_2_4(mExynosDisplay->mDisplayId, timestamp, |
| mExynosDisplay->mVsyncPeriod)) { |
| DISPLAY_ATRACE_INT(vsyncPeriodTag, static_cast<int32_t>(mExynosDisplay->mVsyncPeriod)); |
| return; |
| } |
| |
| exynosDevice->onVsync(mExynosDisplay->mDisplayId, timestamp); |
| } |
| |
| bool ExynosDisplayDrmInterface::ExynosVsyncCallback::Callback( |
| int display, int64_t timestamp) |
| { |
| /* |
| * keep vsync period if mVsyncTimeStamp |
| * is not initialized since vsync is enabled |
| */ |
| if (mVsyncTimeStamp > 0) { |
| mVsyncPeriod = timestamp - mVsyncTimeStamp; |
| } |
| |
| mVsyncTimeStamp = timestamp; |
| |
| /* There was no config chage request */ |
| if (!mDesiredVsyncPeriod) |
| return true; |
| |
| /* |
| * mDesiredVsyncPeriod is nanoseconds |
| * Compare with 20% margin |
| */ |
| if (abs(static_cast<int32_t>(mDesiredVsyncPeriod - mVsyncPeriod)) < (mDesiredVsyncPeriod / 5)) |
| return true; |
| |
| return false; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::getLowPowerDrmModeModeInfo() { |
| auto mode = mDrmConnector->lp_mode(); |
| |
| if (!mode.clock()) { |
| return HWC2_ERROR_UNSUPPORTED; |
| } |
| |
| mDozeDrmMode = mode; |
| |
| return NO_ERROR; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::setLowPowerMode() { |
| if (!isDozeModeAvailable()) { |
| return HWC2_ERROR_UNSUPPORTED; |
| } |
| |
| uint32_t mm_width = mDrmConnector->mm_width(); |
| uint32_t mm_height = mDrmConnector->mm_height(); |
| |
| mExynosDisplay->mXres = mDozeDrmMode.h_display(); |
| mExynosDisplay->mYres = mDozeDrmMode.v_display(); |
| // in nanoseconds |
| mExynosDisplay->mVsyncPeriod = static_cast<uint32_t>(mDozeDrmMode.te_period()); |
| // Dots per 1000 inches |
| mExynosDisplay->mXdpi = mm_width ? (mDozeDrmMode.h_display() * kUmPerInch) / mm_width : -1; |
| // Dots per 1000 inches |
| mExynosDisplay->mYdpi = mm_height ? (mDozeDrmMode.v_display() * kUmPerInch) / mm_height : -1; |
| |
| mExynosDisplay->mRefreshRate = static_cast<int32_t>(mDozeDrmMode.v_refresh()); |
| |
| return setActiveDrmMode(mDozeDrmMode); |
| } |
| |
| int32_t ExynosDisplayDrmInterface::setPowerMode(int32_t mode) |
| { |
| int ret = 0; |
| uint64_t dpms_value = 0; |
| if (mode == HWC_POWER_MODE_OFF) { |
| dpms_value = DRM_MODE_DPMS_OFF; |
| } else { |
| dpms_value = DRM_MODE_DPMS_ON; |
| } |
| |
| const DrmProperty &prop = mDrmConnector->dpms_property(); |
| if ((ret = drmModeConnectorSetProperty(mDrmDevice->fd(), mDrmConnector->id(), prop.id(), |
| dpms_value)) != NO_ERROR) { |
| HWC_LOGE(mExynosDisplay, "setPower mode ret (%d)", ret); |
| } |
| |
| return ret; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::setVsyncEnabled(uint32_t enabled) |
| { |
| if (enabled == HWC2_VSYNC_ENABLE) { |
| mDrmVSyncWorker.VSyncControl(true); |
| } else { |
| if (mVsyncCallback.getDesiredVsyncPeriod() == 0) |
| mDrmVSyncWorker.VSyncControl(false); |
| } |
| |
| mVsyncCallback.enableVSync(HWC2_VSYNC_ENABLE == enabled); |
| |
| ExynosDevice *exynosDevice = mExynosDisplay->mDevice; |
| if (exynosDevice->isCallbackAvailable(HWC2_CALLBACK_VSYNC_2_4)) { |
| DISPLAY_ATRACE_INT(vsyncPeriodTag, 0); |
| } |
| |
| return NO_ERROR; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::choosePreferredConfig() { |
| uint32_t num_configs = 0; |
| int32_t err = getDisplayConfigs(&num_configs, NULL); |
| if (err != HWC2_ERROR_NONE || !num_configs) |
| return err; |
| |
| int32_t config = -1; |
| char modeStr[PROPERTY_VALUE_MAX] = "\0"; |
| int32_t width = 0, height = 0, fps = 0; |
| // only legacy products use this property, kernel preferred mode will be used going forward |
| if (property_get("vendor.display.preferred_mode", modeStr, "") > 0 && |
| sscanf(modeStr, "%dx%d@%d", &width, &height, &fps) == 3) { |
| err = mExynosDisplay->lookupDisplayConfigs(width, height, fps, fps, &config); |
| } else { |
| err = HWC2_ERROR_BAD_CONFIG; |
| } |
| |
| const int32_t drmPreferredConfig = mDrmConnector->get_preferred_mode_id(); |
| if (err != HWC2_ERROR_NONE) { |
| config = drmPreferredConfig; |
| } |
| ALOGI("Preferred mode id: %d(%s), state: %d", config, modeStr, mDrmConnector->state()); |
| |
| auto &configs = mExynosDisplay->mDisplayConfigs; |
| if (config != drmPreferredConfig && |
| (configs[config].width != configs[drmPreferredConfig].width || |
| configs[config].height != configs[drmPreferredConfig].height)) { |
| // HWC cannot send a resolution change commit here until 1st frame update because of |
| // some panels requirement. Therefore, it calls setActiveConfigWithConstraints() help |
| // set mDesiredModeState correctly, and then trigger modeset in the 1s frame update. |
| if ((err = setActiveConfigWithConstraints(config)) < 0) { |
| ALOGE("failed to setActiveConfigWithConstraints(), err %d", err); |
| return err; |
| } |
| } else { |
| if ((err = setActiveConfig(config)) < 0) { |
| ALOGE("failed to set default config, err %d", err); |
| return err; |
| } |
| } |
| |
| return mExynosDisplay->updateInternalDisplayConfigVariables(config); |
| } |
| |
| int32_t ExynosDisplayDrmInterface::getDisplayConfigs( |
| uint32_t* outNumConfigs, |
| hwc2_config_t* outConfigs) |
| { |
| if (!mExynosDisplay || !(mExynosDisplay->mDevice)) { |
| return HWC2_ERROR_BAD_DISPLAY; |
| } |
| |
| std::lock_guard<std::recursive_mutex> lock(mDrmConnector->modesLock()); |
| |
| if (!outConfigs) { |
| bool isVrrApiSupported = mExynosDisplay->mDevice->isVrrApiSupported(); |
| bool useVrrConfigs = mIsVrrModeSupported && isVrrApiSupported; |
| int ret = mDrmConnector->UpdateModes(useVrrConfigs); |
| if (ret < 0) { |
| ALOGE("Failed to update display modes %d", ret); |
| return HWC2_ERROR_BAD_DISPLAY; |
| } |
| if (ret == 0) { |
| // no need to update mExynosDisplay->mDisplayConfigs |
| goto no_mode_changes; |
| } |
| ALOGI("Select Vrr Config for display %s: composer interface compatibility = %s, display " |
| "hardware " |
| "Compatibility = %s, use Vrr config = %s", |
| mExynosDisplay->mDisplayName.c_str(), isVrrApiSupported ? "true" : "false", |
| mIsVrrModeSupported ? "true" : "false", useVrrConfigs ? "true" : "false"); |
| |
| if (mDrmConnector->state() == DRM_MODE_CONNECTED) { |
| /* |
| * EDID property for External Display is created during initialization, |
| * but it is not complete. It will be completed after Hot Plug Detection |
| * & DRM Mode update. |
| */ |
| if (mExynosDisplay->mType == HWC_DISPLAY_EXTERNAL) |
| mDrmConnector->UpdateEdidProperty(); |
| |
| mExynosDisplay->mPlugState = true; |
| } else |
| mExynosDisplay->mPlugState = false; |
| |
| dumpDisplayConfigs(); |
| |
| mExynosDisplay->mDisplayConfigs.clear(); |
| |
| uint32_t mm_width = mDrmConnector->mm_width(); |
| uint32_t mm_height = mDrmConnector->mm_height(); |
| ALOGD("%s: mm_width(%u) mm_height(%u)", |
| mExynosDisplay->mDisplayName.c_str(), mm_width, mm_height); |
| |
| DisplayConfigGroupIdGenerator groupIdGenerator; |
| float peakRr = -1; |
| for (const DrmMode &mode : mDrmConnector->modes()) { |
| displayConfigs_t configs; |
| float rr = mode.v_refresh(); |
| configs.refreshRate = static_cast<int32_t>(rr); |
| configs.vsyncPeriod = static_cast<int32_t>(mode.te_period()); |
| if (configs.vsyncPeriod <= 0.0f) { |
| ALOGE("%s:: invalid vsync period", __func__); |
| return HWC2_ERROR_BAD_DISPLAY; |
| } |
| configs.isOperationRateToBts = mode.is_operation_rate_to_bts(); |
| configs.width = mode.h_display(); |
| configs.height = mode.v_display(); |
| // Dots per 1000 inches |
| configs.Xdpi = mm_width ? (mode.h_display() * kUmPerInch) / mm_width : -1; |
| // Dots per 1000 inches |
| configs.Ydpi = mm_height ? (mode.v_display() * kUmPerInch) / mm_height : -1; |
| // find peak rr |
| if (rr > peakRr) |
| peakRr = rr; |
| // Configure VRR if it's turned on. |
| if (mode.is_vrr_mode()) { |
| VrrConfig_t vrrConfig; |
| vrrConfig.minFrameIntervalNs = static_cast<int>(std::nano::den / rr); |
| // TODO(b/290843234): FrameIntervalPowerHint is currently optional and omitted. |
| // Supply initial values for notifyExpectedPresentConfig; potential changes may come |
| // later. |
| vrrConfig.notifyExpectedPresentConfig.HeadsUpNs = mNotifyExpectedPresentHeadsUpNs; |
| vrrConfig.notifyExpectedPresentConfig.TimeoutNs = mNotifyExpectedPresentTimeoutNs; |
| configs.vrrConfig = std::make_optional(vrrConfig); |
| configs.groupId = groupIdGenerator.getGroupId(configs.width, configs.height, |
| vrrConfig.minFrameIntervalNs, |
| configs.vsyncPeriod); |
| } else { |
| configs.groupId = groupIdGenerator.getGroupId(configs.width, configs.height); |
| } |
| mExynosDisplay->mDisplayConfigs.insert(std::make_pair(mode.id(), configs)); |
| ALOGD("%s: config group(%d), w(%d), h(%d), rr(%f), TE(%d), xdpi(%d), ydpi(%d), " |
| "vrr mode(%s), NS mode(%s)", |
| mExynosDisplay->mDisplayName.c_str(), |
| configs.groupId, configs.width, configs.height, rr, configs.vsyncPeriod, |
| configs.Xdpi, configs.Ydpi, mode.is_vrr_mode() ? "true" : "false", |
| mode.is_ns_mode() ? "true" : "false"); |
| } |
| mExynosDisplay->setPeakRefreshRate(peakRr); |
| } |
| |
| no_mode_changes: |
| uint32_t num_modes = static_cast<uint32_t>(mDrmConnector->modes().size()); |
| if (!outConfigs) { |
| *outNumConfigs = num_modes; |
| return HWC2_ERROR_NONE; |
| } |
| |
| uint32_t idx = 0; |
| |
| for (const DrmMode &mode : mDrmConnector->modes()) { |
| if (idx >= *outNumConfigs) |
| break; |
| outConfigs[idx++] = mode.id(); |
| } |
| *outNumConfigs = idx; |
| |
| return 0; |
| } |
| |
| void ExynosDisplayDrmInterface::dumpDisplayConfigs() |
| { |
| std::lock_guard<std::recursive_mutex> lock(mDrmConnector->modesLock()); |
| |
| uint32_t num_modes = static_cast<uint32_t>(mDrmConnector->modes().size()); |
| for (uint32_t i = 0; i < num_modes; i++) { |
| auto mode = mDrmConnector->modes().at(i); |
| ALOGD("%s: config[%d] %s: id(%d), clock(%d), flags(0x%x), type(0x%x)", |
| mExynosDisplay->mDisplayName.c_str(), i, mode.name().c_str(), mode.id(), |
| mode.clock(), mode.flags(), mode.type()); |
| ALOGD("\th_display(%d), h_sync_start(%d), h_sync_end(%d), h_total(%d), h_skew(%d)", |
| mode.h_display(), mode.h_sync_start(), mode.h_sync_end(), mode.h_total(), |
| mode.h_skew()); |
| ALOGD("\tv_display(%d), v_sync_start(%d), v_sync_end(%d), v_total(%d), v_scan(%d), " |
| "v_refresh(%f)", |
| mode.v_display(), mode.v_sync_start(), mode.v_sync_end(), mode.v_total(), |
| mode.v_scan(), mode.v_refresh()); |
| } |
| } |
| |
| int32_t ExynosDisplayDrmInterface::getDisplayVsyncPeriod(hwc2_vsync_period_t* outVsyncPeriod) |
| { |
| return HWC2_ERROR_UNSUPPORTED; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::getConfigChangeDuration() |
| { |
| const auto [ret, duration] = mDrmConnector->rr_switch_duration().value(); |
| |
| if (!ret && duration > 0) { |
| return duration; |
| } |
| |
| return 2; |
| }; |
| |
| bool ExynosDisplayDrmInterface::needRefreshOnLP() { |
| const auto [ret, refresh_on_lp] = mDrmConnector->refresh_on_lp().value(); |
| |
| if (!ret) { |
| return refresh_on_lp; |
| } |
| |
| return false; |
| }; |
| |
| int32_t ExynosDisplayDrmInterface::getVsyncAppliedTime( |
| hwc2_config_t config, int64_t* actualChangeTime) |
| { |
| if (mDrmCrtc->adjusted_vblank_property().id() == 0) { |
| uint64_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); |
| *actualChangeTime = currentTime + |
| (mExynosDisplay->mVsyncPeriod) * getConfigChangeDuration(); |
| return HWC2_ERROR_NONE; |
| } |
| |
| int ret = 0; |
| if ((ret = mDrmDevice->UpdateCrtcProperty(*mDrmCrtc, |
| &mDrmCrtc->adjusted_vblank_property())) != 0) { |
| HWC_LOGE(mExynosDisplay, "Failed to update vblank property"); |
| return ret; |
| } |
| |
| uint64_t timestamp; |
| std::tie(ret, timestamp) = mDrmCrtc->adjusted_vblank_property().value(); |
| if (ret < 0) { |
| HWC_LOGE(mExynosDisplay, "Failed to get vblank property"); |
| return ret; |
| } |
| |
| *actualChangeTime = static_cast<int64_t>(timestamp); |
| return HWC2_ERROR_NONE; |
| } |
| |
| bool ExynosDisplayDrmInterface::supportDataspace(int32_t dataspace) |
| { |
| bool supportStandard = false; |
| bool supportTransfer = false; |
| bool supportRange = false; |
| |
| /* Check supported standard */ |
| for (auto &e : mStandardEnums) { |
| if (e.first & dataspace) |
| supportStandard = true; |
| } |
| |
| /* Check supported transfer */ |
| for (auto &e : mTransferEnums) { |
| if (e.first & dataspace) |
| supportTransfer = true; |
| } |
| |
| /* Check supported range */ |
| for (auto &e : mRangeEnums) { |
| if (e.first & dataspace) |
| supportRange = true; |
| } |
| |
| return supportStandard && supportTransfer && supportRange; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::getColorModes(uint32_t *outNumModes, int32_t *outModes) |
| { |
| if (mDrmCrtc->color_mode_property().id() == 0) { |
| *outNumModes = 1; |
| |
| if (outModes != NULL) { |
| outModes[0] = HAL_COLOR_MODE_NATIVE; |
| } |
| return HWC2_ERROR_NONE; |
| } |
| |
| uint32_t colorNum = 0; |
| for (auto &e : mColorModeEnums) { |
| if (outModes != NULL) { |
| outModes[colorNum] = e.first; |
| } |
| colorNum++; |
| ALOGD("Colormode [hal: %d, drm: %" PRId64 "]", e.first, e.second); |
| } |
| *outNumModes = colorNum; |
| |
| return HWC2_ERROR_NONE; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::setColorMode(int32_t mode) |
| { |
| int ret = 0; |
| |
| if (mDrmCrtc->color_mode_property().id() == 0) { |
| return HWC2_ERROR_NONE; |
| } |
| |
| DrmModeAtomicReq drmReq(this); |
| |
| if ((ret = drmReq.atomicAddProperty(mDrmCrtc->id(), |
| mDrmCrtc->color_mode_property(), mode)) < 0) |
| return ret; |
| |
| if ((ret = drmReq.commit(0, true)) < 0) |
| return ret; |
| |
| return HWC2_ERROR_NONE; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::setActiveConfigWithConstraints( |
| hwc2_config_t config, bool test) |
| { |
| std::lock_guard<std::recursive_mutex> lock(mDrmConnector->modesLock()); |
| |
| ALOGD("%s:: %s config(%d) test(%d)", __func__, mExynosDisplay->mDisplayName.c_str(), config, |
| test); |
| |
| auto mode = std::find_if(mDrmConnector->modes().begin(), mDrmConnector->modes().end(), |
| [config](DrmMode const &m) { return m.id() == config;}); |
| if (mode == mDrmConnector->modes().end()) { |
| HWC_LOGE(mExynosDisplay, "Could not find active mode for %d", config); |
| return HWC2_ERROR_BAD_CONFIG; |
| } |
| |
| if (mDesiredModeState.needsModeSet()) { |
| ALOGI("Previous mode change %d request is not applied", mDesiredModeState.mode.id()); |
| } else if ((mActiveModeState.blob_id != 0) && (mActiveModeState.mode.id() == config)) { |
| ALOGD("%s:: same mode %d", __func__, config); |
| /* trigger resetConfigRequestStateLocked() */ |
| mVsyncCallback.setDesiredVsyncPeriod(mActiveModeState.mode.te_period()); |
| mDrmVSyncWorker.VSyncControl(true); |
| return HWC2_ERROR_NONE; |
| } |
| |
| int32_t ret = HWC2_ERROR_NONE; |
| DrmModeAtomicReq drmReq(this); |
| uint32_t modeBlob = 0; |
| if (mDesiredModeState.mode.id() != config) { |
| if ((ret = createModeBlob(*mode, modeBlob)) != NO_ERROR) { |
| HWC_LOGE(mExynosDisplay, "%s: Fail to set mode state", |
| __func__); |
| return HWC2_ERROR_BAD_CONFIG; |
| } |
| } |
| const auto isResSwitch = |
| (mActiveModeState.blob_id != 0) && mActiveModeState.isFullModeSwitch(*mode); |
| |
| if (!test) { |
| if (modeBlob) { /* only replace desired mode if it has changed */ |
| mDesiredModeState.setMode(*mode, modeBlob, drmReq); |
| if (mExynosDisplay->mOperationRateManager) { |
| mExynosDisplay->mOperationRateManager->onConfig(config); |
| mExynosDisplay->handleTargetOperationRate(); |
| } |
| DISPLAY_DRM_LOGI("%s: config(%d)", __func__, config); |
| } else { |
| ALOGD("%s:: same desired mode %d", __func__, config); |
| } |
| } else { |
| if (!isResSwitch) { |
| ret = setDisplayMode(drmReq, modeBlob ? modeBlob : mDesiredModeState.blob_id, |
| modeBlob ? mode->id() : mDesiredModeState.mode.id()); |
| if (ret < 0) { |
| HWC_LOGE(mExynosDisplay, "%s: Fail to apply display mode", __func__); |
| return ret; |
| } |
| ret = drmReq.commit(DRM_MODE_ATOMIC_TEST_ONLY, true); |
| if (ret) { |
| drmReq.addOldBlob(modeBlob); |
| HWC_LOGE(mExynosDisplay, |
| "%s:: Failed to commit pset ret=%d in applyDisplayMode()\n", __func__, |
| ret); |
| return ret; |
| } |
| } |
| |
| if (modeBlob) { |
| mDrmDevice->DestroyPropertyBlob(modeBlob); |
| } |
| } |
| return HWC2_ERROR_NONE; |
| } |
| int32_t ExynosDisplayDrmInterface::setActiveDrmMode(DrmMode const &mode) { |
| /* Don't skip when power was off */ |
| if (!(mExynosDisplay->mSkipFrame) && (mActiveModeState.blob_id != 0) && |
| (mActiveModeState.mode.id() == mode.id()) && !mActiveModeState.needsModeSet()) { |
| ALOGD("%s:: same mode %d", __func__, mode.id()); |
| return HWC2_ERROR_NONE; |
| } |
| |
| int32_t ret = HWC2_ERROR_NONE; |
| uint32_t modeBlob; |
| if ((ret = createModeBlob(mode, modeBlob)) != NO_ERROR) { |
| HWC_LOGE(mExynosDisplay, "%s: Fail to set mode state", |
| __func__); |
| return HWC2_ERROR_BAD_CONFIG; |
| } |
| |
| DrmModeAtomicReq drmReq(this); |
| |
| uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET; |
| bool reconfig = false; |
| |
| if (mActiveModeState.isFullModeSwitch(mode)) { |
| reconfig = true; |
| } |
| |
| if ((ret = setDisplayMode(drmReq, modeBlob, mode.id())) != NO_ERROR) { |
| drmReq.addOldBlob(modeBlob); |
| HWC_LOGE(mExynosDisplay, "%s: Fail to apply display mode", |
| __func__); |
| return ret; |
| } |
| |
| if ((ret = drmReq.commit(flags, true))) { |
| drmReq.addOldBlob(modeBlob); |
| HWC_LOGE(mExynosDisplay, "%s:: Failed to commit pset ret=%d in applyDisplayMode()\n", |
| __func__, ret); |
| return ret; |
| } |
| |
| mDrmConnector->set_active_mode(mode); |
| mActiveModeState.setMode(mode, modeBlob, drmReq); |
| mActiveModeState.clearPendingModeState(); |
| |
| if (reconfig) { |
| mDrmConnector->ResetLpMode(); |
| getLowPowerDrmModeModeInfo(); |
| } |
| |
| return HWC2_ERROR_NONE; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::setActiveConfig(hwc2_config_t config) { |
| std::lock_guard<std::recursive_mutex> lock(mDrmConnector->modesLock()); |
| |
| auto mode = std::find_if(mDrmConnector->modes().begin(), mDrmConnector->modes().end(), |
| [config](DrmMode const &m) { return m.id() == config; }); |
| if (mode == mDrmConnector->modes().end()) { |
| HWC_LOGE(mExynosDisplay, "Could not find active mode for %d", config); |
| return HWC2_ERROR_BAD_CONFIG; |
| } |
| |
| if (mExynosDisplay->mOperationRateManager) { |
| mExynosDisplay->mOperationRateManager->onConfig(config); |
| mExynosDisplay->handleTargetOperationRate(); |
| } |
| |
| mExynosDisplay->updateAppliedActiveConfig(config, systemTime(SYSTEM_TIME_MONOTONIC)); |
| if (!setActiveDrmMode(*mode)) { |
| DISPLAY_DRM_LOGI("%s: config(%d)", __func__, config); |
| } else { |
| DISPLAY_DRM_LOGE("%s: config(%d) failed", __func__, config); |
| } |
| |
| return 0; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::createModeBlob(const DrmMode &mode, |
| uint32_t &modeBlob) |
| { |
| struct drm_mode_modeinfo drm_mode; |
| memset(&drm_mode, 0, sizeof(drm_mode)); |
| mode.ToDrmModeModeInfo(&drm_mode); |
| |
| modeBlob = 0; |
| int ret = mDrmDevice->CreatePropertyBlob(&drm_mode, sizeof(drm_mode), |
| &modeBlob); |
| if (ret) { |
| HWC_LOGE(mExynosDisplay, "Failed to create mode property blob %d", ret); |
| return ret; |
| } |
| |
| return NO_ERROR; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::setDisplayMode(DrmModeAtomicReq& drmReq, |
| const uint32_t& modeBlob, |
| const uint32_t& modeId) { |
| int ret = NO_ERROR; |
| |
| if ((ret = drmReq.atomicAddProperty(mDrmCrtc->id(), |
| mDrmCrtc->active_property(), 1)) < 0) |
| return ret; |
| |
| if ((ret = drmReq.atomicAddProperty(mDrmCrtc->id(), |
| mDrmCrtc->mode_property(), modeBlob)) < 0) |
| return ret; |
| |
| if ((ret = drmReq.atomicAddProperty(mDrmConnector->id(), |
| mDrmConnector->crtc_id_property(), mDrmCrtc->id())) < 0) |
| return ret; |
| |
| if (mConfigChangeCallback) { |
| drmReq.setAckCallback(std::bind(mConfigChangeCallback, modeId)); |
| } |
| |
| return NO_ERROR; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::setCursorPositionAsync(uint32_t x_pos, uint32_t y_pos) |
| { |
| return 0; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::updateHdrCapabilities() |
| { |
| /* Init member variables */ |
| mExynosDisplay->mHdrTypes.clear(); |
| mExynosDisplay->mMaxLuminance = 0; |
| mExynosDisplay->mMaxAverageLuminance = 0; |
| mExynosDisplay->mMinLuminance = 0; |
| |
| if (mExynosDisplay->mType == HWC_DISPLAY_EXTERNAL) { |
| int upd_res = mDrmConnector->UpdateLuminanceAndHdrProperties(); |
| if (!upd_res) ALOGW("%s: UpdateLuminanceAndHdrProperties failed (%d)", __func__, upd_res); |
| } |
| const DrmProperty &prop_max_luminance = mDrmConnector->max_luminance(); |
| const DrmProperty &prop_max_avg_luminance = mDrmConnector->max_avg_luminance(); |
| const DrmProperty &prop_min_luminance = mDrmConnector->min_luminance(); |
| const DrmProperty &prop_hdr_formats = mDrmConnector->hdr_formats(); |
| |
| int ret = 0; |
| uint64_t max_luminance = 0; |
| uint64_t max_avg_luminance = 0; |
| uint64_t min_luminance = 0; |
| uint64_t hdr_formats = 0; |
| |
| if ((prop_max_luminance.id() == 0) || |
| (prop_max_avg_luminance.id() == 0) || |
| (prop_min_luminance.id() == 0) || |
| (prop_hdr_formats.id() == 0)) { |
| HWC_LOGE(mExynosDisplay, |
| "%s:: there is no property for hdrCapabilities (max_luminance: %d, " |
| "max_avg_luminance: %d, min_luminance: %d, hdr_formats: %d", |
| __func__, prop_max_luminance.id(), prop_max_avg_luminance.id(), |
| prop_min_luminance.id(), prop_hdr_formats.id()); |
| return -1; |
| } |
| |
| std::tie(ret, max_luminance) = prop_max_luminance.value(); |
| if (ret < 0) { |
| HWC_LOGE(mExynosDisplay, "%s:: there is no max_luminance (ret = %d)", |
| __func__, ret); |
| return -1; |
| } |
| mExynosDisplay->mMaxLuminance = (float)max_luminance / DISPLAY_LUMINANCE_UNIT; |
| |
| std::tie(ret, max_avg_luminance) = prop_max_avg_luminance.value(); |
| if (ret < 0) { |
| HWC_LOGE(mExynosDisplay, "%s:: there is no max_avg_luminance (ret = %d)", |
| __func__, ret); |
| return -1; |
| } |
| mExynosDisplay->mMaxAverageLuminance = (float)max_avg_luminance / DISPLAY_LUMINANCE_UNIT; |
| |
| std::tie(ret, min_luminance) = prop_min_luminance.value(); |
| if (ret < 0) { |
| HWC_LOGE(mExynosDisplay, "%s:: there is no min_luminance (ret = %d)", |
| __func__, ret); |
| return -1; |
| } |
| mExynosDisplay->mMinLuminance = (float)min_luminance / DISPLAY_LUMINANCE_UNIT; |
| |
| std::tie(ret, hdr_formats) = prop_hdr_formats.value(); |
| if (ret < 0) { |
| HWC_LOGE(mExynosDisplay, "%s:: there is no hdr_formats (ret = %d)", |
| __func__, ret); |
| return -1; |
| } |
| |
| uint32_t typeBit; |
| std::tie(typeBit, ret) = prop_hdr_formats.getEnumValueWithName("Dolby Vision"); |
| if ((ret == 0) && (hdr_formats & (1 << typeBit))) { |
| mExynosDisplay->mHdrTypes.push_back(HAL_HDR_DOLBY_VISION); |
| HDEBUGLOGD(eDebugHWC, "%s: supported hdr types : %d", |
| mExynosDisplay->mDisplayName.c_str(), HAL_HDR_DOLBY_VISION); |
| } |
| std::tie(typeBit, ret) = prop_hdr_formats.getEnumValueWithName("HDR10"); |
| if ((ret == 0) && (hdr_formats & (1 << typeBit))) { |
| mExynosDisplay->mHdrTypes.push_back(HAL_HDR_HDR10); |
| if (mExynosDisplay->mDevice->mResourceManager->hasHDR10PlusMPP()) { |
| mExynosDisplay->mHdrTypes.push_back(HAL_HDR_HDR10_PLUS); |
| } |
| HDEBUGLOGD(eDebugHWC, "%s: supported hdr types : %d", |
| mExynosDisplay->mDisplayName.c_str(), HAL_HDR_HDR10); |
| } |
| std::tie(typeBit, ret) = prop_hdr_formats.getEnumValueWithName("HLG"); |
| if ((ret == 0) && (hdr_formats & (1 << typeBit))) { |
| mExynosDisplay->mHdrTypes.push_back(HAL_HDR_HLG); |
| HDEBUGLOGD(eDebugHWC, "%s: supported hdr types : %d", |
| mExynosDisplay->mDisplayName.c_str(), HAL_HDR_HLG); |
| } |
| |
| ALOGI("%s: get hdrCapabilities info max_luminance(%" PRId64 "), " |
| "max_avg_luminance(%" PRId64 "), min_luminance(%" PRId64 "), " |
| "hdr_formats(0x%" PRIx64 ")", |
| mExynosDisplay->mDisplayName.c_str(), |
| max_luminance, max_avg_luminance, min_luminance, hdr_formats); |
| |
| ALOGI("%s: mHdrTypes size(%zu), maxLuminance(%f), maxAverageLuminance(%f), minLuminance(%f)", |
| mExynosDisplay->mDisplayName.c_str(), mExynosDisplay->mHdrTypes.size(), mExynosDisplay->mMaxLuminance, |
| mExynosDisplay->mMaxAverageLuminance, mExynosDisplay->mMinLuminance); |
| |
| return 0; |
| } |
| |
| int ExynosDisplayDrmInterface::getDeconChannel(ExynosMPP *otfMPP) |
| { |
| int32_t channelNum = sizeof(idma_channel_map)/sizeof(dpp_channel_map_t); |
| for (int i = 0; i < channelNum; i++) { |
| if((idma_channel_map[i].type == otfMPP->mPhysicalType) && |
| (idma_channel_map[i].index == otfMPP->mPhysicalIndex)) |
| return idma_channel_map[i].channel; |
| } |
| return -EINVAL; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::setupCommitFromDisplayConfig( |
| ExynosDisplayDrmInterface::DrmModeAtomicReq &drmReq, |
| const exynos_win_config_data &config, |
| const uint32_t configIndex, |
| const std::unique_ptr<DrmPlane> &plane, |
| uint32_t &fbId) |
| { |
| ATRACE_CALL(); |
| int ret = NO_ERROR; |
| |
| if (fbId == 0) { |
| if ((ret = mFBManager.getBuffer(config, fbId)) < 0) { |
| HWC_LOGE(mExynosDisplay, "%s:: Failed to get FB, fbId(%d), ret(%d)", __func__, fbId, |
| ret); |
| return ret; |
| } |
| } |
| |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->crtc_property(), mDrmCrtc->id())) < 0) |
| return ret; |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->fb_property(), fbId)) < 0) |
| return ret; |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->crtc_x_property(), config.dst.x)) < 0) |
| return ret; |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->crtc_y_property(), config.dst.y)) < 0) |
| return ret; |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->crtc_w_property(), config.dst.w)) < 0) |
| return ret; |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->crtc_h_property(), config.dst.h)) < 0) |
| return ret; |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->src_x_property(), (int)(config.src.x) << 16)) < 0) |
| return ret; |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->src_y_property(), (int)(config.src.y) << 16)) < 0) |
| HWC_LOGE(mExynosDisplay, "%s:: Failed to add src_y property to plane", |
| __func__); |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->src_w_property(), (int)(config.src.w) << 16)) < 0) |
| return ret; |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->src_h_property(), (int)(config.src.h) << 16)) < 0) |
| return ret; |
| |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->rotation_property(), |
| halTransformToDrmRot(config.transform), true)) < 0) |
| return ret; |
| |
| uint64_t drmEnum = 0; |
| std::tie(drmEnum, ret) = DrmEnumParser::halToDrmEnum(config.blending, mBlendEnums); |
| if (ret < 0) { |
| HWC_LOGE(mExynosDisplay, "Fail to convert blend(%d)", config.blending); |
| return ret; |
| } |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->blend_property(), drmEnum, true)) < 0) |
| return ret; |
| |
| if (plane->zpos_property().id() && !plane->zpos_property().isImmutable()) { |
| uint64_t min_zpos = 0; |
| |
| // Ignore ret and use min_zpos as 0 by default |
| std::tie(std::ignore, min_zpos) = plane->zpos_property().rangeMin(); |
| |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->zpos_property(), configIndex + min_zpos)) < 0) |
| return ret; |
| } |
| |
| if (plane->alpha_property().id()) { |
| uint64_t min_alpha = 0; |
| uint64_t max_alpha = 0; |
| std::tie(std::ignore, min_alpha) = plane->alpha_property().rangeMin(); |
| std::tie(std::ignore, max_alpha) = plane->alpha_property().rangeMax(); |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->alpha_property(), |
| (uint64_t)(((max_alpha - min_alpha) * config.plane_alpha) + 0.5) + min_alpha, true)) < 0) |
| return ret; |
| } |
| |
| if (config.acq_fence >= 0) { |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->in_fence_fd_property(), config.acq_fence)) < 0) |
| return ret; |
| } |
| |
| if (config.state == config.WIN_STATE_COLOR) |
| { |
| if (plane->colormap_property().id()) { |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->colormap_property(), config.color)) < 0) |
| return ret; |
| } else { |
| HWC_LOGE(mExynosDisplay, "colormap property is not supported"); |
| } |
| } |
| |
| std::tie(drmEnum, ret) = DrmEnumParser::halToDrmEnum( |
| config.dataspace & HAL_DATASPACE_STANDARD_MASK, mStandardEnums); |
| if (ret < 0) { |
| HWC_LOGE(mExynosDisplay, "Fail to convert standard(%d)", |
| config.dataspace & HAL_DATASPACE_STANDARD_MASK); |
| return ret; |
| } |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->standard_property(), |
| drmEnum, true)) < 0) |
| return ret; |
| |
| std::tie(drmEnum, ret) = DrmEnumParser::halToDrmEnum( |
| config.dataspace & HAL_DATASPACE_TRANSFER_MASK, mTransferEnums); |
| if (ret < 0) { |
| HWC_LOGE(mExynosDisplay, "Fail to convert transfer(%d)", |
| config.dataspace & HAL_DATASPACE_TRANSFER_MASK); |
| return ret; |
| } |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->transfer_property(), drmEnum, true)) < 0) |
| return ret; |
| |
| std::tie(drmEnum, ret) = DrmEnumParser::halToDrmEnum( |
| config.dataspace & HAL_DATASPACE_RANGE_MASK, mRangeEnums); |
| if (ret < 0) { |
| HWC_LOGE(mExynosDisplay, "Fail to convert range(%d)", |
| config.dataspace & HAL_DATASPACE_RANGE_MASK); |
| return ret; |
| } |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->range_property(), drmEnum, true)) < 0) |
| return ret; |
| |
| if (hasHdrInfo(config.dataspace)) { |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->min_luminance_property(), config.min_luminance)) < 0) |
| return ret; |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->max_luminance_property(), config.max_luminance)) < 0) |
| return ret; |
| } |
| |
| if (config.state == config.WIN_STATE_RCD) { |
| if (plane->block_property().id()) { |
| if (mBlockState != config.block_area) { |
| uint32_t blobId = 0; |
| ret = mDrmDevice->CreatePropertyBlob(&config.block_area, sizeof(config.block_area), |
| &blobId); |
| if (ret || (blobId == 0)) { |
| HWC_LOGE(mExynosDisplay, "Failed to create blocking region blob id=%d, ret=%d", |
| blobId, ret); |
| return ret; |
| } |
| |
| mBlockState.mRegion = config.block_area; |
| if (mBlockState.mBlobId) { |
| drmReq.addOldBlob(mBlockState.mBlobId); |
| } |
| mBlockState.mBlobId = blobId; |
| } |
| |
| if ((ret = drmReq.atomicAddProperty(plane->id(), plane->block_property(), |
| mBlockState.mBlobId)) < 0) { |
| HWC_LOGE(mExynosDisplay, "Failed to set blocking region property %d", ret); |
| return ret; |
| } |
| } |
| } |
| |
| return NO_ERROR; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::setupPartialRegion(DrmModeAtomicReq &drmReq) |
| { |
| if (!mDrmCrtc->partial_region_property().id()) |
| return NO_ERROR; |
| |
| int ret = NO_ERROR; |
| |
| struct decon_frame &update_region = mExynosDisplay->mDpuData.win_update_region; |
| struct drm_clip_rect partial_rect = { |
| static_cast<unsigned short>(update_region.x), |
| static_cast<unsigned short>(update_region.y), |
| static_cast<unsigned short>(update_region.x + update_region.w), |
| static_cast<unsigned short>(update_region.y + update_region.h), |
| }; |
| if ((mPartialRegionState.blob_id == 0) || |
| mPartialRegionState.isUpdated(partial_rect)) |
| { |
| uint32_t blob_id = 0; |
| ret = mDrmDevice->CreatePropertyBlob(&partial_rect, |
| sizeof(partial_rect),&blob_id); |
| if (ret || (blob_id == 0)) { |
| HWC_LOGE(mExynosDisplay, "Failed to create partial region " |
| "blob id=%d, ret=%d", blob_id, ret); |
| return ret; |
| } |
| |
| HDEBUGLOGD(eDebugWindowUpdate, |
| "%s: partial region updated [%d, %d, %d, %d] -> [%d, %d, %d, %d] blob(%d)", |
| mExynosDisplay->mDisplayName.c_str(), |
| mPartialRegionState.partial_rect.x1, |
| mPartialRegionState.partial_rect.y1, |
| mPartialRegionState.partial_rect.x2, |
| mPartialRegionState.partial_rect.y2, |
| partial_rect.x1, |
| partial_rect.y1, |
| partial_rect.x2, |
| partial_rect.y2, |
| blob_id); |
| mPartialRegionState.partial_rect = partial_rect; |
| |
| if (mPartialRegionState.blob_id) |
| drmReq.addOldBlob(mPartialRegionState.blob_id); |
| mPartialRegionState.blob_id = blob_id; |
| } |
| if ((ret = drmReq.atomicAddProperty(mDrmCrtc->id(), |
| mDrmCrtc->partial_region_property(), |
| mPartialRegionState.blob_id)) < 0) { |
| HWC_LOGE(mExynosDisplay, "Failed to set partial region property %d", ret); |
| return ret; |
| } |
| |
| return ret; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::waitVBlank() { |
| drmVBlank vblank; |
| uint32_t high_crtc = (mDrmCrtc->pipe() << DRM_VBLANK_HIGH_CRTC_SHIFT); |
| memset(&vblank, 0, sizeof(vblank)); |
| vblank.request.type = (drmVBlankSeqType)( |
| DRM_VBLANK_RELATIVE | (high_crtc & DRM_VBLANK_HIGH_CRTC_MASK)); |
| vblank.request.sequence = 1; |
| |
| int ret = drmWaitVBlank(mDrmDevice->fd(), &vblank); |
| return ret; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::updateColorSettings(DrmModeAtomicReq &drmReq, uint64_t dqeEnabled) { |
| int ret = NO_ERROR; |
| |
| if (dqeEnabled) { |
| if ((ret = setDisplayColorSetting(drmReq)) != 0) { |
| HWC_LOGE(mExynosDisplay, "Failed to set display color setting"); |
| return ret; |
| } |
| } |
| |
| for (size_t i = 0; i < mExynosDisplay->mDpuData.configs.size(); i++) { |
| exynos_win_config_data& config = mExynosDisplay->mDpuData.configs[i]; |
| if ((config.state == config.WIN_STATE_BUFFER) || |
| (config.state == config.WIN_STATE_COLOR)) { |
| int channelId = 0; |
| if ((channelId = getDeconChannel(config.assignedMPP)) < 0) { |
| HWC_LOGE(mExynosDisplay, "%s:: Failed to get channel id (%d)", |
| __func__, channelId); |
| ret = -EINVAL; |
| return ret; |
| } |
| |
| auto &plane = mDrmDevice->planes().at(channelId); |
| uint32_t solidColor = config.color; |
| if ((ret = setPlaneColorSetting(drmReq, plane, config, solidColor)) != 0) { |
| HWC_LOGE(mExynosDisplay, "Failed to set plane color setting, config[%zu]", i); |
| return ret; |
| } |
| if (config.state == config.WIN_STATE_COLOR && solidColor != config.color) { |
| config.color = solidColor; |
| } |
| } |
| } |
| |
| return ret; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::deliverWinConfigData() |
| { |
| int ret = NO_ERROR; |
| DrmModeAtomicReq drmReq(this); |
| std::unordered_map<uint32_t, uint32_t> planeEnableInfo; |
| android::String8 result; |
| bool hasSecureFrameBuffer = false; |
| bool hasM2mSecureLayerBuffer = false; |
| |
| mFrameCounter++; |
| |
| funcReturnCallback retCallback([&]() { |
| if ((ret == NO_ERROR) && !drmReq.getError()) { |
| mFBManager.flip(hasSecureFrameBuffer, hasM2mSecureLayerBuffer); |
| } else if (ret == -ENOMEM) { |
| ALOGW("OOM, release all cached buffers by FBManager"); |
| mFBManager.releaseAll(); |
| } |
| }); |
| |
| mFBManager.checkShrink(); |
| |
| bool needModesetForReadback = false; |
| if (mExynosDisplay->mDpuData.enable_readback) { |
| if ((ret = setupWritebackCommit(drmReq)) < 0) { |
| HWC_LOGE(mExynosDisplay, "%s:: Failed to setup writeback commit ret(%d)", |
| __func__, ret); |
| return ret; |
| } |
| needModesetForReadback = true; |
| } else { |
| if (mReadbackInfo.mNeedClearReadbackCommit) { |
| if ((ret = clearWritebackCommit(drmReq)) < 0) { |
| HWC_LOGE(mExynosDisplay, "%s: Failed to clear writeback commit ret(%d)", |
| __func__, ret); |
| return ret; |
| } |
| needModesetForReadback = true; |
| } |
| } |
| |
| uint64_t mipi_sync_type = 0; |
| if (mDesiredModeState.needsModeSet()) { |
| if (mExynosDisplay->checkRrCompensationEnabled()) { |
| mipi_sync_type |= |
| 1 << mMipiSyncEnums[toUnderlying(HalMipiSyncType::HAL_MIPI_CMD_SYNC_REFRESH_RATE)]; |
| } |
| |
| if ((ret = setDisplayMode(drmReq, mDesiredModeState.blob_id, mDesiredModeState.mode.id())) < |
| 0) { |
| HWC_LOGE(mExynosDisplay, "%s: Fail to apply display mode", |
| __func__); |
| return ret; |
| } |
| } |
| |
| if ((ret = setupPartialRegion(drmReq)) != NO_ERROR) |
| return ret; |
| |
| uint64_t out_fences[mDrmDevice->crtcs().size()]; |
| if ((ret = drmReq.atomicAddProperty(mDrmCrtc->id(), |
| mDrmCrtc->out_fence_ptr_property(), |
| (uint64_t)&out_fences[mDrmCrtc->pipe()], true)) < 0) { |
| return ret; |
| } |
| |
| for (auto &plane : mDrmDevice->planes()) { |
| planeEnableInfo[plane->id()] = 0; |
| } |
| |
| uint64_t dqeEnable = 1; |
| if (mExynosDisplay->mDpuData.enable_readback && |
| !mExynosDisplay->mDpuData.readback_info.requested_from_service) { |
| dqeEnable = 0; |
| } |
| |
| if ((mDrmCrtc->dqe_enabled_property().id()) && |
| ((ret = drmReq.atomicAddProperty(mDrmCrtc->id(), |
| mDrmCrtc->dqe_enabled_property(), dqeEnable)) < 0)) { |
| HWC_LOGE(mExynosDisplay, "%s: Fail to dqe_enable setting", __func__); |
| return ret; |
| } |
| |
| // Update of color settings could change layer's solid color. So it should |
| // be called before use of layer's solid color. |
| if ((ret = updateColorSettings(drmReq, dqeEnable)) != 0) { |
| HWC_LOGE(mExynosDisplay, "failed to update color settings (%d)", ret); |
| return ret; |
| } |
| |
| for (size_t i = 0; i < mExynosDisplay->mDpuData.configs.size(); i++) { |
| exynos_win_config_data& config = mExynosDisplay->mDpuData.configs[i]; |
| if ((config.state == config.WIN_STATE_BUFFER) || |
| (config.state == config.WIN_STATE_COLOR)) { |
| int channelId = 0; |
| if ((channelId = getDeconChannel(config.assignedMPP)) < 0) { |
| HWC_LOGE(mExynosDisplay, "%s:: Failed to get channel id (%d)", |
| __func__, channelId); |
| ret = -EINVAL; |
| return ret; |
| } |
| /* src size should be set even in dim layer */ |
| if (config.state == config.WIN_STATE_COLOR) { |
| config.src.w = config.dst.w; |
| config.src.h = config.dst.h; |
| } |
| auto &plane = mDrmDevice->planes().at(channelId); |
| uint32_t fbId = 0; |
| if ((ret = setupCommitFromDisplayConfig(drmReq, config, i, plane, fbId)) < 0) { |
| HWC_LOGE(mExynosDisplay, "setupCommitFromDisplayConfig failed, config[%zu]", i); |
| return ret; |
| } |
| hasSecureFrameBuffer |= (isFramebuffer(config.layer) && config.protection); |
| hasM2mSecureLayerBuffer |= (config.protection && config.layer && config.layer->mM2mMPP); |
| /* Set this plane is enabled */ |
| planeEnableInfo[plane->id()] = 1; |
| } |
| } |
| |
| for (size_t i = 0; i < mExynosDisplay->mDpuData.rcdConfigs.size(); ++i) { |
| exynos_win_config_data &config = mExynosDisplay->mDpuData.rcdConfigs[i]; |
| if ((config.state == config.WIN_STATE_RCD) && |
| (mExynosDisplay->mType == HWC_DISPLAY_PRIMARY)) { |
| const int32_t rcdId = static_cast<ExynosPrimaryDisplay *>(mExynosDisplay)->mRcdId; |
| const int32_t channelId = getSpecialChannelId(rcdId); |
| if (channelId >= 0) { |
| auto &plane = mDrmDevice->planes().at(channelId); |
| uint32_t fbId = 0; |
| if ((ret = setupCommitFromDisplayConfig(drmReq, config, i, plane, fbId)) < 0) { |
| HWC_LOGE(mExynosDisplay, "setupCommitFromDisplayConfig failed, config[%zu]", i); |
| } |
| planeEnableInfo[plane->id()] = 1; |
| } |
| } |
| } |
| |
| /* Disable unused plane */ |
| for (auto &plane : mDrmDevice->planes()) { |
| if (planeEnableInfo[plane->id()] == 0) { |
| /* Don't disable planes that are reserved to other display */ |
| ExynosMPP* exynosMPP = mExynosMPPsForPlane[plane->id()]; |
| if ((exynosMPP != NULL) && (mExynosDisplay != NULL) && |
| (exynosMPP->mAssignedState & MPP_ASSIGN_STATE_RESERVED) && |
| (exynosMPP->mReservedDisplay != (int32_t)mExynosDisplay->mDisplayId)) |
| continue; |
| |
| if ((exynosMPP == NULL) && (mExynosDisplay->mType == HWC_DISPLAY_PRIMARY) && |
| (plane->id() != static_cast<ExynosPrimaryDisplay *>(mExynosDisplay)->mRcdId)) |
| continue; |
| |
| /* If this plane is not supported by the CRTC binded with ExynosDisplay, |
| * it should be disabled by this ExynosDisplay */ |
| if (!plane->GetCrtcSupported(*mDrmCrtc)) |
| continue; |
| |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->crtc_property(), 0)) < 0) |
| return ret; |
| |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->fb_property(), 0)) < 0) |
| return ret; |
| } |
| } |
| |
| if (ATRACE_ENABLED()) { |
| mExynosDisplay->traceLayerTypes(); |
| } |
| |
| if (mExynosDisplay->mBrightnessController) { |
| bool ghbmSync, lhbmSync, blSync, opRateSync; |
| bool mixedComposition = mExynosDisplay->isMixedComposition() |
| || mExynosDisplay->isPriorFrameMixedCompostion(); |
| ret = mExynosDisplay->mBrightnessController->prepareFrameCommit(*mExynosDisplay, |
| *mDrmConnector, drmReq, |
| mixedComposition, ghbmSync, |
| lhbmSync, blSync, |
| opRateSync); |
| if (ret < 0) { |
| HWC_LOGE(mExynosDisplay, "%s: Fail to config brightness", __func__); |
| } else { |
| if (ghbmSync) { |
| mipi_sync_type |= |
| 1 << mMipiSyncEnums[toUnderlying(HalMipiSyncType::HAL_MIPI_CMD_SYNC_GHBM)]; |
| } |
| if (lhbmSync) { |
| mipi_sync_type |= |
| 1 << mMipiSyncEnums[toUnderlying(HalMipiSyncType::HAL_MIPI_CMD_SYNC_LHBM)]; |
| } |
| if (blSync) { |
| mipi_sync_type |= |
| 1 << mMipiSyncEnums[toUnderlying(HalMipiSyncType::HAL_MIPI_CMD_SYNC_BL)]; |
| } |
| if (opRateSync) { |
| mipi_sync_type |= 1 |
| << mMipiSyncEnums[toUnderlying(HalMipiSyncType::HAL_MIPI_CMD_SYNC_OP_RATE)]; |
| } |
| } |
| } |
| |
| uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK; |
| if (needModesetForReadback || !mDesiredModeState.isSeamless()) |
| flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; |
| |
| /* For Histogram */ |
| // TODO: b/300026478 - Skip setDisplayHistogramSetting when multi channel is enabled |
| if (dqeEnable && (ret = setDisplayHistogramSetting(drmReq)) != 0) { |
| HWC_LOGE(mExynosDisplay, "Failed to set display histogram setting (%d)", ret); |
| } |
| |
| /* For multichannel histogram */ |
| if (dqeEnable && mExynosDisplay->mHistogramController) { |
| mExynosDisplay->mHistogramController->prepareAtomicCommit(drmReq); |
| } |
| |
| if (mDrmConnector->mipi_sync().id() && (mipi_sync_type != 0)) { |
| // skip mipi sync in Doze mode |
| bool inDoze = isDozeModeAvailable() && mDozeDrmMode.id() == mActiveModeState.mode.id(); |
| if (!inDoze) { |
| ATRACE_NAME("mipi_sync"); // mark this commit |
| if ((ret = drmReq.atomicAddProperty(mDrmConnector->id(), |
| mDrmConnector->mipi_sync(), |
| mipi_sync_type)) < 0) { |
| HWC_LOGE(mExynosDisplay, "%s: Fail to set mipi_sync property (%d)", __func__, ret); |
| } |
| } |
| } |
| |
| auto expectedPresentTime = mExynosDisplay->getPendingExpectedPresentTime(); |
| if (expectedPresentTime != 0) { |
| /* TODO: don't pass expected present time before we can provide accurate time that desire |
| * refresh rate take effect (b/202346402) |
| */ |
| bool ignoreExpectedPresentTime = false; |
| if (mVsyncCallback.getDesiredVsyncPeriod()) { |
| ignoreExpectedPresentTime = true; |
| |
| /* limit the condition to avoid unexpected early present */ |
| auto desiredVsyncPeriod = mVsyncCallback.getDesiredVsyncPeriod(); |
| auto currentVsyncPeriod = mExynosDisplay->mVsyncPeriod; |
| constexpr auto nsecsPerMs = std::chrono::nanoseconds(1ms).count(); |
| if (currentVsyncPeriod > desiredVsyncPeriod && |
| (((currentVsyncPeriod % desiredVsyncPeriod) < nsecsPerMs) || |
| (desiredVsyncPeriod - (currentVsyncPeriod % desiredVsyncPeriod)) < nsecsPerMs)) { |
| ignoreExpectedPresentTime = false; |
| } |
| } |
| |
| if (!ignoreExpectedPresentTime) { |
| if ((ret = drmReq.atomicAddProperty(mDrmCrtc->id(), |
| mDrmCrtc->expected_present_time_property(), |
| expectedPresentTime)) < 0) { |
| HWC_LOGE(mExynosDisplay, "%s: Fail to set expected_present_time property (%d)", |
| __func__, ret); |
| } |
| } |
| mExynosDisplay->applyExpectedPresentTime(); |
| } |
| |
| if ((ret = drmReq.commit(flags, true)) < 0) { |
| HWC_LOGE(mExynosDisplay, "%s:: Failed to commit pset ret=%d in deliverWinConfigData()\n", |
| __func__, ret); |
| return ret; |
| } |
| |
| mExynosDisplay->mDpuData.retire_fence = (int)out_fences[mDrmCrtc->pipe()]; |
| /* |
| * [HACK] dup retire_fence for each layer's release fence |
| * Do not use hwc_dup because hwc_dup increase usage count of fence treacer |
| * Usage count of this fence is incresed by ExynosDisplay::deliverWinConfigData() |
| */ |
| for (auto &display_config : mExynosDisplay->mDpuData.configs) { |
| if ((display_config.state == display_config.WIN_STATE_BUFFER) || |
| (display_config.state == display_config.WIN_STATE_CURSOR)) { |
| display_config.rel_fence = |
| dup((int)out_fences[mDrmCrtc->pipe()]); |
| } |
| } |
| |
| if (mDesiredModeState.needsModeSet()) { |
| mDesiredModeState.apply(mActiveModeState, drmReq); |
| if (!mActiveModeState.isSeamless()) { |
| mDrmConnector->ResetLpMode(); |
| getLowPowerDrmModeModeInfo(); |
| } |
| mVsyncCallback.setDesiredVsyncPeriod(mActiveModeState.mode.te_period()); |
| /* Enable vsync to check vsync period */ |
| mDrmVSyncWorker.VSyncControl(true); |
| } |
| |
| /* For multichannel histogram */ |
| if (dqeEnable && mExynosDisplay->mHistogramController) { |
| mExynosDisplay->mHistogramController->postAtomicCommit(); |
| } |
| |
| return NO_ERROR; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::clearDisplayMode(DrmModeAtomicReq &drmReq) |
| { |
| int ret = NO_ERROR; |
| |
| if ((ret = drmReq.atomicAddProperty(mDrmConnector->id(), |
| mDrmConnector->crtc_id_property(), 0)) < 0) |
| return ret; |
| |
| if ((ret = drmReq.atomicAddProperty(mDrmCrtc->id(), |
| mDrmCrtc->mode_property(), 0)) < 0) |
| return ret; |
| |
| if ((ret = drmReq.atomicAddProperty(mDrmCrtc->id(), |
| mDrmCrtc->active_property(), 0)) < 0) |
| return ret; |
| |
| return NO_ERROR; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::triggerClearDisplayPlanes() |
| { |
| ATRACE_CALL(); |
| DrmModeAtomicReq drmReq(this); |
| |
| clearDisplayPlanes(drmReq); |
| int ret = NO_ERROR; |
| if ((ret = drmReq.commit(0, true))) { |
| HWC_LOGE(mExynosDisplay, "%s:: Failed to commit pset ret=(%d)\n", |
| __func__, ret); |
| return ret; |
| } |
| return ret; |
| } |
| |
| void ExynosDisplayDrmInterface::setVrrSettings(const VrrSettings_t& vrrSettings) { |
| if (vrrSettings.enabled) { |
| mIsVrrModeSupported = true; |
| mNotifyExpectedPresentHeadsUpNs = vrrSettings.notifyExpectedPresentConfig.HeadsUpNs; |
| mNotifyExpectedPresentTimeoutNs = vrrSettings.notifyExpectedPresentConfig.TimeoutNs; |
| mConfigChangeCallback = vrrSettings.configChangeCallback; |
| } |
| } |
| |
| int32_t ExynosDisplayDrmInterface::clearDisplayPlanes(DrmModeAtomicReq &drmReq) |
| { |
| int ret = NO_ERROR; |
| |
| /* Disable all planes */ |
| for (auto &plane : mDrmDevice->planes()) { |
| /* Do not disable planes that are reserved to other dispaly */ |
| ExynosMPP* exynosMPP = mExynosMPPsForPlane[plane->id()]; |
| if ((exynosMPP != NULL) && (mExynosDisplay != NULL) && |
| (exynosMPP->mAssignedState & MPP_ASSIGN_STATE_RESERVED) && |
| (exynosMPP->mReservedDisplay != (int32_t)mExynosDisplay->mDisplayId)) |
| continue; |
| |
| /* If this plane is not supported by the CRTC binded with ExynosDisplay, |
| * it should be disabled by this ExynosDisplay */ |
| if (!plane->GetCrtcSupported(*mDrmCrtc)) |
| continue; |
| |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->crtc_property(), 0)) < 0) { |
| break; |
| } |
| |
| if ((ret = drmReq.atomicAddProperty(plane->id(), |
| plane->fb_property(), 0)) < 0) { |
| break; |
| } |
| } |
| |
| return ret; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::clearDisplay(bool needModeClear) |
| { |
| ExynosDevice *exynosDevice = mExynosDisplay->mDevice; |
| const bool isAsyncOff = needModeClear && exynosDevice->isDispOffAsyncSupported() && |
| !exynosDevice->hasOtherDisplayOn(mExynosDisplay); |
| int ret = NO_ERROR; |
| DrmModeAtomicReq drmReq(this); |
| |
| ret = clearDisplayPlanes(drmReq); |
| if (ret != NO_ERROR) { |
| HWC_LOGE(mExynosDisplay, "%s: Failed to clear planes", __func__); |
| |
| return ret; |
| } |
| |
| /* Disable readback connector if required */ |
| if (mReadbackInfo.mNeedClearReadbackCommit && |
| !mExynosDisplay->mDpuData.enable_readback) { |
| if ((ret = clearWritebackCommit(drmReq)) < 0) { |
| HWC_LOGE(mExynosDisplay, "%s: Failed to apply writeback", __func__); |
| return ret; |
| } |
| } |
| |
| /* Disable ModeSet */ |
| if (needModeClear && !isAsyncOff) { |
| if ((ret = clearDisplayMode(drmReq)) < 0) { |
| HWC_LOGE(mExynosDisplay, "%s: Failed to apply display mode", __func__); |
| return ret; |
| } |
| } |
| |
| ret = drmReq.commit(DRM_MODE_ATOMIC_ALLOW_MODESET, true); |
| if (ret) { |
| HWC_LOGE(mExynosDisplay, "%s:: Failed to commit pset ret=%d in clearDisplay()\n", |
| __func__, ret); |
| return ret; |
| } |
| |
| /* During async off we're clearing planes within a single refresh cycle |
| * and then offloading display off asynchronously. |
| */ |
| if (isAsyncOff) { |
| if ((ret = clearDisplayMode(drmReq)) < 0) { |
| HWC_LOGE(mExynosDisplay, "%s: Failed to apply display mode", __func__); |
| return ret; |
| } |
| |
| ret = drmReq.commit(DRM_MODE_ATOMIC_ALLOW_MODESET | DRM_MODE_ATOMIC_NONBLOCK, true); |
| if (ret) { |
| HWC_LOGE(mExynosDisplay, "%s:: Failed to commit pset ret=%d in clearDisplay()\n", |
| __func__, ret); |
| return ret; |
| } |
| } |
| |
| if (needModeClear) mActiveModeState.forceModeSet(); |
| |
| return NO_ERROR; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::disableSelfRefresh(uint32_t disable) |
| { |
| return 0; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::setForcePanic() |
| { |
| if (exynosHWCControl.forcePanic == 0) |
| return NO_ERROR; |
| |
| usleep(20000000); |
| |
| FILE *forcePanicFd = fopen(HWC_FORCE_PANIC_PATH, "w"); |
| if (forcePanicFd == NULL) { |
| ALOGW("%s:: Failed to open fd", __func__); |
| return -1; |
| } |
| |
| int val = 1; |
| fwrite(&val, sizeof(int), 1, forcePanicFd); |
| fclose(forcePanicFd); |
| |
| return 0; |
| } |
| |
| ExynosDisplayDrmInterface::DrmModeAtomicReq::DrmModeAtomicReq(ExynosDisplayDrmInterface *displayInterface) |
| : mDrmDisplayInterface(displayInterface) |
| { |
| mPset = drmModeAtomicAlloc(); |
| mSavedPset = NULL; |
| } |
| |
| ExynosDisplayDrmInterface::DrmModeAtomicReq::~DrmModeAtomicReq() |
| { |
| if (mError != 0) { |
| android::String8 result; |
| result.appendFormat("atomic commit error\n"); |
| if (hwcCheckDebugMessages(eDebugDisplayInterfaceConfig) == false) |
| dumpAtomicCommitInfo(result); |
| HWC_LOGE(mDrmDisplayInterface->mExynosDisplay, "%s", result.c_str()); |
| } |
| |
| if(mPset) |
| drmModeAtomicFree(mPset); |
| |
| if (destroyOldBlobs() != NO_ERROR) |
| HWC_LOGE(mDrmDisplayInterface->mExynosDisplay, "destroy blob error"); |
| } |
| |
| int32_t ExynosDisplayDrmInterface::DrmModeAtomicReq::atomicAddProperty( |
| const uint32_t id, |
| const DrmProperty &property, |
| uint64_t value, bool optional) |
| { |
| if (!optional && !property.id()) { |
| HWC_LOGE(mDrmDisplayInterface->mExynosDisplay, "%s:: %s property id(%d) for id(%d) is not available", |
| __func__, property.name().c_str(), property.id(), id); |
| return -EINVAL; |
| } |
| |
| if (property.id() && property.validateChange(value)) { |
| int ret = drmModeAtomicAddProperty(mPset, id, |
| property.id(), value); |
| if (ret < 0) { |
| HWC_LOGE(mDrmDisplayInterface->mExynosDisplay, "%s:: Failed to add property %d(%s) for id(%d), ret(%d)", |
| __func__, property.id(), property.name().c_str(), id, ret); |
| return ret; |
| } |
| } |
| |
| return NO_ERROR; |
| } |
| |
| String8& ExynosDisplayDrmInterface::DrmModeAtomicReq::dumpAtomicCommitInfo( |
| String8 &result, bool debugPrint) |
| { |
| /* print log only if eDebugDisplayInterfaceConfig flag is set when debugPrint is true */ |
| if (debugPrint && |
| (hwcCheckDebugMessages(eDebugDisplayInterfaceConfig) == false)) |
| return result; |
| |
| if (debugPrint) |
| ALOGD("%s atomic config ++++++++++++", mDrmDisplayInterface->mExynosDisplay->mDisplayName.c_str()); |
| |
| for (int i = 0; i < drmModeAtomicGetCursor(mPset); i++) { |
| const DrmProperty *property = NULL; |
| String8 objectName; |
| /* Check crtc properties */ |
| if (mPset->items[i].object_id == mDrmDisplayInterface->mDrmCrtc->id()) { |
| for (auto property_ptr : mDrmDisplayInterface->mDrmCrtc->properties()) { |
| if (mPset->items[i].property_id == property_ptr->id()){ |
| property = property_ptr; |
| objectName.appendFormat("Crtc"); |
| break; |
| } |
| } |
| if (property == NULL) { |
| HWC_LOGE(mDrmDisplayInterface->mExynosDisplay, |
| "%s:: object id is crtc but there is no matched property", |
| __func__); |
| } |
| } else if (mPset->items[i].object_id == mDrmDisplayInterface->mDrmConnector->id()) { |
| for (auto property_ptr : mDrmDisplayInterface->mDrmConnector->properties()) { |
| if (mPset->items[i].property_id == property_ptr->id()){ |
| property = property_ptr; |
| objectName.appendFormat("Connector"); |
| break; |
| } |
| } |
| if (property == NULL) { |
| HWC_LOGE(mDrmDisplayInterface->mExynosDisplay, |
| "%s:: object id is connector but there is no matched property", |
| __func__); |
| } |
| } else { |
| uint32_t channelId = 0; |
| for (auto &plane : mDrmDisplayInterface->mDrmDevice->planes()) { |
| if (mPset->items[i].object_id == plane->id()) { |
| for (auto property_ptr : plane->properties()) { |
| if (mPset->items[i].property_id == property_ptr->id()){ |
| property = property_ptr; |
| objectName.appendFormat("Plane[%d]", channelId); |
| break; |
| } |
| } |
| if (property == NULL) { |
| HWC_LOGE(mDrmDisplayInterface->mExynosDisplay, |
| "%s:: object id is plane but there is no matched property", |
| __func__); |
| } |
| } |
| channelId++; |
| } |
| } |
| if (property == NULL) { |
| HWC_LOGE(mDrmDisplayInterface->mExynosDisplay, |
| "%s:: Fail to get property[%d] (object_id: %d, property_id: %d, value: %" PRId64 ")", |
| __func__, i, mPset->items[i].object_id, mPset->items[i].property_id, |
| mPset->items[i].value); |
| continue; |
| } |
| |
| if (debugPrint) |
| ALOGD("property[%d] %s object_id: %d, property_id: %d, name: %s, value: %" PRId64 ")\n", |
| i, objectName.c_str(), mPset->items[i].object_id, mPset->items[i].property_id, property->name().c_str(), mPset->items[i].value); |
| else |
| result.appendFormat("property[%d] %s object_id: %d, property_id: %d, name: %s, value: %" PRId64 ")\n", |
| i, objectName.c_str(), mPset->items[i].object_id, mPset->items[i].property_id, property->name().c_str(), mPset->items[i].value); |
| } |
| return result; |
| } |
| |
| int ExynosDisplayDrmInterface::DrmModeAtomicReq::commit(uint32_t flags, bool loggingForDebug) |
| { |
| ATRACE_NAME("drmModeAtomicCommit"); |
| android::String8 result; |
| |
| /* |
| * During kernel is in TUI, all atomic commits should be returned with error EPERM(-1). |
| * To avoid handling atomic commit as fail, it needs to check TUI status. |
| */ |
| int ret = drmModeAtomicCommit(mDrmDisplayInterface->mDrmDevice->fd(), |
| mPset, flags, mDrmDisplayInterface->mDrmDevice); |
| if (loggingForDebug) |
| dumpAtomicCommitInfo(result, true); |
| if ((ret == -EPERM) && mDrmDisplayInterface->mDrmDevice->event_listener()->IsDrmInTUI()) { |
| ALOGV("skip atomic commit error handling as kernel is in TUI"); |
| ret = NO_ERROR; |
| } else if (ret < 0) { |
| if (ret == -EINVAL) { |
| dumpDrmAtomicCommitMessage(ret); |
| } |
| HWC_LOGE(mDrmDisplayInterface->mExynosDisplay, "commit error: %d", ret); |
| setError(ret); |
| } |
| |
| if (ret == 0 && mAckCallback) { |
| if (!(flags & DRM_MODE_ATOMIC_TEST_ONLY)) { |
| mAckCallback(); |
| } |
| } |
| |
| return ret; |
| } |
| |
| void ExynosDisplayDrmInterface::DrmModeAtomicReq::dumpDrmAtomicCommitMessage(int err) { |
| const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); |
| const nsecs_t diffMs = ns2ms(now - mDrmDisplayInterface->mLastDumpDrmAtomicMessageTime); |
| if (diffMs < kAllowDumpDrmAtomicMessageTimeMs) { |
| return; |
| } |
| |
| if (writeIntToKernelFile(kDrmModuleParametersDebugNode, kEnableDrmAtomicMessage)) { |
| return; |
| } |
| |
| HWC_LOGE(mDrmDisplayInterface->mExynosDisplay, |
| "commit error, enable atomic message and test again"); |
| int ret = drmModeAtomicCommit(mDrmDisplayInterface->mDrmDevice->fd(), mPset, |
| DRM_MODE_ATOMIC_TEST_ONLY, mDrmDisplayInterface->mDrmDevice); |
| if (ret != err) { |
| HWC_LOGE(mDrmDisplayInterface->mExynosDisplay, |
| "re-try commit error(%d) is different from %d", ret, err); |
| } |
| |
| writeIntToKernelFile(kDrmModuleParametersDebugNode, kDisableDrmDebugMessage); |
| mDrmDisplayInterface->mLastDumpDrmAtomicMessageTime = systemTime(SYSTEM_TIME_MONOTONIC); |
| } |
| |
| int32_t ExynosDisplayDrmInterface::getReadbackBufferAttributes( |
| int32_t* /*android_pixel_format_t*/ outFormat, |
| int32_t* /*android_dataspace_t*/ outDataspace) |
| { |
| DrmConnector *writeback_conn = mReadbackInfo.getWritebackConnector(); |
| if (writeback_conn == NULL) { |
| ALOGE("%s: There is no writeback connection", __func__); |
| return -EINVAL; |
| } |
| mReadbackInfo.pickFormatDataspace(); |
| if (mReadbackInfo.mReadbackFormat == |
| HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) { |
| ALOGE("readback format(%d) is not valid", |
| mReadbackInfo.mReadbackFormat); |
| return -EINVAL; |
| } |
| *outFormat = mReadbackInfo.mReadbackFormat; |
| *outDataspace = HAL_DATASPACE_UNKNOWN; |
| return NO_ERROR; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::setupWritebackCommit(DrmModeAtomicReq &drmReq) |
| { |
| int ret = NO_ERROR; |
| DrmConnector *writeback_conn = mReadbackInfo.getWritebackConnector(); |
| if (writeback_conn == NULL) { |
| ALOGE("%s: There is no writeback connection", __func__); |
| return -EINVAL; |
| } |
| if (writeback_conn->writeback_fb_id().id() == 0 || |
| writeback_conn->writeback_out_fence().id() == 0) { |
| ALOGE("%s: Writeback properties don't exit", __func__); |
| return -EINVAL; |
| } |
| |
| uint32_t writeback_fb_id = 0; |
| exynos_win_config_data writeback_config; |
| VendorGraphicBufferMeta gmeta(mExynosDisplay->mDpuData.readback_info.handle); |
| |
| writeback_config.state = exynos_win_config_data::WIN_STATE_BUFFER; |
| writeback_config.format = mReadbackInfo.mReadbackFormat; |
| writeback_config.src = {0, 0, mExynosDisplay->mXres, mExynosDisplay->mYres, |
| gmeta.stride, gmeta.vstride}; |
| writeback_config.dst = {0, 0, mExynosDisplay->mXres, mExynosDisplay->mYres, |
| gmeta.stride, gmeta.vstride}; |
| writeback_config.fd_idma[0] = gmeta.fd; |
| writeback_config.fd_idma[1] = gmeta.fd1; |
| writeback_config.fd_idma[2] = gmeta.fd2; |
| if ((ret = mFBManager.getBuffer(writeback_config, writeback_fb_id)) < 0) { |
| ALOGE("%s: getBuffer() fail ret(%d)", __func__, ret); |
| return ret; |
| } |
| |
| if ((ret = drmReq.atomicAddProperty(writeback_conn->id(), |
| writeback_conn->writeback_fb_id(), |
| writeback_fb_id)) < 0) |
| return ret; |
| |
| if ((ret = drmReq.atomicAddProperty(writeback_conn->id(), |
| writeback_conn->writeback_out_fence(), |
| (uint64_t)& mExynosDisplay->mDpuData.readback_info.acq_fence)) < 0) |
| return ret; |
| |
| if ((ret = drmReq.atomicAddProperty(writeback_conn->id(), |
| writeback_conn->crtc_id_property(), |
| mDrmCrtc->id())) < 0) |
| return ret; |
| |
| mReadbackInfo.setFbId(writeback_fb_id); |
| mReadbackInfo.mNeedClearReadbackCommit = true; |
| return NO_ERROR; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::clearWritebackCommit(DrmModeAtomicReq &drmReq) |
| { |
| int ret; |
| |
| DrmConnector *writeback_conn = mReadbackInfo.getWritebackConnector(); |
| if (writeback_conn == NULL) { |
| ALOGE("%s: There is no writeback connection", __func__); |
| return -EINVAL; |
| } |
| |
| if ((ret = drmReq.atomicAddProperty(writeback_conn->id(), |
| writeback_conn->writeback_fb_id(), 0)) < 0) |
| return ret; |
| |
| if ((ret = drmReq.atomicAddProperty(writeback_conn->id(), |
| writeback_conn->writeback_out_fence(), 0)) < 0) |
| return ret; |
| |
| if ((ret = drmReq.atomicAddProperty(writeback_conn->id(), |
| writeback_conn->crtc_id_property(), 0)) < 0) |
| return ret; |
| |
| mReadbackInfo.mNeedClearReadbackCommit = false; |
| return NO_ERROR; |
| } |
| |
| void ExynosDisplayDrmInterface::DrmReadbackInfo::init(DrmDevice *drmDevice, uint32_t displayId) |
| { |
| mDrmDevice = drmDevice; |
| mWritebackConnector = mDrmDevice->AvailableWritebackConnector(displayId); |
| if (mWritebackConnector == NULL) { |
| ALOGI("writeback is not supported"); |
| return; |
| } |
| if (mWritebackConnector->writeback_fb_id().id() == 0 || |
| mWritebackConnector->writeback_out_fence().id() == 0) { |
| ALOGE("%s: Writeback properties don't exit", __func__); |
| mWritebackConnector = NULL; |
| return; |
| } |
| |
| if (mWritebackConnector->writeback_pixel_formats().id()) { |
| int32_t ret = NO_ERROR; |
| uint64_t blobId; |
| std::tie(ret, blobId) = mWritebackConnector->writeback_pixel_formats().value(); |
| if (ret) { |
| ALOGE("Fail to get blob id for writeback_pixel_formats"); |
| return; |
| } |
| drmModePropertyBlobPtr blob = drmModeGetPropertyBlob(mDrmDevice->fd(), blobId); |
| if (!blob) { |
| ALOGE("Fail to get blob for writeback_pixel_formats(%" PRId64 ")", blobId); |
| return; |
| } |
| uint32_t formatNum = (blob->length)/sizeof(uint32_t); |
| uint32_t *formats = (uint32_t *)blob->data; |
| for (uint32_t i = 0; i < formatNum; i++) { |
| int halFormat = drmFormatToHalFormat(formats[i]); |
| ALOGD("supported writeback format[%d] %4.4s, %d", i, (char *)&formats[i], halFormat); |
| if (halFormat != HAL_PIXEL_FORMAT_EXYNOS_UNDEFINED) |
| mSupportedFormats.push_back(halFormat); |
| } |
| drmModeFreePropertyBlob(blob); |
| } |
| } |
| |
| void ExynosDisplayDrmInterface::DrmReadbackInfo::pickFormatDataspace() |
| { |
| if (!mSupportedFormats.empty()) |
| mReadbackFormat = mSupportedFormats[0]; |
| auto it = std::find(mSupportedFormats.begin(), |
| mSupportedFormats.end(), PREFERRED_READBACK_FORMAT); |
| if (it != mSupportedFormats.end()) |
| mReadbackFormat = *it; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::getDisplayFakeEdid(uint8_t &outPort, uint32_t &outDataSize, |
| uint8_t *outData) { |
| uint32_t width = mExynosDisplay->mXres; |
| uint32_t height = mExynosDisplay->mYres; |
| uint32_t clock = (width * height * kDefaultRefreshRateFrequency) / 10000; |
| std::array<uint8_t, 128> edid_buf{ |
| 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, /* header */ |
| 0x1C, 0xEC, /* manufacturer GGL */ |
| 0x00, 0x00, /* product */ |
| 0x00, 0x00, 0x00, 0x00, /* serial number */ |
| 0x01, /* week of manufacture */ |
| 0x00, /* year of manufacture */ |
| 0x01, 0x03, /* EDID version */ |
| 0x80, /* capabilities - digital */ |
| 0x00, /* horizontal in cm */ |
| 0x00, /* vertical in cm */ |
| 0x78, /* gamma 2.2 */ |
| 0xEE, 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54, /* chromaticity */ |
| 0x00, 0x00, 0x00, /* no default timings */ |
| /* no standard timings */ |
| 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, |
| 0x01, 0x01, |
| /* descriptor block 1 */ |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, |
| /* descriptor block 2 */ |
| 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00, 0xC8, 0x00, 0xC8, 0x64, 0x00, 0x0A, 0x20, 0x20, |
| 0x20, 0x20, 0x20, 0x20, |
| /* descriptor block 3 */ |
| 0x00, 0x00, 0x00, 0xFC, 0x00, 'C', 'o', 'm', 'm', 'o', 'n', ' ', 'P', 'a', 'n', 'e', |
| 'l', '\n', |
| /* descriptor block 4 */ |
| 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, /* number of extensions */ |
| 0x00 /* checksum */ |
| }; |
| edid_buf[55] = clock >> 8; |
| edid_buf[56] = width & 0xff; |
| edid_buf[58] = (width >> 4) & 0xf0; |
| edid_buf[59] = height & 0xff; |
| edid_buf[61] = (height >> 4) & 0xf0; |
| |
| if (mMonitorDescription[0] != 0) { |
| /* Descriptor block 3 starts at address 90, data offset is 5 bytes */ |
| memcpy(&edid_buf[95], mMonitorDescription.data(), mMonitorDescription.size()); |
| } |
| |
| unsigned int sum = std::accumulate(edid_buf.begin(), edid_buf.end() - 1, 0); |
| edid_buf[127] = (0x100 - (sum & 0xFF)) & 0xFF; |
| if (outData) { |
| outDataSize = std::min<uint32_t>(outDataSize, edid_buf.size()); |
| memcpy(outData, edid_buf.data(), outDataSize); |
| } else { |
| outDataSize = static_cast<uint32_t>(edid_buf.size()); |
| } |
| |
| outPort = mExynosDisplay->mDisplayId; |
| ALOGD("using Display Fake Edid"); |
| return HWC2_ERROR_NONE; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::getDisplayIdentificationData( |
| uint8_t* outPort, uint32_t* outDataSize, uint8_t* outData) |
| { |
| if ((mDrmDevice == nullptr) || (mDrmConnector == nullptr)) { |
| ALOGE("%s: display(%s) mDrmDevice(%p), mDrmConnector(%p)", |
| __func__, mExynosDisplay->mDisplayName.c_str(), |
| mDrmDevice, mDrmConnector); |
| return HWC2_ERROR_UNSUPPORTED; |
| } |
| |
| if (mDrmConnector->edid_property().id() == 0) { |
| ALOGD("%s: edid_property is not supported", |
| mExynosDisplay->mDisplayName.c_str()); |
| return HWC2_ERROR_UNSUPPORTED; |
| } |
| |
| if (outPort == nullptr || outDataSize == nullptr) return HWC2_ERROR_BAD_PARAMETER; |
| |
| drmModePropertyBlobPtr blob; |
| int ret; |
| uint64_t blobId; |
| |
| std::tie(ret, blobId) = mDrmConnector->edid_property().value(); |
| if (ret) { |
| ALOGE("Failed to get edid property value."); |
| return HWC2_ERROR_UNSUPPORTED; |
| } |
| if (blobId == 0) { |
| ALOGD("%s: edid_property is supported but blob is not valid", |
| mExynosDisplay->mDisplayName.c_str()); |
| return getDisplayFakeEdid(*outPort, *outDataSize, outData); |
| } |
| |
| blob = drmModeGetPropertyBlob(mDrmDevice->fd(), blobId); |
| if (blob == nullptr) { |
| ALOGD("%s: Failed to get blob", |
| mExynosDisplay->mDisplayName.c_str()); |
| return HWC2_ERROR_UNSUPPORTED; |
| } |
| |
| if (outData) { |
| *outDataSize = std::min(*outDataSize, blob->length); |
| memcpy(outData, blob->data, *outDataSize); |
| } else { |
| *outDataSize = blob->length; |
| } |
| drmModeFreePropertyBlob(blob); |
| *outPort = mDrmConnector->id(); |
| |
| return HWC2_ERROR_NONE; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::getSpecialChannelId(uint32_t planeId) { |
| ExynosDevice *exynosDevice = mExynosDisplay->mDevice; |
| for (int i = 0; i < exynosDevice->getSpecialPlaneNum(); i++) { |
| const int32_t channelId = exynosDevice->getSpecialPlaneId(i); |
| auto &plane = mDrmDevice->planes().at(channelId); |
| if (plane->id() == planeId) return channelId; |
| } |
| |
| ALOGE("%s: Failed to get RCD planeId.", __func__); |
| |
| return -EINVAL; |
| } |
| |
| bool ExynosDisplayDrmInterface::readHotplugStatus() { |
| if (mDrmConnector == nullptr) { |
| return false; |
| } |
| |
| uint32_t numConfigs; |
| getDisplayConfigs(&numConfigs, NULL); |
| |
| return (mDrmConnector->state() == DRM_MODE_CONNECTED); |
| } |
| |
| void ExynosDisplayDrmInterface::retrievePanelFullResolution() { |
| std::lock_guard<std::recursive_mutex> lock(mDrmConnector->modesLock()); |
| |
| // The largest resolution in the modes of mDrmConnector is the panel full resolution. |
| for (auto it = mDrmConnector->modes().begin(); it != mDrmConnector->modes().end(); it++) { |
| if (it->h_display() * it->v_display() > |
| mPanelFullResolutionHSize * mPanelFullResolutionVSize) { |
| mPanelFullResolutionHSize = it->h_display(); |
| mPanelFullResolutionVSize = it->v_display(); |
| } |
| } |
| |
| if (mPanelFullResolutionHSize <= 0 || mPanelFullResolutionVSize <= 0) { |
| ALOGE("%s: failed to get panel full resolution", __func__); |
| } else { |
| ALOGI("%s: panel full resolution: (%dx%d)", __func__, mPanelFullResolutionHSize, |
| mPanelFullResolutionVSize); |
| } |
| } |
| |
| int32_t ExynosDisplayDrmInterface::setDisplayHistogramChannelSetting( |
| ExynosDisplayDrmInterface::DrmModeAtomicReq &drmReq, uint8_t channelId, void *blobData, |
| size_t blobLength) { |
| int ret = NO_ERROR; |
| uint32_t blobId = 0; |
| |
| ATRACE_NAME(String8::format("%s #%u", __func__, channelId).c_str()); |
| |
| const DrmProperty &prop = mDrmCrtc->histogram_channel_property(channelId); |
| if (!prop.id()) { |
| ALOGE("Unsupported multi-channel histrogram for channel:%d", channelId); |
| return -ENOTSUP; |
| } |
| |
| ret = mDrmDevice->CreatePropertyBlob(blobData, blobLength, &blobId); |
| if (ret) { |
| HWC_LOGE(mExynosDisplay, "Failed to create histogram channel(%d) blob %d", channelId, ret); |
| return ret; |
| } |
| |
| if ((ret = drmReq.atomicAddProperty(mDrmCrtc->id(), prop, blobId)) < 0) { |
| HWC_LOGE(mExynosDisplay, "%s: Failed to add property", __func__); |
| return ret; |
| } |
| |
| // TODO: b/295794044 - Clear the old histogram channel blob |
| |
| return ret; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::clearDisplayHistogramChannelSetting( |
| ExynosDisplayDrmInterface::DrmModeAtomicReq &drmReq, uint8_t channelId) { |
| int ret = NO_ERROR; |
| |
| ATRACE_NAME(String8::format("%s #%u", __func__, channelId).c_str()); |
| |
| const DrmProperty &prop = mDrmCrtc->histogram_channel_property(channelId); |
| if (!prop.id()) { |
| ALOGE("Unsupported multi-channel histrogram for channel:%d", channelId); |
| return -ENOTSUP; |
| } |
| |
| if ((ret = drmReq.atomicAddProperty(mDrmCrtc->id(), prop, 0)) < 0) { |
| HWC_LOGE(mExynosDisplay, "%s: Failed to add property", __func__); |
| return ret; |
| } |
| |
| // TODO: b/295794044 - Clear the old histogram channel blob |
| |
| return ret; |
| } |
| |
| int32_t ExynosDisplayDrmInterface::sendHistogramChannelIoctl(HistogramChannelIoctl_t control, |
| uint8_t channelId) const { |
| ALOGE("%s: kernel doesn't support multi channel histogram ioctl", __func__); |
| return INVALID_OPERATION; |
| } |
| |
| static constexpr auto kDpHotplugErrorCodeSysfsPath = |
| "/sys/devices/platform/110f0000.drmdp/drm-displayport/dp_hotplug_error_code"; |
| |
| int ExynosDisplayDrmInterface::readHotplugErrorCode() { |
| if (mExynosDisplay->mType != HWC_DISPLAY_EXTERNAL) return 0; |
| int hotplug_error_code = 0; |
| std::ifstream ifs(kDpHotplugErrorCodeSysfsPath); |
| if (ifs.is_open()) ifs >> hotplug_error_code; |
| return hotplug_error_code; |
| } |
| |
| void ExynosDisplayDrmInterface::resetHotplugErrorCode() { |
| if (mExynosDisplay->mType != HWC_DISPLAY_EXTERNAL) return; |
| std::ofstream ofs(kDpHotplugErrorCodeSysfsPath); |
| if (ofs.is_open()) ofs << "0"; |
| } |