diff options
author | Xin Li <delphij@google.com> | 2024-03-06 09:30:00 -0800 |
---|---|---|
committer | Xin Li <delphij@google.com> | 2024-03-06 09:30:00 -0800 |
commit | b00b1a03678c6ac7792ab34327e989ab3c634bbd (patch) | |
tree | e004f1a9b525df54a6078e9327258a69f66978b2 | |
parent | 0c73da6ad5371340e6dc915647ec544b2e6e3f30 (diff) | |
parent | ba9c7c27619716648bb47bba27e9bca4d3596f64 (diff) | |
download | common-master.tar.gz |
Bug: 319669529
Merged-In: I64dec227d53882b7ef2d878430a81f80557a6468
Change-Id: If03895348218e16761267293a77a093dcdf4d920
61 files changed, 2979 insertions, 747 deletions
diff --git a/gralloc-headers/Android.bp b/gralloc-headers/Android.bp index b87e472..c596301 100644 --- a/gralloc-headers/Android.bp +++ b/gralloc-headers/Android.bp @@ -6,15 +6,23 @@ cc_library_headers { name: "pixel-gralloc-headers", // TODO(270442578): Change to vendor: true vendor_available: true, + defaults: [ + "android.hardware.graphics.common-ndk_shared", + ], export_include_dirs: [ ".", ], shared_libs: [ - "android.hardware.graphics.common-V4-ndk", + "android.hardware.graphics.mapper@4.0", "libgralloctypes", ], visibility: [ "//visibility:public", ], -} + // should be platform available since this header is used in lib_aion_buffer which is platform-available + apex_available: [ + "//apex_available:platform", + "//apex_available:anyapex", + ], +} diff --git a/gralloc-headers/pixel-gralloc/format.h b/gralloc-headers/pixel-gralloc/format.h index 4463067..2edd3ad 100644 --- a/gralloc-headers/pixel-gralloc/format.h +++ b/gralloc-headers/pixel-gralloc/format.h @@ -47,6 +47,7 @@ enum class Format : uint32_t { // Pixel specific formats GOOGLE_NV12 = 0x301, + GOOGLE_R8 = 0x303, }; #undef MapFormat diff --git a/gralloc-headers/pixel-gralloc/mapper.h b/gralloc-headers/pixel-gralloc/mapper.h new file mode 100644 index 0000000..14990a9 --- /dev/null +++ b/gralloc-headers/pixel-gralloc/mapper.h @@ -0,0 +1,85 @@ +#pragma once + +#include <android/hardware/graphics/mapper/4.0/IMapper.h> +#include <log/log.h> + +#include "metadata.h" +#include "utils.h" + +namespace pixel::graphics::mapper { + +namespace { + +using ::aidl::android::hardware::graphics::common::PlaneLayout; +using android::hardware::graphics::mapper::V4_0::Error; +using android::hardware::graphics::mapper::V4_0::IMapper; +using HidlPixelFormat = ::android::hardware::graphics::common::V1_2::PixelFormat; +using namespace ::pixel::graphics; + +android::sp<IMapper> get_mapper() { + static android::sp<IMapper> mapper = []() { + auto mapper = IMapper::getService(); + if (!mapper) { + ALOGE("Failed to get mapper service"); + } + return mapper; + }(); + + return mapper; +} + +} // namespace + +template <MetadataType T> +struct always_false : std::false_type {}; + +template <MetadataType T> +struct ReturnType { + static_assert(always_false<T>::value, "Unspecialized ReturnType is not supported"); + using type = void; +}; + +template <MetadataType T> +static std::optional<typename ReturnType<T>::type> get(buffer_handle_t /*handle*/) { + static_assert(always_false<T>::value, "Unspecialized get is not supported"); + return {}; +} + +// TODO: Add support for stable-c mapper +#define GET(metadata, return_type) \ + template <> \ + struct ReturnType<MetadataType::metadata> { \ + using type = return_type; \ + }; \ + \ + template <> \ + std::optional<typename ReturnType<MetadataType::metadata>::type> get<MetadataType::metadata>( \ + buffer_handle_t handle) { \ + auto mapper = get_mapper(); \ + IMapper::MetadataType type = { \ + .name = kPixelMetadataTypeName, \ + .value = static_cast<int64_t>(MetadataType::metadata), \ + }; \ + \ + android::hardware::hidl_vec<uint8_t> vec; \ + Error error; \ + auto ret = mapper->get(const_cast<native_handle_t*>(handle), type, \ + [&](const auto& tmpError, \ + const android::hardware::hidl_vec<uint8_t>& tmpVec) { \ + error = tmpError; \ + vec = tmpVec; \ + }); \ + if (!ret.isOk()) { \ + return {}; \ + } \ + \ + return utils::decode<return_type>(vec); \ + } + +GET(PLANE_DMA_BUFS, std::vector<int>); +GET(VIDEO_HDR, void*); +GET(VIDEO_ROI, void*); + +#undef GET + +} // namespace pixel::graphics::mapper diff --git a/gralloc-headers/pixel-gralloc/metadata.h b/gralloc-headers/pixel-gralloc/metadata.h index 7502f29..adb44c0 100644 --- a/gralloc-headers/pixel-gralloc/metadata.h +++ b/gralloc-headers/pixel-gralloc/metadata.h @@ -51,11 +51,18 @@ enum class MetadataType : int64_t { // TODO: These metadata queries returns a pointer inside metadata for now. Need to change that // so we are returning proper data only. + // Returns: void* VIDEO_HDR = std::numeric_limits<int64_t>::max() - (1 << 16), // TODO(b/289448426#comment2): ROIINFO is probably not being used. Remove this after // confirmation. + // Returns: void* VIDEO_ROI, + + // This metadata just refers to the same fd contained in buffer handle and not a clone. + // So the client should not attempt to close these fds. + // Returns: std::vector<int> + PLANE_DMA_BUFS, }; #undef MapMetadataType diff --git a/gralloc-headers/pixel-gralloc/usage.h b/gralloc-headers/pixel-gralloc/usage.h index 323e3c2..54a1053 100644 --- a/gralloc-headers/pixel-gralloc/usage.h +++ b/gralloc-headers/pixel-gralloc/usage.h @@ -39,8 +39,18 @@ enum Usage : uint64_t { MapUsage(VENDOR_MASK), MapUsage(VENDOR_MASK_HI), - // Pixel specific usage + // Pixel specific usages + + // Used for AION to allocate PLACEHOLDER buffers + PLACEHOLDER_BUFFER = 1ULL << 28, + NO_COMPRESSION = 1ULL << 29, + + // Used for the camera ISP image heap of the dual PD buffer. + TPU_INPUT = 1ULL << 62, + + // Used to select specific heap for faceauth raw images used for evaluation + FACEAUTH_RAW_EVAL = 1ULL << 63, }; #undef MapUsage diff --git a/gralloc-headers/pixel-gralloc/utils.h b/gralloc-headers/pixel-gralloc/utils.h new file mode 100644 index 0000000..993ca16 --- /dev/null +++ b/gralloc-headers/pixel-gralloc/utils.h @@ -0,0 +1,88 @@ +#pragma once + +#include <cstdint> +#include <cstring> +#include <optional> +#include <type_traits> +#include <vector> + +namespace { + +// Trivial type +template <typename T, std::enable_if_t<std::is_trivially_copyable_v<T>, bool> = true> +std::vector<uint8_t> encode_helper(const T& val) { + auto begin = reinterpret_cast<const uint8_t*>(&val); + auto end = begin + sizeof(val); + return {begin, end}; +} + +// Container type +template <typename Container, + std::enable_if_t<!std::is_trivially_copyable_v<Container>, bool> = true> +std::vector<uint8_t> encode_helper(const Container& val) { + // Check comment in decode_helper below + static_assert(std::is_trivially_copyable_v<typename Container::value_type>, + "Can encode only a containers of trivial types currently"); + + constexpr auto member_size = sizeof(typename Container::value_type); + auto n_bytes = member_size * val.size(); + + std::vector<uint8_t> out(n_bytes); + std::memcpy(out.data(), val.data(), n_bytes); + + return out; +} + +// Trivial type +template <typename T, std::enable_if_t<std::is_trivially_copyable_v<T>, bool> = true> +std::optional<T> decode_helper(const std::vector<uint8_t>& bytes) { + T t; + + if (sizeof(t) != bytes.size()) { + return {}; + } + + std::memcpy(&t, bytes.data(), bytes.size()); + return t; +} + +// Container type +template <typename Container, + std::enable_if_t<!std::is_trivially_copyable_v<Container>, bool> = true> +std::optional<Container> decode_helper(const std::vector<uint8_t>& bytes) { + Container t; + size_t member_size = sizeof(typename Container::value_type); + + // NOTE: This can only reconstruct container of trivial types, not a + // container of non-trivial types. We can either use a standard serializer + // (like protobuf) or roll one of our own simple ones (like prepending size + // of the object), but have to be careful about securing such a serializer. + // But, do we even need that? I do not see any metadata which is either not + // trivial or a container of trivial type. + size_t to_copy = bytes.size(); + if (to_copy % member_size != 0) { + return {}; + } + + size_t members = to_copy / member_size; + t.resize(members); + std::memcpy(t.data(), bytes.data(), to_copy); + return t; +} + +} // namespace + +namespace pixel::graphics::utils { + +// TODO: Setup a fuzzer for encode/decode +template <typename T> +std::vector<uint8_t> encode(const T& val) { + return encode_helper(val); +} + +template <typename T> +std::optional<T> decode(const std::vector<uint8_t>& bytes) { + return decode_helper<T>(bytes); +} + +} // namespace pixel::graphics::utils diff --git a/hwc3/Android.mk b/hwc3/Android.mk index 6358a90..38f9595 100644 --- a/hwc3/Android.mk +++ b/hwc3/Android.mk @@ -32,11 +32,11 @@ LOCAL_CFLAGS += \ -Wthread-safety # hwc3 re-uses hwc2.2 ComposerResource and libexynosdisplay -LOCAL_SHARED_LIBRARIES := android.hardware.graphics.composer3-V2-ndk \ +LOCAL_SHARED_LIBRARIES := android.hardware.graphics.composer3-V3-ndk \ android.hardware.graphics.composer@2.1-resources \ android.hardware.graphics.composer@2.2-resources \ android.hardware.graphics.composer@2.4 \ - com.google.hardware.pixel.display-V9-ndk \ + com.google.hardware.pixel.display-V10-ndk \ libbase \ libbinder \ libbinder_ndk \ @@ -86,6 +86,20 @@ LOCAL_SHARED_LIBRARIES += libExynosHWCService LOCAL_HEADER_LIBRARIES += libbinder_headers endif +ifeq ($(CLANG_COVERAGE),true) +# enable code coverage (these flags are copied from build/soong/cc/coverage.go) +LOCAL_CFLAGS += -fprofile-instr-generate -fcoverage-mapping +LOCAL_CFLAGS += -Wno-frame-larger-than= +LOCAL_WHOLE_STATIC_LIBRARIES += libprofile-clang-extras_ndk +LOCAL_LDFLAGS += -fprofile-instr-generate +LOCAL_LDFLAGS += -Wl,--wrap,open + +ifeq ($(CLANG_COVERAGE_CONTINUOUS_MODE),true) +LOCAL_CFLAGS += -mllvm -runtime-counter-relocation +LOCAL_LDFLAGS += -Wl,-mllvm=-runtime-counter-relocation +endif +endif + LOCAL_VINTF_FRAGMENTS = hwc3-default.xml LOCAL_INIT_RC := hwc3-pixel.rc diff --git a/hwc3/Composer.cpp b/hwc3/Composer.cpp index 4a57d96..40cadc8 100644 --- a/hwc3/Composer.cpp +++ b/hwc3/Composer.cpp @@ -25,6 +25,17 @@ namespace aidl::android::hardware::graphics::composer3::impl { +Composer::Composer() { + int32_t composerInterfaceVersion = 1; + const auto status = getInterfaceVersion(&composerInterfaceVersion); + if (!status.isOk()) { + ALOGE("Get interface version from Composer constructor failed %s", + status.getDescription().c_str()); + } + mHal = IComposerHal::create(composerInterfaceVersion); + CHECK(mHal != nullptr); +} + ndk::ScopedAStatus Composer::createClient(std::shared_ptr<IComposerClient>* outClient) { DEBUG_FUNC(); std::unique_lock<std::mutex> lock(mClientMutex); diff --git a/hwc3/Composer.h b/hwc3/Composer.h index d35c3de..5ea93a1 100644 --- a/hwc3/Composer.h +++ b/hwc3/Composer.h @@ -26,11 +26,11 @@ namespace aidl::android::hardware::graphics::composer3::impl { class Composer : public BnComposer { public: - Composer(std::unique_ptr<IComposerHal> hal) : mHal(std::move(hal)) {} + Composer(); binder_status_t dump(int fd, const char** args, uint32_t numArgs) override; - // compser3 api + // composer3 api ndk::ScopedAStatus createClient(std::shared_ptr<IComposerClient>* client) override; ndk::ScopedAStatus getCapabilities(std::vector<Capability>* caps) override; @@ -41,7 +41,7 @@ private: bool waitForClientDestroyedLocked(std::unique_lock<std::mutex>& lock); void onClientDestroyed(); - const std::unique_ptr<IComposerHal> mHal; + std::unique_ptr<IComposerHal> mHal; std::mutex mClientMutex; bool mClientAlive = false; // GUARDED_BY(mClientMutex) std::condition_variable mClientDestroyedCondition; diff --git a/hwc3/ComposerClient.cpp b/hwc3/ComposerClient.cpp index c34b13a..104bac6 100644 --- a/hwc3/ComposerClient.cpp +++ b/hwc3/ComposerClient.cpp @@ -20,6 +20,7 @@ #include <android-base/logging.h> #include <android/binder_ibinder_platform.h> +#include <hardware/hwcomposer2.h> #include "Util.h" @@ -76,6 +77,21 @@ ndk::ScopedAStatus ComposerClient::createVirtualDisplay(int32_t width, int32_t h return TO_BINDER_STATUS(err); } +ndk::ScopedAStatus ComposerClient::getDisplayConfigurations( + int64_t display, int32_t maxFrameIntervalNs, std::vector<DisplayConfiguration>* configs) { + DEBUG_DISPLAY_FUNC(display); + auto err = mHal->getDisplayConfigurations(display, maxFrameIntervalNs, configs); + return TO_BINDER_STATUS(err); +} + +ndk::ScopedAStatus ComposerClient::notifyExpectedPresent( + int64_t display, const ClockMonotonicTimestamp& expectedPresentTime, + int32_t frameIntervalNs) { + DEBUG_DISPLAY_FUNC(display); + auto err = mHal->notifyExpectedPresent(display, expectedPresentTime, frameIntervalNs); + return TO_BINDER_STATUS(err); +} + ndk::ScopedAStatus ComposerClient::destroyLayer(int64_t display, int64_t layer) { DEBUG_DISPLAY_FUNC(display); auto err = mHal->destroyLayer(display, layer); @@ -171,6 +187,16 @@ ndk::ScopedAStatus ComposerClient::getDisplayCapabilities(int64_t display, caps->push_back(DisplayCapability::DISPLAY_IDLE_TIMER); } + err = mHal->getDisplayMultiThreadedPresentSupport(display, support); + if (err != ::android::OK) { + LOG(ERROR) << "failed to getDisplayMultiThreadedPresentSupport: " << err; + return TO_BINDER_STATUS(err); + } + + if (support) { + caps->push_back(DisplayCapability::MULTI_THREADED_PRESENT); + } + return TO_BINDER_STATUS(err); } diff --git a/hwc3/ComposerClient.h b/hwc3/ComposerClient.h index 7a76fa4..11e0a52 100644 --- a/hwc3/ComposerClient.h +++ b/hwc3/ComposerClient.h @@ -134,6 +134,12 @@ public: ndk::ScopedAStatus setIdleTimerEnabled(int64_t display, int32_t timeout) override; ndk::ScopedAStatus setRefreshRateChangedCallbackDebugEnabled(int64_t /* display */, bool /* enabled */) override; + ndk::ScopedAStatus getDisplayConfigurations( + int64_t display, int32_t maxFrameIntervalNs, + std::vector<DisplayConfiguration>* configs) override; + ndk::ScopedAStatus notifyExpectedPresent(int64_t display, + const ClockMonotonicTimestamp& expectedPresentTime, + int32_t frameIntervalNs) override; protected: ::ndk::SpAIBinder createBinder() override; diff --git a/hwc3/ComposerCommandEngine.cpp b/hwc3/ComposerCommandEngine.cpp index eac4df2..32ad027 100644 --- a/hwc3/ComposerCommandEngine.cpp +++ b/hwc3/ComposerCommandEngine.cpp @@ -51,11 +51,11 @@ namespace aidl::android::hardware::graphics::composer3::impl { } \ } while (0) -#define DISPATCH_DISPLAY_BOOL_COMMAND_AND_DATA(displayCmd, field, data, funcName) \ - do { \ - if (displayCmd.field) { \ - execute##funcName(displayCmd.display, displayCmd.data); \ - } \ +#define DISPATCH_DISPLAY_COMMAND_AND_TWO_DATA(displayCmd, field, data_1, data_2, funcName) \ + do { \ + if (displayCmd.field) { \ + execute##funcName(displayCmd.display, displayCmd.data_1, displayCmd.data_2); \ + } \ } while (0) int32_t ComposerCommandEngine::init() { @@ -104,12 +104,12 @@ void ComposerCommandEngine::dispatchDisplayCommand(const DisplayCommand& command DISPATCH_DISPLAY_COMMAND(command, colorTransformMatrix, SetColorTransform); DISPATCH_DISPLAY_COMMAND(command, clientTarget, SetClientTarget); DISPATCH_DISPLAY_COMMAND(command, virtualDisplayOutputBuffer, SetOutputBuffer); - DISPATCH_DISPLAY_BOOL_COMMAND_AND_DATA(command, validateDisplay, expectedPresentTime, - ValidateDisplay); + DISPATCH_DISPLAY_COMMAND_AND_TWO_DATA(command, validateDisplay, expectedPresentTime, + frameIntervalNs, ValidateDisplay); DISPATCH_DISPLAY_BOOL_COMMAND(command, acceptDisplayChanges, AcceptDisplayChanges); DISPATCH_DISPLAY_BOOL_COMMAND(command, presentDisplay, PresentDisplay); - DISPATCH_DISPLAY_BOOL_COMMAND_AND_DATA(command, presentOrValidateDisplay, expectedPresentTime, - PresentOrValidateDisplay); + DISPATCH_DISPLAY_COMMAND_AND_TWO_DATA(command, presentOrValidateDisplay, expectedPresentTime, + frameIntervalNs, PresentOrValidateDisplay); } void ComposerCommandEngine::dispatchLayerCommand(int64_t display, const LayerCommand& command) { @@ -213,13 +213,15 @@ void ComposerCommandEngine::executeSetOutputBuffer(uint64_t display, const Buffe } void ComposerCommandEngine::executeSetExpectedPresentTimeInternal( - int64_t display, const std::optional<ClockMonotonicTimestamp> expectedPresentTime) { - mHal->setExpectedPresentTime(display, expectedPresentTime); + int64_t display, const std::optional<ClockMonotonicTimestamp> expectedPresentTime, + int frameIntervalNs) { + mHal->setExpectedPresentTime(display, expectedPresentTime, frameIntervalNs); } void ComposerCommandEngine::executeValidateDisplay( - int64_t display, const std::optional<ClockMonotonicTimestamp> expectedPresentTime) { - executeSetExpectedPresentTimeInternal(display, expectedPresentTime); + int64_t display, const std::optional<ClockMonotonicTimestamp> expectedPresentTime, + int frameIntervalNs) { + executeSetExpectedPresentTimeInternal(display, expectedPresentTime, frameIntervalNs); executeValidateDisplayInternal(display); } @@ -233,8 +235,9 @@ void ComposerCommandEngine::executeSetDisplayBrightness(uint64_t display, } void ComposerCommandEngine::executePresentOrValidateDisplay( - int64_t display, const std::optional<ClockMonotonicTimestamp> expectedPresentTime) { - executeSetExpectedPresentTimeInternal(display, expectedPresentTime); + int64_t display, const std::optional<ClockMonotonicTimestamp> expectedPresentTime, + int frameIntervalNs) { + executeSetExpectedPresentTimeInternal(display, expectedPresentTime, frameIntervalNs); // First try to Present as is. auto presentErr = mResources->mustValidateDisplay(display) ? IComposerClient::EX_NOT_VALIDATED : executePresentDisplay(display); diff --git a/hwc3/ComposerCommandEngine.h b/hwc3/ComposerCommandEngine.h index 872c7e5..1ec110e 100644 --- a/hwc3/ComposerCommandEngine.h +++ b/hwc3/ComposerCommandEngine.h @@ -52,9 +52,11 @@ class ComposerCommandEngine { void executeSetDisplayBrightness(uint64_t display, const DisplayBrightness& command); void executeSetOutputBuffer(uint64_t display, const Buffer& buffer); void executeValidateDisplay(int64_t display, - const std::optional<ClockMonotonicTimestamp> expectedPresentTime); + const std::optional<ClockMonotonicTimestamp> expectedPresentTime, + int frameIntervalNs); void executePresentOrValidateDisplay( - int64_t display, const std::optional<ClockMonotonicTimestamp> expectedPresentTime); + int64_t display, const std::optional<ClockMonotonicTimestamp> expectedPresentTime, + int frameIntervalNs); void executeAcceptDisplayChanges(int64_t display); int executePresentDisplay(int64_t display); @@ -94,7 +96,8 @@ class ComposerCommandEngine { int32_t executeValidateDisplayInternal(int64_t display); void executeSetExpectedPresentTimeInternal( - int64_t display, const std::optional<ClockMonotonicTimestamp> expectedPresentTime); + int64_t display, const std::optional<ClockMonotonicTimestamp> expectedPresentTime, + int frameIntervalNs); IComposerHal* mHal; IResourceManager* mResources; diff --git a/hwc3/hwc3-default.xml b/hwc3/hwc3-default.xml index fd9e638..911f7f8 100644 --- a/hwc3/hwc3-default.xml +++ b/hwc3/hwc3-default.xml @@ -1,7 +1,7 @@ <manifest version="1.0" type="device"> <hal format="aidl"> <name>android.hardware.graphics.composer3</name> - <version>2</version> + <version>3</version> <interface> <name>IComposer</name> <instance>default</instance> diff --git a/hwc3/impl/HalImpl.cpp b/hwc3/impl/HalImpl.cpp index d8fe206..37e7d9f 100644 --- a/hwc3/impl/HalImpl.cpp +++ b/hwc3/impl/HalImpl.cpp @@ -30,14 +30,20 @@ using namespace SOC_VERSION; +namespace { + +static constexpr int32_t kMinComposerInterfaceVersionForVrrApi = 3; + +}; + namespace aidl::android::hardware::graphics::composer3::impl { -std::unique_ptr<IComposerHal> IComposerHal::create() { - auto device = std::make_unique<ExynosDeviceModule>(); +std::unique_ptr<IComposerHal> IComposerHal::create(int32_t composerInterfaceVersion) { + bool vrrApiSupported = composerInterfaceVersion >= kMinComposerInterfaceVersionForVrrApi; + auto device = std::make_unique<ExynosDeviceModule>(vrrApiSupported); if (!device) { return nullptr; } - return std::make_unique<HalImpl>(std::move(device)); } @@ -107,9 +113,11 @@ void refreshRateChangedDebug(hwc2_callback_data_t callbackData, hwc2_display_t h h2a::translate(hwcDisplay, display); h2a::translate(hwcVsyncPeriodNanos, vsyncPeriodNanos); + // TODO (b/314527560) Update refreshPeriodNanos for VRR display hal->getEventCallback()->onRefreshRateChangedDebug(RefreshRateChangedDebugData{ .display = display, .vsyncPeriodNanos = vsyncPeriodNanos, + .refreshPeriodNanos = vsyncPeriodNanos, }); } @@ -373,6 +381,67 @@ int32_t HalImpl::getDisplayConfigs(int64_t display, std::vector<int32_t>* config return HWC2_ERROR_NONE; } +int32_t HalImpl::getDisplayConfigurations(int64_t display, int32_t, + std::vector<DisplayConfiguration>* outConfigs) { + ExynosDisplay* halDisplay; + RET_IF_ERR(getHalDisplay(display, halDisplay)); + + std::vector<int32_t> configIds; + RET_IF_ERR(getDisplayConfigs(display, &configIds)); + + for (const auto configId : configIds) { + DisplayConfiguration config; + config.configId = configId; + // Get required display attributes + RET_IF_ERR(getDisplayAttribute(display, configId, DisplayAttribute::WIDTH, &config.width)); + RET_IF_ERR( + getDisplayAttribute(display, configId, DisplayAttribute::HEIGHT, &config.height)); + RET_IF_ERR(getDisplayAttribute(display, configId, DisplayAttribute::VSYNC_PERIOD, + &config.vsyncPeriod)); + RET_IF_ERR(getDisplayAttribute(display, configId, DisplayAttribute::CONFIG_GROUP, + &config.configGroup)); + // Get optional display attributes + int32_t dpiX, dpiY; + auto statusDpiX = getDisplayAttribute(display, configId, DisplayAttribute::DPI_X, &dpiX); + auto statusDpiY = getDisplayAttribute(display, configId, DisplayAttribute::DPI_Y, &dpiY); + // TODO(b/294120341): getDisplayAttribute for DPI should return dots per inch + if (statusDpiX == HWC2_ERROR_NONE && statusDpiY == HWC2_ERROR_NONE) { + config.dpi = {dpiX / 1000.0f, dpiY / 1000.0f}; + } + // Determine whether there is a need to configure VRR. + hwc2_config_t hwcConfigId; + a2h::translate(configId, hwcConfigId); + std::optional<VrrConfig_t> vrrConfig = halDisplay->getVrrConfigs(hwcConfigId); + if (vrrConfig.has_value()) { + // TODO(b/290843234): complete the remaining values within vrrConfig. + VrrConfig hwc3VrrConfig; + VrrConfig::NotifyExpectedPresentConfig notifyExpectedPresentConfig; + hwc3VrrConfig.minFrameIntervalNs = vrrConfig->minFrameIntervalNs; + notifyExpectedPresentConfig.notifyExpectedPresentHeadsUpNs = + vrrConfig->notifyExpectedPresentConfig.HeadsUpNs; + notifyExpectedPresentConfig.notifyExpectedPresentTimeoutNs = + vrrConfig->notifyExpectedPresentConfig.TimeoutNs; + hwc3VrrConfig.notifyExpectedPresentConfig = + std::make_optional(notifyExpectedPresentConfig); + config.vrrConfig = std::make_optional(hwc3VrrConfig); + } + outConfigs->push_back(config); + } + + return HWC2_ERROR_NONE; +} + +int32_t HalImpl::notifyExpectedPresent(int64_t display, + const ClockMonotonicTimestamp& expectedPresentTime, + int32_t frameIntervalNs) { + ExynosDisplay* halDisplay; + RET_IF_ERR(getHalDisplay(display, halDisplay)); + + RET_IF_ERR( + halDisplay->notifyExpectedPresent(expectedPresentTime.timestampNanos, frameIntervalNs)); + return HWC2_ERROR_NONE; +} + int32_t HalImpl::getDisplayConnectionType(int64_t display, DisplayConnectionType* outType) { ExynosDisplay* halDisplay; RET_IF_ERR(getHalDisplay(display, halDisplay)); @@ -1045,7 +1114,8 @@ int32_t HalImpl::validateDisplay(int64_t display, std::vector<int64_t>* outChang } int HalImpl::setExpectedPresentTime( - int64_t display, const std::optional<ClockMonotonicTimestamp> expectedPresentTime) { + int64_t display, const std::optional<ClockMonotonicTimestamp> expectedPresentTime, + int frameIntervalNs) { ExynosDisplay* halDisplay; RET_IF_ERR(getHalDisplay(display, halDisplay)); @@ -1055,7 +1125,7 @@ int HalImpl::setExpectedPresentTime( ALOGW("HalImpl: set expected present time multiple times in one frame"); } - halDisplay->setExpectedPresentTime(expectedPresentTime->timestampNanos); + halDisplay->setExpectedPresentTime(expectedPresentTime->timestampNanos, frameIntervalNs); return HWC2_ERROR_NONE; } diff --git a/hwc3/impl/HalImpl.h b/hwc3/impl/HalImpl.h index 9822ff7..6a0108a 100644 --- a/hwc3/impl/HalImpl.h +++ b/hwc3/impl/HalImpl.h @@ -60,6 +60,11 @@ class HalImpl : public IComposerHal { int32_t getDisplayBrightnessSupport(int64_t display, bool& outSupport) override; int32_t getDisplayCapabilities(int64_t display, std::vector<DisplayCapability>* caps) override; int32_t getDisplayConfigs(int64_t display, std::vector<int32_t>* configs) override; + int32_t getDisplayConfigurations(int64_t display, int32_t maxFrameIntervalNs, + std::vector<DisplayConfiguration>* outConfigs) override; + int32_t notifyExpectedPresent(int64_t display, + const ClockMonotonicTimestamp& expectedPresentTime, + int32_t frameIntervalNs) override; int32_t getDisplayConnectionType(int64_t display, DisplayConnectionType* outType) override; int32_t getDisplayIdentificationData(int64_t display, DisplayIdentification* id) override; int32_t getDisplayName(int64_t display, std::string* outName) override; @@ -154,9 +159,9 @@ class HalImpl : public IComposerHal { std::vector<int32_t>* outRequestMasks, ClientTargetProperty* outClientTargetProperty, DimmingStage* outDimmingStage) override; - int32_t setExpectedPresentTime( - int64_t display, - const std::optional<ClockMonotonicTimestamp> expectedPresentTime) override; + int32_t setExpectedPresentTime(int64_t display, + const std::optional<ClockMonotonicTimestamp> expectedPresentTime, + int frameIntervalNs) override; EventCallback* getEventCallback() { return mEventCallback; } int32_t setRefreshRateChangedCallbackDebugEnabled(int64_t display, bool enabled) override; diff --git a/hwc3/include/IComposerHal.h b/hwc3/include/IComposerHal.h index 93dda20..2387973 100644 --- a/hwc3/include/IComposerHal.h +++ b/hwc3/include/IComposerHal.h @@ -49,6 +49,7 @@ #include <aidl/android/hardware/graphics/composer3/DisplayBrightness.h> #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h> #include <aidl/android/hardware/graphics/composer3/DisplayCommand.h> +#include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h> #include <aidl/android/hardware/graphics/composer3/DisplayConnectionType.h> #include <aidl/android/hardware/graphics/composer3/DisplayContentSample.h> #include <aidl/android/hardware/graphics/composer3/DisplayContentSamplingAttributes.h> @@ -83,6 +84,7 @@ // avoid naming conflict using AidlPixelFormat = aidl::android::hardware::graphics::common::PixelFormat; using AidlNativeHandle = aidl::android::hardware::common::NativeHandle; +using DisplayConfiguration = aidl::android::hardware::graphics::composer3::DisplayConfiguration; namespace aidl::android::hardware::graphics::composer3::impl { @@ -90,25 +92,25 @@ namespace aidl::android::hardware::graphics::composer3::impl { // IComposerClient interface. class IComposerHal { public: - static std::unique_ptr<IComposerHal> create(); - virtual ~IComposerHal() = default; + static std::unique_ptr<IComposerHal> create(int32_t composerInterfaceVersion); + virtual ~IComposerHal() = default; - virtual void getCapabilities(std::vector<Capability>* caps) = 0; - virtual void dumpDebugInfo(std::string* output) = 0; - virtual bool hasCapability(Capability cap) = 0; + virtual void getCapabilities(std::vector<Capability>* caps) = 0; + virtual void dumpDebugInfo(std::string* output) = 0; + virtual bool hasCapability(Capability cap) = 0; - class EventCallback { - public: - virtual ~EventCallback() = default; - virtual void onHotplug(int64_t display, bool connected) = 0; - virtual void onRefresh(int64_t display) = 0; - virtual void onVsync(int64_t display, int64_t timestamp, int32_t vsyncPeriodNanos) = 0; - virtual void onVsyncPeriodTimingChanged(int64_t display, - const VsyncPeriodChangeTimeline& timeline) = 0; - virtual void onVsyncIdle(int64_t display) = 0; - virtual void onSeamlessPossible(int64_t display) = 0; - virtual void onRefreshRateChangedDebug(const RefreshRateChangedDebugData& data) = 0; - }; + class EventCallback { + public: + virtual ~EventCallback() = default; + virtual void onHotplug(int64_t display, bool connected) = 0; + virtual void onRefresh(int64_t display) = 0; + virtual void onVsync(int64_t display, int64_t timestamp, int32_t vsyncPeriodNanos) = 0; + virtual void onVsyncPeriodTimingChanged(int64_t display, + const VsyncPeriodChangeTimeline& timeline) = 0; + virtual void onVsyncIdle(int64_t display) = 0; + virtual void onSeamlessPossible(int64_t display) = 0; + virtual void onRefreshRateChangedDebug(const RefreshRateChangedDebugData& data) = 0; + }; virtual void registerEventCallback(EventCallback* callback) = 0; virtual void unregisterEventCallback() = 0; @@ -132,6 +134,11 @@ class IComposerHal { virtual int32_t getDisplayCapabilities(int64_t display, std::vector<DisplayCapability>* caps) = 0; virtual int32_t getDisplayConfigs(int64_t display, std::vector<int32_t>* configs) = 0; + virtual int32_t getDisplayConfigurations(int64_t display, int32_t maxFrameIntervalNs, + std::vector<DisplayConfiguration>* configs) = 0; + virtual int32_t notifyExpectedPresent(int64_t display, + const ClockMonotonicTimestamp& expectedPresentTime, + int32_t frameIntervalNs) = 0; virtual int32_t getDisplayConnectionType(int64_t display, DisplayConnectionType* outType) = 0; virtual int32_t getDisplayIdentificationData(int64_t display, DisplayIdentification *id) = 0; virtual int32_t getDisplayName(int64_t display, std::string* outName) = 0; @@ -226,7 +233,8 @@ class IComposerHal { ClientTargetProperty* outClientTargetProperty, DimmingStage* outDimmingStage) = 0; virtual int32_t setExpectedPresentTime( - int64_t display, const std::optional<ClockMonotonicTimestamp> expectedPresentTime) = 0; + int64_t display, const std::optional<ClockMonotonicTimestamp> expectedPresentTime, + int frameIntervalNs) = 0; virtual int32_t setIdleTimerEnabled(int64_t display, int32_t timeout) = 0; virtual int32_t getRCDLayerSupport(int64_t display, bool& outSupport) = 0; virtual int32_t setLayerBlockingRegion( diff --git a/hwc3/service.cpp b/hwc3/service.cpp index 9269470..4b5fb1a 100644 --- a/hwc3/service.cpp +++ b/hwc3/service.cpp @@ -40,10 +40,7 @@ int main(int /*argc*/, char* argv[]) { LOG(ERROR) << "Couldn't set SCHED_FIFO: " << errno; } - std::unique_ptr<IComposerHal> halImpl = IComposerHal::create(); - CHECK(halImpl != nullptr); - - std::shared_ptr<Composer> composer = ndk::SharedRefBase::make<Composer>(std::move(halImpl)); + std::shared_ptr<Composer> composer = ndk::SharedRefBase::make<Composer>(); CHECK(composer != nullptr); const std::string instance = std::string() + Composer::descriptor + "/default"; diff --git a/include/displaycolor/displaycolor.h b/include/displaycolor/displaycolor.h index 8cef849..002ee8e 100644 --- a/include/displaycolor/displaycolor.h +++ b/include/displaycolor/displaycolor.h @@ -86,8 +86,10 @@ enum DisplayType { DISPLAY_PRIMARY = 0, /// builtin secondary display DISPLAY_SECONDARY = 1, + /// external display + DISPLAY_EXTERNAL = 2, /// number of display - DISPLAY_MAX = 2, + DISPLAY_MAX = 3, }; enum BrightnessMode { @@ -149,6 +151,7 @@ class IBrightnessTable { * @brief This structure holds data imported from HWC. */ struct DisplayInfo { + DisplayType display_type; std::string panel_name; std::string panel_serial; @@ -362,7 +365,7 @@ struct DisplayScene { float refresh_rate = 60.0f; /// operation rate to switch between hs/ns mode - uint32_t operation_rate; + uint32_t operation_rate = 120; /// hdr layer state on screen HdrLayerState hdr_layer_state = HdrLayerState::kHdrNone; @@ -483,7 +486,9 @@ class IDisplayColorGeneric { * @param table Return brightness table if successful, nullptr if the table is not valid. * @return OK if successful, error otherwise. */ - virtual int GetBrightnessTable(DisplayType display, const IBrightnessTable *&table) const = 0; + virtual int GetBrightnessTable(DisplayType display, + std::unique_ptr<const IBrightnessTable> &table) const = 0; + }; extern "C" { diff --git a/libacryl/Android.mk b/libacryl/Android.mk index 98a51f2..43471e3 100644 --- a/libacryl/Android.mk +++ b/libacryl/Android.mk @@ -42,6 +42,20 @@ ifdef BOARD_LIBACRYL_G2D_HDR_PLUGIN LOCAL_CFLAGS += -DLIBACRYL_G2D_HDR_PLUGIN endif +ifeq ($(CLANG_COVERAGE),true) +# enable code coverage (these flags are copied from build/soong/cc/coverage.go) +LOCAL_CFLAGS += -fprofile-instr-generate -fcoverage-mapping +LOCAL_CFLAGS += -Wno-frame-larger-than= +LOCAL_WHOLE_STATIC_LIBRARIES += libprofile-clang-extras_ndk +LOCAL_LDFLAGS += -fprofile-instr-generate +LOCAL_LDFLAGS += -Wl,--wrap,open + +ifeq ($(CLANG_COVERAGE_CONTINUOUS_MODE),true) +LOCAL_CFLAGS += -mllvm -runtime-counter-relocation +LOCAL_LDFLAGS += -Wl,-mllvm=-runtime-counter-relocation +endif +endif + LOCAL_HEADER_LIBRARIES += google_libacryl_hdrplugin_headers LOCAL_HEADER_LIBRARIES += google_hal_headers LOCAL_HEADER_LIBRARIES += libgralloc_headers diff --git a/libhwc2.1/Android.mk b/libhwc2.1/Android.mk index 5224595..746bb7a 100644 --- a/libhwc2.1/Android.mk +++ b/libhwc2.1/Android.mk @@ -47,6 +47,20 @@ LOCAL_CFLAGS += -DSOC_VERSION=$(soc_ver) LOCAL_CFLAGS += -Wthread-safety LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libdrm +ifeq ($(CLANG_COVERAGE),true) +# enable code coverage (these flags are copied from build/soong/cc/coverage.go) +LOCAL_CFLAGS += -fprofile-instr-generate -fcoverage-mapping +LOCAL_CFLAGS += -Wno-frame-larger-than= +LOCAL_WHOLE_STATIC_LIBRARIES += libprofile-clang-extras_ndk +LOCAL_LDFLAGS += -fprofile-instr-generate +LOCAL_LDFLAGS += -Wl,--wrap,open + +ifeq ($(CLANG_COVERAGE_CONTINUOUS_MODE),true) +LOCAL_CFLAGS += -mllvm -runtime-counter-relocation +LOCAL_LDFLAGS += -Wl,-mllvm=-runtime-counter-relocation +endif +endif + LOCAL_MODULE := libdrmresource LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 LOCAL_LICENSE_CONDITIONS := notice @@ -68,8 +82,8 @@ LOCAL_SHARED_LIBRARIES := liblog libcutils libhardware \ libvendorgraphicbuffer libbinder_ndk \ android.hardware.power-V2-ndk pixel-power-ext-V1-ndk -LOCAL_SHARED_LIBRARIES += android.hardware.graphics.composer3-V2-ndk \ - com.google.hardware.pixel.display-V9-ndk \ +LOCAL_SHARED_LIBRARIES += android.hardware.graphics.composer3-V3-ndk \ + com.google.hardware.pixel.display-V10-ndk \ libbinder_ndk \ libbase \ libpng \ @@ -105,6 +119,7 @@ LOCAL_C_INCLUDES += \ $(TOP)/hardware/google/graphics/common/libhwc2.1/libhwcService \ $(TOP)/hardware/google/graphics/common/libhwc2.1/libdisplayinterface \ $(TOP)/hardware/google/graphics/common/libhwc2.1/libdrmresource/include \ + $(TOP)/hardware/google/graphics/common/libhwc2.1/libvrr \ $(TOP)/hardware/google/graphics/$(soc_ver) LOCAL_SRC_FILES := \ libhwchelper/ExynosHWCHelper.cpp \ @@ -123,6 +138,7 @@ LOCAL_SRC_FILES := \ libdisplayinterface/ExynosDisplayInterface.cpp \ libdisplayinterface/ExynosDeviceDrmInterface.cpp \ libdisplayinterface/ExynosDisplayDrmInterface.cpp \ + libvrr/VariableRefreshRateController.cpp \ pixel-display.cpp \ histogram_mediator.cpp @@ -142,6 +158,20 @@ LOCAL_CFLAGS += -Wno-unused-parameter LOCAL_CFLAGS += -DSOC_VERSION=$(soc_ver) LOCAL_CFLAGS += -Wthread-safety +ifeq ($(CLANG_COVERAGE),true) +# enable code coverage (these flags are copied from build/soong/cc/coverage.go) +LOCAL_CFLAGS += -fprofile-instr-generate -fcoverage-mapping +LOCAL_CFLAGS += -Wno-frame-larger-than= +LOCAL_WHOLE_STATIC_LIBRARIES += libprofile-clang-extras_ndk +LOCAL_LDFLAGS += -fprofile-instr-generate +LOCAL_LDFLAGS += -Wl,--wrap,open + +ifeq ($(CLANG_COVERAGE_CONTINUOUS_MODE),true) +LOCAL_CFLAGS += -mllvm -runtime-counter-relocation +LOCAL_LDFLAGS += -Wl,-mllvm=-runtime-counter-relocation +endif +endif + LOCAL_MODULE := libexynosdisplay LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 LOCAL_LICENSE_CONDITIONS := notice @@ -163,9 +193,9 @@ LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libbinder libexynosdisplay l android.hardware.graphics.composer@2.4 \ android.hardware.graphics.allocator@2.0 \ android.hardware.graphics.mapper@2.0 \ - android.hardware.graphics.composer3-V2-ndk + android.hardware.graphics.composer3-V3-ndk -LOCAL_SHARED_LIBRARIES += com.google.hardware.pixel.display-V9-ndk \ +LOCAL_SHARED_LIBRARIES += com.google.hardware.pixel.display-V10-ndk \ libbinder_ndk \ libbase @@ -200,6 +230,20 @@ LOCAL_CFLAGS += -DLOG_TAG=\"hwc-service\" LOCAL_CFLAGS += -DSOC_VERSION=$(soc_ver) LOCAL_CFLAGS += -Wthread-safety +ifeq ($(CLANG_COVERAGE),true) +# enable code coverage (these flags are copied from build/soong/cc/coverage.go) +LOCAL_CFLAGS += -fprofile-instr-generate -fcoverage-mapping +LOCAL_CFLAGS += -Wno-frame-larger-than= +LOCAL_WHOLE_STATIC_LIBRARIES += libprofile-clang-extras_ndk +LOCAL_LDFLAGS += -fprofile-instr-generate +LOCAL_LDFLAGS += -Wl,--wrap,open + +ifeq ($(CLANG_COVERAGE_CONTINUOUS_MODE),true) +LOCAL_CFLAGS += -mllvm -runtime-counter-relocation +LOCAL_LDFLAGS += -Wl,-mllvm=-runtime-counter-relocation +endif +endif + LOCAL_SRC_FILES := \ libhwcService/IExynosHWC.cpp \ libhwcService/ExynosHWCService.cpp @@ -227,8 +271,8 @@ LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libexynosdisplay libacryl \ android.hardware.graphics.mapper@2.0 \ libui -LOCAL_SHARED_LIBRARIES += android.hardware.graphics.composer3-V2-ndk \ - com.google.hardware.pixel.display-V9-ndk \ +LOCAL_SHARED_LIBRARIES += android.hardware.graphics.composer3-V3-ndk \ + com.google.hardware.pixel.display-V10-ndk \ libbinder_ndk \ libbase @@ -241,6 +285,20 @@ LOCAL_CFLAGS += -DLOG_TAG=\"hwc-2\" LOCAL_CFLAGS += -DSOC_VERSION=$(soc_ver) LOCAL_CFLAGS += -Wthread-safety +ifeq ($(CLANG_COVERAGE),true) +# enable code coverage (these flags are copied from build/soong/cc/coverage.go) +LOCAL_CFLAGS += -fprofile-instr-generate -fcoverage-mapping +LOCAL_CFLAGS += -Wno-frame-larger-than= +LOCAL_WHOLE_STATIC_LIBRARIES += libprofile-clang-extras_ndk +LOCAL_LDFLAGS += -fprofile-instr-generate +LOCAL_LDFLAGS += -Wl,--wrap,open + +ifeq ($(CLANG_COVERAGE_CONTINUOUS_MODE),true) +LOCAL_CFLAGS += -mllvm -runtime-counter-relocation +LOCAL_LDFLAGS += -Wl,-mllvm=-runtime-counter-relocation +endif +endif + ifeq ($(BOARD_USES_HWC_SERVICES),true) LOCAL_CFLAGS += -DUSES_HWC_SERVICES LOCAL_SHARED_LIBRARIES += libExynosHWCService diff --git a/libhwc2.1/ExynosHWC.cpp b/libhwc2.1/ExynosHWC.cpp index f83a735..cca498f 100644 --- a/libhwc2.1/ExynosHWC.cpp +++ b/libhwc2.1/ExynosHWC.cpp @@ -1340,7 +1340,8 @@ int exynos_open(const struct hw_module_t *module, const char *name, dev = (struct exynos_hwc2_device_t *)malloc(sizeof(*dev)); memset(dev, 0, sizeof(*dev)); - dev->device = new ExynosDeviceModule; + // The legacy HIDL does not provide compatibility for the Vrr API as defined by AIDL. + dev->device = new ExynosDeviceModule(false); g_exynosDevice = dev->device; dev->base.common.tag = HARDWARE_DEVICE_TAG; diff --git a/libhwc2.1/ExynosHWCDebug.cpp b/libhwc2.1/ExynosHWCDebug.cpp index d43f76d..6f2b806 100644 --- a/libhwc2.1/ExynosHWCDebug.cpp +++ b/libhwc2.1/ExynosHWCDebug.cpp @@ -18,7 +18,7 @@ #include <sync/sync.h> #include "exynos_sync.h" -int32_t saveErrorLog(const String8 &errString, ExynosDisplay *display) { +int32_t saveErrorLog(const String8& errString, const ExynosDisplay* display) { if (display == nullptr) return -1; int32_t ret = NO_ERROR; diff --git a/libhwc2.1/ExynosHWCDebug.h b/libhwc2.1/ExynosHWCDebug.h index bd62a2e..50826f9 100644 --- a/libhwc2.1/ExynosHWCDebug.h +++ b/libhwc2.1/ExynosHWCDebug.h @@ -67,7 +67,7 @@ inline int hwcCheckFenceDebug(ExynosDisplay *display, uint32_t fence_type, uint3 return fence; } -int32_t saveErrorLog(const android::String8 &errString, ExynosDisplay *display = NULL); +int32_t saveErrorLog(const android::String8& errString, const ExynosDisplay* display = NULL); #if defined(DISABLE_HWC_DEBUG) #define ALOGD_AND_ATRACE_NAME(debugFlag, fmt, ...) diff --git a/libhwc2.1/libdevice/BrightnessController.cpp b/libhwc2.1/libdevice/BrightnessController.cpp index cc36c94..d8055ed 100644 --- a/libhwc2.1/libdevice/BrightnessController.cpp +++ b/libhwc2.1/libdevice/BrightnessController.cpp @@ -156,10 +156,10 @@ BrightnessController::~BrightnessController() { } } -void BrightnessController::updateBrightnessTable(const IBrightnessTable* table) { +void BrightnessController::updateBrightnessTable(std::unique_ptr<const IBrightnessTable>& table) { if (table && table->GetBrightnessRange(BrightnessMode::BM_NOMINAL)) { ALOGI("%s: apply brightness table from libdisplaycolor", __func__); - mBrightnessTable = table; + mBrightnessTable = std::move(table); } else { ALOGW("%s: table is not valid!", __func__); } @@ -304,7 +304,7 @@ void BrightnessController::initBrightnessTable(const DrmDevice& drmDevice, reinterpret_cast<struct brightness_capability *>(blob->data); mKernelBrightnessTable.Init(cap); if (mKernelBrightnessTable.IsValid()) { - mBrightnessTable = &mKernelBrightnessTable; + mBrightnessTable = std::make_unique<LinearBrightnessTable>(mKernelBrightnessTable); } parseHbmModeEnums(connector.hbm_mode()); @@ -343,6 +343,14 @@ int BrightnessController::updateAclMode() { mAclMode.store(mAclModeDefault); } + if (applyAclViaSysfs() == HWC2_ERROR_NO_RESOURCES) + ALOGW("%s try to apply acl_mode when brightness changed", __func__); + + return NO_ERROR; +} + +int BrightnessController::applyAclViaSysfs() { + if (!mAclModeOfs.is_open()) return NO_ERROR; if (!mAclMode.is_dirty()) return NO_ERROR; mAclModeOfs.seekp(std::ios_base::beg); @@ -376,6 +384,11 @@ int BrightnessController::processDisplayBrightness(float brightness, const nsecs } ATRACE_CALL(); + + /* update ACL */ + if (applyAclViaSysfs() == HWC2_ERROR_NO_RESOURCES) + ALOGE("%s failed to apply acl_mode", __func__); + if (!mBrightnessIntfSupported) { level = brightness < 0 ? 0 : static_cast<uint32_t>(brightness * mMaxBrightness + 0.5f); return applyBrightnessViaSysfs(level); diff --git a/libhwc2.1/libdevice/BrightnessController.h b/libhwc2.1/libdevice/BrightnessController.h index 63747f6..de2f72e 100644 --- a/libhwc2.1/libdevice/BrightnessController.h +++ b/libhwc2.1/libdevice/BrightnessController.h @@ -77,6 +77,7 @@ public: int processOperationRate(int32_t hz); bool isDbmSupported() { return mDbmSupported; } int applyPendingChangeViaSysfs(const nsecs_t vsyncNs); + int applyAclViaSysfs(); bool validateLayerBrightness(float brightness); /** @@ -155,6 +156,11 @@ public: return mOperationRate.get(); } + bool isOperationRatePending() { + std::lock_guard<std::recursive_mutex> lock(mBrightnessMutex); + return mOperationRate.is_dirty(); + } + bool isSupported() { // valid mMaxBrightness means both brightness and max_brightness sysfs exist return mMaxBrightness > 0; @@ -181,7 +187,7 @@ public: return nodeName.c_str(); } - void updateBrightnessTable(const IBrightnessTable* table); + void updateBrightnessTable(std::unique_ptr<const IBrightnessTable>& table); const BrightnessRangeMap& getBrightnessRanges() const { return mKernelBrightnessTable.GetBrightnessRangeMap(); } @@ -386,7 +392,7 @@ private: bool mBrightnessIntfSupported = false; LinearBrightnessTable mKernelBrightnessTable; // External object from libdisplaycolor - const IBrightnessTable* mBrightnessTable = nullptr; + std::unique_ptr<const IBrightnessTable> mBrightnessTable; int32_t mPanelIndex; DrmEnumParser::MapHal2DrmEnum mHbmModeEnums; diff --git a/libhwc2.1/libdevice/ExynosDevice.cpp b/libhwc2.1/libdevice/ExynosDevice.cpp index 0f2d202..e432d34 100644 --- a/libhwc2.1/libdevice/ExynosDevice.cpp +++ b/libhwc2.1/libdevice/ExynosDevice.cpp @@ -68,16 +68,16 @@ uint32_t getDeviceInterfaceType() return INTERFACE_TYPE_FB; } -ExynosDevice::ExynosDevice() - : mGeometryChanged(0), - mVsyncFd(-1), - mExtVsyncFd(-1), - mVsyncDisplayId(getDisplayId(HWC_DISPLAY_PRIMARY, 0)), - mTimestamp(0), - mDisplayMode(0), - mInterfaceType(INTERFACE_TYPE_FB), - mIsInTUI(false) -{ +ExynosDevice::ExynosDevice(bool vrrApiSupported) + : mGeometryChanged(0), + mVsyncFd(-1), + mExtVsyncFd(-1), + mVsyncDisplayId(getDisplayId(HWC_DISPLAY_PRIMARY, 0)), + mTimestamp(0), + mDisplayMode(0), + mInterfaceType(INTERFACE_TYPE_FB), + mIsInTUI(false), + mVrrApiSupported(vrrApiSupported) { exynosHWCControl.forceGpu = false; exynosHWCControl.windowUpdate = true; exynosHWCControl.forcePanic = false; @@ -150,8 +150,7 @@ ExynosDevice::ExynosDevice() mDisplayMap.insert(std::make_pair(exynos_display->mDisplayId, exynos_display)); #ifndef FORCE_DISABLE_DR - if (exynos_display->mDREnable) - exynosHWCControl.useDynamicRecomp = true; + if (exynos_display->mDRDefault) exynosHWCControl.useDynamicRecomp = true; #endif } @@ -222,8 +221,7 @@ void ExynosDevice::initDeviceInterface(uint32_t interfaceType) for (uint32_t i = 0; i < mDisplays.size();) { ExynosDisplay* display = mDisplays[i]; display->initDisplayInterface(interfaceType); - if (mDeviceInterface->initDisplayInterface( - display->mDisplayInterface) != NO_ERROR) { + if (mDeviceInterface->initDisplayInterface(display->mDisplayInterface) != NO_ERROR) { ALOGD("Remove display[%d], Failed to initialize display interface", i); mDisplays.removeAt(i); mDisplayMap.erase(display->mDisplayId); @@ -312,6 +310,7 @@ void ExynosDevice::checkDynamicRecompositionThread() if (mDisplays[i]->mDREnable) return; } + ALOGI("Destroying dynamic recomposition thread"); mDRLoopStatus = false; mDRWakeUpCondition.notify_one(); mDRThread.join(); @@ -321,6 +320,7 @@ void ExynosDevice::checkDynamicRecompositionThread() void ExynosDevice::dynamicRecompositionThreadCreate() { if (exynosHWCControl.useDynamicRecomp == true) { + ALOGI("Creating dynamic recomposition thread"); mDRLoopStatus = true; mDRThread = std::thread(&dynamicRecompositionThreadLoop, this); } @@ -520,11 +520,31 @@ bool ExynosDevice::isCallbackAvailable(int32_t descriptor) { return isCallbackRegisteredLocked(descriptor); } -void ExynosDevice::onHotPlug(uint32_t displayId, bool status) { +void ExynosDevice::onHotPlug(uint32_t displayId, bool status, int hotplugErrorCode) { Mutex::Autolock lock(mDeviceCallbackMutex); if (!isCallbackRegisteredLocked(HWC2_CALLBACK_HOTPLUG)) return; + if (hotplugErrorCode) { + // We need to pass the error code to SurfaceFlinger, but we cannot modify the HWC + // HAL interface, so for now we'll send the hotplug error via a onVsync callback with + // a negative time value indicating the hotplug error. + if (isCallbackRegisteredLocked(HWC2_CALLBACK_VSYNC_2_4)) { + ALOGD("%s: hotplugErrorCode=%d sending to SF via onVsync_2_4", __func__, + hotplugErrorCode); + hwc2_callback_data_t vsyncCallbackData = + mCallbackInfos[HWC2_CALLBACK_VSYNC_2_4].callbackData; + HWC2_PFN_VSYNC_2_4 vsyncCallbackFunc = reinterpret_cast<HWC2_PFN_VSYNC_2_4>( + mCallbackInfos[HWC2_CALLBACK_VSYNC_2_4].funcPointer); + vsyncCallbackFunc(vsyncCallbackData, displayId, -hotplugErrorCode, ~0); + return; + } else { + ALOGW("%s: onVsync_2_4 is not registered, ignoring hotplugErrorCode=%d", __func__, + hotplugErrorCode); + return; + } + } + hwc2_callback_data_t callbackData = mCallbackInfos[HWC2_CALLBACK_HOTPLUG].callbackData; HWC2_PFN_HOTPLUG callbackFunc = reinterpret_cast<HWC2_PFN_HOTPLUG>(mCallbackInfos[HWC2_CALLBACK_HOTPLUG].funcPointer); diff --git a/libhwc2.1/libdevice/ExynosDevice.h b/libhwc2.1/libdevice/ExynosDevice.h index bcfb112..9bdbea7 100644 --- a/libhwc2.1/libdevice/ExynosDevice.h +++ b/libhwc2.1/libdevice/ExynosDevice.h @@ -217,7 +217,7 @@ class ExynosDevice { std::unique_ptr<ExynosDeviceInterface> mDeviceInterface; // Con/Destructors - ExynosDevice(); + ExynosDevice(bool vrrApiSupported); virtual ~ExynosDevice(); bool isFirstValidate(); @@ -280,7 +280,7 @@ class ExynosDevice { int32_t registerCallback ( int32_t descriptor, hwc2_callback_data_t callbackData, hwc2_function_pointer_t point); bool isCallbackAvailable(int32_t descriptor); - void onHotPlug(uint32_t displayId, bool status); + void onHotPlug(uint32_t displayId, bool status, int hotplugErrorCode); void onRefresh(uint32_t displayId); void onRefreshDisplays(); @@ -346,6 +346,8 @@ class ExynosDevice { void onRefreshRateChangedDebug(hwc2_display_t displayId, uint32_t vsyncPeriod); + bool isVrrApiSupported() const { return mVrrApiSupported; }; + protected: void initDeviceInterface(uint32_t interfaceType); protected: @@ -365,6 +367,7 @@ class ExynosDevice { private: bool mIsInTUI; bool mDisplayOffAsync; + bool mVrrApiSupported = false; public: void handleHotplug(); diff --git a/libhwc2.1/libdevice/ExynosDisplay.cpp b/libhwc2.1/libdevice/ExynosDisplay.cpp index 3688207..2f77bb9 100644 --- a/libhwc2.1/libdevice/ExynosDisplay.cpp +++ b/libhwc2.1/libdevice/ExynosDisplay.cpp @@ -63,7 +63,7 @@ constexpr float nsecsPerSec = std::chrono::nanoseconds(1s).count(); constexpr int64_t nsecsIdleHintTimeout = std::chrono::nanoseconds(100ms).count(); ExynosDisplay::PowerHalHintWorker::PowerHalHintWorker(uint32_t displayId, - const String8 &displayTraceName) + const String8& displayTraceName) : Worker("DisplayHints", HAL_PRIORITY_URGENT_DISPLAY), mNeedUpdateRefreshRateHint(false), mLastRefreshRateHint(0), @@ -74,7 +74,7 @@ ExynosDisplay::PowerHalHintWorker::PowerHalHintWorker(uint32_t displayId, mIdleHintIsSupported(false), mDisplayTraceName(displayTraceName), mPowerModeState(HWC2_POWER_MODE_OFF), - mVsyncPeriod(16666666), + mRefreshRate(kDefaultRefreshRateFrequency), mConnectRetryCount(0), mDeathRecipient(AIBinder_DeathRecipient_new(BinderDiedCallback)), mPowerHalExtAidl(nullptr), @@ -196,7 +196,7 @@ int32_t ExynosDisplay::PowerHalHintWorker::sendPowerHalExtHint(const std::string return NO_ERROR; } -int32_t ExynosDisplay::PowerHalHintWorker::checkRefreshRateHintSupport(int refreshRate) { +int32_t ExynosDisplay::PowerHalHintWorker::checkRefreshRateHintSupport(const int32_t refreshRate) { int32_t ret = NO_ERROR; if (!isPowerHalExist()) { @@ -223,7 +223,8 @@ int32_t ExynosDisplay::PowerHalHintWorker::checkRefreshRateHintSupport(int refre return ret; } -int32_t ExynosDisplay::PowerHalHintWorker::sendRefreshRateHint(int refreshRate, bool enabled) { +int32_t ExynosDisplay::PowerHalHintWorker::sendRefreshRateHint(const int32_t refreshRate, + bool enabled) { std::string hintStr = mRefreshRateHintPrefixStr + std::to_string(refreshRate) + "FPS"; int32_t ret = sendPowerHalExtHint(hintStr, enabled); if (ret == -ENOTCONN) { @@ -234,11 +235,10 @@ int32_t ExynosDisplay::PowerHalHintWorker::sendRefreshRateHint(int refreshRate, } int32_t ExynosDisplay::PowerHalHintWorker::updateRefreshRateHintInternal( - hwc2_power_mode_t powerMode, uint32_t vsyncPeriod) { + const hwc2_power_mode_t powerMode, const int32_t refreshRate) { int32_t ret = NO_ERROR; /* TODO: add refresh rate buckets, tracked in b/181100731 */ - int refreshRate = round(nsecsPerSec / vsyncPeriod * 0.1f) * 10; // skip sending unnecessary hint if it's still the same. if (mLastRefreshRateHint == refreshRate && powerMode == HWC2_POWER_MODE_ON) { return NO_ERROR; @@ -340,7 +340,8 @@ int32_t ExynosDisplay::PowerHalHintWorker::checkPowerHintSessionSupport() { return out; } -int32_t ExynosDisplay::PowerHalHintWorker::updateIdleHint(int64_t deadlineTime, bool forceUpdate) { +int32_t ExynosDisplay::PowerHalHintWorker::updateIdleHint(const int64_t deadlineTime, + const bool forceUpdate) { int32_t ret = checkIdleHintSupport(); if (ret != NO_ERROR) { return ret; @@ -461,7 +462,7 @@ void ExynosDisplay::PowerHalHintWorker::signalActualWorkDuration(nsecs_t actualD } mActualWorkDuration = reportedDurationNs; - WorkDuration duration = {.durationNanos = reportedDurationNs, .timeStampNanos = systemTime()}; + WorkDuration duration = {.timeStampNanos = systemTime(), .durationNanos = reportedDurationNs}; if (sTraceHintSessionData) { DISPLAY_ATRACE_INT64("Measured duration", actualDurationNanos); @@ -509,17 +510,17 @@ void ExynosDisplay::PowerHalHintWorker::signalTargetWorkDuration(nsecs_t targetD } void ExynosDisplay::PowerHalHintWorker::signalRefreshRate(hwc2_power_mode_t powerMode, - uint32_t vsyncPeriod) { + int32_t refreshRate) { Lock(); mPowerModeState = powerMode; - mVsyncPeriod = vsyncPeriod; + mRefreshRate = refreshRate; mNeedUpdateRefreshRateHint = true; Unlock(); Signal(); } -void ExynosDisplay::PowerHalHintWorker::signalIdle() { +void ExynosDisplay::PowerHalHintWorker::signalNonIdle() { ATRACE_CALL(); Lock(); @@ -579,7 +580,6 @@ void ExynosDisplay::PowerHalHintWorker::Routine() { bool needUpdateRefreshRateHint = mNeedUpdateRefreshRateHint; int64_t deadlineTime = mIdleHintDeadlineTime; hwc2_power_mode_t powerMode = mPowerModeState; - uint32_t vsyncPeriod = mVsyncPeriod; /* * Clear the flags here instead of clearing them after calling the hint @@ -602,7 +602,7 @@ void ExynosDisplay::PowerHalHintWorker::Routine() { updateIdleHint(deadlineTime, forceUpdateIdleHint); if (needUpdateRefreshRateHint) { - int32_t rc = updateRefreshRateHintInternal(powerMode, vsyncPeriod); + int32_t rc = updateRefreshRateHintInternal(powerMode, mRefreshRate); if (rc != NO_ERROR && rc != -EOPNOTSUPP) { Lock(); if (mPowerModeState == HWC2_POWER_MODE_ON) { @@ -980,8 +980,8 @@ String8 ExynosCompositionInfo::getTypeStr() } } -ExynosDisplay::ExynosDisplay(uint32_t type, uint32_t index, ExynosDevice *device, - const std::string &displayName) +ExynosDisplay::ExynosDisplay(uint32_t type, uint32_t index, ExynosDevice* device, + const std::string& displayName) : mDisplayId(getDisplayId(type, index)), mType(type), mIndex(index), @@ -990,8 +990,9 @@ ExynosDisplay::ExynosDisplay(uint32_t type, uint32_t index, ExynosDevice *device mYres(2960), mXdpi(25400), mYdpi(25400), - mVsyncPeriod(16666666), - mBtsVsyncPeriod(16666666), + mVsyncPeriod(kDefaultVsyncPeriodNanoSecond), + mBtsFrameScanoutPeriod(kDefaultVsyncPeriodNanoSecond), + mBtsPendingOperationRatePeriod(0), mDevice(device), mDisplayName(displayName.c_str()), mDisplayTraceName(String8::format("%s(%d)", displayName.c_str(), mDisplayId)), @@ -1403,15 +1404,21 @@ int ExynosDisplay::checkDynamicReCompMode() { } unsigned int incomingPixels = 0; + hwc_rect_t dispRect = {INT_MAX, INT_MAX, 0, 0}; for (size_t i = 0; i < mLayers.size(); i++) { - auto w = WIDTH(mLayers[i]->mPreprocessedInfo.displayFrame); - auto h = HEIGHT(mLayers[i]->mPreprocessedInfo.displayFrame); + auto& r = mLayers[i]->mPreprocessedInfo.displayFrame; + if (r.top < dispRect.top) dispRect.top = r.top; + if (r.left < dispRect.left) dispRect.left = r.left; + if (r.bottom > dispRect.bottom) dispRect.bottom = r.bottom; + if (r.right > dispRect.right) dispRect.right = r.right; + auto w = WIDTH(r); + auto h = HEIGHT(r); incomingPixels += w * h; } /* Mode Switch is not required if total pixels are not more than the threshold */ - unsigned int lcdSize = mXres * mYres; - if (incomingPixels <= lcdSize) { + unsigned int mergedDisplayFrameSize = WIDTH(dispRect) * HEIGHT(dispRect); + if (incomingPixels <= mergedDisplayFrameSize) { auto ret = switchDynamicReCompMode(CLIENT_2_DEVICE); if (ret) { mUpdateCallCnt = 0; @@ -1719,18 +1726,26 @@ int ExynosDisplay::skipStaticLayers(ExynosCompositionInfo& compositionInfo) return NO_ERROR; } -bool ExynosDisplay::skipSignalIdle(void) { +bool ExynosDisplay::shouldSignalNonIdle(void) { + // Some cases such that we can skip calling mPowerHalHint.signalNonIdle(): + // 1. Updating source crop or buffer for video layer + // 2. Updating refresh rate indicator layer + uint64_t exclude = GEOMETRY_LAYER_SOURCECROP_CHANGED; + if ((mGeometryChanged & ~exclude) != 0) { + return true; + } for (size_t i = 0; i < mLayers.size(); i++) { // Frame update for refresh rate overlay indicator layer can be ignored if (mLayers[i]->mRequestedCompositionType == HWC2_COMPOSITION_REFRESH_RATE_INDICATOR) continue; // Frame update for video layer can be ignored if (mLayers[i]->isLayerFormatYuv()) continue; - if (mLayers[i]->mLastLayerBuffer != mLayers[i]->mLayerBuffer) { - return false; + if (mLayers[i]->mLastLayerBuffer != mLayers[i]->mLayerBuffer || + mLayers[i]->mGeometryChanged != 0) { + return true; } } - return true; + return false; } /** @@ -1823,8 +1838,8 @@ int ExynosDisplay::doExynosComposition() { return -EINVAL; } - if ((ret = mExynosCompositionInfo.mM2mMPP->doPostProcessing(mExynosCompositionInfo.mSrcImg, - mExynosCompositionInfo.mDstImg)) != NO_ERROR) { + if ((ret = mExynosCompositionInfo.mM2mMPP->doPostProcessing( + mExynosCompositionInfo.mDstImg)) != NO_ERROR) { DISPLAY_LOGE("exynosComposition doPostProcessing fail ret(%d)", ret); return ret; } @@ -3489,6 +3504,8 @@ int32_t ExynosDisplay::presentDisplay(int32_t* outRetireFence) { return ret; } + tryUpdateBtsFromOperationRate(true); + if (mRenderingState != RENDERING_STATE_ACCEPTED_CHANGE) { /* * presentDisplay() can be called before validateDisplay() @@ -3596,11 +3613,10 @@ int32_t ExynosDisplay::presentDisplay(int32_t* outRetireFence) { if ((mDisplayControl.earlyStartMPP == false) && (mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_DEVICE) && (mLayers[i]->mM2mMPP != NULL)) { - ExynosMPP *m2mMpp = mLayers[i]->mM2mMPP; - srcImg = mLayers[i]->mSrcImg; + ExynosMPP* m2mMpp = mLayers[i]->mM2mMPP; midImg = mLayers[i]->mMidImg; m2mMpp->requestHWStateChange(MPP_HW_STATE_RUNNING); - if ((ret = m2mMpp->doPostProcessing(srcImg, midImg)) != NO_ERROR) { + if ((ret = m2mMpp->doPostProcessing(midImg)) != NO_ERROR) { HWC_LOGE(this, "%s:: doPostProcessing() failed, layer(%zu), ret(%d)", __func__, i, ret); errString.appendFormat("%s:: doPostProcessing() failed, layer(%zu), ret(%d)\n", @@ -3625,12 +3641,12 @@ int32_t ExynosDisplay::presentDisplay(int32_t* outRetireFence) { goto err; } - if (mGeometryChanged != 0 || !skipSignalIdle()) { - mPowerHalHint.signalIdle(); + if (shouldSignalNonIdle()) { + mPowerHalHint.signalNonIdle(); } - if (needUpdateRRIndicator()) { - updateRefreshRateIndicator(); + if (mRefreshRateIndicatorHandler && needUpdateRRIndicator()) { + mRefreshRateIndicatorHandler->checkOnPresentDisplay(); } handleWindowUpdate(); @@ -3733,6 +3749,8 @@ int32_t ExynosDisplay::presentDisplay(int32_t* outRetireFence) { mPriorFrameMixedComposition = mixedComposition; + tryUpdateBtsFromOperationRate(false); + return ret; err: printDebugInfos(errString); @@ -4022,7 +4040,7 @@ int32_t ExynosDisplay::setDisplayBrightness(float brightness, bool waitPresent) ret = mBrightnessController->processDisplayBrightness(brightness, mVsyncPeriod, waitPresent); if (ret == NO_ERROR) { - setMinIdleRefreshRate(0, VrrThrottleRequester::BRIGHTNESS); + setMinIdleRefreshRate(0, RrThrottleRequester::BRIGHTNESS); if (mOperationRateManager) { mOperationRateManager->onBrightness(mBrightnessController->getBrightnessLevel()); handleTargetOperationRate(); @@ -4047,7 +4065,7 @@ int32_t ExynosDisplay::setBrightnessNits(const float nits) int32_t ret = mBrightnessController->setBrightnessNits(nits, mVsyncPeriod); if (ret == NO_ERROR) { - setMinIdleRefreshRate(0, VrrThrottleRequester::BRIGHTNESS); + setMinIdleRefreshRate(0, RrThrottleRequester::BRIGHTNESS); if (mOperationRateManager) mOperationRateManager->onBrightness(mBrightnessController->getBrightnessLevel()); } @@ -4063,7 +4081,7 @@ int32_t ExynosDisplay::setBrightnessDbv(const uint32_t dbv) { int32_t ret = mBrightnessController->setBrightnessDbv(dbv, mVsyncPeriod); if (ret == NO_ERROR) { - setMinIdleRefreshRate(0, VrrThrottleRequester::BRIGHTNESS); + setMinIdleRefreshRate(0, RrThrottleRequester::BRIGHTNESS); if (mOperationRateManager) { mOperationRateManager->onBrightness(mBrightnessController->getBrightnessLevel()); } @@ -4207,13 +4225,15 @@ int32_t ExynosDisplay::setActiveConfigWithConstraints(hwc2_config_t config, /* mActiveConfig should be changed immediately for internal status */ mActiveConfig = config; mVsyncAppliedTimeLine = *outTimeline; - uint32_t vsync_period = getDisplayVsyncPeriodFromConfig(config); - updateBtsVsyncPeriod(vsync_period); + updateBtsFrameScanoutPeriod(getDisplayFrameScanoutPeriodFromConfig(config)); bool earlyWakeupNeeded = checkRrCompensationEnabled(); if (earlyWakeupNeeded) { setEarlyWakeupDisplay(); } + if (mRefreshRateIndicatorHandler) { + mRefreshRateIndicatorHandler->checkOnSetActiveConfig(mDisplayConfigs[config].refreshRate); + } return HWC2_ERROR_NONE; } @@ -4304,25 +4324,88 @@ int32_t ExynosDisplay::updateInternalDisplayConfigVariables( if (updateVsync) { resetConfigRequestStateLocked(config); } + if (mRefreshRateIndicatorHandler) { + mRefreshRateIndicatorHandler->checkOnSetActiveConfig(mDisplayConfigs[config].refreshRate); + } return NO_ERROR; } -void ExynosDisplay::updateBtsVsyncPeriod(uint32_t vsyncPeriod, bool configApplied) { - if (configApplied || vsyncPeriod < mBtsVsyncPeriod) { - checkBtsReassignResource(vsyncPeriod, mBtsVsyncPeriod); - mBtsVsyncPeriod = vsyncPeriod; +void ExynosDisplay::updateBtsFrameScanoutPeriod(int32_t frameScanoutPeriod, bool configApplied) { + if (mBtsFrameScanoutPeriod == frameScanoutPeriod) { + return; + } + + if (configApplied || frameScanoutPeriod < mBtsFrameScanoutPeriod) { + checkBtsReassignResource(frameScanoutPeriod, mBtsFrameScanoutPeriod); + mBtsFrameScanoutPeriod = frameScanoutPeriod; + ATRACE_INT("btsFrameScanoutPeriod", mBtsFrameScanoutPeriod); + } +} + +void ExynosDisplay::tryUpdateBtsFromOperationRate(bool beforeValidateDisplay) { + if (mOperationRateManager == nullptr || mBrightnessController == nullptr || + mActiveConfig == UINT_MAX) { + return; + } + + if (!mDisplayConfigs[mActiveConfig].isOperationRateToBts) { + return; + } + + if (beforeValidateDisplay && mBrightnessController->isOperationRatePending()) { + uint32_t opRate = mBrightnessController->getOperationRate(); + if (opRate) { + int32_t operationRatePeriod = nsecsPerSec / opRate; + if (operationRatePeriod < mBtsFrameScanoutPeriod) { + updateBtsFrameScanoutPeriod(opRate); + mBtsPendingOperationRatePeriod = 0; + } else if (operationRatePeriod != mBtsFrameScanoutPeriod) { + mBtsPendingOperationRatePeriod = operationRatePeriod; + } + } + } + + if (!beforeValidateDisplay && mBtsPendingOperationRatePeriod && + !mBrightnessController->isOperationRatePending()) { + /* Do not update during rr transition, it will be updated after setting config done */ + if (mConfigRequestState != hwc_request_state_t::SET_CONFIG_STATE_REQUESTED) { + updateBtsFrameScanoutPeriod(mBtsPendingOperationRatePeriod, true); + } + mBtsPendingOperationRatePeriod = 0; + } +} + +inline int32_t ExynosDisplay::getDisplayFrameScanoutPeriodFromConfig(hwc2_config_t config) { + int32_t frameScanoutPeriodNs; + std::optional<VrrConfig_t> vrrConfig = getVrrConfigs(config); + if (vrrConfig.has_value()) { + frameScanoutPeriodNs = vrrConfig->minFrameIntervalNs; + } else { + getDisplayAttribute(config, HWC2_ATTRIBUTE_VSYNC_PERIOD, &frameScanoutPeriodNs); + if (mOperationRateManager && mBrightnessController && + mDisplayConfigs[config].isOperationRateToBts) { + uint32_t opRate = mBrightnessController->getOperationRate(); + if (opRate) { + uint32_t opPeriodNs = nsecsPerSec / opRate; + frameScanoutPeriodNs = + (frameScanoutPeriodNs <= opPeriodNs) ? frameScanoutPeriodNs : opPeriodNs; + } + } } + + assert(frameScanoutPeriodNs > 0); + return frameScanoutPeriodNs; } uint32_t ExynosDisplay::getBtsRefreshRate() const { - return static_cast<uint32_t>(round(nsecsPerSec / mBtsVsyncPeriod * 0.1f) * 10); + return static_cast<uint32_t>(round(nsecsPerSec / mBtsFrameScanoutPeriod * 0.1f) * 10); } void ExynosDisplay::updateRefreshRateHint() { - if (mVsyncPeriod) { + if (mRefreshRate) { mPowerHalHint.signalRefreshRate(mPowerModeState.value_or(HWC2_POWER_MODE_OFF), - mVsyncPeriod); + mRefreshRate); } } @@ -4330,8 +4413,11 @@ void ExynosDisplay::updateRefreshRateHint() { int32_t ExynosDisplay::resetConfigRequestStateLocked(hwc2_config_t config) { ATRACE_CALL(); + assert(isBadConfig(config) == false); + + mRefreshRate = mDisplayConfigs[config].refreshRate; mVsyncPeriod = getDisplayVsyncPeriodFromConfig(config); - updateBtsVsyncPeriod(mVsyncPeriod, true); + updateBtsFrameScanoutPeriod(getDisplayFrameScanoutPeriodFromConfig(config), true); DISPLAY_LOGD(eDebugDisplayConfig, "Update mVsyncPeriod %d by config(%d)", mVsyncPeriod, config); updateRefreshRateHint(); @@ -4598,15 +4684,14 @@ int32_t ExynosDisplay::validateDisplay( for (size_t i = 0; i < mLayers.size(); i++) mLayers[i]->setSrcAcquireFence(); + tryUpdateBtsFromOperationRate(true); doPreProcessing(); checkLayerFps(); - if (exynosHWCControl.useDynamicRecomp == true && mDREnable) + if (exynosHWCControl.useDynamicRecomp == true && mDREnable) { checkDynamicReCompMode(); - - if (exynosHWCControl.useDynamicRecomp == true && - mDevice->isDynamicRecompositionThreadAlive() == false && - mDevice->mDRLoopStatus == false) { - mDevice->dynamicRecompositionThreadCreate(); + if (mDevice->isDynamicRecompositionThreadAlive() == false && + mDevice->mDRLoopStatus == false) + mDevice->dynamicRecompositionThreadCreate(); } if ((ret = mResourceManager->assignResource(this)) != NO_ERROR) { @@ -4722,11 +4807,10 @@ int32_t ExynosDisplay::startPostProcessing() mLayers[i]->setSrcExynosImage(&srcImg); mLayers[i]->setDstExynosImage(&dstImg); mLayers[i]->setExynosImage(srcImg, dstImg); - ExynosMPP *m2mMpp = mLayers[i]->mM2mMPP; - srcImg = mLayers[i]->mSrcImg; + ExynosMPP* m2mMpp = mLayers[i]->mM2mMPP; midImg = mLayers[i]->mMidImg; m2mMpp->requestHWStateChange(MPP_HW_STATE_RUNNING); - if ((ret = m2mMpp->doPostProcessing(srcImg, midImg)) != NO_ERROR) { + if ((ret = m2mMpp->doPostProcessing(midImg)) != NO_ERROR) { DISPLAY_LOGE("%s:: doPostProcessing() failed, layer(%zu), ret(%d)", __func__, i, ret); errString.appendFormat("%s:: doPostProcessing() failed, layer(%zu), ret(%d)\n", @@ -5805,6 +5889,13 @@ int32_t ExynosDisplay::getMountOrientation(HwcMountOrientation *orientation) return HWC2_ERROR_NONE; } +std::optional<VrrConfig_t> ExynosDisplay::getVrrConfigs(hwc2_config_t config) { + if (isBadConfig(config)) { + return std::nullopt; + } + return mDisplayConfigs[config].vrrConfig; +} + // Support DDI scalser void ExynosDisplay::setDDIScalerEnable(int __unused width, int __unused height) { } @@ -6035,7 +6126,7 @@ void ExynosDisplay::cleanupAfterClientDeath() { int32_t ExynosDisplay::flushDisplayBrightnessChange() { if (mBrightnessController) { - setMinIdleRefreshRate(0, VrrThrottleRequester::BRIGHTNESS); + setMinIdleRefreshRate(0, RrThrottleRequester::BRIGHTNESS); if (mOperationRateManager) { mOperationRateManager->onBrightness(mBrightnessController->getBrightnessLevel()); handleTargetOperationRate(); @@ -6174,21 +6265,22 @@ bool ExynosDisplay::isMixedComposition() { int ExynosDisplay::lookupDisplayConfigs(const int32_t &width, const int32_t &height, const int32_t &fps, + const int32_t &vsyncRate, int32_t *outConfig) { - if (!fps) + if (!fps || !vsyncRate) return HWC2_ERROR_BAD_CONFIG; constexpr auto nsecsPerSec = std::chrono::nanoseconds(1s).count(); constexpr auto nsecsPerMs = std::chrono::nanoseconds(1ms).count(); - const auto vsyncPeriod = nsecsPerSec / fps; + const auto vsyncPeriod = nsecsPerSec / vsyncRate; for (auto const& [config, mode] : mDisplayConfigs) { long delta = abs(vsyncPeriod - mode.vsyncPeriod); if ((width == 0 || width == mode.width) && (height == 0 || height == mode.height) && - (delta < nsecsPerMs)) { - ALOGD("%s: found display config for mode: %dx%d@%d=%d", - __func__, width, height, fps, config); + (delta < nsecsPerMs) && (fps == mode.refreshRate)) { + ALOGD("%s: found display config for mode: %dx%d@%d:%d config=%d", + __func__, width, height, fps, vsyncRate, config); *outConfig = config; return HWC2_ERROR_NONE; } @@ -6281,10 +6373,13 @@ bool ExynosDisplay::checkHotplugEventUpdated(bool &hpdStatus) { hpdStatus = mDisplayInterface->readHotplugStatus(); - DISPLAY_LOGI("[%s] mDisplayId(%d), mIndex(%d), HPD Status(previous :%d, current : %d)", - __func__, mDisplayId, mIndex, mHpdStatus, hpdStatus); + int hotplugErrorCode = mDisplayInterface->readHotplugErrorCode(); - return (mHpdStatus != hpdStatus); + DISPLAY_LOGI("[%s] mDisplayId(%d), mIndex(%d), HPD Status(previous :%d, current : %d), " + "hotplugErrorCode=%d", + __func__, mDisplayId, mIndex, mHpdStatus, hpdStatus, hotplugErrorCode); + + return (mHpdStatus != hpdStatus) || (hotplugErrorCode != 0); } void ExynosDisplay::handleHotplugEvent(bool hpdStatus) { @@ -6292,19 +6387,21 @@ void ExynosDisplay::handleHotplugEvent(bool hpdStatus) { } void ExynosDisplay::hotplug() { - mDevice->onHotPlug(mDisplayId, mHpdStatus); - ALOGI("HPD callback(%s, mDisplayId %d) was called", - mHpdStatus ? "connection" : "disconnection", mDisplayId); + int hotplugErrorCode = mDisplayInterface->readHotplugErrorCode(); + mDisplayInterface->resetHotplugErrorCode(); + mDevice->onHotPlug(mDisplayId, mHpdStatus, hotplugErrorCode); + ALOGI("HPD callback(%s, mDisplayId %d, hotplugErrorCode=%d) was called", + mHpdStatus ? "connection" : "disconnection", mDisplayId, hotplugErrorCode); } -ExynosDisplay::RefreshRateIndicatorHandler::RefreshRateIndicatorHandler(ExynosDisplay *display) +ExynosDisplay::SysfsBasedRRIHandler::SysfsBasedRRIHandler(ExynosDisplay* display) : mDisplay(display), mLastRefreshRate(0), mLastCallbackTime(0), mIgnoringLastUpdate(false), mCanIgnoreIncreaseUpdate(false) {} -int32_t ExynosDisplay::RefreshRateIndicatorHandler::init() { +int32_t ExynosDisplay::SysfsBasedRRIHandler::init() { auto path = String8::format(kRefreshRateStatePathFormat, mDisplay->mIndex); mFd.Set(open(path.c_str(), O_RDONLY)); if (mFd.get() < 0) { @@ -6312,11 +6409,21 @@ int32_t ExynosDisplay::RefreshRateIndicatorHandler::init() { strerror(errno)); return -errno; } - + auto ret = mDisplay->mDevice->mDeviceInterface->registerSysfsEventHandler(shared_from_this()); + if (ret != NO_ERROR) { + ALOGE("%s: Failed to register sysfs event handler: %d", __func__, ret); + return ret; + } + // Call the callback immediately + handleSysfsEvent(); return NO_ERROR; } -void ExynosDisplay::RefreshRateIndicatorHandler::updateRefreshRateLocked(int refreshRate) { +int32_t ExynosDisplay::SysfsBasedRRIHandler::disable() { + return mDisplay->mDevice->mDeviceInterface->unregisterSysfsEventHandler(getFd()); +} + +void ExynosDisplay::SysfsBasedRRIHandler::updateRefreshRateLocked(int refreshRate) { ATRACE_CALL(); ATRACE_INT("Refresh rate indicator event", refreshRate); // Ignore refresh rate increase that is caused by refresh rate indicator update but there's @@ -6338,7 +6445,7 @@ void ExynosDisplay::RefreshRateIndicatorHandler::updateRefreshRateLocked(int ref mCanIgnoreIncreaseUpdate = true; } -void ExynosDisplay::RefreshRateIndicatorHandler::handleSysfsEvent() { +void ExynosDisplay::SysfsBasedRRIHandler::handleSysfsEvent() { ATRACE_CALL(); std::scoped_lock lock(mMutex); @@ -6362,7 +6469,7 @@ void ExynosDisplay::RefreshRateIndicatorHandler::handleSysfsEvent() { updateRefreshRateLocked(refreshRate); } -void ExynosDisplay::RefreshRateIndicatorHandler::updateRefreshRate(int refreshRate) { +void ExynosDisplay::SysfsBasedRRIHandler::updateRefreshRate(int refreshRate) { std::scoped_lock lock(mMutex); updateRefreshRateLocked(refreshRate); } @@ -6375,7 +6482,11 @@ int32_t ExynosDisplay::setRefreshRateChangedCallbackDebugEnabled(bool enabled) { } int32_t ret = NO_ERROR; if (enabled) { - mRefreshRateIndicatorHandler = std::make_shared<RefreshRateIndicatorHandler>(this); + if (mType == HWC_DISPLAY_PRIMARY) { + mRefreshRateIndicatorHandler = std::make_shared<SysfsBasedRRIHandler>(this); + } else { + mRefreshRateIndicatorHandler = std::make_shared<ActiveConfigBasedRRIHandler>(this); + } if (!mRefreshRateIndicatorHandler) { ALOGE("%s: Failed to create refresh rate debug handler", __func__); return -ENOMEM; @@ -6386,17 +6497,8 @@ int32_t ExynosDisplay::setRefreshRateChangedCallbackDebugEnabled(bool enabled) { mRefreshRateIndicatorHandler.reset(); return ret; } - ret = mDevice->mDeviceInterface->registerSysfsEventHandler(mRefreshRateIndicatorHandler); - if (ret != NO_ERROR) { - ALOGE("%s: Failed to register sysfs event handler: %d", __func__, ret); - mRefreshRateIndicatorHandler.reset(); - return ret; - } - // Call the callback immediately - mRefreshRateIndicatorHandler->handleSysfsEvent(); } else { - ret = mDevice->mDeviceInterface->unregisterSysfsEventHandler( - mRefreshRateIndicatorHandler->getFd()); + ret = mRefreshRateIndicatorHandler->disable(); mRefreshRateIndicatorHandler.reset(); } return ret; @@ -6414,13 +6516,34 @@ nsecs_t ExynosDisplay::getLastLayerUpdateTime() { return time; } -void ExynosDisplay::updateRefreshRateIndicator() { +void ExynosDisplay::SysfsBasedRRIHandler::checkOnPresentDisplay() { // Update refresh rate indicator if the last update event is ignored to make sure that // the refresh rate caused by the current frame update will be applied immediately since // we may not receive the sysfs event if the refresh rate is the same as the last ignored one. - if (!mRefreshRateIndicatorHandler || !mRefreshRateIndicatorHandler->isIgnoringLastUpdate()) + if (!mIgnoringLastUpdate) { return; - mRefreshRateIndicatorHandler->handleSysfsEvent(); + } + handleSysfsEvent(); +} + +ExynosDisplay::ActiveConfigBasedRRIHandler::ActiveConfigBasedRRIHandler(ExynosDisplay* display) + : mDisplay(display), mLastRefreshRate(0) {} + +int32_t ExynosDisplay::ActiveConfigBasedRRIHandler::init() { + updateRefreshRate(mDisplay->mRefreshRate); + return NO_ERROR; +} + +void ExynosDisplay::ActiveConfigBasedRRIHandler::updateRefreshRate(int refreshRate) { + if (mLastRefreshRate == refreshRate) { + return; + } + mLastRefreshRate = refreshRate; + mDisplay->mDevice->onRefreshRateChangedDebug(mDisplay->mDisplayId, s2ns(1) / refreshRate); +} + +void ExynosDisplay::ActiveConfigBasedRRIHandler::checkOnSetActiveConfig(int refreshRate) { + updateRefreshRate(refreshRate); } bool ExynosDisplay::needUpdateRRIndicator() { @@ -6429,21 +6552,20 @@ bool ExynosDisplay::needUpdateRRIndicator() { } uint32_t ExynosDisplay::getPeakRefreshRate() { - float opRate = mBrightnessController->getOperationRate(); - return static_cast<uint32_t>(std::round(opRate ?: mPeakRefreshRate)); + uint32_t opRate = mBrightnessController->getOperationRate(); + return opRate ?: mPeakRefreshRate; } VsyncPeriodNanos ExynosDisplay::getVsyncPeriod(const int32_t config) { - const auto &it = mDisplayConfigs.find(config); + const auto& it = mDisplayConfigs.find(config); if (it == mDisplayConfigs.end()) return 0; - return mDisplayConfigs[config].vsyncPeriod; + return it->second.vsyncPeriod; } uint32_t ExynosDisplay::getRefreshRate(const int32_t config) { - VsyncPeriodNanos period = getVsyncPeriod(config); - if (!period) return 0; - constexpr float nsecsPerSec = std::chrono::nanoseconds(1s).count(); - return round(nsecsPerSec / period * 0.1f) * 10; + const auto& it = mDisplayConfigs.find(config); + if (it == mDisplayConfigs.end()) return 0; + return it->second.refreshRate; } uint32_t ExynosDisplay::getConfigId(const int32_t refreshRate, const int32_t width, @@ -6494,3 +6616,18 @@ void ExynosDisplay::storePrevValidateCompositionType() { } mClientCompositionInfo.mPrevHasCompositionLayer = mClientCompositionInfo.mHasCompositionLayer; } + +displaycolor::DisplayType ExynosDisplay::getDcDisplayType() const { + switch (mType) { + case HWC_DISPLAY_PRIMARY: + return mIndex == 0 ? displaycolor::DisplayType::DISPLAY_PRIMARY + : displaycolor::DisplayType::DISPLAY_SECONDARY; + case HWC_DISPLAY_EXTERNAL: + return displaycolor::DisplayType::DISPLAY_EXTERNAL; + case HWC_DISPLAY_VIRTUAL: + default: + DISPLAY_LOGE("%s: Unsupported display type(%d)", __func__, mType); + assert(false); + return displaycolor::DisplayType::DISPLAY_PRIMARY; + } +} diff --git a/libhwc2.1/libdevice/ExynosDisplay.h b/libhwc2.1/libdevice/ExynosDisplay.h index 51286ad..b329f11 100644 --- a/libhwc2.1/libdevice/ExynosDisplay.h +++ b/libhwc2.1/libdevice/ExynosDisplay.h @@ -149,7 +149,7 @@ enum class hwc_request_state_t { SET_CONFIG_STATE_REQUESTED, }; -enum class VrrThrottleRequester : uint32_t { +enum class RrThrottleRequester : uint32_t { PIXEL_DISP = 0, TEST, LHBM, @@ -159,7 +159,7 @@ enum class VrrThrottleRequester : uint32_t { enum class DispIdleTimerRequester : uint32_t { SF = 0, - VRR_THROTTLE, + RR_THROTTLE, MAX, }; @@ -350,6 +350,28 @@ struct ResolutionInfo { int nPanelType[3]; }; +typedef struct FrameIntervalPowerHint { + int frameIntervalNs = 0; + int averageRefreshPeriodNs = 0; +} FrameIntervalPowerHint_t; + +typedef struct NotifyExpectedPresentConfig { + int HeadsUpNs = 0; + int TimeoutNs = 0; +} NotifyExpectedPresentConfig_t; + +typedef struct VrrConfig { + int minFrameIntervalNs = 0; + std::vector<FrameIntervalPowerHint_t> frameIntervalPowerHint; + NotifyExpectedPresentConfig_t notifyExpectedPresentConfig; +} VrrConfig_t; + +typedef struct VrrSettings { + bool enabled; + NotifyExpectedPresentConfig_t notifyExpectedPresentConfig; + std::function<void(int)> configChangeCallback; +} VrrSettings_t; + typedef struct displayConfigs { // HWC2_ATTRIBUTE_VSYNC_PERIOD VsyncPeriodNanos vsyncPeriod; @@ -363,6 +385,12 @@ typedef struct displayConfigs { uint32_t Ydpi; // HWC2_ATTRIBUTE_CONFIG_GROUP uint32_t groupId; + + std::optional<VrrConfig_t> vrrConfig; + + /* internal use */ + bool isOperationRateToBts; + int32_t refreshRate; } displayConfigs_t; struct DisplayControl { @@ -403,10 +431,9 @@ class ExynosDisplay { uint32_t mXdpi; uint32_t mYdpi; uint32_t mVsyncPeriod; - uint32_t mBtsVsyncPeriod; - - int mPanelType; - int mPsrMode; + int32_t mRefreshRate; + int32_t mBtsFrameScanoutPeriod; + int32_t mBtsPendingOperationRatePeriod; /* Constructor */ ExynosDisplay(uint32_t type, uint32_t index, ExynosDevice* device, @@ -1145,6 +1172,15 @@ class ExynosDisplay { */ int32_t getMountOrientation(HwcMountOrientation *orientation); + /* + * HWC3 + * + * Retrieve the vrrConfig for the corresponding display configuration. + * If the configuration doesn't exist, return a nullptr. + * + */ + std::optional<VrrConfig_t> getVrrConfigs(hwc2_config_t config); + /* setActiveConfig MISCs */ bool isBadConfig(hwc2_config_t config); bool needNotChangeConfig(hwc2_config_t config); @@ -1160,10 +1196,11 @@ class ExynosDisplay { int32_t getConfigAppliedTime(const uint64_t desiredTime, const uint64_t actualChangeTime, int64_t &appliedTime, int64_t &refreshTime); - void updateBtsVsyncPeriod(uint32_t vsyncPeriod, bool configApplied = false); + void updateBtsFrameScanoutPeriod(int32_t frameScanoutPeriod, bool configApplied = false); + void tryUpdateBtsFromOperationRate(bool beforeValidateDisplay); uint32_t getBtsRefreshRate() const; - virtual void checkBtsReassignResource(const uint32_t __unused vsyncPeriod, - const uint32_t __unused btsVsyncPeriod) {} + virtual void checkBtsReassignResource(const int32_t __unused vsyncPeriod, + const int32_t __unused btsVsyncPeriod) {} /* TODO : TBD */ int32_t setCursorPositionAsync(uint32_t x_pos, uint32_t y_pos); @@ -1241,8 +1278,10 @@ class ExynosDisplay { virtual int32_t setLhbmState(bool __unused enabled) { return NO_ERROR; } virtual bool getLhbmState() { return false; }; virtual void setEarlyWakeupDisplay() {} - virtual void setExpectedPresentTime(uint64_t __unused timestamp) {} + virtual void setExpectedPresentTime(uint64_t __unused timestamp, + int __unused frameIntervalNs) {} virtual uint64_t getPendingExpectedPresentTime() { return 0; } + virtual int getPendingFrameInterval() { return 0; } virtual void applyExpectedPresentTime() {} virtual int32_t getDisplayIdleTimerSupport(bool& outSupport); virtual int32_t getDisplayMultiThreadedPresentSupport(bool& outSupport); @@ -1277,6 +1316,17 @@ class ExynosDisplay { /* set brightness by dbv value */ virtual int32_t setBrightnessDbv(const uint32_t dbv); + virtual std::string getPanelSysfsPath() const { return std::string(); } + + virtual void onVsync(int64_t __unused timestamp) { return; }; + + displaycolor::DisplayType getDcDisplayType() const; + + virtual int32_t notifyExpectedPresent(int64_t __unused timestamp, + int32_t __unused frameIntervalNs) { + return HWC2_ERROR_UNSUPPORTED; + }; + protected: virtual bool getHDRException(ExynosLayer *layer); virtual int32_t getActiveConfigInternal(hwc2_config_t* outConfig); @@ -1295,11 +1345,11 @@ class ExynosDisplay { void requestLhbm(bool on); virtual int setMinIdleRefreshRate(const int __unused fps, - const VrrThrottleRequester __unused requester) { + const RrThrottleRequester __unused requester) { return NO_ERROR; } virtual int setRefreshRateThrottleNanos(const int64_t __unused delayNanos, - const VrrThrottleRequester __unused requester) { + const RrThrottleRequester __unused requester) { return NO_ERROR; } @@ -1324,12 +1374,13 @@ class ExynosDisplay { int lookupDisplayConfigs(const int32_t& width, const int32_t& height, const int32_t& fps, + const int32_t& vsyncRate, int32_t* outConfig); private: bool skipStaticLayerChanged(ExynosCompositionInfo& compositionInfo); - bool skipSignalIdle(); + bool shouldSignalNonIdle(); /// minimum possible dim rate in the case hbm peak is 1000 nits and norml // display brightness is 2 nits @@ -1354,8 +1405,8 @@ class ExynosDisplay { virtual ~PowerHalHintWorker(); int Init(); - void signalRefreshRate(hwc2_power_mode_t powerMode, uint32_t vsyncPeriod); - void signalIdle(); + void signalRefreshRate(hwc2_power_mode_t powerMode, int32_t refreshRate); + void signalNonIdle(); void signalActualWorkDuration(nsecs_t actualDurationNanos); void signalTargetWorkDuration(nsecs_t targetDurationNanos); @@ -1380,14 +1431,14 @@ class ExynosDisplay { int32_t checkPowerHalExtHintSupport(const std::string& mode); int32_t sendPowerHalExtHint(const std::string& mode, bool enabled); - int32_t checkRefreshRateHintSupport(int refreshRate); - int32_t updateRefreshRateHintInternal(hwc2_power_mode_t powerMode, - uint32_t vsyncPeriod); - int32_t sendRefreshRateHint(int refreshRate, bool enabled); + int32_t checkRefreshRateHintSupport(const int32_t refreshRate); + int32_t updateRefreshRateHintInternal(const hwc2_power_mode_t powerMode, + const int32_t refreshRate); + int32_t sendRefreshRateHint(const int32_t refreshRate, bool enabled); void forceUpdateHints(); int32_t checkIdleHintSupport(); - int32_t updateIdleHint(int64_t deadlineTime, bool forceUpdate); + int32_t updateIdleHint(const int64_t deadlineTime, const bool forceUpdate); bool needUpdateIdleHintLocked(int64_t& timeout) REQUIRES(mutex_); // for adpf cpu hints @@ -1411,7 +1462,7 @@ class ExynosDisplay { int mLastRefreshRateHint; // support list of refresh rate hints - std::map<int, bool> mRefreshRateHintSupportMap; + std::map<int32_t, bool> mRefreshRateHintSupportMap; bool mIdleHintIsEnabled; bool mForceUpdateIdleHint; @@ -1428,7 +1479,7 @@ class ExynosDisplay { std::string mRefreshRateHintPrefixStr; hwc2_power_mode_t mPowerModeState; - uint32_t mVsyncPeriod; + int32_t mRefreshRate; uint32_t mConnectRetryCount; bool isPowerHalExist() { return mConnectRetryCount < 10; } @@ -1548,7 +1599,7 @@ class ExynosDisplay { assert(vsync_period > 0); return static_cast<uint32_t>(vsync_period); } - + inline int32_t getDisplayFrameScanoutPeriodFromConfig(hwc2_config_t config); virtual void calculateTimeline( hwc2_config_t config, hwc_vsync_period_change_constraints_t* vsyncPeriodChangeConstraints, @@ -1604,7 +1655,7 @@ class ExynosDisplay { int32_t mLastFileIndex; FILE* mFile; }; - RotatingLogFileWriter mErrLogFileWriter; + mutable RotatingLogFileWriter mErrLogFileWriter; RotatingLogFileWriter mDebugDumpFileWriter; RotatingLogFileWriter mFenceFileWriter; @@ -1619,7 +1670,7 @@ class ExynosDisplay { virtual int32_t onConfig(hwc2_config_t __unused cfg) { return 0; } virtual int32_t onBrightness(uint32_t __unused dbv) { return 0; } virtual int32_t onPowerMode(int32_t __unused mode) { return 0; } - virtual int32_t getTargetOperationRate() { return 0; } + virtual int32_t getTargetOperationRate() const { return 0; } }; public: @@ -1634,14 +1685,30 @@ class ExynosDisplay { virtual void handleHotplugEvent(bool hpdStatus); virtual void hotplug(); - class RefreshRateIndicatorHandler : public DrmSysfsEventHandler { + class RefreshRateIndicator { + public: + virtual ~RefreshRateIndicator() = default; + virtual int32_t init() { return NO_ERROR; } + virtual int32_t disable() { return NO_ERROR; } + virtual void updateRefreshRate(int __unused refreshRate) {} + virtual void checkOnPresentDisplay() {} + virtual void checkOnSetActiveConfig(int __unused refreshRate) {} + }; + + class SysfsBasedRRIHandler : public RefreshRateIndicator, + public DrmSysfsEventHandler, + public std::enable_shared_from_this<SysfsBasedRRIHandler> { public: - RefreshRateIndicatorHandler(ExynosDisplay* display); - int32_t init(); - virtual void handleSysfsEvent() override; - virtual int getFd() override { return mFd.get(); }; - bool isIgnoringLastUpdate() { return mIgnoringLastUpdate; } - void updateRefreshRate(int refreshRate); + SysfsBasedRRIHandler(ExynosDisplay* display); + virtual ~SysfsBasedRRIHandler() = default; + + int32_t init() override; + int32_t disable() override; + void updateRefreshRate(int refreshRate) override; + void checkOnPresentDisplay() override; + + void handleSysfsEvent() override; + int getFd() override { return mFd.get(); } private: void updateRefreshRateLocked(int refreshRate) REQUIRES(mMutex); @@ -1658,9 +1725,24 @@ class ExynosDisplay { "/sys/class/backlight/panel%d-backlight/state"; }; - std::shared_ptr<RefreshRateIndicatorHandler> mRefreshRateIndicatorHandler; + class ActiveConfigBasedRRIHandler : public RefreshRateIndicator { + public: + ActiveConfigBasedRRIHandler(ExynosDisplay* display); + virtual ~ActiveConfigBasedRRIHandler() = default; + + int32_t init() override; + void updateRefreshRate(int refreshRate) override; + void checkOnSetActiveConfig(int refreshRate) override; + + private: + void updateVsyncPeriod(int vsyncPeriod); + + ExynosDisplay* mDisplay; + int mLastRefreshRate; + }; + + std::shared_ptr<RefreshRateIndicator> mRefreshRateIndicatorHandler; int32_t setRefreshRateChangedCallbackDebugEnabled(bool enabled); - void updateRefreshRateIndicator(); nsecs_t getLastLayerUpdateTime(); bool needUpdateRRIndicator(); virtual void checkPreblendingRequirement(){}; diff --git a/libhwc2.1/libdevice/ExynosLayer.cpp b/libhwc2.1/libdevice/ExynosLayer.cpp index 5d5a614..14aaf5f 100644 --- a/libhwc2.1/libdevice/ExynosLayer.cpp +++ b/libhwc2.1/libdevice/ExynosLayer.cpp @@ -606,7 +606,6 @@ int32_t ExynosLayer::setLayerSidebandStream(const native_handle_t* __unused stre } int32_t ExynosLayer::setLayerSourceCrop(hwc_frect_t crop) { - if ((crop.left != mSourceCrop.left) || (crop.top != mSourceCrop.top) || (crop.right != mSourceCrop.right) || @@ -1029,6 +1028,7 @@ void ExynosLayer::dump(String8& result) { int format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; int32_t fd, fd1, fd2; + uint64_t unique_id; if (mLayerBuffer != NULL) { VendorGraphicBufferMeta gmeta(mLayerBuffer); @@ -1036,11 +1036,13 @@ void ExynosLayer::dump(String8& result) fd = gmeta.fd; fd1 = gmeta.fd1; fd2 = gmeta.fd2; + unique_id = gmeta.unique_id; } else { format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; fd = -1; fd1 = -1; fd2 = -1; + unique_id = 0; } { @@ -1083,6 +1085,7 @@ void ExynosLayer::dump(String8& result) .add("exynosType", mExynosCompositionType) .add("validateType", mValidateCompositionType) .add("overlayInfo", mOverlayInfo, true) + .add("GrallocBufferId", unique_id) .build() .c_str()); diff --git a/libhwc2.1/libdevice/HistogramDevice.cpp b/libhwc2.1/libdevice/HistogramDevice.cpp index f31961f..2ac3507 100644 --- a/libhwc2.1/libdevice/HistogramDevice.cpp +++ b/libhwc2.1/libdevice/HistogramDevice.cpp @@ -35,10 +35,10 @@ * * @cookie pointer to the TokenInfo of the binder object. */ -static void histogramOnBinderDied(void *cookie) { +static void histogramOnBinderDied(void* cookie) { ATRACE_CALL(); HistogramDevice::HistogramErrorCode histogramErrorCode; - HistogramDevice::TokenInfo *tokenInfo = (HistogramDevice::TokenInfo *)cookie; + HistogramDevice::TokenInfo* tokenInfo = (HistogramDevice::TokenInfo*)cookie; ALOGI("%s: histogram channel #%u: client with token(%p) is died", __func__, tokenInfo->channelId, tokenInfo->token.get()); @@ -56,23 +56,25 @@ HistogramDevice::ChannelInfo::ChannelInfo() : status(ChannelStatus_t::DISABLED), token(nullptr), pid(-1), - requestedRoi(), + requestedRoi(DISABLED_ROI), + requestedBlockingRoi(DISABLED_ROI), workingConfig(), threshold(0), histDataCollecting(false) {} -HistogramDevice::ChannelInfo::ChannelInfo(const ChannelInfo &other) { +HistogramDevice::ChannelInfo::ChannelInfo(const ChannelInfo& other) { std::scoped_lock lock(other.channelInfoMutex); status = other.status; token = other.token; pid = other.pid; requestedRoi = other.requestedRoi; + requestedBlockingRoi = other.requestedBlockingRoi; workingConfig = other.workingConfig; threshold = other.threshold; histDataCollecting = other.histDataCollecting; } -HistogramDevice::HistogramDevice(ExynosDisplay *display, uint8_t channelCount, +HistogramDevice::HistogramDevice(ExynosDisplay* display, uint8_t channelCount, std::vector<uint8_t> reservedChannels) { mDisplay = display; @@ -89,15 +91,20 @@ HistogramDevice::~HistogramDevice() { } } -void HistogramDevice::initDrm(const DrmCrtc &crtc) { +void HistogramDevice::initDrm(const DrmCrtc& crtc) { // TODO: b/295786065 - Get available channels from crtc property. // TODO: b/295786065 - Check if the multi channel property is supported. initHistogramCapability(crtc.histogram_channel_property(0).id() != 0); + + /* print the histogram capability */ + String8 logString; + dumpHistogramCapability(logString); + ALOGI("%s", logString.c_str()); } ndk::ScopedAStatus HistogramDevice::getHistogramCapability( - HistogramCapability *histogramCapability) const { + HistogramCapability* histogramCapability) const { ATRACE_CALL(); if (!histogramCapability) { @@ -110,9 +117,9 @@ ndk::ScopedAStatus HistogramDevice::getHistogramCapability( return ndk::ScopedAStatus::ok(); } -ndk::ScopedAStatus HistogramDevice::registerHistogram(const ndk::SpAIBinder &token, - const HistogramConfig &histogramConfig, - HistogramErrorCode *histogramErrorCode) { +ndk::ScopedAStatus HistogramDevice::registerHistogram(const ndk::SpAIBinder& token, + const HistogramConfig& histogramConfig, + HistogramErrorCode* histogramErrorCode) { ATRACE_CALL(); if (UNLIKELY(!mHistogramCapability.supportMultiChannel)) { @@ -123,9 +130,9 @@ ndk::ScopedAStatus HistogramDevice::registerHistogram(const ndk::SpAIBinder &tok return configHistogram(token, histogramConfig, histogramErrorCode, false); } -ndk::ScopedAStatus HistogramDevice::queryHistogram(const ndk::SpAIBinder &token, - std::vector<char16_t> *histogramBuffer, - HistogramErrorCode *histogramErrorCode) { +ndk::ScopedAStatus HistogramDevice::queryHistogram(const ndk::SpAIBinder& token, + std::vector<char16_t>* histogramBuffer, + HistogramErrorCode* histogramErrorCode) { ATRACE_CALL(); if (UNLIKELY(!mHistogramCapability.supportMultiChannel)) { @@ -188,9 +195,9 @@ ndk::ScopedAStatus HistogramDevice::queryHistogram(const ndk::SpAIBinder &token, return ndk::ScopedAStatus::ok(); } -ndk::ScopedAStatus HistogramDevice::reconfigHistogram(const ndk::SpAIBinder &token, - const HistogramConfig &histogramConfig, - HistogramErrorCode *histogramErrorCode) { +ndk::ScopedAStatus HistogramDevice::reconfigHistogram(const ndk::SpAIBinder& token, + const HistogramConfig& histogramConfig, + HistogramErrorCode* histogramErrorCode) { ATRACE_CALL(); if (UNLIKELY(!mHistogramCapability.supportMultiChannel)) { @@ -201,8 +208,8 @@ ndk::ScopedAStatus HistogramDevice::reconfigHistogram(const ndk::SpAIBinder &tok return configHistogram(token, histogramConfig, histogramErrorCode, true); } -ndk::ScopedAStatus HistogramDevice::unregisterHistogram(const ndk::SpAIBinder &token, - HistogramErrorCode *histogramErrorCode) { +ndk::ScopedAStatus HistogramDevice::unregisterHistogram(const ndk::SpAIBinder& token, + HistogramErrorCode* histogramErrorCode) { ATRACE_CALL(); if (UNLIKELY(!mHistogramCapability.supportMultiChannel)) { @@ -257,10 +264,10 @@ ndk::ScopedAStatus HistogramDevice::unregisterHistogram(const ndk::SpAIBinder &t return ndk::ScopedAStatus::ok(); } -void HistogramDevice::handleDrmEvent(void *event) { +void HistogramDevice::handleDrmEvent(void* event) { int ret = NO_ERROR; uint8_t channelId; - char16_t *buffer; + char16_t* buffer; if ((ret = parseDrmEvent(event, channelId, buffer))) { ALOGE("%s: failed to parseDrmEvent, ret %d", __func__, ret); @@ -273,7 +280,7 @@ void HistogramDevice::handleDrmEvent(void *event) { return; } - ChannelInfo &channel = mChannels[channelId]; + ChannelInfo& channel = mChannels[channelId]; std::unique_lock<std::mutex> lock(channel.histDataCollectingMutex); /* Check if the histogram channel is collecting the histogram data */ @@ -287,12 +294,28 @@ void HistogramDevice::handleDrmEvent(void *event) { channel.histDataCollecting_cv.notify_all(); } -void HistogramDevice::prepareAtomicCommit(ExynosDisplayDrmInterface::DrmModeAtomicReq &drmReq) { +void HistogramDevice::prepareAtomicCommit(ExynosDisplayDrmInterface::DrmModeAtomicReq& drmReq) { ATRACE_NAME("HistogramAtomicCommit"); + ExynosDisplayDrmInterface* moduleDisplayInterface = + static_cast<ExynosDisplayDrmInterface*>(mDisplay->mDisplayInterface.get()); + if (!moduleDisplayInterface) { + HWC_LOGE(mDisplay, "%s: failed to get ExynosDisplayDrmInterface (nullptr)", __func__); + return; + } + + /* Get the current active region and check if the resolution is changed. */ + int32_t currDisplayActiveH = moduleDisplayInterface->getActiveModeHDisplay(); + int32_t currDisplayActiveV = moduleDisplayInterface->getActiveModeVDisplay(); + bool isResolutionChanged = + (mDisplayActiveH != currDisplayActiveH) || (mDisplayActiveV != currDisplayActiveV); + mDisplayActiveH = currDisplayActiveH; + mDisplayActiveV = currDisplayActiveV; + /* Loop through every channel and call prepareChannelCommit */ for (uint8_t channelId = 0; channelId < mChannels.size(); ++channelId) { - int channelRet = prepareChannelCommit(drmReq, channelId); + int channelRet = prepareChannelCommit(drmReq, channelId, moduleDisplayInterface, + isResolutionChanged); /* Every channel is independent, no early return when the channel commit fails. */ if (channelRet) { @@ -305,7 +328,7 @@ void HistogramDevice::prepareAtomicCommit(ExynosDisplayDrmInterface::DrmModeAtom void HistogramDevice::postAtomicCommit() { /* Atomic commit is success, loop through every channel and update the channel status */ for (uint8_t channelId = 0; channelId < mChannels.size(); ++channelId) { - ChannelInfo &channel = mChannels[channelId]; + ChannelInfo& channel = mChannels[channelId]; std::scoped_lock lock(channel.channelInfoMutex); switch (channel.status) { @@ -321,7 +344,7 @@ void HistogramDevice::postAtomicCommit() { } } -void HistogramDevice::dump(String8 &result) const { +void HistogramDevice::dump(String8& result) const { /* Do not dump the Histogram Device if it is not supported. */ if (!mHistogramCapability.supportMultiChannel) { return; @@ -337,7 +360,7 @@ void HistogramDevice::dump(String8 &result) const { for (uint8_t channelId = 0; channelId < mChannels.size(); ++channelId) { // TODO: b/294489887 - Use buildForMiniDump can eliminate the redundant rows. TableBuilder tb; - const ChannelInfo &channel = mChannels[channelId]; + const ChannelInfo& channel = mChannels[channelId]; std::scoped_lock lock(channel.channelInfoMutex); tb.add("ID", (int)channelId); tb.add("status", toString(channel.status)); @@ -345,6 +368,9 @@ void HistogramDevice::dump(String8 &result) const { tb.add("pid", channel.pid); tb.add("requestedRoi", toString(channel.requestedRoi)); tb.add("workingRoi", toString(channel.workingConfig.roi)); + tb.add("requestedBlockRoi", toString(channel.requestedBlockingRoi)); + tb.add("workingBlockRoi", + toString(channel.workingConfig.blockingRoi.value_or(DISABLED_ROI))); tb.add("threshold", channel.threshold); tb.add("weightRGB", toString(channel.workingConfig.weights)); tb.add("samplePos", @@ -357,7 +383,7 @@ void HistogramDevice::dump(String8 &result) const { } void HistogramDevice::initChannels(uint8_t channelCount, - const std::vector<uint8_t> &reservedChannels) { + const std::vector<uint8_t>& reservedChannels) { mChannels.resize(channelCount); ALOGI("%s: init histogram with %d channels", __func__, channelCount); @@ -382,8 +408,8 @@ void HistogramDevice::initChannels(uint8_t channelCount, } void HistogramDevice::initHistogramCapability(bool supportMultiChannel) { - ExynosDisplayDrmInterface *moduleDisplayInterface = - static_cast<ExynosDisplayDrmInterface *>(mDisplay->mDisplayInterface.get()); + ExynosDisplayDrmInterface* moduleDisplayInterface = + static_cast<ExynosDisplayDrmInterface*>(mDisplay->mDisplayInterface.get()); if (!moduleDisplayInterface) { ALOGE("%s: failed to get ExynosDisplayDrmInterface (nullptr), cannot get panel full " @@ -397,23 +423,18 @@ void HistogramDevice::initHistogramCapability(bool supportMultiChannel) { mHistogramCapability.fullResolutionHeight = moduleDisplayInterface->getPanelFullResolutionVSize(); } - ALOGI("%s: full screen roi: (0,0)x(%dx%d)", __func__, mHistogramCapability.fullResolutionWidth, - mHistogramCapability.fullResolutionHeight); mHistogramCapability.channelCount = mChannels.size(); mHistogramCapability.supportMultiChannel = supportMultiChannel; - - initSupportSamplePosList(); -} - -void HistogramDevice::initSupportSamplePosList() { - mHistogramCapability.supportSamplePosList.push_back(HistogramSamplePos::PRE_POSTPROC); mHistogramCapability.supportSamplePosList.push_back(HistogramSamplePos::POST_POSTPROC); + mHistogramCapability.supportBlockingRoi = false; + + initPlatformHistogramCapability(); } -ndk::ScopedAStatus HistogramDevice::configHistogram(const ndk::SpAIBinder &token, - const HistogramConfig &histogramConfig, - HistogramErrorCode *histogramErrorCode, +ndk::ScopedAStatus HistogramDevice::configHistogram(const ndk::SpAIBinder& token, + const HistogramConfig& histogramConfig, + HistogramErrorCode* histogramErrorCode, bool isReconfig) { /* validate the argument (histogramErrorCode) */ if (!histogramErrorCode) { @@ -437,9 +458,6 @@ ndk::ScopedAStatus HistogramDevice::configHistogram(const ndk::SpAIBinder &token return ndk::ScopedAStatus::ok(); } - /* threshold is calculated based on the roi coordinates rather than configured by the client */ - int threshold = calculateThreshold(histogramConfig.roi); - { ATRACE_BEGIN("getOrAcquireChannelId"); uint8_t channelId; @@ -465,7 +483,7 @@ ndk::ScopedAStatus HistogramDevice::configHistogram(const ndk::SpAIBinder &token /* store the histogram information, and mark the channel ready for atomic commit by setting * the status to CONFIG_PENDING */ - fillupChannelInfo(channelId, token, histogramConfig, threshold); + fillupChannelInfo(channelId, token, histogramConfig); if (!isReconfig) { /* link the binder object (token) to the death recipient. When the binder object is @@ -489,12 +507,12 @@ ndk::ScopedAStatus HistogramDevice::configHistogram(const ndk::SpAIBinder &token return ndk::ScopedAStatus::ok(); } -void HistogramDevice::getHistogramData(uint8_t channelId, std::vector<char16_t> *histogramBuffer, - HistogramErrorCode *histogramErrorCode) { +void HistogramDevice::getHistogramData(uint8_t channelId, std::vector<char16_t>* histogramBuffer, + HistogramErrorCode* histogramErrorCode) { ATRACE_NAME(String8::format("%s #%u", __func__, channelId).c_str()); int32_t ret; - ExynosDisplayDrmInterface *moduleDisplayInterface = - static_cast<ExynosDisplayDrmInterface *>(mDisplay->mDisplayInterface.get()); + ExynosDisplayDrmInterface* moduleDisplayInterface = + static_cast<ExynosDisplayDrmInterface*>(mDisplay->mDisplayInterface.get()); if (!moduleDisplayInterface) { *histogramErrorCode = HistogramErrorCode::BAD_HIST_DATA; ALOGE("%s: histogram channel #%u: BAD_HIST_DATA, moduleDisplayInterface is nullptr", @@ -502,7 +520,7 @@ void HistogramDevice::getHistogramData(uint8_t channelId, std::vector<char16_t> return; } - ChannelInfo &channel = mChannels[channelId]; + ChannelInfo& channel = mChannels[channelId]; std::unique_lock<std::mutex> lock(channel.histDataCollectingMutex); @@ -578,14 +596,14 @@ void HistogramDevice::getHistogramData(uint8_t channelId, std::vector<char16_t> histogramBuffer->assign(channel.histData, channel.histData + HISTOGRAM_BIN_COUNT); } -int HistogramDevice::parseDrmEvent(void *event, uint8_t &channelId, char16_t *&buffer) const { +int HistogramDevice::parseDrmEvent(void* event, uint8_t& channelId, char16_t*& buffer) const { channelId = 0; buffer = nullptr; return INVALID_OPERATION; } HistogramDevice::HistogramErrorCode HistogramDevice::acquireChannelLocked( - const ndk::SpAIBinder &token, uint8_t &channelId) { + const ndk::SpAIBinder& token, uint8_t& channelId) { ATRACE_CALL(); if (mFreeChannels.size() == 0) { ALOGE("%s: NO_CHANNEL_AVAILABLE, there is no histogram channel available", __func__); @@ -613,7 +631,7 @@ void HistogramDevice::releaseChannelLocked(uint8_t channelId) { } HistogramDevice::HistogramErrorCode HistogramDevice::getChannelIdByTokenLocked( - const ndk::SpAIBinder &token, uint8_t &channelId) { + const ndk::SpAIBinder& token, uint8_t& channelId) { if (mTokenInfoMap.find(token.get()) == mTokenInfoMap.end()) { ALOGE("%s: BAD_TOKEN, token (%p) is not registered", __func__, token.get()); return HistogramErrorCode::BAD_TOKEN; @@ -626,72 +644,64 @@ HistogramDevice::HistogramErrorCode HistogramDevice::getChannelIdByTokenLocked( void HistogramDevice::cleanupChannelInfo(uint8_t channelId) { ATRACE_NAME(String8::format("%s #%u", __func__, channelId).c_str()); - ChannelInfo &channel = mChannels[channelId]; + ChannelInfo& channel = mChannels[channelId]; std::scoped_lock lock(channel.channelInfoMutex); channel.status = ChannelStatus_t::DISABLE_PENDING; channel.token = nullptr; channel.pid = -1; - channel.requestedRoi = {.left = 0, .top = 0, .right = 0, .bottom = 0}; - channel.workingConfig = {.roi.left = 0, - .roi.top = 0, - .roi.right = 0, - .roi.bottom = 0, + channel.requestedRoi = DISABLED_ROI; + channel.requestedBlockingRoi = DISABLED_ROI; + channel.workingConfig = {.roi = DISABLED_ROI, .weights.weightR = 0, .weights.weightG = 0, .weights.weightB = 0, - .samplePos = HistogramSamplePos::POST_POSTPROC}; + .samplePos = HistogramSamplePos::POST_POSTPROC, + .blockingRoi = DISABLED_ROI}; channel.threshold = 0; } -void HistogramDevice::fillupChannelInfo(uint8_t channelId, const ndk::SpAIBinder &token, - const HistogramConfig &histogramConfig, int threshold) { +void HistogramDevice::fillupChannelInfo(uint8_t channelId, const ndk::SpAIBinder& token, + const HistogramConfig& histogramConfig) { ATRACE_NAME(String8::format("%s #%u", __func__, channelId).c_str()); - ChannelInfo &channel = mChannels[channelId]; + ChannelInfo& channel = mChannels[channelId]; std::scoped_lock lock(channel.channelInfoMutex); channel.status = ChannelStatus_t::CONFIG_PENDING; channel.token = token; channel.pid = AIBinder_getCallingPid(); channel.requestedRoi = histogramConfig.roi; + channel.requestedBlockingRoi = histogramConfig.blockingRoi.value_or(DISABLED_ROI); channel.workingConfig = histogramConfig; - channel.workingConfig.roi = {}; - channel.threshold = threshold; + channel.workingConfig.roi = DISABLED_ROI; + channel.workingConfig.blockingRoi = DISABLED_ROI; } -int HistogramDevice::prepareChannelCommit(ExynosDisplayDrmInterface::DrmModeAtomicReq &drmReq, - uint8_t channelId) { +int HistogramDevice::prepareChannelCommit(ExynosDisplayDrmInterface::DrmModeAtomicReq& drmReq, + uint8_t channelId, + ExynosDisplayDrmInterface* moduleDisplayInterface, + bool isResolutionChanged) { int ret = NO_ERROR; - ExynosDisplayDrmInterface *moduleDisplayInterface = - static_cast<ExynosDisplayDrmInterface *>(mDisplay->mDisplayInterface.get()); - if (!moduleDisplayInterface) { - HWC_LOGE(mDisplay, "%s: failed to get ExynosDisplayDrmInterface (nullptr)", __func__); - return -EINVAL; - } - ChannelInfo &channel = mChannels[channelId]; + ChannelInfo& channel = mChannels[channelId]; std::scoped_lock lock(channel.channelInfoMutex); if (channel.status == ChannelStatus_t::CONFIG_COMMITTED || channel.status == ChannelStatus_t::CONFIG_PENDING) { - HistogramRoiRect workingRoi; - - /* calculate the roi based on the current active resolution */ - ret = convertRoiLocked(moduleDisplayInterface, channel.requestedRoi, workingRoi); - if (ret == -EAGAIN) { + if (mDisplayActiveH == 0 || mDisplayActiveV == 0) { + /* mActiveModeState is not initialized, postpone histogram config to next atomic commit + */ + ALOGW("%s: mActiveModeState is not initialized, active: (%dx%d), postpone histogram " + "config to next atomic commit", + __func__, mDisplayActiveH, mDisplayActiveV); /* postpone the histogram config to next atomic commit */ ALOGD("%s: histogram channel #%u: set status (CONFIG_PENDING)", __func__, channelId); channel.status = ChannelStatus_t::CONFIG_PENDING; return NO_ERROR; - } else if (ret) { - ALOGE("%s: histogram channel #%u: failed to convert to workingRoi, ret: %d", __func__, - channelId, ret); - channel.status = ChannelStatus_t::CONFIG_ERROR; - return ret; } - /* If the channel status is CONFIG_COMMITTED, also check if the working roi needs to be + /* If the channel status is CONFIG_COMMITTED, check if the working roi needs to be * updated due to resolution changed. */ if (channel.status == ChannelStatus_t::CONFIG_COMMITTED) { - if (LIKELY(channel.workingConfig.roi == workingRoi)) { + if (LIKELY(isResolutionChanged == false)) { return NO_ERROR; } else { ALOGI("%s: histogram channel #%u: detect resolution changed, update roi setting", @@ -699,8 +709,30 @@ int HistogramDevice::prepareChannelCommit(ExynosDisplayDrmInterface::DrmModeAtom } } - /* Adopt the roi calculated from convertRoiLocked */ - channel.workingConfig.roi = workingRoi; + HistogramRoiRect convertedRoi; + + /* calculate the roi based on the current active resolution */ + ret = convertRoiLocked(moduleDisplayInterface, channel.requestedRoi, convertedRoi); + if (ret) { + ALOGE("%s: histogram channel #%u: failed to convert to workingRoi, ret: %d", __func__, + channelId, ret); + channel.status = ChannelStatus_t::CONFIG_ERROR; + return ret; + } + channel.workingConfig.roi = convertedRoi; + + /* calculate the blocking roi based on the current active resolution */ + ret = convertRoiLocked(moduleDisplayInterface, channel.requestedBlockingRoi, convertedRoi); + if (ret) { + ALOGE("%s: histogram channel #%u: failed to convert to workingBlockRoi, ret: %d", + __func__, channelId, ret); + channel.status = ChannelStatus_t::CONFIG_ERROR; + return ret; + } + channel.workingConfig.blockingRoi = convertedRoi; + + /* threshold is calculated based on the roi coordinates rather than configured by client */ + channel.threshold = calculateThreshold(channel.workingConfig.roi); /* Create histogram drm config struct (platform dependent) */ std::shared_ptr<void> blobData; @@ -738,9 +770,9 @@ int HistogramDevice::prepareChannelCommit(ExynosDisplayDrmInterface::DrmModeAtom return ret; } -int HistogramDevice::createHistogramDrmConfigLocked(const ChannelInfo &channel, - std::shared_ptr<void> &configPtr, - size_t &length) const { +int HistogramDevice::createHistogramDrmConfigLocked(const ChannelInfo& channel, + std::shared_ptr<void>& configPtr, + size_t& length) const { /* Default implementation doesn't know the histogram channel config struct in the kernel. * Cannot allocate and initialize the channel config. */ configPtr = nullptr; @@ -748,44 +780,40 @@ int HistogramDevice::createHistogramDrmConfigLocked(const ChannelInfo &channel, return INVALID_OPERATION; } -int HistogramDevice::convertRoiLocked(ExynosDisplayDrmInterface *moduleDisplayInterface, - const HistogramRoiRect &requestedRoi, - HistogramRoiRect &workingRoi) const { - int32_t activeH = moduleDisplayInterface->getActiveModeHDisplay(); - int32_t activeV = moduleDisplayInterface->getActiveModeVDisplay(); - int32_t panelH = mHistogramCapability.fullResolutionWidth; - int32_t panelV = mHistogramCapability.fullResolutionHeight; +int HistogramDevice::convertRoiLocked(ExynosDisplayDrmInterface* moduleDisplayInterface, + const HistogramRoiRect& requestedRoi, + HistogramRoiRect& convertedRoi) const { + const int32_t& panelH = mHistogramCapability.fullResolutionWidth; + const int32_t& panelV = mHistogramCapability.fullResolutionHeight; - ALOGV("%s: active: (%dx%d), panel: (%dx%d)", __func__, activeH, activeV, panelH, panelV); + ALOGV("%s: active: (%dx%d), panel: (%dx%d)", __func__, mDisplayActiveH, mDisplayActiveV, panelH, + panelV); - if (panelH < activeH || activeH < 0 || panelV < activeV || activeV < 0) { - ALOGE("%s: failed to convert roi, active: (%dx%d), panel: (%dx%d)", __func__, activeH, - activeV, panelH, panelV); + if (panelH < mDisplayActiveH || mDisplayActiveH < 0 || panelV < mDisplayActiveV || + mDisplayActiveV < 0) { + ALOGE("%s: failed to convert roi, active: (%dx%d), panel: (%dx%d)", __func__, + mDisplayActiveH, mDisplayActiveV, panelH, panelV); return -EINVAL; - } else if (activeH == 0 || activeV == 0) { - /* mActiveModeState is not initialized, postpone histogram config to next atomic commit */ - ALOGW("%s: mActiveModeState is not initialized, active: (%dx%d), postpone histogram config " - "to next atomic commit", - __func__, activeH, activeV); - return -EAGAIN; } /* Linear transform from full resolution to active resolution */ - workingRoi.left = requestedRoi.left * activeH / panelH; - workingRoi.top = requestedRoi.top * activeV / panelV; - workingRoi.right = requestedRoi.right * activeH / panelH; - workingRoi.bottom = requestedRoi.bottom * activeV / panelV; + convertedRoi.left = requestedRoi.left * mDisplayActiveH / panelH; + convertedRoi.top = requestedRoi.top * mDisplayActiveV / panelV; + convertedRoi.right = requestedRoi.right * mDisplayActiveH / panelH; + convertedRoi.bottom = requestedRoi.bottom * mDisplayActiveV / panelV; - ALOGV("%s: working roi: %s", __func__, toString(workingRoi).c_str()); + ALOGV("%s: working roi: %s", __func__, toString(convertedRoi).c_str()); return NO_ERROR; } -void HistogramDevice::dumpHistogramCapability(String8 &result) const { +void HistogramDevice::dumpHistogramCapability(String8& result) const { /* Append the histogram capability info to the dump string */ result.appendFormat("Histogram capability:\n"); result.appendFormat("\tsupportMultiChannel: %s\n", mHistogramCapability.supportMultiChannel ? "true" : "false"); + result.appendFormat("\tsupportBlockingRoi: %s\n", + mHistogramCapability.supportBlockingRoi ? "true" : "false"); result.appendFormat("\tchannelCount: %d\n", mHistogramCapability.channelCount); result.appendFormat("\tfullscreen roi: (0,0)x(%dx%d)\n", mHistogramCapability.fullResolutionWidth, @@ -800,12 +828,14 @@ void HistogramDevice::dumpHistogramCapability(String8 &result) const { } HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramConfig( - const HistogramConfig &histogramConfig) const { + const HistogramConfig& histogramConfig) const { HistogramErrorCode ret; - if ((ret = validateHistogramRoi(histogramConfig.roi)) != HistogramErrorCode::NONE || + if ((ret = validateHistogramRoi(histogramConfig.roi, "")) != HistogramErrorCode::NONE || (ret = validateHistogramWeights(histogramConfig.weights)) != HistogramErrorCode::NONE || - (ret = validateHistogramSamplePos(histogramConfig.samplePos)) != HistogramErrorCode::NONE) { + (ret = validateHistogramSamplePos(histogramConfig.samplePos)) != HistogramErrorCode::NONE || + (ret = validateHistogramBlockingRoi(histogramConfig.blockingRoi)) != + HistogramErrorCode::NONE) { return ret; } @@ -813,11 +843,13 @@ HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramConfig( } HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramRoi( - const HistogramRoiRect &roi) const { + const HistogramRoiRect& roi, const char* roiType) const { + if (roi == DISABLED_ROI) return HistogramErrorCode::NONE; + if ((roi.left < 0) || (roi.top < 0) || (roi.right - roi.left <= 0) || (roi.bottom - roi.top <= 0) || (roi.right > mHistogramCapability.fullResolutionWidth) || (roi.bottom > mHistogramCapability.fullResolutionHeight)) { - ALOGE("%s: BAD_ROI, roi: %s, full screen roi: (0,0)x(%dx%d)", __func__, + ALOGE("%s: BAD_ROI, %sroi: %s, full screen roi: (0,0)x(%dx%d)", __func__, roiType, toString(roi).c_str(), mHistogramCapability.fullResolutionWidth, mHistogramCapability.fullResolutionHeight); return HistogramErrorCode::BAD_ROI; @@ -827,7 +859,7 @@ HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramRoi( } HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramWeights( - const HistogramWeights &weights) const { + const HistogramWeights& weights) const { if ((weights.weightR + weights.weightG + weights.weightB) != WEIGHT_SUM) { ALOGE("%s: BAD_WEIGHT, weights(%d,%d,%d)\n", __func__, weights.weightR, weights.weightG, weights.weightB); @@ -838,7 +870,7 @@ HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramWeights( } HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramSamplePos( - const HistogramSamplePos &samplePos) const { + const HistogramSamplePos& samplePos) const { for (HistogramSamplePos mSamplePos : mHistogramCapability.supportSamplePosList) { if (samplePos == mSamplePos) { return HistogramErrorCode::NONE; @@ -849,13 +881,32 @@ HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramSamplePos( return HistogramErrorCode::BAD_POSITION; } -int HistogramDevice::calculateThreshold(const HistogramRoiRect &roi) { +HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramBlockingRoi( + const std::optional<HistogramRoiRect>& blockingRoi) const { + /* If the platform doesn't support blockingRoi, client should not enable blockingRoi */ + if (mHistogramCapability.supportBlockingRoi == false) { + if (blockingRoi.has_value() && blockingRoi.value() != DISABLED_ROI) { + ALOGE("%s: BAD_ROI, platform doesn't support blockingRoi, requested: %s", __func__, + toString(blockingRoi.value()).c_str()); + return HistogramErrorCode::BAD_ROI; + } + return HistogramErrorCode::NONE; + } + + /* For the platform that supports blockingRoi, use the same validate rule as roi */ + return validateHistogramRoi(blockingRoi.value_or(DISABLED_ROI), "blocking "); +} + +int HistogramDevice::calculateThreshold(const HistogramRoiRect& roi) const { + /* If roi is disabled, the targeted region is entire screen. */ + int32_t roiH = (roi != DISABLED_ROI) ? (roi.right - roi.left) : mDisplayActiveH; + int32_t roiV = (roi != DISABLED_ROI) ? (roi.bottom - roi.top) : mDisplayActiveV; + int threshold = (roiV * roiH) >> 16; // TODO: b/294491895 - Check if the threshold plus one really need it - int threshold = ((roi.bottom - roi.top) * (roi.right - roi.left)) >> 16; return threshold + 1; } -std::string HistogramDevice::toString(const ChannelStatus_t &status) { +std::string HistogramDevice::toString(const ChannelStatus_t& status) { switch (status) { case ChannelStatus_t::RESERVED: return "RESERVED"; @@ -880,7 +931,9 @@ std::string HistogramDevice::toString(const ChannelStatus_t &status) { return "UNDEFINED"; } -std::string HistogramDevice::toString(const HistogramRoiRect &roi) { +std::string HistogramDevice::toString(const HistogramRoiRect& roi) { + if (roi == DISABLED_ROI) return "OFF"; + std::ostringstream os; os << "(" << roi.left << "," << roi.top << ")"; os << "x"; @@ -888,7 +941,7 @@ std::string HistogramDevice::toString(const HistogramRoiRect &roi) { return os.str(); } -std::string HistogramDevice::toString(const HistogramWeights &weights) { +std::string HistogramDevice::toString(const HistogramWeights& weights) { std::ostringstream os; os << "("; os << (int)weights.weightR << ","; diff --git a/libhwc2.1/libdevice/HistogramDevice.h b/libhwc2.1/libdevice/HistogramDevice.h index ee74cae..ce56e47 100644 --- a/libhwc2.1/libdevice/HistogramDevice.h +++ b/libhwc2.1/libdevice/HistogramDevice.h @@ -47,6 +47,9 @@ public: using HistogramWeights = aidl::com::google::hardware::pixel::display::Weight; using HistogramChannelIoctl_t = ExynosDisplayDrmInterface::HistogramChannelIoctl_t; + /* For blocking roi and roi, (0, 0, 0, 0) means disabled */ + static constexpr HistogramRoiRect DISABLED_ROI = {0, 0, 0, 0}; + /* Histogram weight constraint: weightR + weightG + weightB = WEIGHT_SUM */ static constexpr size_t WEIGHT_SUM = 1024; @@ -99,6 +102,9 @@ public: /* requested roi from the client by registerHistogram or reconfigHistogram */ HistogramRoiRect requestedRoi GUARDED_BY(channelInfoMutex); + /* requested blocking roi from the client by registerHistogram or reconfigHistogram */ + HistogramRoiRect requestedBlockingRoi GUARDED_BY(channelInfoMutex); + /* histogram config that would be applied to hardware, the requestedRoi may be different * from the roi described in workingConfig due to RRS (Runtime Resolution Switch) */ HistogramConfig workingConfig GUARDED_BY(channelInfoMutex); @@ -113,7 +119,7 @@ public: std::condition_variable histDataCollecting_cv; ChannelInfo(); - ChannelInfo(const ChannelInfo &other); + ChannelInfo(const ChannelInfo& other); }; /* TokenInfo is not only used to stored the corresponding channel id but also passed to the @@ -124,7 +130,7 @@ public: /* pointer to the HistogramDevice, binderdied callback would use this pointer to cleanup the * channel in HistogramDevice by the member function unregisterHistogram */ - HistogramDevice *histogramDevice; + HistogramDevice* histogramDevice; /* binderdied callback would call unregisterHistogram with this token */ ndk::SpAIBinder token; @@ -139,7 +145,7 @@ public: * @channelCount number of the histogram channels in the system. * @reservedChannels a list of channel id that are reserved by the driver. */ - explicit HistogramDevice(ExynosDisplay *display, uint8_t channelCount, + explicit HistogramDevice(ExynosDisplay* display, uint8_t channelCount, std::vector<uint8_t> reservedChannels); /** @@ -158,7 +164,7 @@ public: * * @crtc drm crtc object which would contain histogram related information. */ - void initDrm(const DrmCrtc &crtc); + void initDrm(const DrmCrtc& crtc); /** * getHistogramCapability @@ -168,7 +174,7 @@ public: * @histogramCapability: describe the histogram capability for the system. * @return ok() when the interface is supported and arguments are valid, else otherwise. */ - ndk::ScopedAStatus getHistogramCapability(HistogramCapability *histogramCapability) const; + ndk::ScopedAStatus getHistogramCapability(HistogramCapability* histogramCapability) const; /** * registerHistogram @@ -186,9 +192,9 @@ public: * @return ok() when the interface is supported, or EX_UNSUPPORTED_OPERATION when the interface * is not supported yet. */ - ndk::ScopedAStatus registerHistogram(const ndk::SpAIBinder &token, - const HistogramConfig &histogramConfig, - HistogramErrorCode *histogramErrorCode); + ndk::ScopedAStatus registerHistogram(const ndk::SpAIBinder& token, + const HistogramConfig& histogramConfig, + HistogramErrorCode* histogramErrorCode); /** * queryHistogram @@ -204,9 +210,9 @@ public: * @return ok() when the interface is supported, or EX_UNSUPPORTED_OPERATION when the interface * is not supported yet. */ - ndk::ScopedAStatus queryHistogram(const ndk::SpAIBinder &token, - std::vector<char16_t> *histogramBuffer, - HistogramErrorCode *histogramErrorCode); + ndk::ScopedAStatus queryHistogram(const ndk::SpAIBinder& token, + std::vector<char16_t>* histogramBuffer, + HistogramErrorCode* histogramErrorCode); /** * reconfigHistogram @@ -221,9 +227,9 @@ public: * @return ok() when the interface is supported, or EX_UNSUPPORTED_OPERATION when the interface * is not supported yet. */ - ndk::ScopedAStatus reconfigHistogram(const ndk::SpAIBinder &token, - const HistogramConfig &histogramConfig, - HistogramErrorCode *histogramErrorCode); + ndk::ScopedAStatus reconfigHistogram(const ndk::SpAIBinder& token, + const HistogramConfig& histogramConfig, + HistogramErrorCode* histogramErrorCode); /** * unregisterHistogram @@ -237,8 +243,8 @@ public: * @return ok() when the interface is supported, or EX_UNSUPPORTED_OPERATION when the interface * is not supported yet. */ - ndk::ScopedAStatus unregisterHistogram(const ndk::SpAIBinder &token, - HistogramErrorCode *histogramErrorCode); + ndk::ScopedAStatus unregisterHistogram(const ndk::SpAIBinder& token, + HistogramErrorCode* histogramErrorCode); /** * handleDrmEvent @@ -248,7 +254,7 @@ public: * * @event histogram channel drm event pointer (struct exynos_drm_histogram_channel_event *) */ - void handleDrmEvent(void *event); + void handleDrmEvent(void* event); /** * prepareAtomicCommit @@ -257,7 +263,7 @@ public: * * @drmReq drm atomic request object */ - void prepareAtomicCommit(ExynosDisplayDrmInterface::DrmModeAtomicReq &drmReq); + void prepareAtomicCommit(ExynosDisplayDrmInterface::DrmModeAtomicReq& drmReq); /** * postAtomicCommit @@ -276,7 +282,7 @@ public: * * @result histogram channel dump information would be appended to this string */ - void dump(String8 &result) const; + void dump(String8& result) const; protected: HistogramCapability mHistogramCapability; @@ -284,12 +290,14 @@ protected: private: mutable std::mutex mAllocatorMutex; std::queue<uint8_t> mFreeChannels GUARDED_BY(mAllocatorMutex); // free channel list - std::unordered_map<AIBinder *, TokenInfo> mTokenInfoMap GUARDED_BY(mAllocatorMutex); + std::unordered_map<AIBinder*, TokenInfo> mTokenInfoMap GUARDED_BY(mAllocatorMutex); std::vector<ChannelInfo> mChannels; - ExynosDisplay *mDisplay = nullptr; + int32_t mDisplayActiveH = 0; + int32_t mDisplayActiveV = 0; + ExynosDisplay* mDisplay = nullptr; /* Death recipient for the binderdied callback, would be deleted in the destructor */ - AIBinder_DeathRecipient *mDeathRecipient = nullptr; + AIBinder_DeathRecipient* mDeathRecipient = nullptr; /** * initChannels @@ -299,7 +307,7 @@ private: * @channelCount number of channels in the system including the reserved channels. * @reservedChannels a list of channel id that are reserved by the driver. */ - void initChannels(uint8_t channelCount, const std::vector<uint8_t> &reservedChannels); + void initChannels(uint8_t channelCount, const std::vector<uint8_t>& reservedChannels); /** * initHistogramCapability @@ -312,11 +320,11 @@ private: void initHistogramCapability(bool supportMultiChannel); /** - * initSupportSamplePosList + * initPlatformHistogramCapability * - * Initialize the supported sample position list. + * Initialize platform specific histogram capability. */ - virtual void initSupportSamplePosList(); + virtual void initPlatformHistogramCapability() {} /** * configHistogram @@ -329,9 +337,9 @@ private: * @isReconfig is true if it is not the register request, only need to change the config. * @return ok() when the interface is supported, or else otherwise. */ - ndk::ScopedAStatus configHistogram(const ndk::SpAIBinder &token, - const HistogramConfig &histogramConfig, - HistogramErrorCode *histogramErrorCode, bool isReconfig); + ndk::ScopedAStatus configHistogram(const ndk::SpAIBinder& token, + const HistogramConfig& histogramConfig, + HistogramErrorCode* histogramErrorCode, bool isReconfig); /** * getHistogramData @@ -344,8 +352,8 @@ private: * @histogramBuffer AIDL created buffer which will be sent back to the client. * @histogramErrorCode::NONE when success, or else otherwise. */ - void getHistogramData(uint8_t channelId, std::vector<char16_t> *histogramBuffer, - HistogramErrorCode *histogramErrorCode); + void getHistogramData(uint8_t channelId, std::vector<char16_t>* histogramBuffer, + HistogramErrorCode* histogramErrorCode); /** * parseDrmEvent @@ -359,7 +367,7 @@ private: * @buffer stores the extracted buffer address from the event. * @return NO_ERROR on success, else otherwise. */ - virtual int parseDrmEvent(void *event, uint8_t &channelId, char16_t *&buffer) const; + virtual int parseDrmEvent(void* event, uint8_t& channelId, char16_t*& buffer) const; /** * acquireChannelLocked @@ -371,7 +379,7 @@ private: * @channelId store the acquired channel id. * @return HistogramErrorCode::NONE when success, or else otherwise. */ - HistogramErrorCode acquireChannelLocked(const ndk::SpAIBinder &token, uint8_t &channelId) + HistogramErrorCode acquireChannelLocked(const ndk::SpAIBinder& token, uint8_t& channelId) REQUIRES(mAllocatorMutex); /** @@ -392,7 +400,7 @@ private: * @token binder object created by the client. * @return HistogramErrorCode::NONE when success, or else otherwise. */ - HistogramErrorCode getChannelIdByTokenLocked(const ndk::SpAIBinder &token, uint8_t &channelId) + HistogramErrorCode getChannelIdByTokenLocked(const ndk::SpAIBinder& token, uint8_t& channelId) REQUIRES(mAllocatorMutex); /** @@ -415,10 +423,9 @@ private: * @channelId the channel id to be configured. * @token binder object created by the client. * @histogramConfig histogram config requested by the client. - * @threshold histogram threshold calculated from the roi. */ - void fillupChannelInfo(uint8_t channelId, const ndk::SpAIBinder &token, - const HistogramConfig &histogramConfig, int threshold); + void fillupChannelInfo(uint8_t channelId, const ndk::SpAIBinder& token, + const HistogramConfig& histogramConfig); /** * prepareChannelCommit @@ -437,10 +444,13 @@ private: * * @drmReq drm atomic request object * @channelId histogram channel id + * @moduleDisplayInterface display drm interface pointer + * @isResolutionChanged true if the resolution change is detected, false otherwise. * @return NO_ERROR on success, else otherwise */ - int prepareChannelCommit(ExynosDisplayDrmInterface::DrmModeAtomicReq &drmReq, - uint8_t channelId); + int prepareChannelCommit(ExynosDisplayDrmInterface::DrmModeAtomicReq& drmReq, uint8_t channelId, + ExynosDisplayDrmInterface* moduleDisplayInterface, + bool isResolutionChanged); /** * createHistogramDrmConfigLocked @@ -455,9 +465,9 @@ private: * @length size of the histogram config. * @return NO_ERROR on success, else otherwise */ - virtual int createHistogramDrmConfigLocked(const ChannelInfo &channel, - std::shared_ptr<void> &configPtr, - size_t &length) const + virtual int createHistogramDrmConfigLocked(const ChannelInfo& channel, + std::shared_ptr<void>& configPtr, + size_t& length) const REQUIRES(channel.channelInfoMutex); /** @@ -471,18 +481,21 @@ private: * @workingRoi converted roi from the requested roi * @return NO_ERROR on success, else otherwise */ - int convertRoiLocked(ExynosDisplayDrmInterface *moduleDisplayInterface, - const HistogramRoiRect &requestedRoi, HistogramRoiRect &workingRoi) const; + int convertRoiLocked(ExynosDisplayDrmInterface* moduleDisplayInterface, + const HistogramRoiRect& requestedRoi, HistogramRoiRect& workingRoi) const; + + void dumpHistogramCapability(String8& result) const; - void dumpHistogramCapability(String8 &result) const; + HistogramErrorCode validateHistogramConfig(const HistogramConfig& histogramConfig) const; + HistogramErrorCode validateHistogramRoi(const HistogramRoiRect& roi, const char* roiType) const; + HistogramErrorCode validateHistogramWeights(const HistogramWeights& weights) const; + HistogramErrorCode validateHistogramSamplePos(const HistogramSamplePos& samplePos) const; + HistogramErrorCode validateHistogramBlockingRoi( + const std::optional<HistogramRoiRect>& blockingRoi) const; - HistogramErrorCode validateHistogramConfig(const HistogramConfig &histogramConfig) const; - HistogramErrorCode validateHistogramRoi(const HistogramRoiRect &roi) const; - HistogramErrorCode validateHistogramWeights(const HistogramWeights &weights) const; - HistogramErrorCode validateHistogramSamplePos(const HistogramSamplePos &samplePos) const; + int calculateThreshold(const HistogramRoiRect& roi) const; - static int calculateThreshold(const HistogramRoiRect &roi); - static std::string toString(const ChannelStatus_t &status); - static std::string toString(const HistogramRoiRect &roi); - static std::string toString(const HistogramWeights &weights); + static std::string toString(const ChannelStatus_t& status); + static std::string toString(const HistogramRoiRect& roi); + static std::string toString(const HistogramWeights& weights); }; diff --git a/libhwc2.1/libdisplayinterface/ExynosDisplayDrmInterface.cpp b/libhwc2.1/libdisplayinterface/ExynosDisplayDrmInterface.cpp index e8a4001..badaa61 100644 --- a/libhwc2.1/libdisplayinterface/ExynosDisplayDrmInterface.cpp +++ b/libhwc2.1/libdisplayinterface/ExynosDisplayDrmInterface.cpp @@ -40,7 +40,6 @@ 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 nsecsPerSec = std::chrono::nanoseconds(1s).count(); constexpr auto vsyncPeriodTag = "VsyncPeriod"; typedef struct _drmModeAtomicReqItem drmModeAtomicReqItem, *drmModeAtomicReqItemPtr; @@ -62,6 +61,19 @@ 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() { { @@ -449,6 +461,16 @@ void ExynosDisplayDrmInterface::destroyLayer(ExynosLayer *layer) { } int32_t ExynosDisplayDrmInterface::getDisplayIdleTimerSupport(bool &outSupport) { + if (isFullVrrSupported()) { + outSupport = false; + return NO_ERROR; + } else if (isPseudoVrrSupported()) { + // Retuen true to avoid SF idle timer working. We insert frames manually + // for pseudo VRR, so ideally panel idle should be disabled in the driver. + outSupport = true; + 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); @@ -628,7 +650,7 @@ void ExynosDisplayDrmInterface::updateMountOrientation() for (auto &e : orientationEnums) { uint64_t enumValue; - std::tie(enumValue, err) = orientation.GetEnumValueWithName(e.second); + std::tie(enumValue, err) = orientation.getEnumValueWithName(e.second); if (!err && enumValue == drmOrientation) { mExynosDisplay->mMountOrientation = e.first; return; @@ -723,7 +745,7 @@ int32_t ExynosDisplayDrmInterface::initDrmDevice(DrmDevice *drmDevice) auto &plane = mDrmDevice->planes().at(i); uint32_t plane_id = plane->id(); - if (!plane->zpos_property().is_immutable()) { + if (!plane->zpos_property().isImmutable()) { /* Plane can be used for composition */ ExynosMPP *exynosMPP = mExynosDisplay->mResourceManager->getOtfMPPWithChannel(i); @@ -760,10 +782,10 @@ int32_t ExynosDisplayDrmInterface::initDrmDevice(DrmDevice *drmDevice) parseRangeEnums(plane->range_property()); } - chosePreferredConfig(); + choosePreferredConfig(); - // After chosePreferredConfig, the mDrmConnector->modes array is initialized, get the panel full - // resolution information here. + // After choosePreferredConfig, the mDrmConnector->modes array is initialized, get the panel + // full resolution information here. if (mExynosDisplay->mType == HWC_DISPLAY_PRIMARY) { retrievePanelFullResolution(); } @@ -841,6 +863,8 @@ void ExynosDisplayDrmInterface::Callback( mExynosDisplay->mLastVsyncTimestamp = timestamp; } + mExynosDisplay->onVsync(timestamp); + ExynosDevice *exynosDevice = mExynosDisplay->mDevice; if (exynosDevice->onVsync_2_4(mExynosDisplay->mDisplayId, timestamp, @@ -902,12 +926,14 @@ int32_t ExynosDisplayDrmInterface::setLowPowerMode() { mExynosDisplay->mXres = mDozeDrmMode.h_display(); mExynosDisplay->mYres = mDozeDrmMode.v_display(); // in nanoseconds - mExynosDisplay->mVsyncPeriod = nsecsPerSec / mDozeDrmMode.v_refresh(); + 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); } @@ -949,8 +975,7 @@ int32_t ExynosDisplayDrmInterface::setVsyncEnabled(uint32_t enabled) return NO_ERROR; } -int32_t ExynosDisplayDrmInterface::chosePreferredConfig() -{ +int32_t ExynosDisplayDrmInterface::choosePreferredConfig() { uint32_t num_configs = 0; int32_t err = getDisplayConfigs(&num_configs, NULL); if (err != HWC2_ERROR_NONE || !num_configs) @@ -959,9 +984,10 @@ int32_t ExynosDisplayDrmInterface::chosePreferredConfig() 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, &config); + err = mExynosDisplay->lookupDisplayConfigs(width, height, fps, fps, &config); } else { err = HWC2_ERROR_BAD_CONFIG; } @@ -997,19 +1023,25 @@ 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) { - int ret = mDrmConnector->UpdateModes(); + bool useVrrConfigs = isFullVrrSupported(); + 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: %s", mExynosDisplay->mDisplayName.c_str(), + useVrrConfigs ? "full" : (isPseudoVrrSupported() ? "pseudo" : "non-Vrr")); if (mDrmConnector->state() == DRM_MODE_CONNECTED) { /* @@ -1030,28 +1062,23 @@ int32_t ExynosDisplayDrmInterface::getDisplayConfigs( 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); - /* key: (width<<32 | height) */ - std::map<uint64_t, uint32_t> groupIds; - uint32_t groupId = 0; + DisplayConfigGroupIdGenerator groupIdGenerator; float peakRr = -1; - for (const DrmMode &mode : mDrmConnector->modes()) { displayConfigs_t configs; float rr = mode.v_refresh(); - configs.vsyncPeriod = nsecsPerSec / rr; + 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(); - uint64_t key = ((uint64_t)configs.width<<32) | configs.height; - auto it = groupIds.find(key); - if (it != groupIds.end()) { - configs.groupId = it->second; - } else { - configs.groupId = groupId; - groupIds.insert(std::make_pair(key, groupId)); - groupId++; - } - // Dots per 1000 inches configs.Xdpi = mm_width ? (mode.h_display() * kUmPerInch) / mm_width : -1; // Dots per 1000 inches @@ -1059,10 +1086,29 @@ int32_t ExynosDisplayDrmInterface::getDisplayConfigs( // 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("config group(%d), w(%d), h(%d), vsync(%d), xdpi(%d), ydpi(%d)", - configs.groupId, configs.width, configs.height, - configs.vsyncPeriod, configs.Xdpi, configs.Ydpi); + 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); } @@ -1093,13 +1139,16 @@ void ExynosDisplayDrmInterface::dumpDisplayConfigs() 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 display config[%d] %s:: id(%d), clock(%d), flags(%d), type(%d)", - mExynosDisplay->mDisplayName.c_str(), i, mode.name().c_str(), mode.id(), mode.clock(), mode.flags(), mode.type()); + 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()); - + 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()); } } @@ -1110,7 +1159,7 @@ int32_t ExynosDisplayDrmInterface::getDisplayVsyncPeriod(hwc2_vsync_period_t* ou int32_t ExynosDisplayDrmInterface::getConfigChangeDuration() { - const auto [ret, duration] = mDrmConnector->vrr_switch_duration().value(); + const auto [ret, duration] = mDrmConnector->rr_switch_duration().value(); if (!ret && duration > 0) { return duration; @@ -1248,7 +1297,7 @@ int32_t ExynosDisplayDrmInterface::setActiveConfigWithConstraints( } else if ((mActiveModeState.blob_id != 0) && (mActiveModeState.mode.id() == config)) { ALOGD("%s:: same mode %d", __func__, config); /* trigger resetConfigRequestStateLocked() */ - mVsyncCallback.setDesiredVsyncPeriod(nsecsPerSec / mActiveModeState.mode.v_refresh()); + mVsyncCallback.setDesiredVsyncPeriod(mActiveModeState.mode.te_period()); mDrmVSyncWorker.VSyncControl(true); return HWC2_ERROR_NONE; } @@ -1279,7 +1328,8 @@ int32_t ExynosDisplayDrmInterface::setActiveConfigWithConstraints( } } else { if (!isResSwitch) { - ret = setDisplayMode(drmReq, modeBlob ? modeBlob : mDesiredModeState.blob_id); + 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; @@ -1325,7 +1375,7 @@ int32_t ExynosDisplayDrmInterface::setActiveDrmMode(DrmMode const &mode) { reconfig = true; } - if ((ret = setDisplayMode(drmReq, modeBlob)) != NO_ERROR) { + if ((ret = setDisplayMode(drmReq, modeBlob, mode.id())) != NO_ERROR) { drmReq.addOldBlob(modeBlob); HWC_LOGE(mExynosDisplay, "%s: Fail to apply display mode", __func__); @@ -1394,9 +1444,9 @@ int32_t ExynosDisplayDrmInterface::createModeBlob(const DrmMode &mode, return NO_ERROR; } -int32_t ExynosDisplayDrmInterface::setDisplayMode( - DrmModeAtomicReq &drmReq, const uint32_t modeBlob) -{ +int32_t ExynosDisplayDrmInterface::setDisplayMode(DrmModeAtomicReq& drmReq, + const uint32_t& modeBlob, + const uint32_t& modeId) { int ret = NO_ERROR; if ((ret = drmReq.atomicAddProperty(mDrmCrtc->id(), @@ -1411,6 +1461,10 @@ int32_t ExynosDisplayDrmInterface::setDisplayMode( mDrmConnector->crtc_id_property(), mDrmCrtc->id())) < 0) return ret; + if (mConfigChangeCallback) { + drmReq.setAckCallback(std::bind(mConfigChangeCallback, modeId)); + } + return NO_ERROR; } @@ -1427,6 +1481,10 @@ int32_t ExynosDisplayDrmInterface::updateHdrCapabilities() 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(); @@ -1442,9 +1500,11 @@ int32_t ExynosDisplayDrmInterface::updateHdrCapabilities() (prop_max_avg_luminance.id() == 0) || (prop_min_luminance.id() == 0) || (prop_hdr_formats.id() == 0)) { - ALOGE("%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()); + 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; } @@ -1480,13 +1540,13 @@ int32_t ExynosDisplayDrmInterface::updateHdrCapabilities() } uint32_t typeBit; - std::tie(typeBit, ret) = prop_hdr_formats.GetEnumValueWithName("Dolby Vision"); + 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"); + 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()) { @@ -1495,7 +1555,7 @@ int32_t ExynosDisplayDrmInterface::updateHdrCapabilities() HDEBUGLOGD(eDebugHWC, "%s: supported hdr types : %d", mExynosDisplay->mDisplayName.c_str(), HAL_HDR_HDR10); } - std::tie(typeBit, ret) = prop_hdr_formats.GetEnumValueWithName("HLG"); + 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", @@ -1591,12 +1651,11 @@ int32_t ExynosDisplayDrmInterface::setupCommitFromDisplayConfig( plane->blend_property(), drmEnum, true)) < 0) return ret; - if (plane->zpos_property().id() && - !plane->zpos_property().is_immutable()) { + 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().range_min(); + std::tie(std::ignore, min_zpos) = plane->zpos_property().rangeMin(); if ((ret = drmReq.atomicAddProperty(plane->id(), plane->zpos_property(), configIndex + min_zpos)) < 0) @@ -1606,8 +1665,8 @@ int32_t ExynosDisplayDrmInterface::setupCommitFromDisplayConfig( if (plane->alpha_property().id()) { uint64_t min_alpha = 0; uint64_t max_alpha = 0; - std::tie(std::ignore, min_alpha) = plane->alpha_property().range_min(); - std::tie(std::ignore, max_alpha) = plane->alpha_property().range_max(); + 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) @@ -1855,7 +1914,8 @@ int32_t ExynosDisplayDrmInterface::deliverWinConfigData() 1 << mMipiSyncEnums[toUnderlying(HalMipiSyncType::HAL_MIPI_CMD_SYNC_REFRESH_RATE)]; } - if ((ret = setDisplayMode(drmReq, mDesiredModeState.blob_id)) < 0) { + if ((ret = setDisplayMode(drmReq, mDesiredModeState.blob_id, mDesiredModeState.mode.id())) < + 0) { HWC_LOGE(mExynosDisplay, "%s: Fail to apply display mode", __func__); return ret; @@ -2091,8 +2151,7 @@ int32_t ExynosDisplayDrmInterface::deliverWinConfigData() mDrmConnector->ResetLpMode(); getLowPowerDrmModeModeInfo(); } - mVsyncCallback.setDesiredVsyncPeriod( - nsecsPerSec/mActiveModeState.mode.v_refresh()); + mVsyncCallback.setDesiredVsyncPeriod(mActiveModeState.mode.te_period()); /* Enable vsync to check vsync period */ mDrmVSyncWorker.VSyncControl(true); } @@ -2139,6 +2198,15 @@ int32_t ExynosDisplayDrmInterface::triggerClearDisplayPlanes() 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; @@ -2292,7 +2360,7 @@ int32_t ExynosDisplayDrmInterface::DrmModeAtomicReq::atomicAddProperty( return -EINVAL; } - if (property.id()) { + if (property.id() && property.validateChange(value)) { int ret = drmModeAtomicAddProperty(mPset, id, property.id(), value); if (ret < 0) { @@ -2401,13 +2469,46 @@ int ExynosDisplayDrmInterface::DrmModeAtomicReq::commit(uint32_t flags, bool log 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) @@ -2559,9 +2660,9 @@ void ExynosDisplayDrmInterface::DrmReadbackInfo::pickFormatDataspace() int32_t ExynosDisplayDrmInterface::getDisplayFakeEdid(uint8_t &outPort, uint32_t &outDataSize, uint8_t *outData) { - int width = mExynosDisplay->mXres; - int height = mExynosDisplay->mYres; - int clock = (width) * (height) * 60 / 10000; + 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 */ @@ -2771,3 +2872,20 @@ int32_t ExynosDisplayDrmInterface::sendHistogramChannelIoctl(HistogramChannelIoc 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"; +} diff --git a/libhwc2.1/libdisplayinterface/ExynosDisplayDrmInterface.h b/libhwc2.1/libdisplayinterface/ExynosDisplayDrmInterface.h index d47e45a..0d28aa7 100644 --- a/libhwc2.1/libdisplayinterface/ExynosDisplayDrmInterface.h +++ b/libhwc2.1/libdisplayinterface/ExynosDisplayDrmInterface.h @@ -51,6 +51,28 @@ class ExynosDevice; template <typename T> using DrmArray = std::array<T, HWC_DRM_BO_MAX_PLANES>; +class DisplayConfigGroupIdGenerator { +public: + DisplayConfigGroupIdGenerator() = default; + ~DisplayConfigGroupIdGenerator() = default; + + // Vrr will utilize the last two parameters. In the case of non-vrr, they are automatically set + // to 0. Avoid using this class with a mix of Vrr and non-Vrr settings, as doing so may yield + // unexpected results. + int getGroupId(int width, int height, int minFrameInterval = 0, int vsyncPeriod = 0) { + const auto &key = std::make_tuple(width, height, minFrameInterval, vsyncPeriod); + if (groups_.count(key) > 0) { + return groups_[key]; + } + size_t last_id = groups_.size(); + groups_[key] = last_id; + return last_id; + } + +private: + std::map<std::tuple<int, int, int, int>, int> groups_; +}; + class FramebufferManager { public: FramebufferManager(){}; @@ -234,6 +256,12 @@ class ExynosDisplayDrmInterface : mOldBlobs.clear(); return NO_ERROR; }; + void dumpDrmAtomicCommitMessage(int err); + + void setAckCallback(std::function<void()> callback) { + mAckCallback = std::move(callback); + }; + private: drmModeAtomicReqPtr mPset; drmModeAtomicReqPtr mSavedPset; @@ -242,6 +270,15 @@ class ExynosDisplayDrmInterface : /* Destroy old blobs after commit */ std::vector<uint32_t> mOldBlobs; int drmFd() const { return mDrmDisplayInterface->mDrmDevice->fd(); } + + std::function<void()> mAckCallback; + + static constexpr uint32_t kAllowDumpDrmAtomicMessageTimeMs = 5000U; + static constexpr const char* kDrmModuleParametersDebugNode = + "/sys/module/drm/parameters/debug"; + static constexpr const int kEnableDrmAtomicMessage = 16; + static constexpr const int kDisableDrmDebugMessage = 0; + }; class ExynosVsyncCallback { public: @@ -369,6 +406,14 @@ class ExynosDisplayDrmInterface : uint32_t getCrtcId() { return mDrmCrtc->id(); } int32_t triggerClearDisplayPlanes(); + virtual void setVrrSettings(const VrrSettings_t& vrrSettings) override; + bool isFullVrrSupported() const { + return (mIsVrrModeSupported && mExynosDisplay->mDevice->isVrrApiSupported()); + } + bool isPseudoVrrSupported() const { + return (mIsVrrModeSupported && !mExynosDisplay->mDevice->isVrrApiSupported()); + } + protected: enum class HalMipiSyncType : uint32_t { HAL_MIPI_CMD_SYNC_REFRESH_RATE = 0, @@ -424,10 +469,11 @@ class ExynosDisplayDrmInterface : } }; int32_t createModeBlob(const DrmMode &mode, uint32_t &modeBlob); - int32_t setDisplayMode(DrmModeAtomicReq &drmReq, const uint32_t modeBlob); + int32_t setDisplayMode(DrmModeAtomicReq& drmReq, const uint32_t& modeBlob, + const uint32_t& modeId); int32_t clearDisplayMode(DrmModeAtomicReq &drmReq); int32_t clearDisplayPlanes(DrmModeAtomicReq &drmReq); - int32_t chosePreferredConfig(); + int32_t choosePreferredConfig(); int getDeconChannel(ExynosMPP *otfMPP); /* * This function adds FB and gets new fb id if fbId is 0, @@ -534,6 +580,7 @@ class ExynosDisplayDrmInterface : DrmReadbackInfo mReadbackInfo; FramebufferManager mFBManager; std::array<uint8_t, MONITOR_DESCRIPTOR_DATA_LENGTH> mMonitorDescription; + nsecs_t mLastDumpDrmAtomicMessageTime; private: int32_t getDisplayFakeEdid(uint8_t &outPort, uint32_t &outDataSize, uint8_t *outData); @@ -545,6 +592,12 @@ class ExynosDisplayDrmInterface : int32_t mPanelFullResolutionHSize = 0; int32_t mPanelFullResolutionVSize = 0; + // Vrr related settings. + bool mIsVrrModeSupported = false; + int32_t mNotifyExpectedPresentHeadsUpNs = 0; + int32_t mNotifyExpectedPresentTimeoutNs = 0; + std::function<void(int)> mConfigChangeCallback; + /** * retrievePanelFullResolution * @@ -558,6 +611,8 @@ class ExynosDisplayDrmInterface : public: virtual bool readHotplugStatus(); + virtual int readHotplugErrorCode(); + virtual void resetHotplugErrorCode(); }; #endif diff --git a/libhwc2.1/libdisplayinterface/ExynosDisplayInterface.cpp b/libhwc2.1/libdisplayinterface/ExynosDisplayInterface.cpp index 14d7ff4..4d99aaf 100644 --- a/libhwc2.1/libdisplayinterface/ExynosDisplayInterface.cpp +++ b/libhwc2.1/libdisplayinterface/ExynosDisplayInterface.cpp @@ -67,3 +67,5 @@ bool ExynosDisplayInterface::isPrimary() return ((mExynosDisplay != nullptr) && (mExynosDisplay->mType == HWC_DISPLAY_PRIMARY)); } + +void ExynosDisplayInterface::setVrrSettings(const VrrSettings_t& vrrSettings) {} diff --git a/libhwc2.1/libdisplayinterface/ExynosDisplayInterface.h b/libhwc2.1/libdisplayinterface/ExynosDisplayInterface.h index 2855618..de68715 100644 --- a/libhwc2.1/libdisplayinterface/ExynosDisplayInterface.h +++ b/libhwc2.1/libdisplayinterface/ExynosDisplayInterface.h @@ -17,13 +17,17 @@ #ifndef _EXYNOSDISPLAYINTERFACE_H #define _EXYNOSDISPLAYINTERFACE_H -#include <sys/types.h> #include <hardware/hwcomposer2.h> +#include <sys/types.h> #include <utils/Errors.h> + #include "ExynosHWCHelper.h" class ExynosDisplay; +struct VrrSettings; +typedef struct VrrSettings VrrSettings_t; + using namespace android; class ExynosDisplayInterface { protected: @@ -86,6 +90,10 @@ class ExynosDisplayInterface { virtual int32_t waitVBlank() { return 0; }; virtual bool readHotplugStatus() { return true; }; + virtual int readHotplugErrorCode() { return 0; }; + virtual void resetHotplugErrorCode(){}; + + virtual void setVrrSettings(const VrrSettings_t& vrrSettings); public: uint32_t mType = INTERFACE_TYPE_NONE; diff --git a/libhwc2.1/libdrmresource/drm/drmconnector.cpp b/libhwc2.1/libdrmresource/drm/drmconnector.cpp index 6aad4bb..12de593 100644 --- a/libhwc2.1/libdrmresource/drm/drmconnector.cpp +++ b/libhwc2.1/libdrmresource/drm/drmconnector.cpp @@ -17,17 +17,18 @@ #define LOG_TAG "hwc-drm-connector" #include "drmconnector.h" -#include "drmdevice.h" +#include <cutils/properties.h> #include <errno.h> #include <inttypes.h> +#include <log/log.h> #include <stdint.h> +#include <xf86drmMode.h> #include <array> #include <sstream> -#include <log/log.h> -#include <xf86drmMode.h> +#include "drmdevice.h" #ifndef DRM_MODE_CONNECTOR_WRITEBACK #define DRM_MODE_CONNECTOR_WRITEBACK 18 @@ -155,9 +156,9 @@ int DrmConnector::Init() { ALOGE("Could not get panel_idle_support property\n"); } - ret = drm_->GetConnectorProperty(*this, "vrr_switch_duration", &vrr_switch_duration_); + ret = drm_->GetConnectorProperty(*this, "rr_switch_duration", &rr_switch_duration_); if (ret) { - ALOGE("Could not get vrr_switch_duration property\n"); + ALOGE("Could not get rr_switch_duration property\n"); } ret = drm_->GetConnectorProperty(*this, "operation_rate", &operation_rate_); @@ -191,7 +192,7 @@ int DrmConnector::Init() { properties_.push_back(&lhbm_on_); properties_.push_back(&mipi_sync_); properties_.push_back(&panel_idle_support_); - properties_.push_back(&vrr_switch_duration_); + properties_.push_back(&rr_switch_duration_); properties_.push_back(&operation_rate_); properties_.push_back(&refresh_on_lp_); @@ -251,7 +252,7 @@ std::string DrmConnector::name() const { } } -int DrmConnector::UpdateModes() { +int DrmConnector::UpdateModes(bool is_vrr_mode) { std::lock_guard<std::recursive_mutex> lock(modes_lock_); int fd = drm_->fd(); @@ -276,6 +277,10 @@ int DrmConnector::UpdateModes() { state_ = c->connection; + // Update mm_width_ and mm_height_ for xdpi/ydpi calculations + mm_width_ = c->mmWidth; + mm_height_ = c->mmHeight; + bool preferred_mode_found = false; std::vector<DrmMode> new_modes; for (int i = 0; i < c->count_modes; ++i) { @@ -288,6 +293,10 @@ int DrmConnector::UpdateModes() { } } if (!exists) { + // Remove modes that mismatch with the VRR setting.. + if (is_vrr_mode != ((c->modes[i].type & DRM_MODE_TYPE_VRR) != 0)) { + continue; + } DrmMode m(&c->modes[i]); m.set_id(drm_->next_mode_id()); new_modes.push_back(m); @@ -310,6 +319,24 @@ int DrmConnector::UpdateEdidProperty() { return drm_->UpdateConnectorProperty(*this, &edid_property_); } +int DrmConnector::UpdateLuminanceAndHdrProperties() { + int res = 0; + + res = drm_->UpdateConnectorProperty(*this, &max_luminance_); + if (res) + return res; + res = drm_->UpdateConnectorProperty(*this, &max_avg_luminance_); + if (res) + return res; + res = drm_->UpdateConnectorProperty(*this, &min_luminance_); + if (res) + return res; + res = drm_->UpdateConnectorProperty(*this, &hdr_formats_); + if (res) + return res; + return 0; +} + const DrmMode &DrmConnector::active_mode() const { return active_mode_; } @@ -435,8 +462,8 @@ const DrmProperty &DrmConnector::panel_idle_support() const { return panel_idle_support_; } -const DrmProperty &DrmConnector::vrr_switch_duration() const { - return vrr_switch_duration_; +const DrmProperty &DrmConnector::rr_switch_duration() const { + return rr_switch_duration_; } DrmEncoder *DrmConnector::encoder() const { diff --git a/libhwc2.1/libdrmresource/drm/drmdevice.cpp b/libhwc2.1/libdrmresource/drm/drmdevice.cpp index ced174c..d01f68a 100644 --- a/libhwc2.1/libdrmresource/drm/drmdevice.cpp +++ b/libhwc2.1/libdrmresource/drm/drmdevice.cpp @@ -85,10 +85,6 @@ std::tuple<int, int> DrmDevice::Init(const char *path, int num_displays) { max_resolution_ = std::pair<uint32_t, uint32_t>(res->max_width, res->max_height); - // Assumes that the primary display will always be in the first - // drm_device opened. - bool found_primary = num_displays != 0; - for (int i = 0; !ret && i < res->count_crtcs; ++i) { drmModeCrtcPtr c = drmModeGetCrtc(fd(), res->crtcs[i]); if (!c) { @@ -177,31 +173,22 @@ std::tuple<int, int> DrmDevice::Init(const char *path, int num_displays) { connectors_.emplace_back(std::move(conn)); } - // First look for primary amongst internal connectors + // First look for primary amongst internal connectors and for + // the others assign consecutive display_numbers. for (auto &conn : connectors_) { - if (conn->internal() && !found_primary) { + if (conn->internal() && conn->display() < 0) { conn->set_display(num_displays); displays_[num_displays] = num_displays; ++num_displays; - found_primary = true; - break; } } - // Then pick first available as primary and for the others assign - // consecutive display_numbers. + // Assign consecutive display_numbers for external connectors. for (auto &conn : connectors_) { - if (conn->external() || conn->internal()) { - if (!found_primary) { - conn->set_display(num_displays); - displays_[num_displays] = num_displays; - found_primary = true; - ++num_displays; - } else if (conn->display() < 0) { - conn->set_display(num_displays); - displays_[num_displays] = num_displays; - ++num_displays; - } + if (conn->external() && conn->display() < 0) { + conn->set_display(num_displays); + displays_[num_displays] = num_displays; + ++num_displays; } } @@ -484,14 +471,14 @@ int DrmDevice::GetProperty(uint32_t obj_id, uint32_t obj_type, for (int i = 0; !found && (size_t)i < props->count_props; ++i) { drmModePropertyPtr p = drmModeGetProperty(fd(), props->props[i]); if (!strcmp(p->name, prop_name)) { - property->Init(p, props->prop_values[i]); + property->init(p, props->prop_values[i]); found = true; } drmModeFreeProperty(p); } if (!found) - property->SetName(prop_name); + property->setName(prop_name); drmModeFreeObjectProperties(props); return found ? 0 : -ENOENT; @@ -525,7 +512,7 @@ int DrmDevice::UpdateObjectProperty(int id, int type, DrmProperty *property) { for (int i = 0; !found && (size_t)i < props->count_props; ++i) { drmModePropertyPtr p = drmModeGetProperty(fd(), props->props[i]); if (props->props[i] == property->id()) { - property->UpdateValue(props->prop_values[i]); + property->updateValue(props->prop_values[i]); found = true; } drmModeFreeProperty(p); diff --git a/libhwc2.1/libdrmresource/drm/drmmode.cpp b/libhwc2.1/libdrmresource/drm/drmmode.cpp index c3ab385..a883a20 100644 --- a/libhwc2.1/libdrmresource/drm/drmmode.cpp +++ b/libhwc2.1/libdrmresource/drm/drmmode.cpp @@ -23,6 +23,12 @@ namespace android { +namespace { +inline bool HasFlag(const int value, const int &flag) { + return ((value & flag) == flag); +} +}; // namespace + DrmMode::DrmMode(drmModeModeInfoPtr m) : id_(0), clock_(m->clock), @@ -123,9 +129,52 @@ uint32_t DrmMode::v_scan() const { float DrmMode::v_refresh() const { // Always recalculate refresh to report correct float rate + if (v_total_ == 0 || h_total_ == 0) { + return 0.0f; + } return clock_ / (float)(v_total_ * h_total_) * 1000.0f; } +float DrmMode::te_frequency() const { + auto freq = v_refresh(); + if (is_vrr_mode()) { + if (HasFlag(flags_, DRM_MODE_FLAG_TE_FREQ_X2)) { + freq *= 2; + } else if (HasFlag(flags_, DRM_MODE_FLAG_TE_FREQ_X4)) { + freq *= 4; + } else { + if (!HasFlag(flags_, DRM_MODE_FLAG_TE_FREQ_X1)) { + return 0.0f; + } + } + } + return freq; +} + +// vertical refresh period. +float DrmMode::v_period(int64_t unit) const { + auto frequency = v_refresh(); + if (frequency == 0.0f) { + return 0.0f; + } + return (1.0 / frequency) * unit; +} + +float DrmMode::te_period(int64_t unit) const { + auto frequency = te_frequency(); + if (frequency == 0.0f) { + return 0.0f; + } + return (1.0 / frequency) * unit; +} + +bool DrmMode::is_operation_rate_to_bts() const { + if (!is_vrr_mode()) { + return HasFlag(flags_, DRM_MODE_FLAG_BTS_OP_RATE); + } + return false; +} + uint32_t DrmMode::flags() const { return flags_; } diff --git a/libhwc2.1/libdrmresource/drm/drmproperty.cpp b/libhwc2.1/libdrmresource/drm/drmproperty.cpp index 8f856d6..6c1fff4 100644 --- a/libhwc2.1/libdrmresource/drm/drmproperty.cpp +++ b/libhwc2.1/libdrmresource/drm/drmproperty.cpp @@ -26,6 +26,10 @@ #include <inttypes.h> #include <utils/Errors.h> +static inline int64_t U642I64(uint64_t val) { + return *(reinterpret_cast<int64_t *>(&val)); +} + namespace android { DrmProperty::DrmPropertyEnum::DrmPropertyEnum(drm_mode_property_enum *e) @@ -37,26 +41,22 @@ DrmProperty::DrmPropertyEnum::~DrmPropertyEnum() { DrmProperty::DrmProperty(drmModePropertyPtr p, uint64_t value) : id_(0), type_(DRM_PROPERTY_TYPE_INVALID), flags_(0), name_("") { - Init(p, value); + init(p, value); } -void DrmProperty::Init(drmModePropertyPtr p, uint64_t value) { +void DrmProperty::init(drmModePropertyPtr p, uint64_t value) { id_ = p->prop_id; flags_ = p->flags; name_ = p->name; value_ = value; - for (int i = 0; i < p->count_values; ++i) - values_.push_back(p->values[i]); + for (int i = 0; i < p->count_values; ++i) values_.push_back(p->values[i]); - for (int i = 0; i < p->count_enums; ++i) - enums_.push_back(DrmPropertyEnum(&p->enums[i])); + for (int i = 0; i < p->count_enums; ++i) enums_.push_back(DrmPropertyEnum(&p->enums[i])); - for (int i = 0; i < p->count_blobs; ++i) - blob_ids_.push_back(p->blob_ids[i]); + for (int i = 0; i < p->count_blobs; ++i) blob_ids_.push_back(p->blob_ids[i]); - if ((flags_ & DRM_MODE_PROP_RANGE) || - (flags_ & DRM_MODE_PROP_SIGNED_RANGE)) + if ((flags_ & DRM_MODE_PROP_RANGE) || (flags_ & DRM_MODE_PROP_SIGNED_RANGE)) type_ = DRM_PROPERTY_TYPE_INT; else if (flags_ & DRM_MODE_PROP_ENUM) type_ = DRM_PROPERTY_TYPE_ENUM; @@ -106,29 +106,37 @@ std::tuple<int, uint64_t> DrmProperty::value() const { void DrmProperty::printProperty() const { - ALOGD("====================================================="); - ALOGD("name: %s, type(%d), value_(%" PRId64 ")", name_.c_str(), type_, value_); - ALOGD("values.size(%zu)", values_.size()); - for(uint32_t i = 0; i < values_.size(); i++) { - ALOGD("[%d] %" PRId64 "", i, values_[i]); - } - ALOGD("enums.size(%zu)", enums_.size()); - uint32_t i = 0; - for (auto const &it : enums_) { - ALOGD("[%d] %s %" PRId64 "", i++, it.name_.c_str(), it.value_); - } + ALOGD("====================================================="); + ALOGD("name: %s, type(%d), value_(%" PRId64 ")", name_.c_str(), type_, value_); + ALOGD("values.size(%zu)", values_.size()); + for (uint32_t i = 0; i < values_.size(); i++) { + ALOGD("[%d] %" PRId64 "", i, values_[i]); + } + ALOGD("enums.size(%zu)", enums_.size()); + uint32_t i = 0; + for (auto const &it : enums_) { + ALOGD("[%d] %s %" PRId64 "", i++, it.name_.c_str(), it.value_); + } } -bool DrmProperty::is_immutable() const { +bool DrmProperty::isImmutable() const { return id_ && (flags_ & DRM_MODE_PROP_IMMUTABLE); } -bool DrmProperty::is_range() const { +bool DrmProperty::isRange() const { return id_ && (flags_ & DRM_MODE_PROP_RANGE); } -std::tuple<int, uint64_t> DrmProperty::range_min() const { - if (!is_range()) +bool DrmProperty::isSignedRange() const { + return (flags_ & DRM_MODE_PROP_EXTENDED_TYPE) == DRM_MODE_PROP_SIGNED_RANGE; +} + +bool DrmProperty::isBitmask() const { + return id_ && (flags_ & DRM_MODE_PROP_BITMASK); +} + +std::tuple<int, uint64_t> DrmProperty::rangeMin() const { + if (!isRange()) return std::make_tuple(-EINVAL, 0); if (values_.size() < 1) return std::make_tuple(-ENOENT, 0); @@ -136,8 +144,8 @@ std::tuple<int, uint64_t> DrmProperty::range_min() const { return std::make_tuple(0, values_[0]); } -std::tuple<int, uint64_t> DrmProperty::range_max() const { - if (!is_range()) +std::tuple<int, uint64_t> DrmProperty::rangeMax() const { + if (!isRange()) return std::make_tuple(-EINVAL, 0); if (values_.size() < 2) return std::make_tuple(-ENOENT, 0); @@ -145,8 +153,7 @@ std::tuple<int, uint64_t> DrmProperty::range_max() const { return std::make_tuple(0, values_[1]); } -std::tuple<uint64_t, int> DrmProperty::GetEnumValueWithName( - std::string name) const { +std::tuple<uint64_t, int> DrmProperty::getEnumValueWithName(std::string name) const { for (auto it : enums_) { if (it.name_.compare(name) == 0) { return std::make_tuple(it.value_, 0); @@ -156,34 +163,68 @@ std::tuple<uint64_t, int> DrmProperty::GetEnumValueWithName( return std::make_tuple(UINT64_MAX, -EINVAL); } -void DrmProperty::UpdateValue(uint64_t value) { +bool DrmProperty::validateChange(uint64_t value) const { + if (isImmutable()) { + ALOGE("%s: %s is immutable drm property (%zu)", __func__, name().c_str()); + return false; + } else if (isRange()) { + if (value < values_[0] || value > values_[1]) { + ALOGE("%s: range property %s set to %" PRIu64 " is invalid [%" PRIu64 "-%" PRIu64 "]", + __func__, name().c_str(), value, values_[0], values_[1]); + return false; + } + } else if (isSignedRange()) { + int64_t svalue = U642I64(value); + + if (svalue < U642I64(values_[0]) || svalue > U642I64(values_[1])) { + ALOGE("%s: signed property %s set to %" PRIi64 " is invalid [%" PRIi64 "-%" PRIi64 "]", + __func__, name().c_str(), svalue, U642I64(values_[0]), U642I64(values_[1])); + return false; + } + } else if (isBitmask()) { + uint64_t valid_mask = 0; + + for (auto i = 0; i < values_.size(); i++) { + valid_mask |= (1ULL << values_[i]); + } + if (value & ~valid_mask) { + ALOGE("%s: bitmask property %s set to 0x%" PRIx64 " is invalid [0x%" PRIx64 "]", __func__, + name().c_str(), value, valid_mask); + return false; + } + } + + return true; +} + +void DrmProperty::updateValue(uint64_t value) { value_ = value; } std::tuple<uint64_t, int> DrmEnumParser::halToDrmEnum(const uint32_t halData, const MapHal2DrmEnum& drmEnums) { - auto it = drmEnums.find(halData); - if (it != drmEnums.end()) { - return std::make_tuple(it->second, NO_ERROR); - } else { - ALOGE("%s::Failed to find standard enum(%d)", - __func__, halData); - return std::make_tuple(0, -EINVAL); - } + auto it = drmEnums.find(halData); + if (it != drmEnums.end()) { + return std::make_tuple(it->second, NO_ERROR); + } else { + ALOGE("%s: Failed to find standard enum(%d)", __func__, halData); + return std::make_tuple(0, -EINVAL); + } } void DrmEnumParser::parseEnums(const DrmProperty &property, const std::vector<std::pair<uint32_t, const char *>> &enums, MapHal2DrmEnum& out_enums) { - uint64_t value; - int err; - for (auto &e : enums) { - std::tie(value, err) = property.GetEnumValueWithName(e.second); - if (err) - ALOGE("Fail to find enum value with name %s", e.second); - else - out_enums[e.first] = value; + uint64_t value; + int err; + for (auto &e : enums) { + std::tie(value, err) = property.getEnumValueWithName(e.second); + if (err) { + ALOGE("%s: Fail to find enum value with name %s", __func__, e.second); + } else { + out_enums[e.first] = value; } + } } } // namespace android diff --git a/libhwc2.1/libdrmresource/drm/vsyncworker.cpp b/libhwc2.1/libdrmresource/drm/vsyncworker.cpp index a795fb3..792946f 100644 --- a/libhwc2.1/libdrmresource/drm/vsyncworker.cpp +++ b/libhwc2.1/libdrmresource/drm/vsyncworker.cpp @@ -41,93 +41,99 @@ namespace android { VSyncWorker::VSyncWorker() : Worker("vsync", 2, true), - drm_(NULL), - display_(-1), - enabled_(false), - last_timestamp_(-1) { -} + mDrmDevice(NULL), + mDisplay(-1), + mEnabled(false), + mLastTimestampNs(-1) {} VSyncWorker::~VSyncWorker() { Exit(); } -int VSyncWorker::Init(DrmDevice *drm, int display, const String8 &display_trace_name) { - drm_ = drm; - display_ = display; - display_trace_name_ = display_trace_name; - hw_vsync_period_tag_.appendFormat("HWVsyncPeriod for %s", display_trace_name.c_str()); - hw_vsync_enabled_tag_.appendFormat("HWCVsync for %s", display_trace_name.c_str()); +int VSyncWorker::Init(DrmDevice *drm, int display, const String8 &displayTraceName) { + mDrmDevice = drm; + mDisplay = display; + mDisplayTraceName = displayTraceName; + mHwVsyncPeriodTag.appendFormat("HWVsyncPeriod for %s", displayTraceName.c_str()); + mHwVsyncEnabledTag.appendFormat("HWCVsync for %s", displayTraceName.c_str()); return InitWorker(); } void VSyncWorker::RegisterCallback(std::shared_ptr<VsyncCallback> callback) { Lock(); - callback_ = callback; + mCallback = callback; Unlock(); } void VSyncWorker::VSyncControl(bool enabled) { Lock(); - enabled_ = enabled; - last_timestamp_ = -1; + mEnabled = enabled; + mLastTimestampNs = -1; Unlock(); - ATRACE_INT(hw_vsync_enabled_tag_.c_str(), static_cast<int32_t>(enabled)); - ATRACE_INT64(hw_vsync_period_tag_.c_str(), 0); + ATRACE_INT(mHwVsyncEnabledTag.c_str(), static_cast<int32_t>(enabled)); + ATRACE_INT64(mHwVsyncPeriodTag.c_str(), 0); Signal(); } /* - * Returns the timestamp of the next vsync in phase with last_timestamp_. + * Returns the timestamp of the next vsync in phase with mLastTimestampNs. * For example: - * last_timestamp_ = 137 - * frame_ns = 50 - * current = 683 + * mLastTimestampNs = 137 + * vsyncPeriodNs = 50 + * currentTimeNs = 683 * - * expect = (50 * ((683 - 137)/50 + 1)) + 137 - * expect = 687 + * expectTimeNs = (50 * ((683 - 137) / 50 + 1)) + 137 + * expectTimeNs = 687 * * Thus, we must sleep until timestamp 687 to maintain phase with the last * timestamp. But if we don't know last vblank timestamp, sleep one vblank * then try to get vblank from driver again. */ -int VSyncWorker::GetPhasedVSync(int64_t frame_ns, int64_t &expect) { +int VSyncWorker::GetPhasedVSync(uint32_t vsyncPeriodNs, int64_t &expectTimeNs) { struct timespec now; if (clock_gettime(CLOCK_MONOTONIC, &now)) { ALOGE("clock_gettime failed %d", errno); return -EPERM; } - int64_t current = now.tv_sec * nsecsPerSec + now.tv_nsec; - if (last_timestamp_ < 0) { - expect = current + frame_ns; + int64_t currentTimeNs = now.tv_sec * nsecsPerSec + now.tv_nsec; + if (mLastTimestampNs < 0) { + expectTimeNs = currentTimeNs + vsyncPeriodNs; return -EAGAIN; } - expect = frame_ns * ((current - last_timestamp_) / frame_ns + 1) + last_timestamp_; + expectTimeNs = vsyncPeriodNs * ((currentTimeNs - mLastTimestampNs) / vsyncPeriodNs + 1) + + mLastTimestampNs; return 0; } -int VSyncWorker::SyntheticWaitVBlank(int64_t ×tamp) { - float refresh = 60.0f; // Default to 60Hz refresh rate +int VSyncWorker::SyntheticWaitVBlank(int64_t ×tampNs) { + uint32_t vsyncPeriodNs = kDefaultVsyncPeriodNanoSecond; + int32_t refreshRate = kDefaultRefreshRateFrequency; - DrmConnector *conn = drm_->GetConnectorForDisplay(display_); - if (conn && conn->active_mode().v_refresh() != 0.0f) { - refresh = conn->active_mode().v_refresh(); + DrmConnector *conn = mDrmDevice->GetConnectorForDisplay(mDisplay); + if (conn && conn->active_mode().te_period() != 0.0f && + conn->active_mode().v_refresh() != 0.0f) { + vsyncPeriodNs = static_cast<uint32_t>(conn->active_mode().te_period()); + refreshRate = static_cast<int32_t>(conn->active_mode().v_refresh()); } else { - ALOGW("Vsync worker active with conn=%p refresh=%f\n", conn, - conn ? conn->active_mode().v_refresh() : 0.0f); + ALOGW("Vsync worker active with conn=%p vsync=%u refresh=%d\n", conn, + conn ? static_cast<uint32_t>(conn->active_mode().te_period()) : + kDefaultVsyncPeriodNanoSecond, + conn ? static_cast<int32_t>(conn->active_mode().v_refresh()) : + kDefaultRefreshRateFrequency); } - int64_t phased_timestamp; - int ret = GetPhasedVSync(nsecsPerSec / refresh, phased_timestamp); + int64_t phasedTimestampNs; + int ret = GetPhasedVSync(vsyncPeriodNs, phasedTimestampNs); if (ret && ret != -EAGAIN) return -1; struct timespec vsync; - vsync.tv_sec = phased_timestamp / nsecsPerSec; - vsync.tv_nsec = phased_timestamp % nsecsPerSec; + vsync.tv_sec = phasedTimestampNs / nsecsPerSec; + vsync.tv_nsec = phasedTimestampNs % nsecsPerSec; int err; do { @@ -135,7 +141,7 @@ int VSyncWorker::SyntheticWaitVBlank(int64_t ×tamp) { } while (err == EINTR); if (err || ret) return -1; - timestamp = (int64_t)vsync.tv_sec * nsecsPerSec + (int64_t)vsync.tv_nsec; + timestampNs = (int64_t)vsync.tv_sec * nsecsPerSec + (int64_t)vsync.tv_nsec; return 0; } @@ -144,7 +150,7 @@ void VSyncWorker::Routine() { int ret; Lock(); - if (!enabled_) { + if (!mEnabled) { ret = WaitForSignalOrExitLocked(); if (ret == -EINTR) { Unlock(); @@ -152,32 +158,32 @@ void VSyncWorker::Routine() { } } - int display = display_; - std::shared_ptr<VsyncCallback> callback(callback_); + int display = mDisplay; + std::shared_ptr<VsyncCallback> callback(mCallback); Unlock(); - DrmCrtc *crtc = drm_->GetCrtcForDisplay(display); + DrmCrtc *crtc = mDrmDevice->GetCrtcForDisplay(display); if (!crtc) { ALOGE("Failed to get crtc for display"); return; } - uint32_t high_crtc = (crtc->pipe() << DRM_VBLANK_HIGH_CRTC_SHIFT); + uint32_t highCrtc = (crtc->pipe() << DRM_VBLANK_HIGH_CRTC_SHIFT); drmVBlank vblank; memset(&vblank, 0, sizeof(vblank)); vblank.request.type = - (drmVBlankSeqType)(DRM_VBLANK_RELATIVE | (high_crtc & DRM_VBLANK_HIGH_CRTC_MASK)); + (drmVBlankSeqType)(DRM_VBLANK_RELATIVE | (highCrtc & DRM_VBLANK_HIGH_CRTC_MASK)); vblank.request.sequence = 1; - int64_t timestamp; - ret = drmWaitVBlank(drm_->fd(), &vblank); + int64_t timestampNs; + ret = drmWaitVBlank(mDrmDevice->fd(), &vblank); if (ret) { - if (SyntheticWaitVBlank(timestamp)) { + if (SyntheticWaitVBlank(timestampNs)) { // postpone the callback until we get a real value from the hardware return; } } else { - timestamp = (int64_t)vblank.reply.tval_sec * nsecsPerSec + + timestampNs = (int64_t)vblank.reply.tval_sec * nsecsPerSec + (int64_t)vblank.reply.tval_usec * 1000; } @@ -199,24 +205,25 @@ void VSyncWorker::Routine() { * Please note that issue described below is different one and it is related * to RegisterCallback, not to disabling vsync via VSyncControl. */ - if (!enabled_) return; + if (!mEnabled) + return; /* - * There's a race here where a change in callback_ will not take effect until + * There's a race here where a change in mCallback will not take effect until * the next subsequent requested vsync. This is unavoidable since we can't * call the vsync hook while holding the thread lock. * - * We could shorten the race window by caching callback_ right before calling - * the hook. However, in practice, callback_ is only updated once, so it's not + * We could shorten the race window by caching mCallback right before calling + * the hook. However, in practice, mCallback is only updated once, so it's not * worth the overhead. */ - if (callback) callback->Callback(display, timestamp); + if (callback) callback->Callback(display, timestampNs); - if (last_timestamp_ >= 0) { - int64_t period = timestamp - last_timestamp_; - ATRACE_INT64(hw_vsync_period_tag_.c_str(), period); - ALOGV("HW vsync period %" PRId64 "ns for %s", period, display_trace_name_.c_str()); + if (mLastTimestampNs >= 0) { + int64_t period = timestampNs - mLastTimestampNs; + ATRACE_INT64(mHwVsyncPeriodTag.c_str(), period); + ALOGV("HW vsync period %" PRId64 "ns for %s", period, mDisplayTraceName.c_str()); } - last_timestamp_ = timestamp; + mLastTimestampNs = timestampNs; } } // namespace android diff --git a/libhwc2.1/libdrmresource/include/drmconnector.h b/libhwc2.1/libdrmresource/include/drmconnector.h index 98960b3..d05c1d5 100644 --- a/libhwc2.1/libdrmresource/include/drmconnector.h +++ b/libhwc2.1/libdrmresource/include/drmconnector.h @@ -53,8 +53,9 @@ class DrmConnector { std::string name() const; - int UpdateModes(); + int UpdateModes(bool is_vrr_mode = false); int UpdateEdidProperty(); + int UpdateLuminanceAndHdrProperties(); const std::vector<DrmMode> &modes() const { return modes_; @@ -86,7 +87,7 @@ class DrmConnector { const DrmProperty &lhbm_on() const; const DrmProperty &mipi_sync() const; const DrmProperty &panel_idle_support() const; - const DrmProperty &vrr_switch_duration() const; + const DrmProperty &rr_switch_duration() const; const DrmProperty &operation_rate() const; const DrmProperty &refresh_on_lp() const; @@ -147,7 +148,7 @@ class DrmConnector { DrmProperty lhbm_on_; DrmProperty mipi_sync_; DrmProperty panel_idle_support_; - DrmProperty vrr_switch_duration_; + DrmProperty rr_switch_duration_; DrmProperty operation_rate_; DrmProperty refresh_on_lp_; std::vector<DrmProperty *> properties_; diff --git a/libhwc2.1/libdrmresource/include/drmeventlistener.h b/libhwc2.1/libdrmresource/include/drmeventlistener.h index c26a458..ecaf68a 100644 --- a/libhwc2.1/libdrmresource/include/drmeventlistener.h +++ b/libhwc2.1/libdrmresource/include/drmeventlistener.h @@ -26,6 +26,9 @@ namespace android { +constexpr uint32_t kDefaultVsyncPeriodNanoSecond = 16666666; +constexpr int32_t kDefaultRefreshRateFrequency = 60; + class DrmDevice; class DrmEventHandler { diff --git a/libhwc2.1/libdrmresource/include/drmmode.h b/libhwc2.1/libdrmresource/include/drmmode.h index 4cc06b1..2dbcc60 100644 --- a/libhwc2.1/libdrmresource/include/drmmode.h +++ b/libhwc2.1/libdrmresource/include/drmmode.h @@ -19,8 +19,24 @@ #include <stdint.h> #include <xf86drmMode.h> + +#include <ratio> #include <string> +// Alternative definitions(alias) of DRM modes and flags for VRR. +// The kernel contains corresponding defines that MUST align with those specified here.. +#define DRM_MODE_TYPE_VRR DRM_MODE_TYPE_USERDEF +#define DRM_MODE_FLAG_NS DRM_MODE_FLAG_CLKDIV2 +#define DRM_MODE_FLAG_TE_FREQ_X1 DRM_MODE_FLAG_PHSYNC +#define DRM_MODE_FLAG_TE_FREQ_X2 DRM_MODE_FLAG_NHSYNC +#define DRM_MODE_FLAG_TE_FREQ_X4 DRM_MODE_FLAG_PVSYNC + +// BTS needs to take operation rate into account +#define DRM_MODE_FLAG_BTS_OP_RATE DRM_MODE_FLAG_NVSYNC + +#define PANEL_REFRESH_CTRL_FI (1 << 0) +#define PANEL_REFRESH_CTRL_IDLE (1 << 1) + namespace android { class DrmMode { @@ -31,6 +47,9 @@ class DrmMode { bool operator==(const drmModeModeInfo &m) const; void ToDrmModeModeInfo(drm_mode_modeinfo *m) const; + inline bool is_vrr_mode() const { return (type_ & DRM_MODE_TYPE_VRR); }; + inline bool is_ns_mode() const { return (flags_ & DRM_MODE_FLAG_NS); }; + uint32_t id() const; void set_id(uint32_t id); @@ -48,7 +67,12 @@ class DrmMode { uint32_t v_total() const; uint32_t v_scan() const; float v_refresh() const; + float te_frequency() const; + // Convert frequency to period, with the default unit being nanoseconds. + float v_period(int64_t unit = std::nano::den) const; + float te_period(int64_t unit = std::nano::den) const; + bool is_operation_rate_to_bts() const; uint32_t flags() const; uint32_t type() const; diff --git a/libhwc2.1/libdrmresource/include/drmproperty.h b/libhwc2.1/libdrmresource/include/drmproperty.h index 2c4eb7a..7cc6f91 100644 --- a/libhwc2.1/libdrmresource/include/drmproperty.h +++ b/libhwc2.1/libdrmresource/include/drmproperty.h @@ -41,21 +41,24 @@ class DrmProperty { DrmProperty(const DrmProperty &) = delete; DrmProperty &operator=(const DrmProperty &) = delete; - void Init(drmModePropertyPtr p, uint64_t value); - void SetName(std::string name) { name_ = name; }; - std::tuple<uint64_t, int> GetEnumValueWithName(std::string name) const; + void init(drmModePropertyPtr p, uint64_t value); + void setName(std::string name) { name_ = name; }; + std::tuple<uint64_t, int> getEnumValueWithName(std::string name) const; uint32_t id() const; std::string name() const; std::tuple<int, uint64_t> value() const; - bool is_immutable() const; + bool isImmutable() const; + bool isRange() const; + bool isSignedRange() const; + bool isBitmask() const; - bool is_range() const; - std::tuple<int, uint64_t> range_min() const; - std::tuple<int, uint64_t> range_max() const; + std::tuple<int, uint64_t> rangeMin() const; + std::tuple<int, uint64_t> rangeMax() const; - void UpdateValue(const uint64_t value); + bool validateChange(uint64_t value) const; + void updateValue(const uint64_t value); void printProperty() const; private: diff --git a/libhwc2.1/libdrmresource/include/vsyncworker.h b/libhwc2.1/libdrmresource/include/vsyncworker.h index ce12eea..7e9c124 100644 --- a/libhwc2.1/libdrmresource/include/vsyncworker.h +++ b/libhwc2.1/libdrmresource/include/vsyncworker.h @@ -30,41 +30,41 @@ namespace android { class VsyncCallback { - public: - virtual ~VsyncCallback() {} - virtual void Callback(int display, int64_t timestamp) = 0; + public: + virtual ~VsyncCallback() {} + virtual void Callback(int display, int64_t timestamp) = 0; }; class VSyncWorker : public Worker { - public: - VSyncWorker(); - ~VSyncWorker() override; + public: + VSyncWorker(); + ~VSyncWorker() override; - int Init(DrmDevice *drm, int display, const String8 &display_trace_name); - void RegisterCallback(std::shared_ptr<VsyncCallback> callback); + int Init(DrmDevice* drm, int display, const String8& displayTraceName); + void RegisterCallback(std::shared_ptr<VsyncCallback> callback); - void VSyncControl(bool enabled); + void VSyncControl(bool enabled); - protected: - void Routine() override; + protected: + void Routine() override; - private: - int GetPhasedVSync(int64_t frame_ns, int64_t &expect); - int SyntheticWaitVBlank(int64_t ×tamp); + private: + int GetPhasedVSync(uint32_t vsyncPeriodNs, int64_t& expectTimeNs); + int SyntheticWaitVBlank(int64_t& timestamp); - DrmDevice *drm_; + DrmDevice* mDrmDevice; - // shared_ptr since we need to use this outside of the thread lock (to - // actually call the hook) and we don't want the memory freed until we're - // done - std::shared_ptr<VsyncCallback> callback_ = NULL; + // shared_ptr since we need to use this outside of the thread lock (to + // actually call the hook) and we don't want the memory freed until we're + // done + std::shared_ptr<VsyncCallback> mCallback = NULL; - int display_; - std::atomic_bool enabled_; - int64_t last_timestamp_; - String8 hw_vsync_period_tag_; - String8 hw_vsync_enabled_tag_; - String8 display_trace_name_; + int mDisplay; + std::atomic_bool mEnabled; + int64_t mLastTimestampNs; + String8 mHwVsyncPeriodTag; + String8 mHwVsyncEnabledTag; + String8 mDisplayTraceName; }; } // namespace android diff --git a/libhwc2.1/libexternaldisplay/ExynosExternalDisplay.cpp b/libhwc2.1/libexternaldisplay/ExynosExternalDisplay.cpp index 84a6971..921fe3c 100644 --- a/libhwc2.1/libexternaldisplay/ExynosExternalDisplay.cpp +++ b/libhwc2.1/libexternaldisplay/ExynosExternalDisplay.cpp @@ -48,6 +48,9 @@ ExynosExternalDisplay::ExynosExternalDisplay(uint32_t index, ExynosDevice* devic mIsSkipFrame = false; mVirtualDisplayState = 0; + mDRDefault = true; + mDREnable = false; + //TODO : Hard coded currently mNumMaxPriorityAllowed = 1; mPowerModeState = (hwc2_power_mode_t)HWC_POWER_MODE_OFF; @@ -136,7 +139,7 @@ int ExynosExternalDisplay::getDisplayConfigs(uint32_t* outNumConfigs, hwc2_confi if (property_get("vendor.display.external.preferred_mode", modeStr, "") > 0) { if (sscanf(modeStr, "%dx%d@%d", &width, &height, &fps) == 3) { - err = lookupDisplayConfigs(width, height, fps, &config); + err = lookupDisplayConfigs(width, height, fps, fps, &config); if (err != HWC2_ERROR_NONE) { DISPLAY_LOGW("%s: display does not support preferred mode %dx%d@%d", __func__, width, height, fps); @@ -157,6 +160,7 @@ int ExynosExternalDisplay::getDisplayConfigs(uint32_t* outNumConfigs, hwc2_confi mXres = displayConfig.width; mYres = displayConfig.height; mVsyncPeriod = displayConfig.vsyncPeriod; + mRefreshRate = displayConfig.refreshRate; if (mDisplayInterface->mType == INTERFACE_TYPE_DRM) { ret = mDisplayInterface->setActiveConfig(mActiveConfig); @@ -448,11 +452,16 @@ int ExynosExternalDisplay::disable() { ALOGI("[ExternalDisplay] %s +", __func__); - if (!mEnabled) - return HWC2_ERROR_NONE; - if (mHpdStatus) { - clearDisplay(false); + /* + * DP cable is connected and link is up + * + * Currently, we don't power down here for two reasons: + * - power up would require DP link re-training (slow) + * - DP audio can continue playing while display is blank + */ + if (mEnabled) + clearDisplay(false); return HWC2_ERROR_NONE; } @@ -556,10 +565,13 @@ void ExynosExternalDisplay::handleHotplugEvent(bool hpdStatus) mHpdStatus = false; return; } + mDREnable = mDRDefault; } else { disable(); closeExternalDisplay(); + mDREnable = false; } + mDevice->checkDynamicRecompositionThread(); ALOGI("HPD status changed to %s, mDisplayId %d, mDisplayFd %d", mHpdStatus ? "enabled" : "disabled", mDisplayId, mDisplayInterface->getDisplayFd()); } diff --git a/libhwc2.1/libhwcService/ExynosHWCService.cpp b/libhwc2.1/libhwcService/ExynosHWCService.cpp index 4ae010a..6d84074 100644 --- a/libhwc2.1/libhwcService/ExynosHWCService.cpp +++ b/libhwc2.1/libhwcService/ExynosHWCService.cpp @@ -462,7 +462,7 @@ int32_t ExynosHWCService::setMinIdleRefreshRate(uint32_t display_id, int32_t fps auto display = mHWCCtx->device->getDisplay(display_id); if (display != nullptr) { - return display->setMinIdleRefreshRate(fps, VrrThrottleRequester::TEST); + return display->setMinIdleRefreshRate(fps, RrThrottleRequester::TEST); } return -EINVAL; @@ -478,7 +478,7 @@ int32_t ExynosHWCService::setRefreshRateThrottle(uint32_t display_id, int32_t de ->setRefreshRateThrottleNanos(std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::milliseconds(delayMs)) .count(), - VrrThrottleRequester::TEST); + RrThrottleRequester::TEST); } return -EINVAL; diff --git a/libhwc2.1/libhwchelper/ExynosHWCHelper.h b/libhwc2.1/libhwchelper/ExynosHWCHelper.h index ed9964a..de99b99 100644 --- a/libhwc2.1/libhwchelper/ExynosHWCHelper.h +++ b/libhwc2.1/libhwchelper/ExynosHWCHelper.h @@ -26,6 +26,7 @@ #include <optional> #include <sstream> #include <string> +#include <unordered_map> #include <vector> #include "DeconCommonHeader.h" @@ -162,7 +163,7 @@ const format_description_t exynos_format_desc[] = { 1, 1, 32, RGB | BIT10 | COMP_TYPE_NONE | COMP_TYPE_AFBC, true, String8("RGBA_1010102"), 0}, {HAL_PIXEL_FORMAT_EXYNOS_ARGB_8888, DECON_PIXEL_FORMAT_MAX, DRM_FORMAT_ARGB8888, 1, 1, 32, RGB | BIT8 | COMP_TYPE_NONE | COMP_TYPE_AFBC, true, String8("EXYNOS_ARGB_8888"), 0}, - {HAL_PIXEL_FORMAT_RGBA_FP16, DECON_PIXEL_FORMAT_MAX, DRM_FORMAT_ARGB16161616F, + {HAL_PIXEL_FORMAT_RGBA_FP16, DECON_PIXEL_FORMAT_MAX, DRM_FORMAT_ABGR16161616F, 1, 1, 64, RGB | BIT16 | COMP_TYPE_NONE | COMP_TYPE_AFBC, true, String8("RGBA_FP16"), 0}, /* YUV 420 */ @@ -628,11 +629,18 @@ public: CtrlValue() : value_(), dirty_(false) {} CtrlValue(const T& value) : value_(value), dirty_(false) {} - void store(T value) { + void store(const T& value) { if (value == value_) return; dirty_ = true; value_ = value; }; + + void store(T&& value) { + if (value == value_) return; + dirty_ = true; + value_ = std::move(value); + }; + const T &get() { return value_; }; bool is_dirty() { return dirty_; }; void clear_dirty() { dirty_ = false; }; @@ -664,6 +672,56 @@ struct RollingAverage { } }; +class FileNodeWriter { +public: + FileNodeWriter(const std::string& nodePath) : mNodePath(nodePath) {} + + ~FileNodeWriter() { + for (auto& node : mOperateNodes) { + close(node.second); + } + } + + template <typename T> + bool WriteCommandString(const std::string& nodeName, T cmd) { + // ref: https://elixir.bootlin.com/linux/latest/source/include/linux/kstrtox.h + static_assert(std::is_integral_v<T>); + + int fd = getOperateNodeFileHandle(nodeName); + if (fd >= 0) { + std::string cmdString = std::to_string(cmd); + int ret = write(fd, cmdString.c_str(), std::strlen(cmdString.c_str())); + if (ret < 0) { + ALOGE("Write to file node %s failed, ret = %d errno = %d", mNodePath.c_str(), ret, + errno); + return false; + } + } else { + ALOGE("Write to invalid file node %s", mNodePath.c_str()); + return false; + } + return true; + } + +private: + int getOperateNodeFileHandle(const std::string& nodeName) { + if (mOperateNodes.count(nodeName) > 0) { + return mOperateNodes[nodeName]; + } + std::string fullPath = mNodePath + nodeName; + int fd = open(fullPath.c_str(), O_WRONLY, 0); + if (fd < 0) { + ALOGE("Open file node failed, fd = %d", fd); + return fd; + } + mOperateNodes[nodeName] = fd; + return fd; + } + + std::string mNodePath; + std::unordered_map<std::string, int> mOperateNodes; +}; + // Waits for a given property value, or returns std::nullopt if unavailable std::optional<std::string> waitForPropertyValue(const std::string &property, int64_t timeoutMs); diff --git a/libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.cpp b/libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.cpp index ec1e8ba..d440ac2 100644 --- a/libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.cpp +++ b/libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.cpp @@ -38,11 +38,26 @@ extern struct exynos_hwc_control exynosHWCControl; using namespace SOC_VERSION; + +namespace { + constexpr auto nsecsPerSec = std::chrono::nanoseconds(1s).count(); +inline constexpr int kDefaultNotifyExpectedPresentConfigHeadsUpNs = + std::chrono::nanoseconds(30ms).count(); +inline constexpr int kDefaultNotifyExpectedPresentConfigTimeoutNs = + std::chrono::nanoseconds(30ms).count(); + +static constexpr int kMaximumPropertyIdentifierLength = 128; + static const std::map<const DisplayType, const std::string> panelSysfsPath = {{DisplayType::DISPLAY_PRIMARY, "/sys/devices/platform/exynos-drm/primary-panel/"}, - {DisplayType::DISPLAY_SECONDARY, "/sys/devices/platform/exynos-drm/secondary-panel/"}}; +#ifdef USES_IDISPLAY_INTF_SEC + {DisplayType::DISPLAY_SECONDARY, "/sys/devices/platform/exynos-drm/secondary-panel/"} +#endif + +}; +} // namespace static String8 getPropertyBootModeStr(const int32_t dispId) { String8 str; @@ -87,8 +102,8 @@ ExynosPrimaryDisplay::ExynosPrimaryDisplay(uint32_t index, ExynosDevice *device, : ExynosDisplay(HWC_DISPLAY_PRIMARY, index, device, displayName), mUseBlockingZoneForMinIdleRefreshRate(false), mMinIdleRefreshRate(0), - mVrrThrottleFps{0}, - mVrrThrottleNanos{0}, + mRrThrottleFps{0}, + mRrThrottleNanos{0}, mRefreshRateDelayNanos(0), mLastRefreshRateAppliedNanos(0), mAppliedActiveConfig(0), @@ -117,6 +132,36 @@ ExynosPrimaryDisplay::ExynosPrimaryDisplay(uint32_t index, ExynosDevice *device, mDbvThresholdForBlockingZone); } + DisplayType displayType = getDcDisplayType(); + std::string displayTypeIdentifier; + if (displayType == DisplayType::DISPLAY_PRIMARY) { + displayTypeIdentifier = "primarydisplay"; + } else if (displayType == DisplayType::DISPLAY_EXTERNAL) { + displayTypeIdentifier = "externaldisplay"; + } +#ifdef USES_IDISPLAY_INTF_SEC + else if (displayType == DisplayType::DISPLAY_SECONDARY) { + displayTypeIdentifier = "secondarydisplay"; + } +#endif + if (!displayTypeIdentifier.empty()) { + char pathBuffer[kMaximumPropertyIdentifierLength]; + sprintf(pathBuffer, "ro.vendor.%s.vrr.enabled", displayTypeIdentifier.c_str()); + mVrrSettings.enabled = property_get_bool(pathBuffer, false); + if (mVrrSettings.enabled) { + sprintf(pathBuffer, "ro.vendor.%s.vrr.expected_present.headsup_ns", + displayTypeIdentifier.c_str()); + mVrrSettings.notifyExpectedPresentConfig.HeadsUpNs = + property_get_int32(pathBuffer, kDefaultNotifyExpectedPresentConfigHeadsUpNs); + sprintf(pathBuffer, "ro.vendor.%s.vrr.expected_present.timeout_ns", + displayTypeIdentifier.c_str()); + mVrrSettings.notifyExpectedPresentConfig.TimeoutNs = + property_get_int32(pathBuffer, kDefaultNotifyExpectedPresentConfigTimeoutNs); + mVrrSettings.configChangeCallback = + std::bind(&ExynosPrimaryDisplay::onConfigChange, this, std::placeholders::_1); + } + } + // Allow to enable dynamic recomposition after every power on // since it will always be disabled for every power off // TODO(b/268474771): to enable DR by default if video mode panel is detected @@ -146,7 +191,7 @@ ExynosPrimaryDisplay::ExynosPrimaryDisplay(uint32_t index, ExynosDevice *device, char value[PROPERTY_VALUE_MAX]; const char *earlyWakeupNodeBase = early_wakeup_node_0_base; - if (getDisplayTypeFromIndex(mIndex) == DisplayType::DISPLAY_SECONDARY && + if (getDcDisplayType() == DisplayType::DISPLAY_SECONDARY && property_get("vendor.display.secondary_early_wakeup_node", value, "") > 0) { earlyWakeupNodeBase = value; } @@ -234,10 +279,14 @@ int32_t ExynosPrimaryDisplay::setActiveConfigInternal(hwc2_config_t config, bool } int32_t ExynosPrimaryDisplay::applyPendingConfig() { - if (!isConfigSettingEnabled()) return HWC2_ERROR_NONE; + if (!isConfigSettingEnabled()) { + ALOGI("%s:: config setting is disabled", __func__); + return HWC2_ERROR_NONE; + } hwc2_config_t config; if (mPendingConfig != UINT_MAX) { + ALOGI("%s:: mPendingConfig: %d", __func__, mPendingConfig); config = mPendingConfig; mPendingConfig = UINT_MAX; } else { @@ -260,15 +309,14 @@ int32_t ExynosPrimaryDisplay::setBootDisplayConfig(int32_t config) { if (mode.vsyncPeriod == 0) return HWC2_ERROR_BAD_CONFIG; - int refreshRate = round(nsecsPerSec / mode.vsyncPeriod * 0.1f) * 10; + int vsyncRate = round(static_cast<float>(nsecsPerSec) / mode.vsyncPeriod); char modeStr[PROPERTY_VALUE_MAX]; - int ret = snprintf(modeStr, sizeof(modeStr), "%dx%d@%d", - mode.width, mode.height, refreshRate); + int ret = snprintf(modeStr, sizeof(modeStr), "%dx%d@%d:%d", + mode.width, mode.height, mode.refreshRate, vsyncRate); if (ret <= 0) return HWC2_ERROR_BAD_CONFIG; - ALOGD("%s: mode=%s (%d) vsyncPeriod=%d", __func__, modeStr, config, - mode.vsyncPeriod); + ALOGD("%s: mode=%s (%d)", __func__, modeStr, config); ret = property_set(getPropertyBootModeStr(mDisplayId).c_str(), modeStr); return !ret ? HWC2_ERROR_NONE : HWC2_ERROR_BAD_CONFIG; @@ -290,15 +338,31 @@ int32_t ExynosPrimaryDisplay::getPreferredDisplayConfigInternal(int32_t *outConf } int width, height; - int fps = 0; - - ret = sscanf(modeStr, "%dx%d@%d", &width, &height, &fps); - if ((ret < 3) || !fps) { - ALOGD("%s: unable to find boot config for mode: %s", __func__, modeStr); + int fps = 0, vsyncRate = 0; + + ret = sscanf(modeStr, "%dx%d@%d:%d", &width, &height, &fps, &vsyncRate); + if (ret < 4) { + ret = sscanf(modeStr, "%dx%d@%d", &width, &height, &fps); + if ((ret < 3) || !fps) { + ALOGW("%s: unable to find boot config for mode: %s", __func__, modeStr); + return HWC2_ERROR_BAD_CONFIG; + } + if (lookupDisplayConfigs(width, height, fps, fps, outConfig) != HWC2_ERROR_NONE) { + ALOGE("%s: kernel doesn't support mode: %s", __func__, modeStr); + return HWC2_ERROR_BAD_CONFIG; + } + ret = setBootDisplayConfig(*outConfig); + if (ret == HWC2_ERROR_NONE) + ALOGI("%s: succeeded to replace %s with new format", __func__, modeStr); + else + ALOGE("%s: failed to replace %s with new format", __func__, modeStr); + return ret; + } + if (!fps || !vsyncRate || (fps > vsyncRate)) { + ALOGE("%s: bad boot config: %s", __func__, modeStr); return HWC2_ERROR_BAD_CONFIG; } - - return lookupDisplayConfigs(width, height, fps, outConfig); + return lookupDisplayConfigs(width, height, fps, vsyncRate, outConfig); } int32_t ExynosPrimaryDisplay::setPowerOn() { @@ -430,27 +494,35 @@ int32_t ExynosPrimaryDisplay::setPowerMode(int32_t mode) { mOperationRateManager->getTargetOperationRate()); } + int32_t res = HWC2_ERROR_BAD_PARAMETER; switch (mode) { case HWC2_POWER_MODE_DOZE: - case HWC2_POWER_MODE_DOZE_SUSPEND: + case HWC2_POWER_MODE_DOZE_SUSPEND: { if (mode == HWC2_POWER_MODE_DOZE && mDisplayInterface->needRefreshOnLP()) { ALOGI("Refresh before setting power doze."); mDevice->onRefresh(mDisplayId); } - return setPowerDoze(static_cast<hwc2_power_mode_t>(mode)); + res = setPowerDoze(static_cast<hwc2_power_mode_t>(mode)); + break; + } case HWC2_POWER_MODE_OFF: - setPowerOff(); + res = setPowerOff(); break; case HWC2_POWER_MODE_ON: - setPowerOn(); + res = setPowerOn(); break; default: - return HWC2_ERROR_BAD_PARAMETER; + return res; + } + if (res != HWC2_ERROR_NONE) { + return res; } ExynosDisplay::updateRefreshRateHint(); - - return HWC2_ERROR_NONE; + if (mVariableRefreshRateController) { + mVariableRefreshRateController->setPowerMode(mode); + } + return res; } void ExynosPrimaryDisplay::firstPowerOn() { @@ -474,19 +546,23 @@ void ExynosPrimaryDisplay::initDisplayInterface(uint32_t interfaceType) __func__, interfaceType); mDisplayInterface->init(this); + if (mVrrSettings.enabled) { + mDisplayInterface->setVrrSettings(mVrrSettings); + } + mDpuData.init(mMaxWindowNum, mDevice->getSpecialPlaneNum(mDisplayId)); mLastDpuData.init(mMaxWindowNum, mDevice->getSpecialPlaneNum(mDisplayId)); ALOGI("window configs size(%zu) rcd configs zie(%zu)", mDpuData.configs.size(), mDpuData.rcdConfigs.size()); } -std::string ExynosPrimaryDisplay::getPanelSysfsPath(const DisplayType &type) { +std::string ExynosPrimaryDisplay::getPanelSysfsPath(const DisplayType& type) const { if ((type < DisplayType::DISPLAY_PRIMARY) || (type >= DisplayType::DISPLAY_MAX)) { ALOGE("Invalid display panel type %d", type); return {}; } - auto iter = panelSysfsPath.find(type); + const auto& iter = panelSysfsPath.find(type); if (iter == panelSysfsPath.end()) { return {}; } @@ -578,7 +654,7 @@ bool ExynosPrimaryDisplay::isConfigSettingEnabled() { void ExynosPrimaryDisplay::enableConfigSetting(bool en) { DISPLAY_ATRACE_INT("ConfigSettingDisabled", !en); - + ALOGI("%s:: mConfigSettingDisabled: %d", __func__, !en); if (!en) { mConfigSettingDisabled = true; mConfigSettingDisabledTimestamp = systemTime(SYSTEM_TIME_MONOTONIC); @@ -588,6 +664,57 @@ void ExynosPrimaryDisplay::enableConfigSetting(bool en) { mConfigSettingDisabled = false; } +int32_t ExynosPrimaryDisplay::getDisplayConfigs(uint32_t* outNumConfigs, + hwc2_config_t* outConfigs) { + int32_t ret = ExynosDisplay::getDisplayConfigs(outNumConfigs, outConfigs); + if (ret == HWC2_ERROR_NONE) { + if (mVrrSettings.enabled && mDisplayConfigs.size()) { + if (!mVariableRefreshRateController) { + mVariableRefreshRateController = + VariableRefreshRateController::CreateInstance(this); + std::unordered_map<hwc2_config_t, VrrConfig_t> vrrConfigs; + for (const auto& it : mDisplayConfigs) { + if (!it.second.vrrConfig.has_value()) { + return HWC2_ERROR_BAD_CONFIG; + } + vrrConfigs[it.first] = it.second.vrrConfig.value(); + } + mVariableRefreshRateController->setVrrConfigurations(std::move(vrrConfigs)); + hwc2_config_t activeConfig; + if (ExynosDisplay::getActiveConfig(&activeConfig) == HWC2_ERROR_NONE) { + mVariableRefreshRateController->setActiveVrrConfiguration(activeConfig); + mVariableRefreshRateController->setEnable(true); + } + } + } + } + return ret; +} + +int32_t ExynosPrimaryDisplay::presentDisplay(int32_t* outRetireFence) { + auto res = ExynosDisplay::presentDisplay(outRetireFence); + // Forward presentDisplay if there is a listener. + const auto presentListener = getPresentListener(); + if (res == HWC2_ERROR_NONE && presentListener) { + presentListener->onPresent(*outRetireFence); + } + return res; +} + +void ExynosPrimaryDisplay::onVsync(int64_t timestamp) { + const auto vsyncListener = getVsyncListener(); + if (vsyncListener) { + vsyncListener->onVsync(timestamp, 0); + } +} + +int32_t ExynosPrimaryDisplay::notifyExpectedPresent(int64_t timestamp, int32_t frameIntervalNs) { + if (mVariableRefreshRateController) { + mVariableRefreshRateController->notifyExpectedPresent(timestamp, frameIntervalNs); + } + return NO_ERROR; +} + int32_t ExynosPrimaryDisplay::setLhbmDisplayConfigLocked(uint32_t peakRate) { auto hwConfig = mDisplayInterface->getActiveModeId(); auto config = getConfigId(peakRate, mDisplayConfigs[hwConfig].width, @@ -641,10 +768,11 @@ int32_t ExynosPrimaryDisplay::setLhbmState(bool enabled) { { ATRACE_NAME("wait_for_power_on"); std::unique_lock<std::mutex> lock(mPowerModeMutex); - if (mPowerModeState != HWC2_POWER_MODE_ON) { + if (!mPowerModeState.has_value() || (*mPowerModeState != HWC2_POWER_MODE_ON)) { mNotifyPowerOn = true; if (!mPowerOnCondition.wait_for(lock, std::chrono::milliseconds(2000), [this]() { - return (mPowerModeState == HWC2_POWER_MODE_ON); + return (mPowerModeState.has_value() && + (*mPowerModeState == HWC2_POWER_MODE_ON)); })) { DISPLAY_LOGW("%s: wait for power mode on timeout !", __func__); return TIMED_OUT; @@ -786,6 +914,18 @@ int32_t ExynosPrimaryDisplay::setLhbmState(bool enabled) { } return NO_ERROR; enable_err: + { + // We may receive LHBM request during the power off sequence due to the + // race condition between display and sensor. If the failure happens + // after requestLhbm(), we will get a wrong LHBM state in the 1st commit + // after power on. We should reset the state in this case. + std::unique_lock<std::mutex> lock(mPowerModeMutex); + if (!mPowerModeState.has_value() || (*mPowerModeState == HWC2_POWER_MODE_OFF)) { + DISPLAY_LOGW("%s: request lhbm during power off sequence, reset the state", __func__); + mBrightnessController->resetLhbmState(); + } + } + Mutex::Autolock lock(mDisplayMutex); restoreLhbmDisplayConfigLocked(); return ret; @@ -807,7 +947,7 @@ void ExynosPrimaryDisplay::setLHBMRefreshRateThrottle(const uint32_t delayMs) { setRefreshRateThrottleNanos(std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::milliseconds(delayMs)) .count(), - VrrThrottleRequester::LHBM); + RrThrottleRequester::LHBM); } void ExynosPrimaryDisplay::setEarlyWakeupDisplay() { @@ -816,20 +956,33 @@ void ExynosPrimaryDisplay::setEarlyWakeupDisplay() { } } -void ExynosPrimaryDisplay::setExpectedPresentTime(uint64_t timestamp) { - mExpectedPresentTime.store(timestamp); +void ExynosPrimaryDisplay::setExpectedPresentTime(uint64_t timestamp, int frameIntervalNs) { + mExpectedPresentTimeAndInterval.store(std::make_tuple(timestamp, frameIntervalNs)); + // Forward presentDisplay if there is a listener. + const auto presentListener = getPresentListener(); + if (presentListener) { + presentListener->setExpectedPresentTime(timestamp, frameIntervalNs); + } } uint64_t ExynosPrimaryDisplay::getPendingExpectedPresentTime() { - if (mExpectedPresentTime.is_dirty()) { - return mExpectedPresentTime.get(); + if (mExpectedPresentTimeAndInterval.is_dirty()) { + return std::get<0>(mExpectedPresentTimeAndInterval.get()); + } + + return 0; +} + +int ExynosPrimaryDisplay::getPendingFrameInterval() { + if (mExpectedPresentTimeAndInterval.is_dirty()) { + return std::get<1>(mExpectedPresentTimeAndInterval.get()); } return 0; } void ExynosPrimaryDisplay::applyExpectedPresentTime() { - mExpectedPresentTime.clear_dirty(); + mExpectedPresentTimeAndInterval.clear_dirty(); } int32_t ExynosPrimaryDisplay::setDisplayIdleTimer(const int32_t timeoutMs) { @@ -865,7 +1018,7 @@ int32_t ExynosPrimaryDisplay::getDisplayIdleTimerEnabled(bool &enabled) { return HWC2_ERROR_UNSUPPORTED; } - const std::string path = getPanelSysfsPath(getDisplayTypeFromIndex(mIndex)) + "panel_idle"; + const std::string path = getPanelSysfsPath() + "panel_idle"; std::ifstream ifs(path); if (!ifs.is_open()) { ALOGW("%s() unable to open node '%s', error = %s", __func__, path.c_str(), strerror(errno)); @@ -881,7 +1034,7 @@ int32_t ExynosPrimaryDisplay::getDisplayIdleTimerEnabled(bool &enabled) { } int32_t ExynosPrimaryDisplay::setDisplayIdleTimerEnabled(const bool enabled) { - const std::string path = getPanelSysfsPath(getDisplayTypeFromIndex(mIndex)) + "panel_idle"; + const std::string path = getPanelSysfsPath() + "panel_idle"; std::ofstream ofs(path); if (!ofs.is_open()) { ALOGW("%s() unable to open node '%s', error = %s", __func__, path.c_str(), strerror(errno)); @@ -915,7 +1068,7 @@ int32_t ExynosPrimaryDisplay::setDisplayIdleDelayNanos(const int32_t delayNanos, const int32_t displayIdleDelayMs = std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::nanoseconds(mDisplayIdleDelayNanos)) .count(); - const std::string path = getPanelSysfsPath(DisplayType::DISPLAY_PRIMARY) + "idle_delay_ms"; + const std::string path = getPanelSysfsPath() + "idle_delay_ms"; std::ofstream ofs(path); if (!ofs.is_open()) { ALOGW("%s() unable to open node '%s', error = %s", __func__, path.c_str(), strerror(errno)); @@ -934,8 +1087,7 @@ void ExynosPrimaryDisplay::initDisplayHandleIdleExit() { return; } - const std::string path = - getPanelSysfsPath(getDisplayTypeFromIndex(mIndex)) + "panel_need_handle_idle_exit"; + const std::string path = getPanelSysfsPath() + "panel_need_handle_idle_exit"; mDisplayNeedHandleIdleExitOfs.open(path, std::ofstream::out); if (!mDisplayNeedHandleIdleExitOfs.is_open()) { ALOGI("%s() '%s' doesn't exist(%s)", __func__, path.c_str(), strerror(errno)); @@ -972,18 +1124,23 @@ void ExynosPrimaryDisplay::setDisplayNeedHandleIdleExit(const bool needed, const } void ExynosPrimaryDisplay::handleDisplayIdleEnter(const uint32_t idleTeRefreshRate) { - Mutex::Autolock lock(mDisplayMutex); - uint32_t btsRefreshRate = getBtsRefreshRate(); - if (idleTeRefreshRate <= btsRefreshRate) { - return; + { + Mutex::Autolock lock(mDisplayMutex); + uint32_t btsRefreshRate = getBtsRefreshRate(); + if (idleTeRefreshRate <= btsRefreshRate) { + return; + } } bool needed = false; - for (size_t i = 0; i < mLayers.size(); i++) { - if (mLayers[i]->mOtfMPP && mLayers[i]->mM2mMPP == nullptr && - !mLayers[i]->checkBtsCap(idleTeRefreshRate)) { - needed = true; - break; + { + Mutex::Autolock lock(mDRMutex); + for (size_t i = 0; i < mLayers.size(); i++) { + if (mLayers[i]->mOtfMPP && mLayers[i]->mM2mMPP == nullptr && + !mLayers[i]->checkBtsCap(idleTeRefreshRate)) { + needed = true; + break; + } } } @@ -991,28 +1148,28 @@ void ExynosPrimaryDisplay::handleDisplayIdleEnter(const uint32_t idleTeRefreshRa } int ExynosPrimaryDisplay::setMinIdleRefreshRate(const int targetFps, - const VrrThrottleRequester requester) { + const RrThrottleRequester requester) { int fps = (targetFps <= 0) ? mDefaultMinIdleRefreshRate : targetFps; - if (requester == VrrThrottleRequester::BRIGHTNESS && mUseBlockingZoneForMinIdleRefreshRate) { + if (requester == RrThrottleRequester::BRIGHTNESS && mUseBlockingZoneForMinIdleRefreshRate) { uint32_t level = mBrightnessController->getBrightnessLevel(); fps = (level < mDbvThresholdForBlockingZone) ? mMinIdleRefreshRateForBlockingZone : mDefaultMinIdleRefreshRate; } std::lock_guard<std::mutex> lock(mMinIdleRefreshRateMutex); - if (fps == mVrrThrottleFps[toUnderlying(requester)]) return NO_ERROR; + if (fps == mRrThrottleFps[toUnderlying(requester)]) return NO_ERROR; ALOGD("%s requester %u, fps %d", __func__, toUnderlying(requester), fps); - mVrrThrottleFps[toUnderlying(requester)] = fps; + mRrThrottleFps[toUnderlying(requester)] = fps; int maxMinIdleFps = 0; - for (uint32_t i = 0; i < toUnderlying(VrrThrottleRequester::MAX); i++) { - if (mVrrThrottleFps[i] > maxMinIdleFps) { - maxMinIdleFps = mVrrThrottleFps[i]; + for (uint32_t i = 0; i < toUnderlying(RrThrottleRequester::MAX); i++) { + if (mRrThrottleFps[i] > maxMinIdleFps) { + maxMinIdleFps = mRrThrottleFps[i]; } } if (maxMinIdleFps == mMinIdleRefreshRate) return NO_ERROR; - const std::string path = getPanelSysfsPath(getDisplayTypeFromIndex(mIndex)) + "min_vrefresh"; + const std::string path = getPanelSysfsPath() + "min_vrefresh"; std::ofstream ofs(path); if (!ofs.is_open()) { ALOGW("%s Unable to open node '%s', error = %s", __func__, path.c_str(), strerror(errno)); @@ -1028,7 +1185,7 @@ int ExynosPrimaryDisplay::setMinIdleRefreshRate(const int targetFps, } int ExynosPrimaryDisplay::setRefreshRateThrottleNanos(const int64_t delayNanos, - const VrrThrottleRequester requester) { + const RrThrottleRequester requester) { ATRACE_CALL(); if (delayNanos < 0) { ALOGW("%s() set invalid delay(%" PRId64 ")", __func__, delayNanos); @@ -1036,15 +1193,15 @@ int ExynosPrimaryDisplay::setRefreshRateThrottleNanos(const int64_t delayNanos, } std::lock_guard<std::mutex> lock(mIdleRefreshRateThrottleMutex); - if (delayNanos == mVrrThrottleNanos[toUnderlying(requester)]) return NO_ERROR; + if (delayNanos == mRrThrottleNanos[toUnderlying(requester)]) return NO_ERROR; ALOGI("%s() requester(%u) set delay to %" PRId64 "ns", __func__, toUnderlying(requester), delayNanos); - mVrrThrottleNanos[toUnderlying(requester)] = delayNanos; + mRrThrottleNanos[toUnderlying(requester)] = delayNanos; int64_t maxDelayNanos = 0; - for (uint32_t i = 0; i < toUnderlying(VrrThrottleRequester::MAX); i++) { - if (mVrrThrottleNanos[i] > maxDelayNanos) { - maxDelayNanos = mVrrThrottleNanos[i]; + for (uint32_t i = 0; i < toUnderlying(RrThrottleRequester::MAX); i++) { + if (mRrThrottleNanos[i] > maxDelayNanos) { + maxDelayNanos = mRrThrottleNanos[i]; } } @@ -1054,7 +1211,7 @@ int ExynosPrimaryDisplay::setRefreshRateThrottleNanos(const int64_t delayNanos, } mRefreshRateDelayNanos = maxDelayNanos; - return setDisplayIdleDelayNanos(mRefreshRateDelayNanos, DispIdleTimerRequester::VRR_THROTTLE); + return setDisplayIdleDelayNanos(mRefreshRateDelayNanos, DispIdleTimerRequester::RR_THROTTLE); } void ExynosPrimaryDisplay::dump(String8 &result) { @@ -1074,13 +1231,13 @@ void ExynosPrimaryDisplay::dump(String8 &result) { result.appendFormat("\n"); } - for (uint32_t i = 0; i < toUnderlying(VrrThrottleRequester::MAX); i++) { - result.appendFormat("\t[%u] vote to %d hz\n", i, mVrrThrottleFps[i]); + for (uint32_t i = 0; i < toUnderlying(RrThrottleRequester::MAX); i++) { + result.appendFormat("\t[%u] vote to %d hz\n", i, mRrThrottleFps[i]); } result.appendFormat("Refresh rate delay: %" PRId64 " ns\n", mRefreshRateDelayNanos); - for (uint32_t i = 0; i < toUnderlying(VrrThrottleRequester::MAX); i++) { - result.appendFormat("\t[%u] vote to %" PRId64 " ns\n", i, mVrrThrottleNanos[i]); + for (uint32_t i = 0; i < toUnderlying(RrThrottleRequester::MAX); i++) { + result.appendFormat("\t[%u] vote to %" PRId64 " ns\n", i, mRrThrottleNanos[i]); } result.appendFormat("\n"); } @@ -1154,8 +1311,8 @@ void ExynosPrimaryDisplay::updateAppliedActiveConfig(const hwc2_config_t newConf mAppliedActiveConfig = newConfig; } -void ExynosPrimaryDisplay::checkBtsReassignResource(const uint32_t vsyncPeriod, - const uint32_t btsVsyncPeriod) { +void ExynosPrimaryDisplay::checkBtsReassignResource(const int32_t vsyncPeriod, + const int32_t btsVsyncPeriod) { ATRACE_CALL(); uint32_t refreshRate = static_cast<uint32_t>(round(nsecsPerSec / vsyncPeriod * 0.1f) * 10); @@ -1195,3 +1352,23 @@ int32_t ExynosPrimaryDisplay::setDbmState(bool enabled) { mBrightnessController->processDimBrightness(enabled); return NO_ERROR; } + +PresentListener* ExynosPrimaryDisplay::getPresentListener() { + if (mVariableRefreshRateController) { + return mVariableRefreshRateController.get(); + } + return nullptr; +} + +VsyncListener* ExynosPrimaryDisplay::getVsyncListener() { + if (mVariableRefreshRateController) { + return mVariableRefreshRateController.get(); + } + return nullptr; +} + +void ExynosPrimaryDisplay::onConfigChange(int configId) { + if (mVariableRefreshRateController) { + return mVariableRefreshRateController->setActiveVrrConfiguration(configId); + } +} diff --git a/libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.h b/libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.h index f308d7d..f3b412d 100644 --- a/libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.h +++ b/libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.h @@ -19,7 +19,12 @@ #include <map> #include "../libdevice/ExynosDisplay.h" +#include "../libvrr/VariableRefreshRateController.h" +#include "../libvrr/VariableRefreshRateInterface.h" +using android::hardware::graphics::composer::PresentListener; +using android::hardware::graphics::composer::VariableRefreshRateController; +using android::hardware::graphics::composer::VsyncListener; using namespace displaycolor; class ExynosPrimaryDisplay : public ExynosDisplay { @@ -40,8 +45,9 @@ class ExynosPrimaryDisplay : public ExynosDisplay { virtual bool getLhbmState(); virtual void setEarlyWakeupDisplay(); - virtual void setExpectedPresentTime(uint64_t timestamp); - virtual uint64_t getPendingExpectedPresentTime(); + virtual void setExpectedPresentTime(uint64_t timestamp, int frameIntervalNs) override; + virtual uint64_t getPendingExpectedPresentTime() override; + virtual int getPendingFrameInterval() override; virtual void applyExpectedPresentTime(); virtual int32_t setDisplayIdleTimer(const int32_t timeoutMs) override; virtual void handleDisplayIdleEnter(const uint32_t idleTeRefreshRate) override; @@ -50,17 +56,17 @@ class ExynosPrimaryDisplay : public ExynosDisplay { virtual int32_t doDisplayConfigInternal(hwc2_config_t config) override; virtual int setMinIdleRefreshRate(const int fps, - const VrrThrottleRequester requester) override; + const RrThrottleRequester requester) override; virtual int setRefreshRateThrottleNanos(const int64_t delayNs, - const VrrThrottleRequester requester) override; + const RrThrottleRequester requester) override; virtual bool isDbmSupported() override; virtual int32_t setDbmState(bool enabled) override; virtual void dump(String8& result) override; virtual void updateAppliedActiveConfig(const hwc2_config_t newConfig, const int64_t ts) override; - virtual void checkBtsReassignResource(const uint32_t vsyncPeriod, - const uint32_t btsVsyncPeriod) override; + virtual void checkBtsReassignResource(const int32_t vsyncPeriod, + const int32_t btsVsyncPeriod) override; virtual int32_t setBootDisplayConfig(int32_t config) override; virtual int32_t clearBootDisplayConfig() override; @@ -68,6 +74,14 @@ class ExynosPrimaryDisplay : public ExynosDisplay { virtual bool isConfigSettingEnabled() override; virtual void enableConfigSetting(bool en) override; + virtual int32_t getDisplayConfigs(uint32_t* outNumConfigs, + hwc2_config_t* outConfigs) override; + virtual int32_t presentDisplay(int32_t* outRetireFence) override; + + virtual void onVsync(int64_t timestamp) override; + + int32_t notifyExpectedPresent(int64_t timestamp, int32_t frameIntervalNs) override; + protected: /* setPowerMode(int32_t mode) * Descriptor: HWC2_FUNCTION_SET_POWER_MODE @@ -81,15 +95,14 @@ class ExynosPrimaryDisplay : public ExynosDisplay { virtual bool getHDRException(ExynosLayer* __unused layer); virtual int32_t setActiveConfigInternal(hwc2_config_t config, bool force) override; virtual int32_t getActiveConfigInternal(hwc2_config_t* outConfig) override; - DisplayType getDisplayTypeFromIndex(uint32_t index) { - return (index >= DisplayType::DISPLAY_MAX) ? DisplayType::DISPLAY_PRIMARY - : DisplayType(mIndex); - }; public: // Prepare multi resolution ResolutionInfo mResolutionInfo; - std::string getPanelSysfsPath(const displaycolor::DisplayType& type); + std::string getPanelSysfsPath() const override { + return getPanelSysfsPath(getDcDisplayType()); + } + std::string getPanelSysfsPath(const displaycolor::DisplayType& type) const; uint32_t mRcdId = -1; @@ -120,6 +133,8 @@ class ExynosPrimaryDisplay : public ExynosDisplay { int32_t setLhbmDisplayConfigLocked(uint32_t peakRate); void restoreLhbmDisplayConfigLocked(); + void onConfigChange(int configId); + // LHBM FILE* mLhbmFd; std::atomic<bool> mLhbmOn; @@ -141,7 +156,7 @@ class ExynosPrimaryDisplay : public ExynosDisplay { static constexpr const char* kWakeupDispFilePath = "/sys/devices/platform/1c300000.drmdecon/early_wakeup"; - CtrlValue<uint64_t> mExpectedPresentTime; + CtrlValue<std::tuple<int64_t, int>> mExpectedPresentTimeAndInterval; void calculateTimeline(hwc2_config_t config, hwc_vsync_period_change_constraints_t* vsyncPeriodChangeConstraints, @@ -155,11 +170,11 @@ class ExynosPrimaryDisplay : public ExynosDisplay { uint32_t mDbvThresholdForBlockingZone; bool mUseBlockingZoneForMinIdleRefreshRate; int mMinIdleRefreshRate; - int mVrrThrottleFps[toUnderlying(VrrThrottleRequester::MAX)]; + int mRrThrottleFps[toUnderlying(RrThrottleRequester::MAX)]; std::mutex mMinIdleRefreshRateMutex; std::mutex mIdleRefreshRateThrottleMutex; - int64_t mVrrThrottleNanos[toUnderlying(VrrThrottleRequester::MAX)]; + int64_t mRrThrottleNanos[toUnderlying(RrThrottleRequester::MAX)]; int64_t mRefreshRateDelayNanos; int64_t mLastRefreshRateAppliedNanos; hwc2_config_t mAppliedActiveConfig; @@ -170,6 +185,13 @@ class ExynosPrimaryDisplay : public ExynosDisplay { std::ofstream mDisplayNeedHandleIdleExitOfs; int64_t mDisplayIdleDelayNanos; bool mDisplayNeedHandleIdleExit; + + // Function and variables related to Vrr. + PresentListener* getPresentListener(); + VsyncListener* getVsyncListener(); + + VrrSettings_t mVrrSettings; + std::shared_ptr<VariableRefreshRateController> mVariableRefreshRateController; }; #endif diff --git a/libhwc2.1/libresource/ExynosMPP.cpp b/libhwc2.1/libresource/ExynosMPP.cpp index 406996a..622e46a 100644 --- a/libhwc2.1/libresource/ExynosMPP.cpp +++ b/libhwc2.1/libresource/ExynosMPP.cpp @@ -1669,12 +1669,10 @@ bool ExynosMPP::canSkipProcessing() } /** - * @param src * @param dst - * @return int32_t releaseFenceFd of src buffer + * @return int32_t 0 on success, or a negative error code on failure. */ -int32_t ExynosMPP::doPostProcessing(struct exynos_image &src, struct exynos_image &dst) -{ +int32_t ExynosMPP::doPostProcessing(struct exynos_image& dst) { ATRACE_CALL(); MPP_LOGD(eDebugMPP, "total assigned sources (%zu)++++++++", mAssignedSources.size()); diff --git a/libhwc2.1/libresource/ExynosMPP.h b/libhwc2.1/libresource/ExynosMPP.h index 7ea3fbe..2ea51c9 100644 --- a/libhwc2.1/libresource/ExynosMPP.h +++ b/libhwc2.1/libresource/ExynosMPP.h @@ -583,8 +583,7 @@ public: int32_t allocOutBuf(uint32_t w, uint32_t h, uint32_t format, uint64_t usage, uint32_t index); int32_t setOutBuf(buffer_handle_t outbuf, int32_t fence); int32_t freeOutBuf(exynos_mpp_img_info dst); - int32_t doPostProcessing(struct exynos_image &src, struct exynos_image &dst); - int32_t doPostProcessing(uint32_t totalImags, uint32_t imageIndex, struct exynos_image &src, struct exynos_image &dst); + int32_t doPostProcessing(struct exynos_image& dst); int32_t setupRestriction(); int32_t getSrcReleaseFence(uint32_t srcIndex); int32_t resetSrcReleaseFence(); diff --git a/libhwc2.1/libvrr/RingBuffer.h b/libhwc2.1/libvrr/RingBuffer.h new file mode 100644 index 0000000..e899cbc --- /dev/null +++ b/libhwc2.1/libvrr/RingBuffer.h @@ -0,0 +1,64 @@ +/* + * Copyright 2023 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. + */ + +#pragma once + +#include <stddef.h> +#include <array> + +namespace android::hardware::graphics::composer { + +template <class T, size_t SIZE> +class RingBuffer { + // RingBuffer(const RingBuffer&) = delete; + // void operator=(const RingBuffer&) = delete; + +public: + RingBuffer() = default; + ~RingBuffer() = default; + + constexpr size_t capacity() const { return SIZE; } + + size_t size() const { return mCount; } + + T& next() { + mHead = static_cast<size_t>(mHead + 1) % SIZE; + if (mCount < SIZE) { + mCount++; + } + return mBuffer[static_cast<size_t>(mHead)]; + } + + T& operator[](size_t index) { + return mBuffer[(static_cast<size_t>(mHead + 1) + index) % mCount]; + } + + const T& operator[](size_t index) const { + return mBuffer[(static_cast<size_t>(mHead + 1) + index) % mCount]; + } + + void clear() { + mCount = 0; + mHead = -1; + } + +private: + std::array<T, SIZE> mBuffer; + int mHead = -1; + size_t mCount = 0; +}; + +} // namespace android::hardware::graphics::composer diff --git a/libhwc2.1/libvrr/VariableRefreshRateController.cpp b/libhwc2.1/libvrr/VariableRefreshRateController.cpp new file mode 100644 index 0000000..a93c3b9 --- /dev/null +++ b/libhwc2.1/libvrr/VariableRefreshRateController.cpp @@ -0,0 +1,583 @@ +/* + * Copyright (C) 2023 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 "VariableRefreshRateController.h" + +#include <android-base/logging.h> +#include <sync/sync.h> +#include <utils/Trace.h> + +#include "ExynosHWCHelper.h" +#include "drmmode.h" + +#include <chrono> +#include <tuple> + +namespace android::hardware::graphics::composer { + +const std::string VariableRefreshRateController::kFrameInsertionNodeName = "refresh_ctrl"; + +namespace { + +int64_t getNowNs() { + const auto t = std::chrono::high_resolution_clock::now(); + return std::chrono::duration_cast<std::chrono::nanoseconds>(t.time_since_epoch()).count(); +} + +} // namespace + +auto VariableRefreshRateController::CreateInstance(ExynosDisplay* display) + -> std::shared_ptr<VariableRefreshRateController> { + if (!display) { + LOG(ERROR) + << "VrrController: create VariableRefreshRateController without display handler."; + return nullptr; + } + auto controller = std::shared_ptr<VariableRefreshRateController>( + new VariableRefreshRateController(display)); + std::thread thread = std::thread(&VariableRefreshRateController::threadBody, controller.get()); + std::string threadName = "VrrCtrl_"; + threadName += display->mIndex == 0 ? "Primary" : "Second"; + int error = pthread_setname_np(thread.native_handle(), threadName.c_str()); + if (error != 0) { + LOG(WARNING) << "VrrController: Unable to set thread name, error = " << strerror(error); + } + thread.detach(); + return controller; +} + +VariableRefreshRateController::VariableRefreshRateController(ExynosDisplay* display) + : mDisplay(display) { + mState = VrrControllerState::kDisable; + std::string displayFileNodePath = mDisplay->getPanelSysfsPath(); + if (displayFileNodePath.empty()) { + LOG(WARNING) << "VrrController: Cannot find file node of display: " + << mDisplay->mDisplayName; + } else { + mFileNodeWritter = std::make_unique<FileNodeWriter>(displayFileNodePath); + } +} + +VariableRefreshRateController::~VariableRefreshRateController() { + stopThread(true); + + const std::lock_guard<std::mutex> lock(mMutex); + if (mLastPresentFence.has_value()) { + if (close(mLastPresentFence.value())) { + LOG(ERROR) << "VrrController: close fence file failed, errno = " << errno; + } + mLastPresentFence = std::nullopt; + } +}; + +int VariableRefreshRateController::notifyExpectedPresent(int64_t timestamp, + int32_t frameIntervalNs) { + ATRACE_CALL(); + { + const std::lock_guard<std::mutex> lock(mMutex); + mRecord.mNextExpectedPresentTime = {mVrrActiveConfig, timestamp, frameIntervalNs}; + // Post kNotifyExpectedPresentConfig event. + postEvent(VrrControllerEventType::kNotifyExpectedPresentConfig, getNowNs()); + } + mCondition.notify_all(); + return 0; +} + +void VariableRefreshRateController::reset() { + ATRACE_CALL(); + + const std::lock_guard<std::mutex> lock(mMutex); + mEventQueue = std::priority_queue<VrrControllerEvent>(); + mRecord.clear(); + dropEventLocked(); + if (mLastPresentFence.has_value()) { + if (close(mLastPresentFence.value())) { + LOG(ERROR) << "VrrController: close fence file failed, errno = " << errno; + } + mLastPresentFence = std::nullopt; + } +} + +void VariableRefreshRateController::setActiveVrrConfiguration(hwc2_config_t config) { + LOG(INFO) << "VrrController: Set active Vrr configuration = " << config + << ", power mode = " << mPowerMode; + ATRACE_CALL(); + { + const std::lock_guard<std::mutex> lock(mMutex); + if (mVrrConfigs.count(config) == 0) { + LOG(ERROR) << "VrrController: Set an undefined active configuration"; + return; + } + mVrrActiveConfig = config; + if (mState == VrrControllerState::kDisable) { + return; + } + mState = VrrControllerState::kRendering; + dropEventLocked(kRenderingTimeout); + + const auto& vrrConfig = mVrrConfigs[mVrrActiveConfig]; + postEvent(VrrControllerEventType::kRenderingTimeout, + getNowNs() + vrrConfig.notifyExpectedPresentConfig.TimeoutNs); + } + mCondition.notify_all(); +} + +void VariableRefreshRateController::setEnable(bool isEnabled) { + ATRACE_CALL(); + { + const std::lock_guard<std::mutex> lock(mMutex); + if (mEnabled == isEnabled) { + return; + } + mEnabled = isEnabled; + if (mEnabled == false) { + dropEventLocked(); + } + } + mCondition.notify_all(); +} + +void VariableRefreshRateController::setPowerMode(int32_t powerMode) { + ATRACE_CALL(); + LOG(INFO) << "VrrController: Set power mode to " << powerMode; + + { + const std::lock_guard<std::mutex> lock(mMutex); + if (mPowerMode == powerMode) { + return; + } + switch (powerMode) { + case HWC_POWER_MODE_OFF: + case HWC_POWER_MODE_DOZE: + case HWC_POWER_MODE_DOZE_SUSPEND: { + mState = VrrControllerState::kDisable; + dropEventLocked(); + break; + } + case HWC_POWER_MODE_NORMAL: { + // We should transition from either HWC_POWER_MODE_OFF, HWC_POWER_MODE_DOZE, or + // HWC_POWER_MODE_DOZE_SUSPEND. At this point, there should be no pending events + // posted. + if (!mEventQueue.empty()) { + LOG(WARNING) << "VrrController: there should be no pending event when resume " + "from power mode = " + << mPowerMode << " to power mode = " << powerMode; + LOG(INFO) << dumpEventQueueLocked(); + } + mState = VrrControllerState::kRendering; + const auto& vrrConfig = mVrrConfigs[mVrrActiveConfig]; + postEvent(VrrControllerEventType::kRenderingTimeout, + getNowNs() + vrrConfig.notifyExpectedPresentConfig.TimeoutNs); + break; + } + default: { + LOG(ERROR) << "VrrController: Unknown power mode = " << powerMode; + return; + } + } + mPowerMode = powerMode; + } + mCondition.notify_all(); +} + +void VariableRefreshRateController::setVrrConfigurations( + std::unordered_map<hwc2_config_t, VrrConfig_t> configs) { + ATRACE_CALL(); + + for (const auto& it : configs) { + LOG(INFO) << "VrrController: set Vrr configuration id = " << it.first; + } + + const std::lock_guard<std::mutex> lock(mMutex); + mVrrConfigs = std::move(configs); +} + +void VariableRefreshRateController::stopThread(bool exit) { + ATRACE_CALL(); + { + const std::lock_guard<std::mutex> lock(mMutex); + mThreadExit = exit; + mEnabled = false; + mState = VrrControllerState::kDisable; + } + mCondition.notify_all(); +} + +void VariableRefreshRateController::onPresent(int fence) { + if (fence < 0) { + return; + } + ATRACE_CALL(); + { + const std::lock_guard<std::mutex> lock(mMutex); + if (mState == VrrControllerState::kDisable) { + return; + } + if (!mRecord.mPendingCurrentPresentTime.has_value()) { + LOG(WARNING) << "VrrController: VrrController: Present without expected present time " + "information"; + return; + } else { + mRecord.mPresentHistory.next() = mRecord.mPendingCurrentPresentTime.value(); + mRecord.mPendingCurrentPresentTime = std::nullopt; + } + if (mState == VrrControllerState::kHibernate) { + LOG(WARNING) << "VrrController: Present during hibernation without prior notification " + "via notifyExpectedPresent."; + mState = VrrControllerState::kRendering; + dropEventLocked(kHibernateTimeout); + } + } + + // Prior to pushing the most recent fence update, verify the release timestamps of all preceding + // fences. + // TODO(b/309873055): delegate the task of executing updateVsyncHistory to the Vrr controller's + // loop thread in order to reduce the workload of calling thread. + updateVsyncHistory(); + int dupFence = dup(fence); + if (dupFence < 0) { + LOG(ERROR) << "VrrController: duplicate fence file failed." << errno; + } + + { + const std::lock_guard<std::mutex> lock(mMutex); + if (mLastPresentFence.has_value()) { + LOG(WARNING) << "VrrController: last present fence remains open."; + } + mLastPresentFence = dupFence; + // Drop the out of date timeout. + dropEventLocked(kRenderingTimeout); + cancelFrameInsertionLocked(); + // Post next rendering timeout. + postEvent(VrrControllerEventType::kRenderingTimeout, + getNowNs() + mVrrConfigs[mVrrActiveConfig].notifyExpectedPresentConfig.TimeoutNs); + // Post next frmae insertion event. + mPendingFramesToInsert = kDefaultNumFramesToInsert; + postEvent(VrrControllerEventType::kNextFrameInsertion, + getNowNs() + kDefaultFrameInsertionTimer); + } + mCondition.notify_all(); +} + +void VariableRefreshRateController::setExpectedPresentTime(int64_t timestampNanos, + int frameIntervalNs) { + ATRACE_CALL(); + + const std::lock_guard<std::mutex> lock(mMutex); + mRecord.mPendingCurrentPresentTime = {mVrrActiveConfig, timestampNanos, frameIntervalNs}; +} + +void VariableRefreshRateController::onVsync(int64_t timestampNanos, + int32_t __unused vsyncPeriodNanos) { + const std::lock_guard<std::mutex> lock(mMutex); + mRecord.mVsyncHistory + .next() = {.mType = VariableRefreshRateController::VsyncEvent::Type::kVblank, + .mTime = timestampNanos}; +} + +void VariableRefreshRateController::cancelFrameInsertionLocked() { + dropEventLocked(kNextFrameInsertion); + mPendingFramesToInsert = 0; +} + +int VariableRefreshRateController::doFrameInsertionLocked() { + ATRACE_CALL(); + + if (mState == VrrControllerState::kDisable) { + cancelFrameInsertionLocked(); + return 0; + } + if (mPendingFramesToInsert <= 0) { + LOG(ERROR) << "VrrController: the number of frames to be inserted should >= 1, but is " + << mPendingFramesToInsert << " now."; + return -1; + } + bool ret = mFileNodeWritter->WriteCommandString(kFrameInsertionNodeName, PANEL_REFRESH_CTRL_FI); + if (!ret) { + LOG(ERROR) << "VrrController: write command to file node failed. " << getStateName(mState) + << " " << mPowerMode; + return -1; + } + if (--mPendingFramesToInsert > 0) { + postEvent(VrrControllerEventType::kNextFrameInsertion, + getNowNs() + mVrrConfigs[mVrrActiveConfig].minFrameIntervalNs); + } + return 0; +} + +int VariableRefreshRateController::doFrameInsertionLocked(int frames) { + mPendingFramesToInsert = frames; + return doFrameInsertionLocked(); +} + +void VariableRefreshRateController::dropEventLocked() { + mEventQueue = std::priority_queue<VrrControllerEvent>(); + mPendingFramesToInsert = 0; +} + +void VariableRefreshRateController::dropEventLocked(VrrControllerEventType event_type) { + std::priority_queue<VrrControllerEvent> q; + while (!mEventQueue.empty()) { + const auto& it = mEventQueue.top(); + if (it.mEventType != event_type) { + q.push(it); + } + mEventQueue.pop(); + } + mEventQueue = std::move(q); +} + +std::string VariableRefreshRateController::dumpEventQueueLocked() { + std::string content; + if (mEventQueue.empty()) { + return content; + } + + std::priority_queue<VrrControllerEvent> q; + while (!mEventQueue.empty()) { + const auto& it = mEventQueue.top(); + content += "VrrController: event = "; + content += it.toString(); + content += "\n"; + q.push(it); + mEventQueue.pop(); + } + mEventQueue = std::move(q); + return content; +} + +int64_t VariableRefreshRateController::getLastFenceSignalTimeUnlocked(int fd) { + if (fd == -1) { + return SIGNAL_TIME_INVALID; + } + struct sync_file_info* finfo = sync_file_info(fd); + if (finfo == nullptr) { + LOG(ERROR) << "VrrController: sync_file_info returned NULL for fd " << fd; + return SIGNAL_TIME_INVALID; + } + if (finfo->status != 1) { + const auto status = finfo->status; + if (status < 0) { + LOG(ERROR) << "VrrController: sync_file_info contains an error: " << status; + } + sync_file_info_free(finfo); + return status < 0 ? SIGNAL_TIME_INVALID : SIGNAL_TIME_PENDING; + } + uint64_t timestamp = 0; + struct sync_fence_info* pinfo = sync_get_fence_info(finfo); + if (finfo->num_fences != 1) { + LOG(WARNING) << "VrrController:: there is more than one fence in the file descriptor = " + << fd; + } + for (size_t i = 0; i < finfo->num_fences; i++) { + if (pinfo[i].timestamp_ns > timestamp) { + timestamp = pinfo[i].timestamp_ns; + } + } + sync_file_info_free(finfo); + return timestamp; +} + +int64_t VariableRefreshRateController::getNextEventTimeLocked() const { + if (mEventQueue.empty()) { + LOG(WARNING) << "VrrController: event queue should NOT be empty."; + return -1; + } + const auto& event = mEventQueue.top(); + return event.mWhenNs; +} + +std::string VariableRefreshRateController::getStateName(VrrControllerState state) const { + switch (state) { + case VrrControllerState::kDisable: + return "Disable"; + case VrrControllerState::kRendering: + return "Rendering"; + case VrrControllerState::kHibernate: + return "Hibernate"; + default: + return "Unknown"; + } +} + +void VariableRefreshRateController::handleCadenceChange() { + ATRACE_CALL(); + if (!mRecord.mNextExpectedPresentTime.has_value()) { + LOG(WARNING) << "VrrController: cadence change occurs without the expected present timing " + "information."; + return; + } + // TODO(b/305311056): handle frame rate change. + mRecord.mNextExpectedPresentTime = std::nullopt; +} + +void VariableRefreshRateController::handleResume() { + ATRACE_CALL(); + if (!mRecord.mNextExpectedPresentTime.has_value()) { + LOG(WARNING) + << "VrrController: resume occurs without the expected present timing information."; + return; + } + // TODO(b/305311281): handle panel resume. + mRecord.mNextExpectedPresentTime = std::nullopt; +} + +void VariableRefreshRateController::handleHibernate() { + ATRACE_CALL(); + // TODO(b/305311206): handle entering panel hibernate. + postEvent(VrrControllerEventType::kHibernateTimeout, + getNowNs() + kDefaultWakeUpTimeInPowerSaving); +} + +void VariableRefreshRateController::handleStayHibernate() { + ATRACE_CALL(); + // TODO(b/305311698): handle keeping panel hibernate. + postEvent(VrrControllerEventType::kHibernateTimeout, + getNowNs() + kDefaultWakeUpTimeInPowerSaving); +} + +void VariableRefreshRateController::threadBody() { + struct sched_param param = {.sched_priority = 2}; + if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) { + LOG(ERROR) << "VrrController: fail to set scheduler to SCHED_FIFO."; + return; + } + for (;;) { + bool stateChanged = false; + { + std::unique_lock<std::mutex> lock(mMutex); + if (mThreadExit) break; + if (!mEnabled) mCondition.wait(lock); + if (!mEnabled) continue; + + if (mEventQueue.empty()) { + mCondition.wait(lock); + } + int64_t whenNs = getNextEventTimeLocked(); + int64_t nowNs = getNowNs(); + if (whenNs > nowNs) { + int64_t delayNs = whenNs - nowNs; + auto res = mCondition.wait_for(lock, std::chrono::nanoseconds(delayNs)); + if (res != std::cv_status::timeout) { + continue; + } + } + if (mEventQueue.empty()) { + LOG(ERROR) << "VrrController: event queue should NOT be empty."; + break; + } + const auto event = mEventQueue.top(); + mEventQueue.pop(); + + if (mState == VrrControllerState::kRendering) { + if (event.mEventType == VrrControllerEventType::kHibernateTimeout) { + LOG(ERROR) << "VrrController: receiving a hibernate timeout event while in the " + "rendering state."; + } + switch (event.mEventType) { + case VrrControllerEventType::kRenderingTimeout: { + handleHibernate(); + mState = VrrControllerState::kHibernate; + stateChanged = true; + break; + } + case VrrControllerEventType::kNotifyExpectedPresentConfig: { + handleCadenceChange(); + break; + } + case VrrControllerEventType::kNextFrameInsertion: { + doFrameInsertionLocked(); + break; + } + default: { + break; + } + } + } else { + if (event.mEventType == VrrControllerEventType::kRenderingTimeout) { + LOG(ERROR) << "VrrController: receiving a rendering timeout event while in the " + "hibernate state."; + } + if (mState != VrrControllerState::kHibernate) { + LOG(ERROR) << "VrrController: expecting to be in hibernate, but instead in " + "state = " + << getStateName(mState); + } + switch (event.mEventType) { + case VrrControllerEventType::kHibernateTimeout: { + handleStayHibernate(); + break; + } + case VrrControllerEventType::kNotifyExpectedPresentConfig: { + handleResume(); + mState = VrrControllerState::kRendering; + stateChanged = true; + break; + } + default: { + break; + } + } + } + } + // TODO(b/309873055): implement a handler to serialize all outer function calls to the same + // thread owned by the VRR controller. + if (stateChanged) { + updateVsyncHistory(); + } + } +} + +void VariableRefreshRateController::postEvent(VrrControllerEventType type, int64_t when) { + VrrControllerEvent event; + event.mEventType = type; + event.mWhenNs = when; + mEventQueue.emplace(event); +} + +void VariableRefreshRateController::updateVsyncHistory() { + int fence = -1; + + { + const std::lock_guard<std::mutex> lock(mMutex); + if (!mLastPresentFence.has_value()) { + return; + } + fence = mLastPresentFence.value(); + mLastPresentFence = std::nullopt; + } + + // Execute the following logic unlocked to enhance performance. + int64_t lastSignalTime = getLastFenceSignalTimeUnlocked(fence); + if (close(fence)) { + LOG(ERROR) << "VrrController: close fence file failed, errno = " << errno; + return; + } else if (lastSignalTime == SIGNAL_TIME_PENDING || lastSignalTime == SIGNAL_TIME_INVALID) { + return; + } + + { + // Acquire the mutex again to store the vsync record. + const std::lock_guard<std::mutex> lock(mMutex); + mRecord.mVsyncHistory + .next() = {.mType = VariableRefreshRateController::VsyncEvent::Type::kReleaseFence, + .mTime = lastSignalTime}; + } +} + +} // namespace android::hardware::graphics::composer diff --git a/libhwc2.1/libvrr/VariableRefreshRateController.h b/libhwc2.1/libvrr/VariableRefreshRateController.h new file mode 100644 index 0000000..1a20109 --- /dev/null +++ b/libhwc2.1/libvrr/VariableRefreshRateController.h @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2023 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. + */ + +#pragma once + +#include <utils/Mutex.h> +#include <condition_variable> +#include <list> +#include <map> +#include <optional> +#include <queue> +#include <thread> + +#include "../libdevice/ExynosDisplay.h" +#include "RingBuffer.h" +#include "VariableRefreshRateInterface.h" + +namespace android::hardware::graphics::composer { + +constexpr uint64_t kMillisecondToNanoSecond = 1000000; + +class VariableRefreshRateController : public VsyncListener, public PresentListener { +public: + ~VariableRefreshRateController(); + + auto static CreateInstance(ExynosDisplay* display) + -> std::shared_ptr<VariableRefreshRateController>; + + int notifyExpectedPresent(int64_t timestamp, int32_t frameIntervalNs); + + // Clear historical record data. + void reset(); + + // After setting the active Vrr configuration, we will automatically transition into the + // rendering state and post the timeout event. + void setActiveVrrConfiguration(hwc2_config_t config); + + void setEnable(bool isEnabled); + + void setPowerMode(int32_t mode); + + void setVrrConfigurations(std::unordered_map<hwc2_config_t, VrrConfig_t> configs); + +private: + static constexpr int kDefaultRingBufferCapacity = 128; + static constexpr int64_t kDefaultWakeUpTimeInPowerSaving = + 500 * (std::nano::den / std::milli::den); // 500 ms + static constexpr int64_t SIGNAL_TIME_PENDING = INT64_MAX; + static constexpr int64_t SIGNAL_TIME_INVALID = -1; + + static const std::string kFrameInsertionNodeName; + static constexpr int kDefaultNumFramesToInsert = 2; + static constexpr int64_t kDefaultFrameInsertionTimer = + 33 * (std::nano::den / std::milli::den); // 33 ms + + enum class VrrControllerState { + kDisable = 0, + kRendering, + kHibernate, + }; + + typedef struct PresentEvent { + hwc2_config_t config; + int64_t mTime; + int mDuration; + } PresentEvent; + + typedef struct VsyncEvent { + enum class Type { + kVblank, + kReleaseFence, + }; + Type mType; + int64_t mTime; + } VsyncEvent; + + typedef struct VrrRecord { + static constexpr int kDefaultRingBufferCapacity = 128; + + void clear() { + mNextExpectedPresentTime = std::nullopt; + mPendingCurrentPresentTime = std::nullopt; + mPresentHistory.clear(); + mVsyncHistory.clear(); + } + + std::optional<PresentEvent> mNextExpectedPresentTime = std::nullopt; + std::optional<PresentEvent> mPendingCurrentPresentTime = std::nullopt; + + typedef RingBuffer<PresentEvent, kDefaultRingBufferCapacity> PresentTimeRecord; + typedef RingBuffer<VsyncEvent, kDefaultRingBufferCapacity> VsyncRecord; + PresentTimeRecord mPresentHistory; + VsyncRecord mVsyncHistory; + } VrrRecord; + + enum VrrControllerEventType { + kRenderingTimeout = 0, + kHibernateTimeout, + kNotifyExpectedPresentConfig, + kNextFrameInsertion, + // Sensors, outer events... + }; + + struct VrrControllerEvent { + bool operator<(const VrrControllerEvent& b) const { return mWhenNs > b.mWhenNs; } + std::string getName() const { + switch (mEventType) { + case kRenderingTimeout: + return "RenderingTimeout"; + case kHibernateTimeout: + return "kHibernateTimeout"; + case kNotifyExpectedPresentConfig: + return "NotifyExpectedPresentConfig"; + case kNextFrameInsertion: + return "kNextFrameInsertion"; + default: + return "Unknown"; + } + } + + std::string toString() const { + std::ostringstream os; + os << "Vrr event: ["; + os << "type = " << getName() << ", "; + os << "when = " << mWhenNs << "ns]"; + return os.str(); + } + int64_t mDisplay; + VrrControllerEventType mEventType; + int64_t mWhenNs; + }; + + VariableRefreshRateController(ExynosDisplay* display); + + // Implement interface PresentListener. + virtual void onPresent(int32_t fence) override; + virtual void setExpectedPresentTime(int64_t timestampNanos, int frameIntervalNs) override; + + // Implement interface VsyncListener. + virtual void onVsync(int64_t timestamp, int32_t vsyncPeriodNanos) override; + + void cancelFrameInsertionLocked(); + + int doFrameInsertionLocked(); + int doFrameInsertionLocked(int frames); + + void dropEventLocked(); + void dropEventLocked(VrrControllerEventType event_type); + + std::string dumpEventQueueLocked(); + + int64_t getLastFenceSignalTimeUnlocked(int fd); + + int64_t getNextEventTimeLocked() const; + + std::string getStateName(VrrControllerState state) const; + + // Functions responsible for state machine transitions. + void handleCadenceChange(); + void handleResume(); + void handleHibernate(); + void handleStayHibernate(); + + void postEvent(VrrControllerEventType type, int64_t when); + + void stopThread(bool exit); + + // The core function of the VRR controller thread. + void threadBody(); + + void updateVsyncHistory(); + + ExynosDisplay* mDisplay; + + // The subsequent variables must be guarded by mMutex when accessed. + int mPendingFramesToInsert = 0; + std::priority_queue<VrrControllerEvent> mEventQueue; + VrrRecord mRecord; + int32_t mPowerMode = -1; + VrrControllerState mState; + hwc2_config_t mVrrActiveConfig; + std::unordered_map<hwc2_config_t, VrrConfig_t> mVrrConfigs; + std::optional<int> mLastPresentFence; + + std::unique_ptr<FileNodeWriter> mFileNodeWritter; + + bool mEnabled = false; + bool mThreadExit = false; + + std::mutex mMutex; + std::condition_variable mCondition; +}; + +} // namespace android::hardware::graphics::composer diff --git a/libhwc2.1/libvrr/VariableRefreshRateInterface.h b/libhwc2.1/libvrr/VariableRefreshRateInterface.h new file mode 100644 index 0000000..51d4848 --- /dev/null +++ b/libhwc2.1/libvrr/VariableRefreshRateInterface.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023 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. + */ + +#pragma once + +namespace android::hardware::graphics::composer { + +class PresentListener { +public: + virtual ~PresentListener() = default; + + virtual void setExpectedPresentTime(int64_t timestampNanos, int frameIntervalNs) = 0; + + virtual void onPresent(int32_t fence) = 0; +}; + +class VsyncListener { +public: + virtual ~VsyncListener() = default; + + virtual void onVsync(int64_t timestamp, int32_t vsyncPeriodNanos) = 0; +}; + +} // namespace android::hardware::graphics::composer diff --git a/libhwc2.1/pixel-display-default.xml b/libhwc2.1/pixel-display-default.xml index 50835eb..ca2892b 100644 --- a/libhwc2.1/pixel-display-default.xml +++ b/libhwc2.1/pixel-display-default.xml @@ -1,7 +1,7 @@ <manifest version="1.0" type="device"> <hal format="aidl"> <name>com.google.hardware.pixel.display</name> - <version>9</version> + <version>10</version> <fqname>IDisplay/default</fqname> </hal> </manifest> diff --git a/libhwc2.1/pixel-display-secondary.xml b/libhwc2.1/pixel-display-secondary.xml index 3cf2883..907cf27 100644 --- a/libhwc2.1/pixel-display-secondary.xml +++ b/libhwc2.1/pixel-display-secondary.xml @@ -1,7 +1,7 @@ <manifest version="1.0" type="device"> <hal format="aidl"> <name>com.google.hardware.pixel.display</name> - <version>9</version> + <version>10</version> <fqname>IDisplay/secondary</fqname> </hal> </manifest> diff --git a/libhwc2.1/pixel-display.cpp b/libhwc2.1/pixel-display.cpp index c958346..5edd194 100644 --- a/libhwc2.1/pixel-display.cpp +++ b/libhwc2.1/pixel-display.cpp @@ -175,7 +175,7 @@ ndk::ScopedAStatus Display::setCompensationImageHandle(const NativeHandle &nativ ndk::ScopedAStatus Display::setMinIdleRefreshRate(int fps, int *_aidl_return) { if (mDisplay) { - *_aidl_return = mDisplay->setMinIdleRefreshRate(fps, VrrThrottleRequester::PIXEL_DISP); + *_aidl_return = mDisplay->setMinIdleRefreshRate(fps, RrThrottleRequester::PIXEL_DISP); return ndk::ScopedAStatus::ok(); } return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); @@ -194,7 +194,7 @@ ndk::ScopedAStatus Display::setRefreshRateThrottle(int delayMs, int *_aidl_retur std::chrono::nanoseconds>( std::chrono::milliseconds(delayMs)) .count(), - VrrThrottleRequester::PIXEL_DISP); + RrThrottleRequester::PIXEL_DISP); return ndk::ScopedAStatus::ok(); } return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); @@ -209,13 +209,13 @@ bool Display::runMediator(const RoiRect &roi, const Weight &weight, const Histog std::scoped_lock lock(mMediator.mConfigMutex); isConfigChanged = mMediator.mConfig != pendingConfig; - if (isConfigChanged && - mMediator.setRoiWeightThreshold(roi, weight, pos) != HistogramErrorCode::NONE) { - ALOGE("histogram error, SET_ROI_WEIGHT_THRESHOLD ERROR\n"); - return false; + if (isConfigChanged) { + if (mMediator.setRoiWeightThreshold(roi, weight, pos) != HistogramErrorCode::NONE) { + ALOGE("histogram error, SET_ROI_WEIGHT_THRESHOLD ERROR\n"); + return false; + } + mMediator.mConfig = pendingConfig; } - - mMediator.mConfig = pendingConfig; } if (!mMediator.histRequested() && |