summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-03-12 23:15:53 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-03-12 23:15:53 +0000
commit701092d6c7c48cfeac9fcfe1884c3e956b5e4ec3 (patch)
treee004f1a9b525df54a6078e9327258a69f66978b2
parentf70c5cdf389680ebe8fe3f033d97a0d548e691d7 (diff)
parentb00b1a03678c6ac7792ab34327e989ab3c634bbd (diff)
downloadcommon-sdk-release.tar.gz
Snap for 11566117 from b00b1a03678c6ac7792ab34327e989ab3c634bbd to sdk-releaseplatform-tools-35.0.1sdk-release
Change-Id: I2524ffabeb4e5b177a191bdd6f1c2c039ec5b2fa
-rw-r--r--gralloc-headers/Android.bp12
-rw-r--r--gralloc-headers/pixel-gralloc/format.h1
-rw-r--r--gralloc-headers/pixel-gralloc/mapper.h85
-rw-r--r--gralloc-headers/pixel-gralloc/metadata.h7
-rw-r--r--gralloc-headers/pixel-gralloc/usage.h12
-rw-r--r--gralloc-headers/pixel-gralloc/utils.h88
-rw-r--r--hwc3/Android.mk18
-rw-r--r--hwc3/Composer.cpp11
-rw-r--r--hwc3/Composer.h6
-rw-r--r--hwc3/ComposerClient.cpp26
-rw-r--r--hwc3/ComposerClient.h6
-rw-r--r--hwc3/ComposerCommandEngine.cpp33
-rw-r--r--hwc3/ComposerCommandEngine.h9
-rw-r--r--hwc3/hwc3-default.xml2
-rw-r--r--hwc3/impl/HalImpl.cpp80
-rw-r--r--hwc3/impl/HalImpl.h11
-rw-r--r--hwc3/include/IComposerHal.h44
-rw-r--r--hwc3/service.cpp5
-rw-r--r--include/displaycolor/displaycolor.h11
-rw-r--r--libacryl/Android.mk14
-rw-r--r--libhwc2.1/Android.mk70
-rw-r--r--libhwc2.1/ExynosHWC.cpp3
-rw-r--r--libhwc2.1/ExynosHWCDebug.cpp2
-rw-r--r--libhwc2.1/ExynosHWCDebug.h2
-rw-r--r--libhwc2.1/libdevice/BrightnessController.cpp19
-rw-r--r--libhwc2.1/libdevice/BrightnessController.h10
-rw-r--r--libhwc2.1/libdevice/ExynosDevice.cpp50
-rw-r--r--libhwc2.1/libdevice/ExynosDevice.h7
-rw-r--r--libhwc2.1/libdevice/ExynosDisplay.cpp329
-rw-r--r--libhwc2.1/libdevice/ExynosDisplay.h150
-rw-r--r--libhwc2.1/libdevice/ExynosLayer.cpp5
-rw-r--r--libhwc2.1/libdevice/HistogramDevice.cpp303
-rw-r--r--libhwc2.1/libdevice/HistogramDevice.h119
-rw-r--r--libhwc2.1/libdisplayinterface/ExynosDisplayDrmInterface.cpp240
-rw-r--r--libhwc2.1/libdisplayinterface/ExynosDisplayDrmInterface.h59
-rw-r--r--libhwc2.1/libdisplayinterface/ExynosDisplayInterface.cpp2
-rw-r--r--libhwc2.1/libdisplayinterface/ExynosDisplayInterface.h10
-rw-r--r--libhwc2.1/libdrmresource/drm/drmconnector.cpp45
-rw-r--r--libhwc2.1/libdrmresource/drm/drmdevice.cpp35
-rw-r--r--libhwc2.1/libdrmresource/drm/drmmode.cpp49
-rw-r--r--libhwc2.1/libdrmresource/drm/drmproperty.cpp133
-rw-r--r--libhwc2.1/libdrmresource/drm/vsyncworker.cpp125
-rw-r--r--libhwc2.1/libdrmresource/include/drmconnector.h7
-rw-r--r--libhwc2.1/libdrmresource/include/drmeventlistener.h3
-rw-r--r--libhwc2.1/libdrmresource/include/drmmode.h24
-rw-r--r--libhwc2.1/libdrmresource/include/drmproperty.h19
-rw-r--r--libhwc2.1/libdrmresource/include/vsyncworker.h50
-rw-r--r--libhwc2.1/libexternaldisplay/ExynosExternalDisplay.cpp22
-rw-r--r--libhwc2.1/libhwcService/ExynosHWCService.cpp4
-rw-r--r--libhwc2.1/libhwchelper/ExynosHWCHelper.h62
-rw-r--r--libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.cpp317
-rw-r--r--libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.h50
-rw-r--r--libhwc2.1/libresource/ExynosMPP.cpp6
-rw-r--r--libhwc2.1/libresource/ExynosMPP.h3
-rw-r--r--libhwc2.1/libvrr/RingBuffer.h64
-rw-r--r--libhwc2.1/libvrr/VariableRefreshRateController.cpp583
-rw-r--r--libhwc2.1/libvrr/VariableRefreshRateController.h207
-rw-r--r--libhwc2.1/libvrr/VariableRefreshRateInterface.h37
-rw-r--r--libhwc2.1/pixel-display-default.xml2
-rw-r--r--libhwc2.1/pixel-display-secondary.xml2
-rw-r--r--libhwc2.1/pixel-display.cpp16
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 &timestamp) {
- float refresh = 60.0f; // Default to 60Hz refresh rate
+int VSyncWorker::SyntheticWaitVBlank(int64_t &timestampNs) {
+ 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 &timestamp) {
} 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 &timestamp);
+ 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, &param) != 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() &&