From f9062b696444f35c4385d207e6cafbb99ed8828f Mon Sep 17 00:00:00 2001 From: Sasha McIntosh Date: Tue, 12 Nov 2024 10:55:06 -0500 Subject: [PATCH] drm_hwcomposer: Set HDR metadata on the connector Implement a function to set HDR metadata on the connector. Support HDR10 and HLG, which are common HDR types. Change-Id: Id3dbe8eea2ee6b8ba700af23845a43e2070dd14e Signed-off-by: Sasha McIntosh --- Android.bp | 1 + drm/DrmAtomicStateManager.cpp | 15 +++++++ drm/DrmAtomicStateManager.h | 2 + drm/DrmConnector.cpp | 3 ++ drm/DrmConnector.h | 5 +++ hwc2_device/HwcDisplay.cpp | 71 +++++++++++++++++++++++++++++++++ hwc2_device/HwcDisplay.h | 2 + meson.build | 1 + utils/EdidWrapper.h | 33 +++++++++------ utils/LibdisplayEdidWrapper.cpp | 67 +++++++++++++++++++++++++++++++ 10 files changed, 188 insertions(+), 12 deletions(-) create mode 100644 utils/LibdisplayEdidWrapper.cpp diff --git a/Android.bp b/Android.bp index a8d95eb..fab7a98 100644 --- a/Android.bp +++ b/Android.bp @@ -104,6 +104,7 @@ filegroup { "utils/fd.cpp", "utils/properties.cpp", + "utils/LibdisplayEdidWrapper.cpp", ], } diff --git a/drm/DrmAtomicStateManager.cpp b/drm/DrmAtomicStateManager.cpp index 9a8769a..9ce9a93 100644 --- a/drm/DrmAtomicStateManager.cpp +++ b/drm/DrmAtomicStateManager.cpp @@ -150,6 +150,21 @@ auto DrmAtomicStateManager::CommitFrame(AtomicCommitArgs &args) -> int { return -EINVAL; } + if (args.hdr_metadata && connector->GetHdrOutputMetadataProperty()) { + auto blob = drm->RegisterUserPropertyBlob(args.hdr_metadata.get(), + sizeof(hdr_output_metadata)); + new_frame_state.hdr_metadata_blob = std::move(blob); + if (!new_frame_state.hdr_metadata_blob) { + ALOGE("Failed to create %s blob", + connector->GetHdrOutputMetadataProperty().GetName().c_str()); + return -EINVAL; + } + + if (!connector->GetHdrOutputMetadataProperty() + .AtomicSet(*pset, *new_frame_state.hdr_metadata_blob)) + return -EINVAL; + } + auto unused_planes = new_frame_state.used_planes; if (args.composition) { diff --git a/drm/DrmAtomicStateManager.h b/drm/DrmAtomicStateManager.h index 8d22b99..4af04d1 100644 --- a/drm/DrmAtomicStateManager.h +++ b/drm/DrmAtomicStateManager.h @@ -40,6 +40,7 @@ struct AtomicCommitArgs { std::shared_ptr color_matrix; std::optional colorspace; std::optional content_type; + std::shared_ptr hdr_metadata; std::shared_ptr writeback_fb; SharedFd writeback_release_fence; @@ -84,6 +85,7 @@ class DrmAtomicStateManager { DrmModeUserPropertyBlobUnique mode_blob; DrmModeUserPropertyBlobUnique ctm_blob; + DrmModeUserPropertyBlobUnique hdr_metadata_blob; int release_fence_pt_index{}; diff --git a/drm/DrmConnector.cpp b/drm/DrmConnector.cpp index 6be4067..37e1be4 100644 --- a/drm/DrmConnector.cpp +++ b/drm/DrmConnector.cpp @@ -143,6 +143,9 @@ auto DrmConnector::Init()-> bool { GetOptionalConnectorProperty("content type", &content_type_property_); + GetOptionalConnectorProperty("HDR_OUTPUT_METADATA", + &hdr_output_metadata_property_); + if (GetOptionalConnectorProperty("panel orientation", &panel_orientation_)) { panel_orientation_ .AddEnumToMapReverse("Normal", diff --git a/drm/DrmConnector.h b/drm/DrmConnector.h index fc17206..c22d059 100644 --- a/drm/DrmConnector.h +++ b/drm/DrmConnector.h @@ -115,6 +115,10 @@ class DrmConnector : public PipelineBindable { return content_type_property_; } + auto &GetHdrOutputMetadataProperty() const { + return hdr_output_metadata_property_; + } + auto &GetWritebackFbIdProperty() const { return writeback_fb_id_; } @@ -169,6 +173,7 @@ class DrmConnector : public PipelineBindable { DrmProperty edid_property_; DrmProperty colorspace_property_; DrmProperty content_type_property_; + DrmProperty hdr_output_metadata_property_; DrmProperty link_status_property_; DrmProperty writeback_pixel_formats_; diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp index a0c200e..5abbc4d 100644 --- a/hwc2_device/HwcDisplay.cpp +++ b/hwc2_device/HwcDisplay.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -39,6 +40,7 @@ #include "utils/properties.h" using ::android::DrmDisplayPipeline; +using ColorGamut = ::android::ColorSpace; namespace android { @@ -787,6 +789,7 @@ AtomicCommitArgs HwcDisplay::CreateModesetCommit( args.color_matrix = color_matrix_; args.content_type = content_type_; args.colorspace = colorspace_; + args.hdr_metadata = hdr_metadata_; std::vector composition_layers; if (modeset_layer) { @@ -816,6 +819,7 @@ HWC2::Error HwcDisplay::CreateComposition(AtomicCommitArgs &a_args) { a_args.color_matrix = color_matrix_; a_args.content_type = content_type_; a_args.colorspace = colorspace_; + a_args.hdr_metadata = hdr_metadata_; uint32_t prev_vperiod_ns = 0; GetDisplayVsyncPeriod(&prev_vperiod_ns); @@ -979,6 +983,18 @@ HWC2::Error HwcDisplay::SetActiveConfigInternal(uint32_t config, staged_mode_change_time_ = change_time; staged_mode_config_id_ = config; + std::vector hdr_types; + GetEdid()->GetSupportedHdrTypes(hdr_types); + if (hdr_types.empty()) { + hdr_metadata_.reset(); + colorspace_ = Colorspace::kDefault; + } else { + auto ret = SetHdrOutputMetadata(hdr_types.front()); + if (ret != HWC2::Error::None) + return ret; + colorspace_ = Colorspace::kBt2020Rgb; + } + return HWC2::Error::None; } @@ -1246,6 +1262,61 @@ HWC2::Error HwcDisplay::GetDisplayVsyncPeriod( (int32_t *)(outVsyncPeriod)); } +// Display primary values are coded as unsigned 16-bit values in units of +// 0.00002, where 0x0000 represents zero and 0xC350 represents 1.0000. +static uint64_t ToU16ColorValue(float in) { + constexpr float kPrimariesFixedPoint = 50000.F; + return static_cast(kPrimariesFixedPoint * in); +} + +HWC2::Error HwcDisplay::SetHdrOutputMetadata(ui::Hdr type) { + hdr_metadata_ = std::make_shared(); + hdr_metadata_->metadata_type = 0; + auto *m = &hdr_metadata_->hdmi_metadata_type1; + m->metadata_type = 0; + + switch (type) { + case ui::Hdr::HDR10: + m->eotf = 2; // PQ + break; + case ui::Hdr::HLG: + m->eotf = 3; // HLG + break; + default: + return HWC2::Error::Unsupported; + } + + // Most luminance values are coded as an unsigned 16-bit value in units of 1 + // cd/m2, where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. + std::vector types; + float hdr_luminance[3]{0.F, 0.F, 0.F}; + GetEdid()->GetHdrCapabilities(types, &hdr_luminance[0], &hdr_luminance[1], + &hdr_luminance[2]); + m->max_display_mastering_luminance = m->max_cll = static_cast( + hdr_luminance[0]); + m->max_fall = static_cast(hdr_luminance[1]); + // The min luminance value is coded as an unsigned 16-bit value in units of + // 0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF + // represents 6.5535 cd/m2. + m->min_display_mastering_luminance = static_cast(hdr_luminance[2] * + 10000.F); + + auto gamut = ColorGamut::BT2020(); + auto primaries = gamut.getPrimaries(); + m->display_primaries[0].x = ToU16ColorValue(primaries[0].x); + m->display_primaries[0].y = ToU16ColorValue(primaries[0].y); + m->display_primaries[1].x = ToU16ColorValue(primaries[1].x); + m->display_primaries[1].y = ToU16ColorValue(primaries[1].y); + m->display_primaries[2].x = ToU16ColorValue(primaries[2].x); + m->display_primaries[2].y = ToU16ColorValue(primaries[2].y); + + auto whitePoint = gamut.getWhitePoint(); + m->white_point.x = ToU16ColorValue(whitePoint.x); + m->white_point.y = ToU16ColorValue(whitePoint.y); + + return HWC2::Error::None; +} + #if __ANDROID_API__ > 29 HWC2::Error HwcDisplay::GetDisplayConnectionType(uint32_t *outType) { if (IsInHeadlessMode()) { diff --git a/hwc2_device/HwcDisplay.h b/hwc2_device/HwcDisplay.h index 01ea33d..24c7465 100644 --- a/hwc2_device/HwcDisplay.h +++ b/hwc2_device/HwcDisplay.h @@ -284,6 +284,7 @@ class HwcDisplay { android_color_transform_t color_transform_hint_{}; int32_t content_type_{}; Colorspace colorspace_{}; + std::shared_ptr hdr_metadata_; std::shared_ptr current_plan_; @@ -297,6 +298,7 @@ class HwcDisplay { HWC2::Error Init(); HWC2::Error SetActiveConfigInternal(uint32_t config, int64_t change_time); + HWC2::Error SetHdrOutputMetadata(ui::Hdr hdrType); auto GetEdid() -> EdidWrapperUnique & { return GetPipe().connector->Get()->GetParsedEdid(); } diff --git a/meson.build b/meson.build index 97474e2..3d5c9f0 100644 --- a/meson.build +++ b/meson.build @@ -16,6 +16,7 @@ src_common = files( 'backend/Backend.cpp', 'backend/BackendClient.cpp', 'utils/fd.cpp', + 'utils/LibdisplayEdidWrapper.cpp', 'utils/properties.cpp', ) diff --git a/utils/EdidWrapper.h b/utils/EdidWrapper.h index 3552001..867d1a0 100644 --- a/utils/EdidWrapper.h +++ b/utils/EdidWrapper.h @@ -24,6 +24,8 @@ extern "C" { } #endif +#include + #include "drm/DrmUnique.h" #include "utils/log.h" @@ -35,6 +37,16 @@ class EdidWrapper { EdidWrapper() = default; EdidWrapper(const EdidWrapper &) = delete; virtual ~EdidWrapper() = default; + + virtual void GetSupportedHdrTypes(std::vector &types) { + types.clear(); + }; + virtual void GetHdrCapabilities(std::vector &types, + const float * /*max_luminance*/, + const float * /*max_average_luminance*/, + const float * /*min_luminance*/) { + GetSupportedHdrTypes(types); + }; }; #if HAS_LIBDISPLAY_INFO @@ -42,26 +54,23 @@ class EdidWrapper { class LibdisplayEdidWrapper final : public EdidWrapper { public: LibdisplayEdidWrapper() = delete; - LibdisplayEdidWrapper(di_info *info) : info_(info) { - } ~LibdisplayEdidWrapper() override { di_info_destroy(info_); } static auto Create(DrmModePropertyBlobUnique blob) - -> std::unique_ptr { - if (!blob) - return nullptr; + -> std::unique_ptr; - auto *info = di_info_parse_edid(blob->data, blob->length); - if (!info) { - ALOGW("Failed to parse edid blob."); - return nullptr; - } + void GetSupportedHdrTypes(std::vector &types) override; - return std::make_unique(std::move(info)); - } + void GetHdrCapabilities(std::vector &types, + const float *max_luminance, + const float *max_average_luminance, + const float *min_luminance) override; private: + LibdisplayEdidWrapper(di_info *info) : info_(std::move(info)) { + } + di_info *info_{}; }; #endif diff --git a/utils/LibdisplayEdidWrapper.cpp b/utils/LibdisplayEdidWrapper.cpp new file mode 100644 index 0000000..5a17b93 --- /dev/null +++ b/utils/LibdisplayEdidWrapper.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2024 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 LOG_TAG "drmhwc" + +#if HAS_LIBDISPLAY_INFO +#include "utils/EdidWrapper.h" +#include "utils/log.h" + +namespace android { + +auto LibdisplayEdidWrapper::Create(DrmModePropertyBlobUnique blob) + -> std::unique_ptr { + if (!blob) + return nullptr; + + auto *info = di_info_parse_edid(blob->data, blob->length); + if (!info) { + ALOGW("Failed to parse edid blob."); + return nullptr; + } + + return std::unique_ptr( + new LibdisplayEdidWrapper(std::move(info))); +} + +void LibdisplayEdidWrapper::GetSupportedHdrTypes(std::vector &types) { + types.clear(); + + const auto *hdr_static_meta = di_info_get_hdr_static_metadata(info_); + const auto *colorimetries = di_info_get_supported_signal_colorimetry(info_); + if (colorimetries->bt2020_cycc || colorimetries->bt2020_ycc || + colorimetries->bt2020_rgb) { + if (hdr_static_meta->pq) + types.emplace_back(ui::Hdr::HDR10); + if (hdr_static_meta->hlg) + types.emplace_back(ui::Hdr::HLG); + } +} + +void LibdisplayEdidWrapper::GetHdrCapabilities( + std::vector &types, const float *max_luminance, + const float *max_average_luminance, const float *min_luminance) { + GetSupportedHdrTypes(types); + + const auto *hdr_static_meta = di_info_get_hdr_static_metadata(info_); + max_luminance = &hdr_static_meta->desired_content_max_luminance; + max_average_luminance = &hdr_static_meta + ->desired_content_max_frame_avg_luminance; + min_luminance = &hdr_static_meta->desired_content_min_luminance; +} + +} // namespace android +#endif