Merge remote-tracking branch 'goog/qcom/release/LA.UM.7.8.9.C3.08.00.00.787.021' into sc-dev
Bug: 185197826
Change-Id: I2466b46543d6daa92fa6d6660eaafd8e9157eef3
diff --git a/Android.bp b/Android.bp
index bc72fd6..6867776 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,3 +1,32 @@
+package {
+ default_applicable_licenses: ["hardware_qcom_sdm845_display_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+ name: "hardware_qcom_sdm845_display_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ "SPDX-license-identifier-BSD",
+ "legacy_not_a_contribution",
+ ],
+ // large-scale-change unable to identify any license_text files
+}
+
cc_defaults {
name: "display_defaults",
cflags: [
@@ -28,6 +57,7 @@
"sdm/include",
"gralloc",
"libdebug",
+ "libhistogram",
],
header_libs: ["libhardware_headers"],
export_header_lib_headers: ["libhardware_headers"],
diff --git a/Android.mk b/Android.mk
index 707acf1..76150c7 100644
--- a/Android.mk
+++ b/Android.mk
@@ -4,11 +4,12 @@
ifneq ($(TARGET_IS_HEADLESS), true)
display-hals += libcopybit liblight libmemtrack hdmi_cec \
- $(sdm-libs)/hwc2 gpu_tonemapper libdrmutils libdisplayconfig
+ $(sdm-libs)/hwc2 gpu_tonemapper libdrmutils libdisplayconfig libhistogram drm.vendor
endif
display-hals += gralloc
+ifneq ($(BUILD_WITHOUT_VENDOR),true)
ifeq ($(call is-vendor-board-platform,QCOM),true)
include $(call all-named-subdir-makefiles,$(display-hals))
else
@@ -16,4 +17,5 @@
include $(call all-named-subdir-makefiles,$(display-hals))
endif
endif
+endif
endif #TARGET_DISABLE_DISPLAY
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..a4802d9
--- /dev/null
+++ b/CleanSpec.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# move to system_ext b/138285503
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libdisplayconfig.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/libdisplayconfig.so)
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+ license_type: NOTICE
+}
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..79ec05d
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,5 @@
+# Default code reviewers picked from top 3 or more developers.
+# Please update this list if you find better candidates.
+spyffe@google.com
+yichichen@google.com
+salidoa@google.com
diff --git a/common.mk b/common.mk
index 72bd489..fb4774a 100644
--- a/common.mk
+++ b/common.mk
@@ -1,13 +1,13 @@
#Common headers
display_top := $(call my-dir)
display_config_version := $(shell \
- if [ -d "$(TOP)/vendor/qcom/opensource/interfaces/display/config/1.1" ];\
+ if [ -d "$(QC_OPEN_PATH)/interfaces/vendor/display/config/1.1" ];\
then echo DISPLAY_CONFIG_1_1; fi)
display_config_version := $(shell \
- if [ -d "$(TOP)/vendor/qcom/opensource/interfaces/display/config/1.2" ];\
+ if [ -d "$(QC_OPEN_PATH)/interfaces/vendor/display/config/1.2" ];\
then echo DISPLAY_CONFIG_1_2; fi)
display_config_version := $(shell \
- if [ -d "$(TOP)/vendor/qcom/opensource/interfaces/display/config/1.3" ];\
+ if [ -d "$(QC_OPEN_PATH)/interfaces/vendor/display/config/1.3" ];\
then echo DISPLAY_CONFIG_1_3; fi)
#Common C flags
@@ -51,6 +51,10 @@
common_flags += -DVIDEO_MODE_DEFER_RETIRE_FENCE
endif
+ifeq ($(TARGET_USES_NV21_CAMERA_PREVIEW),true)
+ common_flags += -DUSE_NV21_CAMERA_PREVIEW
+endif
+
ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
common_flags += -DUSER_DEBUG
endif
@@ -59,7 +63,7 @@
common_flags += --compile-and-analyze --analyzer-perf --analyzer-Werror
endif
-common_includes := system/core/base/include
+common_includes := system/libbase/include
CHECK_VERSION_LE = $(shell if [ $(1) -le $(2) ] ; then echo true ; else echo false ; fi)
PLATFORM_SDK_NOUGAT = 25
ifeq "REL" "$(PLATFORM_VERSION_CODENAME)"
diff --git a/config/sdm710.mk b/config/sdm710.mk
index 8aff94b..83ba143 100644
--- a/config/sdm710.mk
+++ b/config/sdm710.mk
@@ -36,8 +36,11 @@
TARGET_USES_COLOR_METADATA := true
TARGET_USES_DRM_PP := true
TARGET_HAS_WIDE_COLOR_DISPLAY := true
-TARGET_HAS_HDR_DISPLAY := true
TARGET_USES_DISPLAY_RENDER_INTENTS := true
+TARGET_HAS_HDR_DISPLAY := true
+ifneq (,$(filter sdm670, $(PRODUCT_PLATFORM)))
+ TARGET_HAS_HDR_DISPLAY := false
+endif
PRODUCT_PROPERTY_OVERRIDES += \
persist.demo.hdmirotationlock=false \
diff --git a/config/sdm845.mk b/config/sdm845.mk
index 4284ecf..2170a4e 100644
--- a/config/sdm845.mk
+++ b/config/sdm845.mk
@@ -20,7 +20,6 @@
TARGET_FORCE_HWC_FOR_VIRTUAL_DISPLAYS := true
-MAX_VIRTUAL_DISPLAY_DIMENSION := 4096
NUM_FRAMEBUFFER_SURFACE_BUFFERS := 2
#Enable Charging Icon
TARGET_RECOVERY_PIXEL_FORMAT := RGBX_8888
@@ -36,6 +35,9 @@
ro.vendor.display.cabl=2 \
debug.sf.latch_unsignaled=1 \
+PRODUCT_DEFAULT_PROPERTY_OVERRIDES += \
+ ro.surface_flinger.max_virtual_display_dimension=4096
+
# This matrix should be in column major order, per SurfaceFlinger requirement
# 1.16868 -0.16868 0.00000
# -0.03155 1.03155 0.00000
diff --git a/gpu_tonemapper/Android.mk b/gpu_tonemapper/Android.mk
index 32cec0c..845cc5f 100644
--- a/gpu_tonemapper/Android.mk
+++ b/gpu_tonemapper/Android.mk
@@ -9,6 +9,8 @@
include $(CLEAR_VARS)
LOCAL_MODULE := libgpu_tonemapper
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 legacy_not_a_contribution
+LOCAL_LICENSE_CONDITIONS := by_exception_only not_allowed notice
LOCAL_VENDOR_MODULE := true
LOCAL_MODULE_TAGS := optional
LOCAL_C_INCLUDES := $(TARGET_OUT_HEADERS)/qcom/display/
diff --git a/gpu_tonemapper/glengine.cpp b/gpu_tonemapper/glengine.cpp
index 35e1932..bf3b58a 100644
--- a/gpu_tonemapper/glengine.cpp
+++ b/gpu_tonemapper/glengine.cpp
@@ -315,36 +315,30 @@
//-----------------------------------------------------------------------------
{
for (GLint error = glGetError(); error; error = glGetError()) {
- char *pError;
+ const char *pError = "<unknown error>";
switch (error) {
case GL_NO_ERROR:
- pError = (char *)"GL_NO_ERROR";
+ pError = "GL_NO_ERROR";
break;
case GL_INVALID_ENUM:
- pError = (char *)"GL_INVALID_ENUM";
+ pError = "GL_INVALID_ENUM";
break;
case GL_INVALID_VALUE:
- pError = (char *)"GL_INVALID_VALUE";
+ pError = "GL_INVALID_VALUE";
break;
case GL_INVALID_OPERATION:
- pError = (char *)"GL_INVALID_OPERATION";
+ pError = "GL_INVALID_OPERATION";
break;
case GL_OUT_OF_MEMORY:
- pError = (char *)"GL_OUT_OF_MEMORY";
+ pError = "GL_OUT_OF_MEMORY";
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
- pError = (char *)"GL_INVALID_FRAMEBUFFER_OPERATION";
+ pError = "GL_INVALID_FRAMEBUFFER_OPERATION";
break;
-
- default:
- ALOGE("glError (0x%x) %s:%d\n", error, file, line);
- return;
}
ALOGE("glError (%s) %s:%d\n", pError, file, line);
- return;
}
- return;
}
//-----------------------------------------------------------------------------
@@ -357,59 +351,54 @@
break;
}
- char *pError;
+ const char *pError = "<unknown error>";
switch (error) {
case EGL_SUCCESS:
- pError = (char *)"EGL_SUCCESS";
+ pError = "EGL_SUCCESS";
break;
case EGL_NOT_INITIALIZED:
- pError = (char *)"EGL_NOT_INITIALIZED";
+ pError = "EGL_NOT_INITIALIZED";
break;
case EGL_BAD_ACCESS:
- pError = (char *)"EGL_BAD_ACCESS";
+ pError = "EGL_BAD_ACCESS";
break;
case EGL_BAD_ALLOC:
- pError = (char *)"EGL_BAD_ALLOC";
+ pError = "EGL_BAD_ALLOC";
break;
case EGL_BAD_ATTRIBUTE:
- pError = (char *)"EGL_BAD_ATTRIBUTE";
+ pError = "EGL_BAD_ATTRIBUTE";
break;
case EGL_BAD_CONTEXT:
- pError = (char *)"EGL_BAD_CONTEXT";
+ pError = "EGL_BAD_CONTEXT";
break;
case EGL_BAD_CONFIG:
- pError = (char *)"EGL_BAD_CONFIG";
+ pError = "EGL_BAD_CONFIG";
break;
case EGL_BAD_CURRENT_SURFACE:
- pError = (char *)"EGL_BAD_CURRENT_SURFACE";
+ pError = "EGL_BAD_CURRENT_SURFACE";
break;
case EGL_BAD_DISPLAY:
- pError = (char *)"EGL_BAD_DISPLAY";
+ pError = "EGL_BAD_DISPLAY";
break;
case EGL_BAD_SURFACE:
- pError = (char *)"EGL_BAD_SURFACE";
+ pError = "EGL_BAD_SURFACE";
break;
case EGL_BAD_MATCH:
- pError = (char *)"EGL_BAD_MATCH";
+ pError = "EGL_BAD_MATCH";
break;
case EGL_BAD_PARAMETER:
- pError = (char *)"EGL_BAD_PARAMETER";
+ pError = "EGL_BAD_PARAMETER";
break;
case EGL_BAD_NATIVE_PIXMAP:
- pError = (char *)"EGL_BAD_NATIVE_PIXMAP";
+ pError = "EGL_BAD_NATIVE_PIXMAP";
break;
case EGL_BAD_NATIVE_WINDOW:
- pError = (char *)"EGL_BAD_NATIVE_WINDOW";
+ pError = "EGL_BAD_NATIVE_WINDOW";
break;
case EGL_CONTEXT_LOST:
- pError = (char *)"EGL_CONTEXT_LOST";
+ pError = "EGL_CONTEXT_LOST";
break;
- default:
- ALOGE("eglError (0x%x) %s:%d\n", error, file, line);
- return;
}
ALOGE("eglError (%s) %s:%d\n", pError, file, line);
- return;
}
- return;
}
diff --git a/gralloc/Android.mk b/gralloc/Android.mk
index b97b5df..c1a5042 100644
--- a/gralloc/Android.mk
+++ b/gralloc/Android.mk
@@ -4,18 +4,19 @@
include $(CLEAR_VARS)
LOCAL_MODULE := gralloc.$(TARGET_BOARD_PLATFORM)
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD legacy_not_a_contribution
+LOCAL_LICENSE_CONDITIONS := by_exception_only not_allowed notice
LOCAL_VENDOR_MODULE := true
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_MODULE_TAGS := optional
-LOCAL_C_INCLUDES := $(common_includes)
+LOCAL_C_INCLUDES := $(common_includes) $(kernel_includes)
LOCAL_HEADER_LIBRARIES := display_headers
LOCAL_SHARED_LIBRARIES := $(common_libs) libqdMetaData libsync libgrallocutils \
libgralloccore \
android.hardware.graphics.mapper@2.0 \
android.hardware.graphics.mapper@2.1
-LOCAL_CFLAGS := $(common_flags) -DLOG_TAG=\"qdgralloc\" -Wall -std=c++14 -Werror
-LOCAL_CFLAGS += -isystem $(kernel_includes)
+LOCAL_CFLAGS := $(common_flags) -DLOG_TAG=\"qdgralloc\" -Wall -Werror
LOCAL_CLANG := true
LOCAL_ADDITIONAL_DEPENDENCIES := $(common_deps)
LOCAL_SRC_FILES := gr_device_impl.cpp
@@ -26,6 +27,8 @@
#libgrallocutils
include $(CLEAR_VARS)
LOCAL_MODULE := libgrallocutils
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD legacy_not_a_contribution
+LOCAL_LICENSE_CONDITIONS := by_exception_only not_allowed notice
LOCAL_VENDOR_MODULE := true
LOCAL_MODULE_TAGS := optional
LOCAL_C_INCLUDES := $(common_includes) $(kernel_includes)
@@ -41,6 +44,8 @@
#libgralloccore
include $(CLEAR_VARS)
LOCAL_MODULE := libgralloccore
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD legacy_not_a_contribution
+LOCAL_LICENSE_CONDITIONS := by_exception_only not_allowed notice
LOCAL_VENDOR_MODULE := true
LOCAL_MODULE_TAGS := optional
LOCAL_C_INCLUDES := $(common_includes) $(kernel_includes)
@@ -65,6 +70,8 @@
#mapper
include $(CLEAR_VARS)
LOCAL_MODULE := android.hardware.graphics.mapper@2.0-impl-qti-display
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD legacy_not_a_contribution
+LOCAL_LICENSE_CONDITIONS := by_exception_only not_allowed notice
LOCAL_VENDOR_MODULE := true
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_MODULE_TAGS := optional
@@ -72,7 +79,6 @@
LOCAL_HEADER_LIBRARIES := display_headers
LOCAL_SHARED_LIBRARIES := $(common_libs) \
libhidlbase \
- libhidltransport \
libqdMetaData \
libgrallocutils \
libgralloccore \
@@ -86,13 +92,14 @@
include $(CLEAR_VARS)
LOCAL_MODULE := vendor.qti.hardware.display.allocator@1.0-service
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD legacy_not_a_contribution
+LOCAL_LICENSE_CONDITIONS := by_exception_only not_allowed notice
LOCAL_VENDOR_MODULE := true
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_MODULE_TAGS := optional
LOCAL_HEADER_LIBRARIES := display_headers
LOCAL_SHARED_LIBRARIES := $(common_libs) \
libhidlbase \
- libhidltransport\
libqdMetaData \
libgrallocutils \
libgralloccore \
diff --git a/gralloc/QtiMapper.cpp b/gralloc/QtiMapper.cpp
index 8065536..ee1d36f 100644
--- a/gralloc/QtiMapper.cpp
+++ b/gralloc/QtiMapper.cpp
@@ -245,13 +245,15 @@
auto err = Error::BAD_BUFFER;
auto hnd = static_cast<private_handle_t *>(buffer);
uint32_t num_fds = 0, num_ints = 0;
- if (buffer != nullptr && private_handle_t::validate(hnd) == 0) {
+ if (buffer != nullptr && private_handle_t::validate(hnd) == 0 &&
+ buf_mgr_->IsBufferImported(hnd) == Error::NONE) {
num_fds = 2;
// TODO(user): reduce to transported values;
num_ints = static_cast<uint32_t >(hnd->numInts);
err = Error::NONE;
}
- ALOGD_IF(DEBUG, "GetTransportSize: num fds: %d num ints: %d err:%d", num_fds, num_ints, err);
+ ALOGD_IF(DEBUG, "GetTransportSize: num fds: %d num ints: %d IsBufferImported:%d err:%d",
+ num_fds, num_ints, buf_mgr_->IsBufferImported(hnd), err);
hidl_cb(err, num_fds, num_ints);
return Void();
}
diff --git a/gralloc/gr_allocator.cpp b/gralloc/gr_allocator.cpp
index 912ada0..4eb7620 100644
--- a/gralloc/gr_allocator.cpp
+++ b/gralloc/gr_allocator.cpp
@@ -212,7 +212,11 @@
if (format == HAL_PIXEL_FORMAT_YCbCr_420_888) {
gr_format = HAL_PIXEL_FORMAT_NV21_ZSL; // NV21
} else {
+#ifdef USE_NV21_CAMERA_PREVIEW
+ gr_format = HAL_PIXEL_FORMAT_YCrCb_420_SP_VENUS; // NV21 preview
+#else
gr_format = HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS; // NV12 preview
+#endif
}
} else if (usage & BufferUsage::COMPOSER_OVERLAY) {
// XXX: If we still haven't set a format, default to RGBA8888
diff --git a/gralloc/gr_priv_handle.h b/gralloc/gr_priv_handle.h
index 44ecb78..cbfc867 100644
--- a/gralloc/gr_priv_handle.h
+++ b/gralloc/gr_priv_handle.h
@@ -20,6 +20,7 @@
#ifndef __GR_PRIV_HANDLE_H__
#define __GR_PRIV_HANDLE_H__
+#include <errno.h>
#include <log/log.h>
#include <hardware/gralloc.h>
#include <hardware/gralloc1.h>
diff --git a/gralloc/gr_utils.cpp b/gralloc/gr_utils.cpp
index 8fd2906..6a7e54b 100644
--- a/gralloc/gr_utils.cpp
+++ b/gralloc/gr_utils.cpp
@@ -589,10 +589,12 @@
// Allow UBWC, if an OpenGL client sets UBWC usage flag and GPU plus MDP
// support the format. OR if a non-OpenGL client like Rotator, sets UBWC
// usage flag and MDP supports the format.
- if ((usage & GRALLOC_USAGE_PRIVATE_ALLOC_UBWC) && IsUBwcSupported(format)) {
- bool enable = true;
+ if (IsUBwcSupported(format)) {
+ bool enable =
+ (usage & GRALLOC_USAGE_PRIVATE_ALLOC_UBWC) | (usage & BufferUsage::COMPOSER_CLIENT_TARGET);
// Query GPU for UBWC only if buffer is intended to be used by GPU.
- if ((usage & BufferUsage::GPU_TEXTURE) || (usage & BufferUsage::GPU_RENDER_TARGET)) {
+ if (enable &&
+ ((usage & BufferUsage::GPU_TEXTURE) || (usage & BufferUsage::GPU_RENDER_TARGET))) {
if (AdrenoMemInfo::GetInstance()) {
enable = AdrenoMemInfo::GetInstance()->IsUBWCSupportedByGPU(format);
}
@@ -850,7 +852,13 @@
aligned_w = ALIGN(width * 12 / 8, 16);
break;
case HAL_PIXEL_FORMAT_RAW10:
- aligned_w = ALIGN(width * 10 / 8, 16);
+ {
+ const unsigned int gpu_alignment =
+ AdrenoMemInfo::GetInstance()->GetGpuPixelAlignment();
+ // gpu_alignment can return 1. Make sure it's at least 8.
+ const unsigned int raw10_alignment = std::max(gpu_alignment, 8u);
+ aligned_w = ALIGN(width * 10 / 8, raw10_alignment);
+ }
break;
case HAL_PIXEL_FORMAT_RAW8:
aligned_w = ALIGN(width, 16);
@@ -994,6 +1002,8 @@
int is_ubwc_enabled = IsUBwcEnabled(info.format, info.usage);
if (!is_ubwc_enabled) {
adreno_usage &= ~(GRALLOC_USAGE_PRIVATE_ALLOC_UBWC);
+ } else {
+ adreno_usage |= GRALLOC_USAGE_PRIVATE_ALLOC_UBWC;
}
// Call adreno api for populating metadata blob
diff --git a/hdmi_cec/Android.mk b/hdmi_cec/Android.mk
index a333654..c73d20d 100644
--- a/hdmi_cec/Android.mk
+++ b/hdmi_cec/Android.mk
@@ -3,6 +3,8 @@
include $(CLEAR_VARS)
LOCAL_MODULE := hdmi_cec.$(TARGET_BOARD_PLATFORM)
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-BSD
+LOCAL_LICENSE_CONDITIONS := notice
LOCAL_VENDOR_MODULE := true
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_MODULE_TAGS := optional
diff --git a/include/display_properties.h b/include/display_properties.h
index 3944dc6..e59e800 100644
--- a/include/display_properties.h
+++ b/include/display_properties.h
@@ -94,6 +94,8 @@
#define ENABLE_DEFAULT_COLOR_MODE DISPLAY_PROP("enable_default_color_mode")
#define DISABLE_HDR DISPLAY_PROP("disable_hdr")
#define DATASPACE_SATURATION_MATRIX_PROP DISPLAY_PROP("dataspace_saturation_matrix")
+#define ADAPTIVE_WHITE_COEFFICIENT_PROP DISPLAY_PROP("adaptive_white_coefficient")
+#define ADAPTIVE_SATURATION_PARAMETER_PROP DISPLAY_PROP("adaptive_saturation_parameter")
#define HDR_CONFIG_PROP RO_DISPLAY_PROP("hdr.config")
#define QDCM_PCC_TRANS_PROP DISPLAY_PROP("qdcm.pcc_for_trans")
diff --git a/libdebug/Android.mk b/libdebug/Android.mk
index bbe8555..a27b041 100644
--- a/libdebug/Android.mk
+++ b/libdebug/Android.mk
@@ -2,6 +2,8 @@
include $(CLEAR_VARS)
LOCAL_MODULE := libdisplaydebug
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-BSD
+LOCAL_LICENSE_CONDITIONS := notice
LOCAL_VENDOR_MODULE := true
LOCAL_MODULE_TAGS := optional
LOCAL_SHARED_LIBRARIES := libdl
diff --git a/libdisplayconfig/Android.mk b/libdisplayconfig/Android.mk
index ec9340d..4286e04 100644
--- a/libdisplayconfig/Android.mk
+++ b/libdisplayconfig/Android.mk
@@ -2,10 +2,14 @@
include $(CLEAR_VARS)
LOCAL_MODULE := libdisplayconfig
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-BSD
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_SYSTEM_EXT_MODULE := true
LOCAL_MODULE_TAGS := optional
+LOCAL_VENDOR_MODULE := true
LOCAL_HEADER_LIBRARIES := display_headers
LOCAL_SRC_FILES := DisplayConfig.cpp
-LOCAL_SHARED_LIBRARIES := libhidlbase libhidltransport libutils \
+LOCAL_SHARED_LIBRARIES := libhidlbase libutils \
vendor.display.config@1.0
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
diff --git a/libdrmutils/Android.mk b/libdrmutils/Android.mk
index 172233f..75a24e0 100644
--- a/libdrmutils/Android.mk
+++ b/libdrmutils/Android.mk
@@ -2,6 +2,8 @@
include $(CLEAR_VARS)
LOCAL_MODULE := libdrmutils
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-BSD
+LOCAL_LICENSE_CONDITIONS := notice
LOCAL_VENDOR_MODULE := true
LOCAL_MODULE_TAGS := optional
LOCAL_C_INCLUDES := external/libdrm \
diff --git a/libhistogram/Android.mk b/libhistogram/Android.mk
new file mode 100644
index 0000000..634ead8
--- /dev/null
+++ b/libhistogram/Android.mk
@@ -0,0 +1,64 @@
+# Copyright (C) 2018 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_HEADER_LIBRARIES := display_headers
+LOCAL_MODULE := libhistogram
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_SRC_FILES := histogram_collector.cpp ringbuffer.cpp
+LOCAL_SHARED_LIBRARIES := libdrm liblog libcutils libutils
+LOCAL_CFLAGS := -DLOG_TAG=\"SDM-histogram\" -Wall -std=c++14 -Werror -fno-operator-names
+LOCAL_CLANG := true
+LOCAL_MODULE_TAGS := optional
+LOCAL_VENDOR_MODULE := true
+
+include $(BUILD_STATIC_LIBRARY)
+
+
+include $(CLEAR_VARS)
+
+LOCAL_HEADER_LIBRARIES := display_headers
+LOCAL_MODULE := color_sampling_tool
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_SRC_FILES := color_sampling_tool.cpp
+
+LOCAL_STATIC_LIBRARIES := libhistogram
+LOCAL_SHARED_LIBRARIES := libdrm liblog libcutils libutils libbase
+LOCAL_CFLAGS := -DLOG_TAG=\"SDM-histogram\" -Wall -std=c++14 -Werror -fno-operator-names
+LOCAL_CLANG := true
+LOCAL_MODULE_TAGS := optional
+LOCAL_VENDOR_MODULE := true
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
+LOCAL_HEADER_LIBRARIES := display_headers
+LOCAL_MODULE := color_sampling_test
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_SRC_FILES := ringbuffer_test.cpp
+
+LOCAL_STATIC_LIBRARIES := libhistogram libgtest libgmock
+LOCAL_SHARED_LIBRARIES := libdrm liblog libcutils libutils libbase
+LOCAL_CFLAGS := -DLOG_TAG=\"SDM-histogram\" -Wall -std=c++14 -Werror -fno-operator-names
+LOCAL_CLANG := true
+LOCAL_MODULE_TAGS := optional
+LOCAL_VENDOR_MODULE := true
+
+include $(BUILD_EXECUTABLE)
diff --git a/libhistogram/color_sampling_tool.cpp b/libhistogram/color_sampling_tool.cpp
new file mode 100644
index 0000000..ca4da65
--- /dev/null
+++ b/libhistogram/color_sampling_tool.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <chrono>
+#include <iostream>
+#include <fstream>
+#include <signal.h>
+#include <thread>
+#include <unistd.h>
+
+#include "histogram_collector.h"
+
+void sigint_handler(int) {
+}
+
+void show_usage(char* progname) {
+ std::cout << "Usage: ./" + std::string(progname) + " {options} \n"
+ << "Sample the V (as in HSV) channel of the pixels that were displayed onscreen.\n\n"
+ << "\tOptions:\n"
+ << "\t-h display this help message\n"
+ << "\t-o write output to specified filename\n"
+ << "\t-t NUM Collect results over NUM seconds, and then exit\n"
+ << "\t-m NUM Only store the last NUM frames of statistics\n";
+}
+
+int main(int argc, char** argv) {
+ struct sigaction sigHandler;
+ sigHandler.sa_handler = sigint_handler;
+ sigemptyset(&sigHandler.sa_mask);
+ sigHandler.sa_flags = 0;
+ sigaction(SIGINT, &sigHandler, NULL);
+
+ int c;
+ char * output_filename = NULL;
+ int timeout = -1;
+ while ((c = getopt(argc, argv, "o:t:h")) != -1) {
+ switch (c) {
+ case 'o': output_filename = optarg; break;
+ case 't': timeout = strtol(optarg, NULL, 10); break;
+ default:
+ case 'h': show_usage(argv[0]); return EXIT_SUCCESS;
+ }
+ }
+
+ histogram::HistogramCollector histogram;
+ histogram.start();
+
+ bool cancelled_during_wait = false;
+ if (timeout > 0) {
+ std::cout << "Sampling for " << timeout << " seconds.\n";
+ struct timespec request, remaining;
+ request.tv_sec = timeout;
+ request.tv_nsec = 0;
+ cancelled_during_wait = (nanosleep(&request, &remaining) != 0);
+ } else {
+ std::cout << "Sampling until Ctrl-C is pressed\n";
+ sigsuspend(&sigHandler.sa_mask);
+ }
+
+ std::cout << "Sampling results:\n";
+
+ histogram.stop();
+
+ if (cancelled_during_wait) {
+ std::cout << "Timed histogram collection cancelled via signal\n";
+ return EXIT_SUCCESS;
+ }
+
+ if (output_filename) {
+ std::cout << "\nWriting statistics to: " << output_filename << '\n';
+ std::ofstream output_file;
+ output_file.open(output_filename);
+ if (!output_file.is_open()) {
+ std::cerr << "Error, could not open given file: " << output_filename << "\n";
+ return EXIT_FAILURE;
+ }
+ output_file << histogram.Dump();
+ output_file.close();
+ } else {
+ std::cout << histogram.Dump() << '\n';
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/libhistogram/histogram_collector.cpp b/libhistogram/histogram_collector.cpp
new file mode 100644
index 0000000..868aaff
--- /dev/null
+++ b/libhistogram/histogram_collector.cpp
@@ -0,0 +1,556 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <chrono>
+#include <ctime>
+#include <iomanip>
+#include <fcntl.h>
+#include <fstream>
+#include <log/log.h>
+#include <memory>
+#include <sstream>
+#include <sys/epoll.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <tuple>
+#include <unistd.h>
+#include <unordered_map>
+#include <vector>
+
+#include <drm/msm_drm.h>
+#include <drm/msm_drm_pp.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "histogram_collector.h"
+#include "ringbuffer.h"
+
+namespace {
+
+class ManagedFd
+{
+public:
+ static std::unique_ptr<ManagedFd> create(int fd) {
+ if (fd < 0)
+ return nullptr;
+ return std::unique_ptr<ManagedFd>(new ManagedFd(fd));
+ }
+
+ ~ManagedFd() {
+ close(drmfd_);
+ }
+
+ operator int() const {
+ return drmfd_;
+ }
+
+private:
+ ManagedFd(ManagedFd const&) = delete;
+ ManagedFd& operator=(ManagedFd const&) = delete;
+
+ ManagedFd(int fd) : drmfd_(fd) {
+ }
+ int const drmfd_ = -1;
+};
+
+class DrmResources
+{
+public:
+ static std::unique_ptr<DrmResources> create(int drm_fd) {
+ auto resources = drmModeGetResources(drm_fd);
+ if (!resources || !resources->connectors || !resources->crtcs || !resources->encoders) {
+ return nullptr;
+ }
+ return std::unique_ptr<DrmResources>(new DrmResources(drm_fd, resources));
+ }
+
+ ~DrmResources() {
+ for (auto encoder : encoders_)
+ drmModeFreeEncoder(encoder.second);
+ for (auto crtc : crtcs_ )
+ drmModeFreeCrtc(crtc.second);
+ for (auto connector : connectors_)
+ drmModeFreeConnector(connector.second);
+ drmModeFreeResources(resources_);
+ }
+
+ drmModeConnectorPtr find_first_connector_of_type(uint32_t type) {
+ auto connector = std::find_if(connectors_.begin(), connectors_.end(),
+ [type] (auto const& c) { return c.second->connector_type == type; });
+ if (connector != connectors_.end()) {
+ return connector->second;
+ }
+ return nullptr;
+ }
+
+ drmModeEncoderPtr find_encoder_by_connector_and_type(drmModeConnectorPtr con, uint32_t type) {
+ for (auto i = 0; i < con->count_encoders; i++) {
+ auto enc = encoders_.find(con->encoders[i]);
+ if (enc != encoders_.end() && (enc->second->encoder_type == type)) {
+ return enc->second;
+ }
+ }
+ return nullptr;
+ }
+
+ bool find_histogram_supporting_crtc(int fd, drmModeEncoderPtr encoder,
+ drmModeCrtcPtr* crtc, int* histogram_ctrl, int* histogram_irq) {
+
+ for (auto i = 0; i < resources_->count_crtcs; i++) {
+ if (!(encoder->possible_crtcs & (1 << i)))
+ continue;
+
+ auto it = crtcs_.find(resources_->crtcs[i]);
+ if (it == crtcs_.end()) {
+ ALOGW("Could not find CRTC %i reported as possible by encoder %i",
+ resources_->crtcs[i], encoder->encoder_id);
+ continue;
+ }
+ *crtc = it->second;
+
+ int hist_ctl_found = -1;
+ int hist_irq_found = -1;
+ auto props = drmModeObjectGetProperties(fd, (*crtc)->crtc_id, DRM_MODE_OBJECT_CRTC);
+ for (auto j = 0u; j < props->count_props; j++) {
+ auto info = drmModeGetProperty(fd, props->props[j]);
+ if (std::string(info->name) == "SDE_DSPP_HIST_CTRL_V1") {
+ hist_ctl_found = props->props[j];
+ }
+ if (std::string(info->name) == "SDE_DSPP_HIST_IRQ_V1") {
+ hist_irq_found = props->props[j];
+ }
+ drmModeFreeProperty(info);
+ }
+ drmModeFreeObjectProperties(props);
+ if ((hist_ctl_found != -1 ) && (hist_irq_found != -1)) {
+ *histogram_ctrl = hist_ctl_found;
+ *histogram_irq = hist_irq_found;
+ return true;
+ }
+ }
+ return false;
+ }
+
+private:
+ DrmResources(DrmResources const&) = delete;
+ DrmResources& operator=(DrmResources const&) = delete;
+
+ DrmResources(int drm_fd, drmModeResPtr resources) :
+ resources_(resources),
+ crtcs_(resources_->count_crtcs),
+ connectors_(resources_->count_connectors),
+ encoders_(resources_->count_encoders) {
+
+ for (auto i = 0; i < resources_->count_connectors; i++) {
+ auto connector = drmModeGetConnector(drm_fd, resources_->connectors[i]);
+ connectors_[connector->connector_id] = connector;
+ }
+
+ for (auto i = 0; i < resources_->count_crtcs; i++) {
+ auto crtc = drmModeGetCrtc(drm_fd, resources_->crtcs[i]);
+ crtcs_[crtc->crtc_id] = crtc;
+ }
+
+ for (auto i = 0; i < resources_->count_encoders; i++) {
+ auto encoder = drmModeGetEncoder(drm_fd, resources_->encoders[i]);
+ encoders_[encoder->encoder_id] = encoder;
+ }
+ }
+
+ drmModeResPtr resources_;
+ std::unordered_map<int, drmModeCrtcPtr> crtcs_;
+ std::unordered_map<int, drmModeConnectorPtr> connectors_;
+ std::unordered_map<int, drmModeEncoderPtr> encoders_;
+};
+
+// Registering DRM_EVENT_CRTC_POWER does not trigger a notification on the DRM fd.
+struct PowerEventRegistration
+{
+ static std::unique_ptr<PowerEventRegistration> create(int drm_fd, int crtc_id) {
+ auto r = std::unique_ptr<PowerEventRegistration>(new PowerEventRegistration(drm_fd, crtc_id));
+ if (drmIoctl(drm_fd, DRM_IOCTL_MSM_REGISTER_EVENT, &r->req))
+ return nullptr;
+ return r;
+ }
+
+ ~PowerEventRegistration() {
+ drmIoctl(fd, DRM_IOCTL_MSM_DEREGISTER_EVENT, &req);
+ }
+private:
+ PowerEventRegistration(PowerEventRegistration const&) = delete;
+ PowerEventRegistration operator=(PowerEventRegistration const&) = delete;
+
+ PowerEventRegistration(int drm_fd, int crtc_id) :
+ fd(drm_fd) {
+ req.object_id = crtc_id;
+ req.object_type = DRM_MODE_OBJECT_CRTC;
+ req.event = DRM_EVENT_CRTC_POWER;
+ }
+
+ int const fd; //non-owning.
+ struct drm_msm_event_req req = {};
+};
+
+struct HistogramRAIIEnabler
+{
+ static std::unique_ptr<HistogramRAIIEnabler> create(int fd, int crtc_id, int histogram_prop) {
+ auto hist = std::unique_ptr<HistogramRAIIEnabler>(
+ new HistogramRAIIEnabler(fd, crtc_id, histogram_prop));
+ if (drmModeObjectSetProperty(fd, crtc_id, DRM_MODE_OBJECT_CRTC, histogram_prop, 1))
+ return nullptr;
+ return hist;
+ }
+
+ ~HistogramRAIIEnabler() {
+ drmModeObjectSetProperty(fd, crtc_id, DRM_MODE_OBJECT_CRTC, histogram_property, 0);
+ }
+
+private:
+ HistogramRAIIEnabler(HistogramRAIIEnabler const&) = delete;
+ HistogramRAIIEnabler& operator=(HistogramRAIIEnabler const&) = delete;
+
+ HistogramRAIIEnabler(int fd, int crtc_id, int histogram_property) :
+ fd(fd),
+ crtc_id(crtc_id),
+ histogram_property(histogram_property) {
+ }
+
+ int fd;
+ int crtc_id;
+ int histogram_property;
+};
+
+struct EventRegistration
+{
+ static std::unique_ptr<EventRegistration> create(
+ int drm_fd, int crtc_id, int histogram_property) {
+ auto reg = std::unique_ptr<EventRegistration>(
+ new EventRegistration(drm_fd, crtc_id, histogram_property));
+ if (!reg->property_registration ||
+ drmIoctl(drm_fd, DRM_IOCTL_MSM_REGISTER_EVENT, ®->req))
+ return nullptr;
+ return reg;
+ }
+
+ ~EventRegistration() {
+ drmIoctl(fd, DRM_IOCTL_MSM_DEREGISTER_EVENT, &req);
+ }
+
+private:
+ EventRegistration(int drm_fd, int crtc_id, int histogram_property) :
+ property_registration(HistogramRAIIEnabler::create(drm_fd, crtc_id, histogram_property)),
+ fd(drm_fd) {
+ req.object_id = crtc_id;
+ req.object_type = DRM_MODE_OBJECT_CRTC;
+ req.event = DRM_EVENT_HISTOGRAM;
+ }
+ EventRegistration(EventRegistration const&) = delete;
+ EventRegistration operator&(EventRegistration const&) = delete;
+
+ //SDE_DSPP_HIST_CTRL_V1 must be turned on before receiving events
+ std::unique_ptr<HistogramRAIIEnabler> property_registration;
+ int const fd; //non-owning.
+ struct drm_msm_event_req req = {};
+};
+
+//These are not the DPMS enum encodings.
+enum class CrtcPowerState
+{
+ OFF,
+ ON,
+ UNKNOWN
+};
+
+constexpr static auto implementation_defined_max_frame_ringbuffer = 300;
+}
+
+histogram::HistogramCollector::HistogramCollector() :
+ histogram(histogram::Ringbuffer::create(
+ implementation_defined_max_frame_ringbuffer, std::make_unique<histogram::DefaultTimeKeeper>())) {
+}
+
+histogram::HistogramCollector::~HistogramCollector() {
+ stop();
+}
+
+namespace {
+static constexpr size_t numBuckets = 8;
+static_assert((HIST_V_SIZE % numBuckets) == 0,
+ "histogram cannot be rebucketed to smaller number of buckets");
+static constexpr int bucket_compression = HIST_V_SIZE / numBuckets;
+
+std::array<uint64_t, numBuckets> rebucketTo8Buckets(std::array<uint64_t, HIST_V_SIZE> const& frame) {
+ std::array<uint64_t, numBuckets> bins;
+ bins.fill(0);
+ for (auto i = 0u; i < HIST_V_SIZE; i++)
+ bins[i / bucket_compression] += frame[i];
+ return bins;
+}
+}
+
+std::string histogram::HistogramCollector::Dump() const {
+ uint64_t num_frames;
+ std::array<uint64_t, HIST_V_SIZE> all_sample_buckets;
+ std::tie(num_frames, all_sample_buckets) = histogram->collect_cumulative();
+ std::array<uint64_t, numBuckets> samples = rebucketTo8Buckets(all_sample_buckets);
+
+ std::stringstream ss;
+ ss << "Color Sampling, dark (0.0) to light (1.0): sampled frames: " << num_frames << '\n';
+ if (num_frames == 0) {
+ ss << "\tno color statistics collected\n";
+ return ss.str();
+ }
+
+ ss << std::fixed << std::setprecision(3);
+ ss << "\tbucket\t\t: # of displayed pixels at bucket value\n";
+ for (auto i = 0u; i < samples.size(); i++) {
+ ss << "\t" << i / static_cast<float>(samples.size()) <<
+ " to " << ( i + 1 ) / static_cast<float>(samples.size()) << "\t: " <<
+ samples[i] << '\n';
+ }
+
+ return ss.str();
+}
+
+HWC2::Error histogram::HistogramCollector::collect(
+ uint64_t max_frames,
+ uint64_t timestamp,
+ int32_t out_samples_size[NUM_HISTOGRAM_COLOR_COMPONENTS],
+ uint64_t* out_samples[NUM_HISTOGRAM_COLOR_COMPONENTS],
+ uint64_t* out_num_frames) const {
+
+ if (!out_samples_size || !out_num_frames)
+ return HWC2::Error::BadParameter;
+
+ out_samples_size[0] = 0;
+ out_samples_size[1] = 0;
+ out_samples_size[2] = numBuckets;
+ out_samples_size[3] = 0;
+
+ uint64_t num_frames;
+ std::array<uint64_t, HIST_V_SIZE> samples;
+
+ if (max_frames == 0 && timestamp == 0) {
+ std::tie(num_frames, samples) = histogram->collect_cumulative();
+ } else if (max_frames == 0) {
+ std::tie(num_frames, samples) = histogram->collect_after(timestamp);
+ } else if (timestamp == 0) {
+ std::tie(num_frames, samples) = histogram->collect_max(max_frames);
+ } else {
+ std::tie(num_frames, samples) = histogram->collect_max_after(timestamp, max_frames);
+ }
+
+ auto samples_rebucketed = rebucketTo8Buckets(samples);
+ *out_num_frames = num_frames;
+ if (out_samples && out_samples[2])
+ memcpy(out_samples[2], samples_rebucketed.data(), sizeof(uint64_t) * samples_rebucketed.size());
+
+ return HWC2::Error::None;
+}
+
+HWC2::Error histogram::HistogramCollector::getAttributes(int32_t* format,
+ int32_t* dataspace,
+ uint8_t* supported_components) const {
+ if (!format || !dataspace || !supported_components)
+ return HWC2::Error::BadParameter;
+
+ *format = HAL_PIXEL_FORMAT_HSV_888;
+ *dataspace = HAL_DATASPACE_UNKNOWN;
+ *supported_components = HWC2_FORMAT_COMPONENT_2;
+ return HWC2::Error::None;
+}
+
+void histogram::HistogramCollector::start() {
+ start(implementation_defined_max_frame_ringbuffer);
+}
+
+void histogram::HistogramCollector::start(uint64_t max_frames) {
+ std::unique_lock<decltype(thread_control)> lk(thread_control);
+ if (started) {
+ return;
+ }
+
+ if (pipe2(selfpipe, O_CLOEXEC | O_NONBLOCK )) {
+ ALOGE("histogram thread not started, could not create control pipe.");
+ return;
+ }
+ histogram = histogram::Ringbuffer::create(max_frames, std::make_unique<histogram::DefaultTimeKeeper>());
+ monitoring_thread = std::thread(&HistogramCollector::collecting_thread, this, selfpipe[0]);
+ started = true;
+}
+
+void histogram::HistogramCollector::stop() {
+ std::unique_lock<decltype(thread_control)> lk(thread_control);
+ if (!started) {
+ return;
+ }
+
+ char dummy = 's';
+ write(selfpipe[1], &dummy, 1);
+ if (monitoring_thread.joinable())
+ monitoring_thread.join();
+ close(selfpipe[0]);
+ close(selfpipe[1]);
+ started = false;
+}
+
+void histogram::HistogramCollector::collecting_thread(int selfpipe) {
+ if (prctl(PR_SET_NAME, "histogram-collector", 0, 0, 0))
+ ALOGW("could not set thread name for histogram collector.");
+
+ int const control_minor_version { 64 };
+ auto drm = ManagedFd::create(drmOpenControl(control_minor_version));
+ if (!drm) {
+ ALOGW("could not find DRM control node. Histogram collection disabled.");
+ return;
+ }
+ auto drm_resources = DrmResources::create(*drm);
+ if (!drm_resources) {
+ ALOGW("could not get DRM resources. Histogram collection disabled.");
+ return;
+ }
+
+ //Find the connector and encoder on the DSI. Check the possible CRTCs for support
+ //for the histogram property.
+ auto connector = drm_resources->find_first_connector_of_type(DRM_MODE_CONNECTOR_DSI);
+ if (!connector) {
+ ALOGE("Could not find connector. Histogram collection disabled.");
+ return;
+ }
+
+ auto encoder = drm_resources->find_encoder_by_connector_and_type(
+ connector, DRM_MODE_ENCODER_DSI);
+ if (!encoder) {
+ ALOGE("Could not find encoder. Histogram collection disabled.");
+ return;
+ }
+
+ auto histogram_property = -1;
+ auto histogram_irq = -1;
+ drmModeCrtcPtr crtc = nullptr;
+ if (!drm_resources->find_histogram_supporting_crtc(
+ *drm, encoder, &crtc, &histogram_property, &histogram_irq)) {
+ ALOGE("Could not find CRTC that supports color sampling. Histogram collection disabled.");
+ return;
+ }
+
+ // Set up event loop.
+ // Event loop will listen to 1) FD that exposes color sampling events (1 per displayed frame),
+ // and 2) a self-pipe that will indicate when this thread should shut down.
+ enum class EventType
+ {
+ DRM,
+ CTL,
+ NUM_EVENT_TYPES
+ };
+
+ struct epoll_event ev, events[static_cast<int>(EventType::NUM_EVENT_TYPES)];
+ auto epollfd = ManagedFd::create(epoll_create1(EPOLL_CLOEXEC));
+ if (!epollfd) {
+ ALOGE("Error creating epoll loop. Histogram collection disabled.");
+ return;
+ }
+
+ ev.events = EPOLLIN;
+ ev.data.u32 = static_cast<uint32_t>(EventType::DRM);
+ if (epoll_ctl(*epollfd, EPOLL_CTL_ADD, *drm, &ev) == -1) {
+ ALOGE("Error adding drm fd to epoll. Histogram collection disabled.");
+ return;
+ }
+
+ ev.events = EPOLLIN;
+ ev.data.u32 = static_cast<uint32_t>(EventType::CTL);
+ if (epoll_ctl(*epollfd, EPOLL_CTL_ADD, selfpipe, &ev) == -1) {
+ ALOGE("Error adding control fd to epoll. Histogram collection disabled.");
+ return;
+ }
+
+ if (fcntl(*drm, F_SETFL, fcntl(*drm, F_GETFL) | O_NONBLOCK)) {
+ ALOGE("Error making drm read nonblocking. Histogram collection disabled.");
+ return;
+ }
+
+ /* Attempting to set SDE_DSPP_HIST_CTRL_V1, SDE_DSPP_HIST_IRQ_V1, or DRM_EVENT_HISTOGRAM
+ * while the screen is off will result in an error.
+ *
+ * Since we have to wait on events (or poll the connector for power state), and then issue
+ * based on that info, there's no 100% certain way to know if enabling those histogram events
+ * are done when the screen is actually on. We work around this by retrying when those
+ * events fail, and not trying to enable those when we know the screen is off.
+ */
+ std::unique_ptr<EventRegistration> hist_registration = nullptr;
+ CrtcPowerState state = CrtcPowerState::UNKNOWN;
+ bool collecting = true;
+
+ auto power_registration = PowerEventRegistration::create(*drm, crtc->crtc_id);
+ if (!power_registration) {
+ ALOGE("could not register event to monitor power events. Histogram collection disabled.");
+ return;
+ }
+
+ while (collecting) {
+ if (state != CrtcPowerState::OFF) {
+ if (!hist_registration) {
+ hist_registration = EventRegistration::create(
+ *drm, crtc->crtc_id, histogram_property);
+ }
+
+ if (drmModeObjectSetProperty(*drm,
+ crtc->crtc_id, DRM_MODE_OBJECT_CRTC, histogram_irq, 1)) {
+ ALOGI("Failed to enable histogram property on crtc, will retry");
+ state = CrtcPowerState::OFF;
+ hist_registration = nullptr;
+ }
+ }
+
+ int nfds = epoll_wait(*epollfd, events, static_cast<int>(EventType::NUM_EVENT_TYPES), -1);
+ if (nfds == -1) {
+ if (errno != EINTR)
+ collecting = false;
+ continue;
+ }
+
+ for (auto i = 0; i < nfds; i++) {
+ if (events[i].data.u32 == static_cast<uint32_t>(EventType::CTL)) {
+ collecting = false;
+ } else if (events[i].data.u32 == static_cast<uint32_t>(EventType::DRM)) {
+ //VLA has a single int as blob id, or power mode
+ char buffer[sizeof(drm_msm_event_resp) + sizeof(uint32_t)];
+ auto size_read = read(*drm, buffer, sizeof(buffer));
+ if (size_read != sizeof(buffer)) {
+ ALOGW("Histogram event wrong size (%zu bytes, errno: %X). Skipping event.",
+ size_read, errno);
+ continue;
+ }
+
+ struct drm_msm_event_resp* response =
+ reinterpret_cast<struct drm_msm_event_resp*>(buffer);
+ if (response->base.type == DRM_EVENT_HISTOGRAM) {
+ uint32_t blob_id = *reinterpret_cast<uint32_t*>(response->data);
+ drmModePropertyBlobPtr blob = drmModeGetPropertyBlob(*drm, blob_id);
+ histogram->insert(*static_cast<struct drm_msm_hist*>(blob->data));
+ drmModeFreePropertyBlob(blob);
+ }
+
+ if (response->base.type == DRM_EVENT_CRTC_POWER) {
+ uint32_t state_raw = *reinterpret_cast<uint32_t*>(response->data);
+ state = (state_raw) ? CrtcPowerState::ON : CrtcPowerState::OFF;
+ }
+ }
+ }
+ }
+}
diff --git a/libhistogram/histogram_collector.h b/libhistogram/histogram_collector.h
new file mode 100644
index 0000000..d2527dd
--- /dev/null
+++ b/libhistogram/histogram_collector.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HISTOGRAM_HISTOGRAM_COLLECTOR_H_
+#define HISTOGRAM_HISTOGRAM_COLLECTOR_H_
+#include <string>
+#include <thread>
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+//number of enums in hwc2_format_color_component_t;
+#define NUM_HISTOGRAM_COLOR_COMPONENTS 4
+
+namespace histogram {
+
+class Ringbuffer;
+class HistogramCollector
+{
+public:
+ HistogramCollector();
+ ~HistogramCollector();
+
+ void start();
+ void start(uint64_t max_frames);
+ void stop();
+
+ std::string Dump() const;
+
+ HWC2::Error collect(uint64_t max_frames,
+ uint64_t timestamp,
+ int32_t samples_size[NUM_HISTOGRAM_COLOR_COMPONENTS],
+ uint64_t* samples[NUM_HISTOGRAM_COLOR_COMPONENTS],
+ uint64_t* numFrames) const;
+ HWC2::Error getAttributes(int32_t* format,
+ int32_t* dataspace,
+ uint8_t* supported_components) const;
+
+private:
+ HistogramCollector(HistogramCollector const&) = delete;
+ HistogramCollector& operator=(HistogramCollector const&) = delete;
+ void collecting_thread(int pipe);
+
+ std::mutex thread_control;
+ bool started = false;
+ std::thread monitoring_thread;
+ int selfpipe[2];
+
+ std::unique_ptr<histogram::Ringbuffer> histogram;
+
+};
+
+} // namespace histogram
+
+#endif // HISTOGRAM_HISTOGRAM_COLLECTOR_H_
diff --git a/libhistogram/ringbuffer.cpp b/libhistogram/ringbuffer.cpp
new file mode 100644
index 0000000..43d8c3f
--- /dev/null
+++ b/libhistogram/ringbuffer.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+#include <cutils/compiler.h>
+#include <algorithm>
+
+#include "ringbuffer.h"
+
+nsecs_t histogram::DefaultTimeKeeper::current_time() const {
+ return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
+histogram::Ringbuffer::Ringbuffer(size_t ringbuffer_size, std::unique_ptr<histogram::TimeKeeper> tk) :
+ rb_max_size(ringbuffer_size),
+ timekeeper(std::move(tk)),
+ cumulative_frame_count(0) {
+ cumulative_bins.fill(0);
+}
+
+std::unique_ptr<histogram::Ringbuffer> histogram::Ringbuffer::create(
+ size_t ringbuffer_size, std::unique_ptr<histogram::TimeKeeper> tk) {
+ if ((ringbuffer_size == 0) || !tk)
+ return nullptr;
+ return std::unique_ptr<histogram::Ringbuffer>(new histogram::Ringbuffer(ringbuffer_size, std::move(tk)));
+}
+
+void histogram::Ringbuffer::update_cumulative(nsecs_t now,
+ uint64_t& count, std::array<uint64_t, HIST_V_SIZE>& bins) const {
+
+ if (ringbuffer.empty())
+ return;
+
+ count++;
+
+ const auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::nanoseconds(now - ringbuffer.front().start_timestamp));
+
+ for (auto i = 0u; i < bins.size(); i++) {
+ auto const increment = ringbuffer.front().histogram.data[i] * delta.count();
+ if (CC_UNLIKELY((bins[i] + increment < bins[i]) || (increment < ringbuffer.front().histogram.data[i]))) {
+ bins[i] = std::numeric_limits<uint64_t>::max();
+ } else {
+ bins[i] += increment;
+ }
+ }
+}
+
+void histogram::Ringbuffer::insert(drm_msm_hist const& frame) {
+ std::unique_lock<decltype(mutex)> lk(mutex);
+ auto now = timekeeper->current_time();
+ update_cumulative(now, cumulative_frame_count, cumulative_bins);
+
+ if (ringbuffer.size() == rb_max_size)
+ ringbuffer.pop_back();
+ if (!ringbuffer.empty())
+ ringbuffer.front().end_timestamp = now;
+ ringbuffer.push_front({frame, now, 0});
+}
+
+bool histogram::Ringbuffer::resize(size_t ringbuffer_size) {
+ std::unique_lock<decltype(mutex)> lk(mutex);
+ if (ringbuffer_size == 0)
+ return false;
+ rb_max_size = ringbuffer_size;
+ if (ringbuffer.size() > rb_max_size)
+ ringbuffer.resize(rb_max_size);
+ return true;
+}
+
+histogram::Ringbuffer::Sample histogram::Ringbuffer::collect_cumulative() const {
+ std::unique_lock<decltype(mutex)> lk(mutex);
+ histogram::Ringbuffer::Sample sample { cumulative_frame_count, cumulative_bins };
+ update_cumulative(timekeeper->current_time(), std::get<0>(sample), std::get<1>(sample));
+ return sample;
+}
+
+histogram::Ringbuffer::Sample histogram::Ringbuffer::collect_ringbuffer_all() const {
+ std::unique_lock<decltype(mutex)> lk(mutex);
+ return collect_max(ringbuffer.size(), lk);
+}
+
+histogram::Ringbuffer::Sample histogram::Ringbuffer::collect_after(
+ nsecs_t timestamp) const {
+ std::unique_lock<decltype(mutex)> lk(mutex);
+ return collect_max_after(timestamp, ringbuffer.size(), lk);
+}
+
+histogram::Ringbuffer::Sample histogram::Ringbuffer::collect_max(uint32_t max_frames) const {
+ std::unique_lock<decltype(mutex)> lk(mutex);
+ return collect_max(max_frames, lk);
+}
+
+histogram::Ringbuffer::Sample histogram::Ringbuffer::collect_max_after(
+ nsecs_t timestamp, uint32_t max_frames) const {
+ std::unique_lock<decltype(mutex)> lk(mutex);
+ return collect_max_after(timestamp, max_frames, lk);
+}
+
+histogram::Ringbuffer::Sample histogram::Ringbuffer::collect_max(
+ uint32_t max_frames, std::unique_lock<std::mutex> const&) const {
+ auto collect_first = std::min(static_cast<size_t>(max_frames), ringbuffer.size());
+ if (collect_first == 0)
+ return {0, {}};
+ std::array<uint64_t, HIST_V_SIZE> bins;
+ bins.fill(0);
+ for (auto it = ringbuffer.begin(); it != ringbuffer.begin() + collect_first; it++) {
+ nsecs_t end_timestamp = it->end_timestamp;
+ if (it == ringbuffer.begin() ) {
+ end_timestamp = timekeeper->current_time();
+ }
+ const auto time_displayed = std::chrono::nanoseconds(end_timestamp - it->start_timestamp);
+ const auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(time_displayed);
+ for (auto i = 0u; i < HIST_V_SIZE; i++) {
+ bins[i] += it->histogram.data[i] * delta.count();
+ }
+ }
+ return { collect_first, bins };
+}
+
+histogram::Ringbuffer::Sample histogram::Ringbuffer::collect_max_after(
+ nsecs_t timestamp, uint32_t max_frames, std::unique_lock<std::mutex> const& lk) const {
+ auto ts_filter_begin = std::lower_bound(
+ ringbuffer.begin(), ringbuffer.end(), HistogramEntry{{}, timestamp, 0},
+ [](auto const &a, auto const &b) { return a.start_timestamp >= b.start_timestamp; });
+
+ auto collect_last = std::min(std::distance(ringbuffer.begin(), ts_filter_begin),
+ static_cast<std::ptrdiff_t>(max_frames));
+ return collect_max(collect_last, lk);
+}
diff --git a/libhistogram/ringbuffer.h b/libhistogram/ringbuffer.h
new file mode 100644
index 0000000..a116553
--- /dev/null
+++ b/libhistogram/ringbuffer.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 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 <sys/types.h>
+#include <tuple>
+#include <unistd.h>
+#include <memory>
+#include <drm/msm_drm.h>
+#include <drm/msm_drm_pp.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <array>
+#include <deque>
+#include <mutex>
+#include <utils/Timers.h>
+
+namespace histogram {
+
+struct TimeKeeper {
+ virtual nsecs_t current_time() const = 0;
+ virtual ~TimeKeeper() = default;
+protected:
+ TimeKeeper() = default;
+ TimeKeeper& operator=(TimeKeeper const&) = delete;
+ TimeKeeper(TimeKeeper const&) = delete;
+};
+
+struct DefaultTimeKeeper final : TimeKeeper
+{
+ nsecs_t current_time() const final;
+};
+
+class Ringbuffer
+{
+public:
+ static std::unique_ptr<Ringbuffer> create(size_t ringbuffer_size, std::unique_ptr<TimeKeeper> tk);
+ void insert(drm_msm_hist const& frame);
+ bool resize(size_t ringbuffer_size);
+
+ using Sample = std::tuple<uint64_t /* numFrames */, std::array<uint64_t, HIST_V_SIZE> /* bins */>;
+ Sample collect_cumulative() const;
+ Sample collect_ringbuffer_all() const;
+ Sample collect_after(nsecs_t timestamp) const;
+ Sample collect_max(uint32_t max_frames) const;
+ Sample collect_max_after(nsecs_t timestamp, uint32_t max_frames) const;
+ ~Ringbuffer() = default;
+
+private:
+ Ringbuffer(size_t ringbuffer_size, std::unique_ptr<TimeKeeper> tk);
+ Ringbuffer(Ringbuffer const&) = delete;
+ Ringbuffer& operator=(Ringbuffer const&) = delete;
+
+ Sample collect_max(uint32_t max_frames, std::unique_lock<std::mutex> const&) const;
+ Sample collect_max_after(nsecs_t timestamp, uint32_t max_frames, std::unique_lock<std::mutex> const&) const;
+ void update_cumulative(nsecs_t now, uint64_t& count, std::array<uint64_t, HIST_V_SIZE>& bins) const;
+
+ std::mutex mutable mutex;
+ struct HistogramEntry {
+ drm_msm_hist histogram;
+ nsecs_t start_timestamp;
+ nsecs_t end_timestamp;
+ };
+ std::deque<HistogramEntry> ringbuffer;
+ size_t rb_max_size;
+ std::unique_ptr<TimeKeeper> const timekeeper;
+
+ uint64_t cumulative_frame_count;
+ std::array<uint64_t, HIST_V_SIZE> cumulative_bins;
+};
+
+}
diff --git a/libhistogram/ringbuffer_test.cpp b/libhistogram/ringbuffer_test.cpp
new file mode 100644
index 0000000..8e01aad
--- /dev/null
+++ b/libhistogram/ringbuffer_test.cpp
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <chrono>
+#include <numeric>
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+#include "ringbuffer.h"
+using namespace testing;
+using namespace std::chrono_literals;
+
+template <typename Rep, typename Per>
+nsecs_t toNsecs(std::chrono::duration<Rep, Per> time) {
+ return std::chrono::duration_cast<std::chrono::nanoseconds>(time).count();
+}
+
+template <typename Rep, typename Per>
+uint64_t toMs(std::chrono::duration<Rep, Per> time) {
+ return std::chrono::duration_cast<std::chrono::milliseconds>(time).count();
+}
+
+struct TimeKeeperWrapper : histogram::TimeKeeper {
+ TimeKeeperWrapper(std::shared_ptr<histogram::TimeKeeper> const &tk) : tk(tk) {}
+ nsecs_t current_time() const final { return tk->current_time(); }
+ std::shared_ptr<histogram::TimeKeeper> const tk;
+};
+
+struct TickingTimeKeeper : histogram::TimeKeeper {
+ void tick() { fake_time = fake_time + toNsecs(1ms); }
+
+ void increment_by(std::chrono::nanoseconds inc) { fake_time = fake_time + inc.count(); }
+
+ nsecs_t current_time() const final { return fake_time; }
+
+private:
+ nsecs_t mutable fake_time = 0;
+};
+
+void insertFrameIncrementTimeline(histogram::Ringbuffer &rb, TickingTimeKeeper &tk,
+ drm_msm_hist &frame) {
+ rb.insert(frame);
+ tk.tick();
+}
+
+class RingbufferTestCases : public ::testing::Test {
+ void SetUp() {
+ for (auto i = 0u; i < HIST_V_SIZE; i++) {
+ frame0.data[i] = fill_frame0;
+ frame1.data[i] = fill_frame1;
+ frame2.data[i] = fill_frame2;
+ frame3.data[i] = fill_frame3;
+ frame4.data[i] = fill_frame4;
+ frame_saturate.data[i] = std::numeric_limits<uint32_t>::max();
+ }
+ }
+
+protected:
+ std::unique_ptr<histogram::Ringbuffer> createFilledRingbuffer(
+ std::shared_ptr<TickingTimeKeeper> const &tk) {
+ auto rb = histogram::Ringbuffer::create(4, std::make_unique<TimeKeeperWrapper>(tk));
+ insertFrameIncrementTimeline(*rb, *tk, frame0);
+ insertFrameIncrementTimeline(*rb, *tk, frame1);
+ insertFrameIncrementTimeline(*rb, *tk, frame2);
+ insertFrameIncrementTimeline(*rb, *tk, frame3);
+ return rb;
+ }
+
+ uint64_t fill_frame0 = 9;
+ uint64_t fill_frame1 = 11;
+ uint64_t fill_frame2 = 303;
+ uint64_t fill_frame3 = 1030;
+ uint64_t fill_frame4 = 112200;
+ drm_msm_hist frame0;
+ drm_msm_hist frame1;
+ drm_msm_hist frame2;
+ drm_msm_hist frame3;
+ drm_msm_hist frame4;
+ drm_msm_hist frame_saturate;
+
+ int numFrames = 0;
+ std::array<uint64_t, HIST_V_SIZE> bins;
+};
+
+TEST_F(RingbufferTestCases, ZeroSizedRingbufferReturnsNull) {
+ EXPECT_THAT(histogram::Ringbuffer::create(0, std::make_unique<TickingTimeKeeper>()),
+ Eq(nullptr));
+}
+
+TEST_F(RingbufferTestCases, NullTimekeeperReturnsNull) {
+ EXPECT_THAT(histogram::Ringbuffer::create(10, nullptr), Eq(nullptr));
+}
+
+TEST_F(RingbufferTestCases, CollectionWithNoFrames) {
+ auto rb = histogram::Ringbuffer::create(1, std::make_unique<TickingTimeKeeper>());
+
+ std::tie(numFrames, bins) = rb->collect_ringbuffer_all();
+ EXPECT_THAT(numFrames, Eq(0));
+ EXPECT_THAT(bins, Each(0));
+}
+
+TEST_F(RingbufferTestCases, SimpleTest) {
+ static constexpr int numInsertions = 3u;
+ auto tk = std::make_shared<TickingTimeKeeper>();
+ auto rb = histogram::Ringbuffer::create(numInsertions, std::make_unique<TimeKeeperWrapper>(tk));
+
+ drm_msm_hist frame;
+ for (auto i = 0u; i < HIST_V_SIZE; i++) {
+ frame.data[i] = i;
+ }
+
+ insertFrameIncrementTimeline(*rb, *tk, frame);
+ insertFrameIncrementTimeline(*rb, *tk, frame);
+ insertFrameIncrementTimeline(*rb, *tk, frame);
+
+ std::tie(numFrames, bins) = rb->collect_ringbuffer_all();
+
+ ASSERT_THAT(bins.size(), Eq(HIST_V_SIZE));
+ for (auto i = 0u; i < bins.size(); i++) {
+ EXPECT_THAT(bins[i], Eq(toMs(3ms) * i));
+ }
+}
+
+TEST_F(RingbufferTestCases, TestEvictionSingle) {
+ int fill_frame0 = 9;
+ int fill_frame1 = 111;
+ drm_msm_hist frame0;
+ drm_msm_hist frame1;
+ for (auto i = 0u; i < HIST_V_SIZE; i++) {
+ frame0.data[i] = fill_frame0;
+ frame1.data[i] = fill_frame1;
+ }
+
+ auto tk = std::make_shared<TickingTimeKeeper>();
+ auto rb = histogram::Ringbuffer::create(1, std::make_unique<TimeKeeperWrapper>(tk));
+
+ insertFrameIncrementTimeline(*rb, *tk, frame0);
+
+ std::tie(numFrames, bins) = rb->collect_ringbuffer_all();
+ EXPECT_THAT(numFrames, Eq(1));
+ EXPECT_THAT(bins, Each(fill_frame0));
+
+ insertFrameIncrementTimeline(*rb, *tk, frame1);
+ std::tie(numFrames, bins) = rb->collect_ringbuffer_all();
+ EXPECT_THAT(numFrames, Eq(1));
+ EXPECT_THAT(bins, Each(fill_frame1));
+}
+
+TEST_F(RingbufferTestCases, TestEvictionMultiple) {
+ auto tk = std::make_shared<TickingTimeKeeper>();
+ auto rb = histogram::Ringbuffer::create(3, std::make_unique<TimeKeeperWrapper>(tk));
+
+ insertFrameIncrementTimeline(*rb, *tk, frame0);
+ insertFrameIncrementTimeline(*rb, *tk, frame1);
+ insertFrameIncrementTimeline(*rb, *tk, frame2);
+
+ std::tie(numFrames, bins) = rb->collect_ringbuffer_all();
+ EXPECT_THAT(numFrames, Eq(3));
+ EXPECT_THAT(bins, Each(fill_frame0 + fill_frame1 + fill_frame2));
+
+ insertFrameIncrementTimeline(*rb, *tk, frame3);
+ std::tie(numFrames, bins) = rb->collect_ringbuffer_all();
+ EXPECT_THAT(numFrames, Eq(3));
+ EXPECT_THAT(bins, Each(fill_frame1 + fill_frame2 + fill_frame3));
+
+ insertFrameIncrementTimeline(*rb, *tk, frame0);
+ std::tie(numFrames, bins) = rb->collect_ringbuffer_all();
+ EXPECT_THAT(numFrames, Eq(3));
+ EXPECT_THAT(bins, Each(fill_frame2 + fill_frame3 + fill_frame0));
+}
+
+TEST_F(RingbufferTestCases, TestResizeToZero) {
+ auto rb = histogram::Ringbuffer::create(4, std::make_unique<TickingTimeKeeper>());
+ EXPECT_FALSE(rb->resize(0));
+}
+
+TEST_F(RingbufferTestCases, TestResizeDown) {
+ auto tk = std::make_shared<TickingTimeKeeper>();
+ auto rb = createFilledRingbuffer(tk);
+
+ std::tie(numFrames, bins) = rb->collect_ringbuffer_all();
+ EXPECT_THAT(numFrames, Eq(4));
+ EXPECT_THAT(bins, Each(fill_frame0 + fill_frame1 + fill_frame2 + fill_frame3));
+
+ auto rc = rb->resize(2);
+ EXPECT_THAT(rc, Eq(true));
+ std::tie(numFrames, bins) = rb->collect_ringbuffer_all();
+ EXPECT_THAT(numFrames, Eq(2));
+ EXPECT_THAT(bins, Each(fill_frame2 + fill_frame3));
+
+ insertFrameIncrementTimeline(*rb, *tk, frame0);
+ std::tie(numFrames, bins) = rb->collect_ringbuffer_all();
+ EXPECT_THAT(numFrames, Eq(2));
+ EXPECT_THAT(bins, Each(fill_frame0 + fill_frame3));
+}
+
+TEST_F(RingbufferTestCases, TestResizeUp) {
+ auto tk = std::make_shared<TickingTimeKeeper>();
+ auto rb = histogram::Ringbuffer::create(2, std::make_unique<TimeKeeperWrapper>(tk));
+
+ insertFrameIncrementTimeline(*rb, *tk, frame0);
+ insertFrameIncrementTimeline(*rb, *tk, frame1);
+
+ std::tie(numFrames, bins) = rb->collect_ringbuffer_all();
+ EXPECT_THAT(numFrames, Eq(2));
+ EXPECT_THAT(bins, Each(fill_frame0 + fill_frame1));
+
+ auto rc = rb->resize(3);
+ EXPECT_THAT(rc, Eq(true));
+ std::tie(numFrames, bins) = rb->collect_ringbuffer_all();
+ EXPECT_THAT(numFrames, Eq(2));
+ EXPECT_THAT(bins, Each(fill_frame0 + fill_frame1));
+
+ insertFrameIncrementTimeline(*rb, *tk, frame2);
+ std::tie(numFrames, bins) = rb->collect_ringbuffer_all();
+ EXPECT_THAT(numFrames, Eq(3));
+ EXPECT_THAT(bins, Each(fill_frame0 + fill_frame1 + fill_frame2));
+
+ insertFrameIncrementTimeline(*rb, *tk, frame3);
+ std::tie(numFrames, bins) = rb->collect_ringbuffer_all();
+ EXPECT_THAT(numFrames, Eq(3));
+ EXPECT_THAT(bins, Each(fill_frame1 + fill_frame2 + fill_frame3));
+}
+
+TEST_F(RingbufferTestCases, TestTimestampFiltering) {
+ auto rb = createFilledRingbuffer(std::make_shared<TickingTimeKeeper>());
+
+ std::tie(numFrames, bins) = rb->collect_after(toNsecs(1500us));
+ EXPECT_THAT(numFrames, Eq(2));
+ EXPECT_THAT(bins, Each(fill_frame2 + fill_frame3));
+
+ std::tie(numFrames, bins) = rb->collect_after(toNsecs(45000us));
+ EXPECT_THAT(numFrames, Eq(0));
+
+ std::tie(numFrames, bins) = rb->collect_after(0);
+ EXPECT_THAT(numFrames, Eq(4));
+ EXPECT_THAT(bins, Each(fill_frame0 + fill_frame1 + fill_frame2 + fill_frame3));
+}
+
+TEST_F(RingbufferTestCases, TestTimestampFilteringSameTimestamp) {
+ auto tk = std::make_shared<TickingTimeKeeper>();
+ auto rb = histogram::Ringbuffer::create(4, std::make_unique<TimeKeeperWrapper>(tk));
+ insertFrameIncrementTimeline(*rb, *tk, frame0);
+ insertFrameIncrementTimeline(*rb, *tk, frame1);
+ insertFrameIncrementTimeline(*rb, *tk, frame2);
+ rb->insert(frame3);
+ rb->insert(frame4);
+ tk->tick();
+
+ std::tie(numFrames, bins) = rb->collect_after(toNsecs(3ms));
+ EXPECT_THAT(numFrames, Eq(2));
+ EXPECT_THAT(bins, Each(fill_frame4));
+}
+
+TEST_F(RingbufferTestCases, TestFrameFiltering) {
+ auto rb = createFilledRingbuffer(std::make_shared<TickingTimeKeeper>());
+
+ std::tie(numFrames, bins) = rb->collect_max(2);
+ EXPECT_THAT(numFrames, Eq(2));
+ EXPECT_THAT(bins, Each(fill_frame2 + fill_frame3));
+
+ std::tie(numFrames, bins) = rb->collect_max(0);
+ EXPECT_THAT(numFrames, Eq(0));
+ EXPECT_THAT(bins, Each(0));
+
+ std::tie(numFrames, bins) = rb->collect_max(3);
+ EXPECT_THAT(numFrames, Eq(3));
+ EXPECT_THAT(bins, Each(fill_frame1 + fill_frame2 + fill_frame3));
+
+ std::tie(numFrames, bins) = rb->collect_max(8);
+ EXPECT_THAT(numFrames, Eq(4));
+ EXPECT_THAT(bins, Each(fill_frame0 + fill_frame1 + fill_frame2 + fill_frame3));
+}
+
+TEST_F(RingbufferTestCases, TestTimestampAndFrameFiltering) {
+ auto rb = createFilledRingbuffer(std::make_shared<TickingTimeKeeper>());
+
+ std::tie(numFrames, bins) = rb->collect_max_after(toNsecs(1500us), 1);
+ EXPECT_THAT(numFrames, Eq(1));
+ EXPECT_THAT(bins, Each(fill_frame3));
+
+ std::tie(numFrames, bins) = rb->collect_max_after(toNsecs(2500us), 0);
+ EXPECT_THAT(numFrames, Eq(0));
+ EXPECT_THAT(bins, Each(0));
+
+ std::tie(numFrames, bins) = rb->collect_max_after(toNsecs(10ms), 100);
+ EXPECT_THAT(numFrames, Eq(0));
+ EXPECT_THAT(bins, Each(0));
+
+ std::tie(numFrames, bins) = rb->collect_max_after(toNsecs(0ns), 10);
+ EXPECT_THAT(numFrames, Eq(4));
+ EXPECT_THAT(bins, Each(fill_frame0 + fill_frame1 + fill_frame2 + fill_frame3));
+}
+
+TEST_F(RingbufferTestCases, TestTimestampAndFrameFilteringAndResize) {
+ auto rb = createFilledRingbuffer(std::make_shared<TickingTimeKeeper>());
+
+ std::tie(numFrames, bins) = rb->collect_max_after(toNsecs(500us), 1);
+ EXPECT_THAT(numFrames, Eq(1));
+ EXPECT_THAT(bins, Each(fill_frame3));
+
+ std::tie(numFrames, bins) = rb->collect_max_after(toNsecs(500us), 10);
+ EXPECT_THAT(numFrames, Eq(3));
+ EXPECT_THAT(bins, Each(fill_frame1 + fill_frame2 + fill_frame3));
+
+ rb->resize(2);
+ std::tie(numFrames, bins) = rb->collect_max_after(toNsecs(500us), 10);
+ EXPECT_THAT(numFrames, Eq(2));
+ EXPECT_THAT(bins, Each(fill_frame2 + fill_frame3));
+}
+
+TEST_F(RingbufferTestCases, TestCumulativeCounts) {
+ auto tk = std::make_shared<TickingTimeKeeper>();
+ auto rb = histogram::Ringbuffer::create(1, std::make_unique<TimeKeeperWrapper>(tk));
+ insertFrameIncrementTimeline(*rb, *tk, frame0);
+
+ std::tie(numFrames, bins) = rb->collect_ringbuffer_all();
+ EXPECT_THAT(numFrames, Eq(1));
+ EXPECT_THAT(bins, Each(fill_frame0));
+
+ insertFrameIncrementTimeline(*rb, *tk, frame1);
+ std::tie(numFrames, bins) = rb->collect_ringbuffer_all();
+ EXPECT_THAT(numFrames, Eq(1));
+ EXPECT_THAT(bins, Each(fill_frame1));
+
+ std::tie(numFrames, bins) = rb->collect_cumulative();
+ EXPECT_THAT(numFrames, Eq(2));
+ EXPECT_THAT(bins, Each(fill_frame0 + fill_frame1));
+ rb->insert(frame2);
+ auto weight0 = std::chrono::duration_cast<std::chrono::nanoseconds>(1h);
+ tk->increment_by(weight0);
+
+ std::tie(numFrames, bins) = rb->collect_cumulative();
+ EXPECT_THAT(numFrames, Eq(3));
+ EXPECT_THAT(bins, Each(fill_frame0 + fill_frame1 + (fill_frame2 *
+ std::chrono::duration_cast<std::chrono::milliseconds>(weight0).count())));
+
+ auto weight1 = std::chrono::duration_cast<std::chrono::nanoseconds>(2min);
+ tk->increment_by(weight1);
+ std::tie(numFrames, bins) = rb->collect_cumulative();
+ EXPECT_THAT(numFrames, Eq(3));
+ EXPECT_THAT(bins, Each(fill_frame0 + fill_frame1 + (fill_frame2 *
+ std::chrono::duration_cast<std::chrono::milliseconds>(weight0 + weight1).count())));
+}
+
+TEST_F(RingbufferTestCases, TestCumulativeCountsEmpty) {
+ auto tk = std::make_shared<TickingTimeKeeper>();
+ auto rb = histogram::Ringbuffer::create(1, std::make_unique<TimeKeeperWrapper>(tk));
+ std::tie(numFrames, bins) = rb->collect_cumulative();
+ EXPECT_THAT(numFrames, Eq(0));
+}
+
+TEST_F(RingbufferTestCases, TestCumulativeCountsSaturate) {
+ auto tk = std::make_shared<TickingTimeKeeper>();
+ auto rb = histogram::Ringbuffer::create(1, std::make_unique<TimeKeeperWrapper>(tk));
+ insertFrameIncrementTimeline(*rb, *tk, frame_saturate);
+ auto eon = std::chrono::nanoseconds(std::numeric_limits<uint64_t>::max());
+ tk->increment_by(eon);
+ std::tie(numFrames, bins) = rb->collect_cumulative();
+ EXPECT_THAT(numFrames, Eq(1));
+ EXPECT_THAT(bins, Each(std::numeric_limits<uint64_t>::max()));
+}
+
+TEST_F(RingbufferTestCases, TimeWeightingTest) {
+ static constexpr int numInsertions = 4u;
+ auto tk = std::make_shared<TickingTimeKeeper>();
+ auto rb = histogram::Ringbuffer::create(numInsertions, std::make_unique<TimeKeeperWrapper>(tk));
+
+ auto weight0 = std::chrono::duration_cast<std::chrono::nanoseconds>(1ms);
+ auto weight1 = std::chrono::duration_cast<std::chrono::nanoseconds>(1h);
+ auto weight2 = std::chrono::duration_cast<std::chrono::nanoseconds>(1s);
+ using gigasecond = std::chrono::duration<uint64_t, std::giga>;
+ auto weight3 = std::chrono::duration_cast<std::chrono::nanoseconds>(gigasecond(4));
+
+ rb->insert(frame0);
+ tk->increment_by(weight0);
+ rb->insert(frame1);
+ tk->increment_by(weight1);
+ rb->insert(frame2);
+ tk->increment_by(weight2);
+ rb->insert(frame3);
+ tk->increment_by(weight3);
+
+ std::tie(numFrames, bins) = rb->collect_ringbuffer_all();
+
+ ASSERT_THAT(bins.size(), Eq(HIST_V_SIZE));
+ uint64_t expected_weight = fill_frame0 * toMs(weight0) + fill_frame1 * toMs(weight1) +
+ fill_frame2 * toMs(weight2) + fill_frame3 * toMs(weight3);
+ for (auto i = 0u; i < bins.size(); i++) {
+ EXPECT_THAT(bins[i], Eq(expected_weight));
+ }
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/liblight/Android.mk b/liblight/Android.mk
index d7060c8..a701e89 100644
--- a/liblight/Android.mk
+++ b/liblight/Android.mk
@@ -20,13 +20,17 @@
LOCAL_SRC_FILES := lights.c
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_HEADER_LIBRARIES := libhardware_headers
-LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_SHARED_LIBRARIES := liblog libcutils
LOCAL_CFLAGS := -DLOG_TAG=\"qdlights\"
ifeq ($(LLVM_SA), true)
LOCAL_CFLAGS += --compile-and-analyze --analyzer-perf --analyzer-Werror
endif
+LOCAL_CFLAGS += -Wno-error
LOCAL_CLANG := true
-LOCAL_MODULE := lights.$(TARGET_BOARD_PLATFORM)
+LOCAL_MODULE := lights.qcom
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 legacy_not_a_contribution
+LOCAL_LICENSE_CONDITIONS := by_exception_only not_allowed notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
LOCAL_MODULE_TAGS := optional
LOCAL_VENDOR_MODULE := true
diff --git a/liblight/lights.c b/liblight/lights.c
index e83be16..50326df 100644
--- a/liblight/lights.c
+++ b/liblight/lights.c
@@ -21,6 +21,7 @@
#include <log/log.h>
#include <cutils/properties.h>
+
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -29,23 +30,27 @@
#include <fcntl.h>
#include <pthread.h>
+#include <linux/msm_mdp.h>
+
#include <sys/ioctl.h>
#include <sys/types.h>
#include <hardware/lights.h>
-#ifndef DEFAULT_LOW_PERSISTENCE_MODE_BRIGHTNESS
-#define DEFAULT_LOW_PERSISTENCE_MODE_BRIGHTNESS 0x80
-#endif
+/*
+ * Change this to 1 to support battery notifications via BatteryService
+ */
+#define LIGHTS_SUPPORT_BATTERY 0
+#define CG_COLOR_ID_PROPERTY "ro.boot.hardware.color"
/******************************************************************************/
-
static pthread_once_t g_init = PTHREAD_ONCE_INIT;
static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
static struct light_state_t g_notification;
static struct light_state_t g_battery;
static int g_last_backlight_mode = BRIGHTNESS_MODE_USER;
static int g_attention = 0;
+static int rgb_brightness_ratio = 255;
static bool g_has_persistence_node = false;
char const*const RED_LED_FILE
@@ -58,11 +63,11 @@
= "/sys/class/leds/blue/brightness";
char const*const LCD_FILE
- = "/sys/class/leds/lcd-backlight/brightness";
-
-char const*const LCD_FILE2
= "/sys/class/backlight/panel0-backlight/brightness";
+char const*const PERSISTENCE_FILE
+ = "/sys/class/backlight/panel0-backlight/vr_mode";
+
char const*const BUTTON_FILE
= "/sys/class/leds/button-backlight/brightness";
@@ -75,8 +80,23 @@
char const*const BLUE_BLINK_FILE
= "/sys/class/leds/blue/blink";
-char const*const PERSISTENCE_FILE
- = "/sys/class/graphics/fb0/msm_fb_persist_mode";
+char const*const RED_ON_OFF_MS_FILE
+ = "/sys/class/leds/red/on_off_ms";
+
+char const*const GREEN_ON_OFF_MS_FILE
+ = "/sys/class/leds/green/on_off_ms";
+
+char const*const BLUE_ON_OFF_MS_FILE
+ = "/sys/class/leds/blue/on_off_ms";
+
+char const*const RED_RGB_START_FILE
+ = "/sys/class/leds/red/rgb_start";
+
+char const*const GREEN_RGB_START_FILE
+ = "/sys/class/leds/green/rgb_start";
+
+char const*const BLUE_RGB_START_FILE
+ = "/sys/class/leds/blue/rgb_start";
/**
* device methods
@@ -84,8 +104,22 @@
void init_globals(void)
{
+ char color_id_prop[PROPERTY_VALUE_MAX] = {""};
+
// init the mutex
pthread_mutex_init(&g_lock, NULL);
+
+ // check CG color
+ property_get(CG_COLOR_ID_PROPERTY, color_id_prop, "DEF00");
+ if (strcmp(color_id_prop, "GRA00") == 0) {
+ rgb_brightness_ratio = 25;
+ } else if (strcmp(color_id_prop, "SLV00") == 0) {
+ rgb_brightness_ratio = 15;
+ } else if (strcmp(color_id_prop, "BLU00") == 0) {
+ rgb_brightness_ratio = 15;
+ } else {
+ rgb_brightness_ratio = 20;
+ }
}
static int
@@ -94,11 +128,45 @@
int fd;
static int already_warned = 0;
- fd = open(path, O_RDWR);
+ fd = open(path, O_WRONLY);
if (fd >= 0) {
char buffer[20];
- int bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
- ssize_t amt = write(fd, buffer, (size_t)bytes);
+ ssize_t amt;
+ size_t bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
+ if(bytes < sizeof(buffer)) {
+ amt = write(fd, buffer, bytes);
+ } else {
+ amt = -1;
+ errno = -EINVAL;
+ }
+ close(fd);
+ return amt == -1 ? -errno : 0;
+ } else {
+ if (already_warned == 0) {
+ ALOGE("write_int failed to open %s\n", path);
+ already_warned = 1;
+ }
+ return -errno;
+ }
+}
+
+static int
+write_double_int(char const* path, int value1, int value2)
+{
+ int fd;
+ static int already_warned = 0;
+
+ fd = open(path, O_WRONLY);
+ if (fd >= 0) {
+ char buffer[20];
+ ssize_t amt;
+ size_t bytes = snprintf(buffer, sizeof(buffer), "%d %d\n", value1, value2);
+ if(bytes < sizeof(buffer)) {
+ amt = write(fd, buffer, bytes);
+ } else {
+ amt = -1;
+ errno = -EINVAL;
+ }
close(fd);
return amt == -1 ? -errno : 0;
} else {
@@ -136,6 +204,12 @@
return -1;
}
+ // The HAL requires that when low-persistence is requested, errors occur in only
+ // one case: if VR mode is unsupported. So if the display just doesn't happen to
+ // be in the right state for VR mode (an error case in the SDM845 kernel driver),
+ // suppress the error.
+ bool suppress_persistence_error = false;
+
pthread_mutex_lock(&g_lock);
// Toggle low persistence mode state
bool persistence_mode = ((g_last_backlight_mode != state->brightnessMode && lpEnabled) ||
@@ -147,24 +221,24 @@
if ((err = write_int(PERSISTENCE_FILE, lpEnabled)) != 0) {
ALOGE("%s: Failed to write to %s: %s\n", __FUNCTION__,
PERSISTENCE_FILE, strerror(errno));
- }
- if (lpEnabled != 0) {
- brightness = DEFAULT_LOW_PERSISTENCE_MODE_BRIGHTNESS;
+ suppress_persistence_error = true;
}
}
g_last_backlight_mode = state->brightnessMode;
}
- if (!err) {
- if (!access(LCD_FILE, F_OK)) {
- err = write_int(LCD_FILE, brightness);
- } else {
- err = write_int(LCD_FILE2, brightness);
- }
+ g_last_backlight_mode = state->brightnessMode;
+
+ if (!err && !lpEnabled) {
+ err = write_int(LCD_FILE, brightness);
}
pthread_mutex_unlock(&g_lock);
- return cannot_handle_persistence ? -ENOSYS : err;
+ if (suppress_persistence_error) {
+ return 0;
+ } else {
+ return cannot_handle_persistence ? -ENOSYS : err;
+ }
}
static int
@@ -199,43 +273,21 @@
state->flashMode, colorRGB, onMS, offMS);
#endif
- red = (colorRGB >> 16) & 0xFF;
- green = (colorRGB >> 8) & 0xFF;
- blue = colorRGB & 0xFF;
+ red = ((colorRGB >> 16) & 0xFF) * rgb_brightness_ratio / 255;
+ green = ((colorRGB >> 8) & 0xFF) * rgb_brightness_ratio / 255;
+ blue = (colorRGB & 0xFF) * rgb_brightness_ratio / 255;
- if (onMS > 0 && offMS > 0) {
- /*
- * if ON time == OFF time
- * use blink mode 2
- * else
- * use blink mode 1
- */
- if (onMS == offMS)
- blink = 2;
- else
- blink = 1;
- } else {
- blink = 0;
- }
+ write_double_int(RED_ON_OFF_MS_FILE, onMS, offMS);
+ write_int(RED_LED_FILE, red);
+ write_double_int(GREEN_ON_OFF_MS_FILE, onMS, offMS);
+ write_int(GREEN_LED_FILE, green);
+ write_double_int(BLUE_ON_OFF_MS_FILE, onMS, offMS);
+ write_int(BLUE_LED_FILE, blue);
- if (blink) {
- if (red) {
- if (write_int(RED_BLINK_FILE, blink))
- write_int(RED_LED_FILE, 0);
- }
- if (green) {
- if (write_int(GREEN_BLINK_FILE, blink))
- write_int(GREEN_LED_FILE, 0);
- }
- if (blue) {
- if (write_int(BLUE_BLINK_FILE, blink))
- write_int(BLUE_LED_FILE, 0);
- }
- } else {
- write_int(RED_LED_FILE, red);
- write_int(GREEN_LED_FILE, green);
- write_int(BLUE_LED_FILE, blue);
- }
+ if(!write_int(RED_RGB_START_FILE, 1))
+ if(!write_int(GREEN_RGB_START_FILE, 1))
+ if(!write_int(BLUE_RGB_START_FILE, 1))
+ return -1;
return 0;
}
diff --git a/libmemtrack/Android.mk b/libmemtrack/Android.mk
index 10fd40a..bc2e32f 100644
--- a/libmemtrack/Android.mk
+++ b/libmemtrack/Android.mk
@@ -20,11 +20,13 @@
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_VENDOR_MODULE := true
-LOCAL_C_INCLUDES += hardware/libhardware/include
+LOCAL_HEADER_LIBRARIES := libhardware_headers
LOCAL_CFLAGS := -Wconversion -Wall -Werror -Wno-sign-conversion
LOCAL_CLANG := true
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_HEADER_LIBRARIES := libhardware_headers
LOCAL_SRC_FILES := memtrack_msm.c kgsl.c
LOCAL_MODULE := memtrack.$(TARGET_BOARD_PLATFORM)
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
include $(BUILD_SHARED_LIBRARY)
diff --git a/libqdutils/Android.bp b/libqdutils/Android.bp
index 0c1b96a..d088d78 100644
--- a/libqdutils/Android.bp
+++ b/libqdutils/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_qcom_sdm845_display_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-BSD
+ default_applicable_licenses: ["hardware_qcom_sdm845_display_license"],
+}
+
cc_library_shared {
name: "libqdutils",
vendor: true,
diff --git a/libqservice/Android.bp b/libqservice/Android.bp
index fe69d39..6f11a1a 100644
--- a/libqservice/Android.bp
+++ b/libqservice/Android.bp
@@ -1,3 +1,14 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_qcom_sdm845_display_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-BSD
+ // legacy_not_a_contribution
+ default_applicable_licenses: ["hardware_qcom_sdm845_display_license"],
+}
+
cc_library_shared {
name: "libqservice",
vendor: true,
diff --git a/libqservice/IQService.h b/libqservice/IQService.h
index b201256..686b244 100644
--- a/libqservice/IQService.h
+++ b/libqservice/IQService.h
@@ -79,6 +79,10 @@
GET_COMPOSER_STATUS = 37, // Get composer init status-true if primary display init is done
SET_COLOR_MODE_WITH_RENDER_INTENT = 38,
SET_IDLE_PC = 39, // Enable/disable Idle power collapse
+
+ // Start custom transactions from 200
+ SET_COLOR_SAMPLING_ENABLED = 200, // Toggle the collection of display color stats
+ SET_WHITE_COMPENSATION = 201, // Enable/disable white point compensation
COMMAND_LIST_END = 400,
};
diff --git a/sdm/include/utils/constants.h b/sdm/include/utils/constants.h
index 5efe357..608b192 100644
--- a/sdm/include/utils/constants.h
+++ b/sdm/include/utils/constants.h
@@ -75,6 +75,12 @@
const int kPageSize = 4096;
const uint32_t kGridSize = 129; // size used for non-linear transformation before Tone-mapping
const uint32_t kLutDim = 17; // Dim of the 3d LUT for tone-mapping.
+ constexpr int kColorTransformMatrixSize = 16;
+ constexpr float kIdentityMatrix[kColorTransformMatrixSize] = { 1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0 };
+
typedef void * Handle;
diff --git a/sdm/libs/core/Android.mk b/sdm/libs/core/Android.mk
index d4aa933..9d7b6dc 100644
--- a/sdm/libs/core/Android.mk
+++ b/sdm/libs/core/Android.mk
@@ -3,6 +3,8 @@
include $(LOCAL_PATH)/../../../common.mk
LOCAL_MODULE := libsdmcore
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-BSD
+LOCAL_LICENSE_CONDITIONS := notice
LOCAL_VENDOR_MODULE := true
LOCAL_MODULE_TAGS := optional
LOCAL_C_INCLUDES := $(common_includes) $(kernel_includes)
diff --git a/sdm/libs/core/display_base.cpp b/sdm/libs/core/display_base.cpp
index e5f61c1..d096f24 100644
--- a/sdm/libs/core/display_base.cpp
+++ b/sdm/libs/core/display_base.cpp
@@ -160,7 +160,7 @@
hw_layers_info.app_layer_count++;
}
- DLOGD_IF(kTagDisplay, "LayerStack layer_count: %d, app_layer_count: %d, gpu_target_index: %d, "
+ DLOGV_IF(kTagDisplay, "LayerStack layer_count: %d, app_layer_count: %d, gpu_target_index: %d, "
"display type: %d", layers.size(), hw_layers_info.app_layer_count,
hw_layers_info.gpu_target_index, display_type_);
@@ -204,8 +204,8 @@
auto gpu_target_layer_dst_xpixels = out_rect.right - out_rect.left;
auto gpu_target_layer_dst_ypixels = out_rect.bottom - out_rect.top;
- if (gpu_target_layer_dst_xpixels > mixer_attributes_.width ||
- gpu_target_layer_dst_ypixels > mixer_attributes_.height) {
+ if (gpu_target_layer_dst_xpixels > layer_mixer_width ||
+ gpu_target_layer_dst_ypixels > layer_mixer_height) {
DLOGE("GPU target layer dst rect is not with in limits gpu wxh %fx%f, mixer wxh %dx%d",
gpu_target_layer_dst_xpixels, gpu_target_layer_dst_ypixels,
mixer_attributes_.width, mixer_attributes_.height);
@@ -1173,7 +1173,7 @@
// Align the width and height according to fb's aspect ratio
*new_mixer_width = FloorToMultipleOf(UINT32((FLOAT(fb_width) / FLOAT(fb_height)) *
- layer_height), align_x);
+ FLOAT(layer_height)), align_x);
*new_mixer_height = FloorToMultipleOf(layer_height, align_y);
LayerRect dst_domain = {0.0f, 0.0f, FLOAT(*new_mixer_width), FLOAT(*new_mixer_height)};
@@ -1290,7 +1290,8 @@
uint32_t hw_layers_count = UINT32(hw_layers_.info.hw_layers.size());
for (uint32_t i = 0; i < hw_layers_count; i++) {
- Layer *sdm_layer = layer_stack->layers.at(hw_layers_.info.index.at(i));
+ uint32_t sdm_layer_index = hw_layers_.info.index.at(i);
+ Layer *sdm_layer = layer_stack->layers.at(sdm_layer_index);
Layer &hw_layer = hw_layers_.info.hw_layers.at(i);
hw_layer.input_buffer.planes[0].fd = sdm_layer->input_buffer.planes[0].fd;
@@ -1299,7 +1300,12 @@
hw_layer.input_buffer.size = sdm_layer->input_buffer.size;
hw_layer.input_buffer.acquire_fence_fd = sdm_layer->input_buffer.acquire_fence_fd;
hw_layer.input_buffer.handle_id = sdm_layer->input_buffer.handle_id;
- hw_layer.input_buffer.buffer_id = sdm_layer->input_buffer.buffer_id;
+ // TODO(user): Other FBT layer attributes like surface damage, dataspace, secure camera and
+ // secure display flags are also updated during SetClientTarget() called between validate and
+ // commit. Need to revist this and update it accordingly for FBT layer.
+ if (hw_layers_.info.gpu_target_index == sdm_layer_index) {
+ hw_layer.input_buffer.flags.secure = sdm_layer->input_buffer.flags.secure;
+ }
}
return;
diff --git a/sdm/libs/core/drm/hw_device_drm.cpp b/sdm/libs/core/drm/hw_device_drm.cpp
index 40e0b22..aa0e13c 100644
--- a/sdm/libs/core/drm/hw_device_drm.cpp
+++ b/sdm/libs/core/drm/hw_device_drm.cpp
@@ -781,6 +781,13 @@
drm_atomic_intf_->Perform(DRMOps::CONNECTOR_SET_POWER_MODE, token_.conn_id, DRMPowerMode::OFF);
drm_atomic_intf_->Perform(DRMOps::CRTC_SET_ACTIVE, token_.crtc_id, 0);
int ret = drm_atomic_intf_->Commit(true /* synchronous */, false /* retain_planes */);
+ drm_atomic_intf_->Perform(DRMOps::CRTC_SET_MODE, token_.crtc_id, ¤t_mode);
+ if (cwb_config_.enabled) {
+ drm_atomic_intf_->Perform(DRMOps::CONNECTOR_SET_CRTC, cwb_config_.token.conn_id, 0);
+ drm_mgr_intf_->UnregisterDisplay(cwb_config_.token);
+ cwb_config_.enabled = false;
+ registry_.Clear();
+ }
if (ret) {
DLOGE("Failed with error: %d", ret);
return kErrorHardware;
@@ -1514,7 +1521,7 @@
float scale_x = FLOAT(display_attributes_[index].x_pixels) / FLOAT(mixer_attributes.width);
float scale_y = FLOAT(display_attributes_[index].y_pixels) / FLOAT(mixer_attributes.height);
- float max_scale_up = hw_resource_.hw_dest_scalar_info.max_scale_up;
+ float max_scale_up = FLOAT(hw_resource_.hw_dest_scalar_info.max_scale_up);
if (scale_x > max_scale_up || scale_y > max_scale_up) {
DLOGW(
"Up scaling ratio exceeds for destination scalar upscale limit scale_x %f scale_y %f "
diff --git a/sdm/libs/core/drm/hw_device_drm.h b/sdm/libs/core/drm/hw_device_drm.h
index aaa4694..396183a 100644
--- a/sdm/libs/core/drm/hw_device_drm.h
+++ b/sdm/libs/core/drm/hw_device_drm.h
@@ -51,6 +51,11 @@
namespace sdm {
class HWInfoInterface;
+struct CWBConfig {
+ bool enabled = false;
+ sde_drm::DRMDisplayToken token = {};
+};
+
class HWDeviceDRM : public HWInterface {
public:
HWDeviceDRM(BufferSyncHandler *buffer_sync_handler, BufferAllocator *buffer_allocator,
@@ -188,6 +193,7 @@
sde_drm::DRMConnectorInfo connector_info_ = {};
bool first_cycle_ = true;
bool synchronous_commit_ = false;
+ CWBConfig cwb_config_ = {};
private:
HWMixerAttributes mixer_attributes_ = {};
diff --git a/sdm/libs/core/drm/hw_peripheral_drm.h b/sdm/libs/core/drm/hw_peripheral_drm.h
index 9523bd9..4c1a90f 100644
--- a/sdm/libs/core/drm/hw_peripheral_drm.h
+++ b/sdm/libs/core/drm/hw_peripheral_drm.h
@@ -35,11 +35,6 @@
namespace sdm {
-struct CWBConfig {
- bool enabled = false;
- sde_drm::DRMDisplayToken token = {};
-};
-
class HWPeripheralDRM : public HWDeviceDRM {
public:
explicit HWPeripheralDRM(BufferSyncHandler *buffer_sync_handler,
@@ -70,7 +65,6 @@
sde_drm_dest_scaler_data sde_dest_scalar_data_ = {};
std::vector<SDEScaler> scalar_data_ = {};
- CWBConfig cwb_config_ = {};
sde_drm::DRMIdlePCState idle_pc_state_ = sde_drm::DRMIdlePCState::NONE;
};
diff --git a/sdm/libs/core/drm/hw_tv_drm.cpp b/sdm/libs/core/drm/hw_tv_drm.cpp
index 8e141e3..da1a179 100644
--- a/sdm/libs/core/drm/hw_tv_drm.cpp
+++ b/sdm/libs/core/drm/hw_tv_drm.cpp
@@ -261,8 +261,8 @@
// metadata. This will be replaced with an idle timer implementation in the future.
if (reset_hdr_flag) {
gettimeofday(&hdr_reset_end, NULL);
- float hdr_reset_time_start = ((hdr_reset_start.tv_sec*1000) + (hdr_reset_start.tv_usec/1000));
- float hdr_reset_time_end = ((hdr_reset_end.tv_sec*1000) + (hdr_reset_end.tv_usec/1000));
+ float hdr_reset_time_start = FLOAT((hdr_reset_start.tv_sec*1000) + (hdr_reset_start.tv_usec/1000));
+ float hdr_reset_time_end = FLOAT((hdr_reset_end.tv_sec*1000) + (hdr_reset_end.tv_usec/1000));
if (((hdr_reset_time_end-hdr_reset_time_start)/1000) >= MIN_HDR_RESET_WAITTIME) {
memset(&hdr_metadata_, 0, sizeof(hdr_metadata_));
diff --git a/sdm/libs/core/fb/hw_device.cpp b/sdm/libs/core/fb/hw_device.cpp
index 148f866..c7a004c 100644
--- a/sdm/libs/core/fb/hw_device.cpp
+++ b/sdm/libs/core/fb/hw_device.cpp
@@ -1325,7 +1325,7 @@
float scale_x = FLOAT(display_attributes_.x_pixels) / FLOAT(mixer_attributes.width);
float scale_y = FLOAT(display_attributes_.y_pixels) / FLOAT(mixer_attributes.height);
- float max_scale_up = hw_resource_.hw_dest_scalar_info.max_scale_up;
+ float max_scale_up = FLOAT(hw_resource_.hw_dest_scalar_info.max_scale_up);
if (scale_x > max_scale_up || scale_y > max_scale_up) {
DLOGW_IF(kTagDriverConfig, "Up scaling ratio exceeds for destination scalar upscale " \
"limit scale_x %f scale_y %f max_scale_up %f", scale_x, scale_y, max_scale_up);
diff --git a/sdm/libs/core/resource_default.cpp b/sdm/libs/core/resource_default.cpp
index e8cce43..6dd7026 100644
--- a/sdm/libs/core/resource_default.cpp
+++ b/sdm/libs/core/resource_default.cpp
@@ -467,9 +467,10 @@
HWPipeInfo *right_pipe = &layer_config->right_pipe;
float src_width = src_rect.right - src_rect.left;
float dst_width = dst_rect.right - dst_rect.left;
+ float fmax_pipe_width = FLOAT(hw_res_info_.max_pipe_width);
// Layer cannot qualify for SrcSplit if source or destination width exceeds max pipe width.
- if ((src_width > hw_res_info_.max_pipe_width) || (dst_width > hw_res_info_.max_pipe_width)) {
+ if ((src_width > fmax_pipe_width) || (dst_width > fmax_pipe_width)) {
SplitRect(src_rect, dst_rect, &left_pipe->src_roi, &left_pipe->dst_roi, &right_pipe->src_roi,
&right_pipe->dst_roi);
left_pipe->valid = true;
diff --git a/sdm/libs/core/strategy.cpp b/sdm/libs/core/strategy.cpp
index d682070..a2a3ba2 100644
--- a/sdm/libs/core/strategy.cpp
+++ b/sdm/libs/core/strategy.cpp
@@ -159,8 +159,8 @@
return;
}
- float layer_mixer_width = mixer_attributes_.width;
- float layer_mixer_height = mixer_attributes_.height;
+ float layer_mixer_width = FLOAT(mixer_attributes_.width);
+ float layer_mixer_height = FLOAT(mixer_attributes_.height);
if (!hw_resource_info_.is_src_split && display_attributes_.is_device_split) {
split_display = true;
diff --git a/sdm/libs/hwc2/Android.mk b/sdm/libs/hwc2/Android.mk
index 67a410e..1d602f6 100644
--- a/sdm/libs/hwc2/Android.mk
+++ b/sdm/libs/hwc2/Android.mk
@@ -5,6 +5,8 @@
ifeq ($(use_hwc2),true)
LOCAL_MODULE := hwcomposer.$(TARGET_BOARD_PLATFORM)
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD legacy_not_a_contribution
+LOCAL_LICENSE_CONDITIONS := by_exception_only not_allowed notice
LOCAL_VENDOR_MODULE := true
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_MODULE_TAGS := optional
@@ -19,12 +21,17 @@
LOCAL_SHARED_LIBRARIES := libsdmcore libqservice libbinder libhardware libhardware_legacy \
libutils libcutils libsync libqdutils libqdMetaData \
libsdmutils libc++ liblog libgrallocutils libui libgpu_tonemapper \
- libhidlbase libhidltransport vendor.display.config@1.0 \
+ libhidlbase vendor.display.config@1.0 \
android.hardware.graphics.mapper@2.0 \
android.hardware.graphics.mapper@2.1 \
android.hardware.graphics.composer@2.2 \
+ android.hardware.graphics.composer@2.3 \
android.hardware.graphics.allocator@2.0 \
- libdisplaydebug
+ libdisplaydebug \
+ hardware.google.light@1.0 \
+ libdrm \
+
+LOCAL_STATIC_LIBRARIES := libhistogram
ifeq ($(display_config_version), DISPLAY_CONFIG_1_1)
LOCAL_SHARED_LIBRARIES += vendor.display.config@1.1
@@ -38,6 +45,9 @@
LOCAL_SHARED_LIBRARIES += vendor.display.config@1.3
endif
+# Allow implicit fallthroughs in hwc_display.cpp until they are fixed.
+LOCAL_CFLAGS += -Wno-error=implicit-fallthrough
+
LOCAL_SRC_FILES := hwc_session.cpp \
hwc_session_services.cpp \
hwc_display.cpp \
diff --git a/sdm/libs/hwc2/hwc_callbacks.cpp b/sdm/libs/hwc2/hwc_callbacks.cpp
index 48593f1..806214b 100644
--- a/sdm/libs/hwc2/hwc_callbacks.cpp
+++ b/sdm/libs/hwc2/hwc_callbacks.cpp
@@ -35,6 +35,7 @@
namespace sdm {
HWC2::Error HWCCallbacks::Hotplug(hwc2_display_t display, HWC2::Connection state) {
+ std::lock_guard<std::mutex> hotplug_lock(hotplug_mutex_);
if (!hotplug_) {
return HWC2::Error::NoResources;
}
@@ -43,6 +44,7 @@
}
HWC2::Error HWCCallbacks::Refresh(hwc2_display_t display) {
+ std::lock_guard<std::mutex> refresh_lock(refresh_mutex_);
if (!refresh_) {
return HWC2::Error::NoResources;
}
@@ -51,6 +53,7 @@
}
HWC2::Error HWCCallbacks::Vsync(hwc2_display_t display, int64_t timestamp) {
+ std::lock_guard<std::mutex> vsync_lock(vsync_mutex_);
if (!vsync_) {
return HWC2::Error::NoResources;
}
@@ -62,18 +65,21 @@
HWC2::Error HWCCallbacks::Register(HWC2::Callback descriptor, hwc2_callback_data_t callback_data,
hwc2_function_pointer_t pointer) {
switch (descriptor) {
- case HWC2::Callback::Hotplug:
+ case HWC2::Callback::Hotplug: {
+ std::lock_guard<std::mutex> hotplug_lock(hotplug_mutex_);
hotplug_data_ = callback_data;
hotplug_ = reinterpret_cast<HWC2_PFN_HOTPLUG>(pointer);
- break;
- case HWC2::Callback::Refresh:
+ } break;
+ case HWC2::Callback::Refresh: {
+ std::lock_guard<std::mutex> refresh_lock(refresh_mutex_);
refresh_data_ = callback_data;
refresh_ = reinterpret_cast<HWC2_PFN_REFRESH>(pointer);
- break;
- case HWC2::Callback::Vsync:
+ } break;
+ case HWC2::Callback::Vsync: {
+ std::lock_guard<std::mutex> vsync_lock(vsync_mutex_);
vsync_data_ = callback_data;
vsync_ = reinterpret_cast<HWC2_PFN_VSYNC>(pointer);
- break;
+ } break;
default:
return HWC2::Error::BadParameter;
}
diff --git a/sdm/libs/hwc2/hwc_callbacks.h b/sdm/libs/hwc2/hwc_callbacks.h
index d3f4e52..8cb60fc 100644
--- a/sdm/libs/hwc2/hwc_callbacks.h
+++ b/sdm/libs/hwc2/hwc_callbacks.h
@@ -36,6 +36,8 @@
#undef HWC2_INCLUDE_STRINGIFICATION
#undef HWC2_USE_CPP11
+#include <mutex>
+
namespace sdm {
class HWCCallbacks {
@@ -56,6 +58,10 @@
HWC2_PFN_HOTPLUG hotplug_ = nullptr;
HWC2_PFN_REFRESH refresh_ = nullptr;
HWC2_PFN_VSYNC vsync_ = nullptr;
+
+ std::mutex hotplug_mutex_;
+ std::mutex refresh_mutex_;
+ std::mutex vsync_mutex_;
};
} // namespace sdm
diff --git a/sdm/libs/hwc2/hwc_display.cpp b/sdm/libs/hwc2/hwc_display.cpp
index 5b672da..1f267e0 100644
--- a/sdm/libs/hwc2/hwc_display.cpp
+++ b/sdm/libs/hwc2/hwc_display.cpp
@@ -54,6 +54,7 @@
HWC2::Error HWCColorMode::Init() {
PopulateColorModes();
+ InitColorCompensation();
return ApplyDefaultColorMode();
}
@@ -79,7 +80,6 @@
*out_num_modes = std::min(*out_num_modes, UINT32(color_mode_map_.size()));
for (uint32_t i = 0; i < *out_num_modes; it++, i++) {
out_modes[i] = it->first;
- DLOGI("Color mode = %d is supported", out_modes[i]);
}
return HWC2::Error::None;
}
@@ -93,7 +93,6 @@
*out_num_intents = std::min(*out_num_intents, UINT32(color_mode_map_[mode].size()));
for (uint32_t i = 0; i < *out_num_intents; it++, i++) {
out_intents[i] = it->first;
- DLOGI("Color mode = %d is supported with render intent = %d", mode, out_intents[i]);
}
return HWC2::Error::None;
}
@@ -121,11 +120,13 @@
DLOGE("failed for mode = %d intent = %d name = %s", mode, intent, mode_string.c_str());
return HWC2::Error::Unsupported;
}
- // The mode does not have the PCC configured, restore the transform
- RestoreColorTransform();
current_color_mode_ = mode;
current_render_intent_ = intent;
+
+ // The mode does not have the PCC configured, restore the transform
+ RestoreColorTransform();
+
DLOGV_IF(kTagClient, "Successfully applied mode = %d intent = %d name = %s", mode, intent,
mode_string.c_str());
return HWC2::Error::None;
@@ -142,7 +143,8 @@
}
HWC2::Error HWCColorMode::RestoreColorTransform() {
- DisplayError error = display_intf_->SetColorTransform(kColorTransformMatrixCount, color_matrix_);
+ DisplayError error =
+ display_intf_->SetColorTransform(kColorTransformMatrixCount, PickTransferMatrix());
if (error != kErrorNone) {
DLOGE("Failed to set Color Transform");
return HWC2::Error::BadParameter;
@@ -151,19 +153,228 @@
return HWC2::Error::None;
}
+void HWCColorMode::InitColorCompensation() {
+ char value[kPropertyMax] = {0};
+ if (Debug::Get()->GetProperty(ADAPTIVE_WHITE_COEFFICIENT_PROP, value) == kErrorNone) {
+ adaptive_white_ = std::make_unique<WhiteCompensation>(string(value));
+ adaptive_white_->SetEnabled(true);
+ }
+ std::memset(value, 0, sizeof(value));
+ if (Debug::Get()->GetProperty(ADAPTIVE_SATURATION_PARAMETER_PROP, value) == kErrorNone) {
+ adaptive_saturation_ = std::make_unique<SaturationCompensation>(string(value));
+ adaptive_saturation_->SetEnabled(true);
+ }
+}
+
+const double *HWCColorMode::PickTransferMatrix() {
+ double matrix[kColorTransformMatrixCount] = {0};
+ if (current_render_intent_ == RenderIntent::ENHANCE) {
+ CopyColorTransformMatrix(color_matrix_, matrix);
+ if (HasSaturationCompensation())
+ adaptive_saturation_->ApplyToMatrix(matrix);
+
+ if (HasWhiteCompensation())
+ adaptive_white_->ApplyToMatrix(matrix);
+
+ CopyColorTransformMatrix(matrix, compensated_color_matrix_);
+ return compensated_color_matrix_;
+ } else {
+ return color_matrix_;
+ }
+}
+
+HWC2::Error HWCColorMode::SetWhiteCompensation(bool enabled) {
+ if (adaptive_white_ == NULL)
+ return HWC2::Error::Unsupported;
+
+ if (adaptive_white_->SetEnabled(enabled) != HWC2::Error::None) {
+ return HWC2::Error::NotValidated;
+ }
+
+ RestoreColorTransform();
+
+ DLOGI("Set White Compensation: %d", enabled);
+ return HWC2::Error::None;
+}
+
+HWC2::Error HWCColorMatrix::SetEnabled(bool enabled) {
+ enabled_ = enabled;
+ return HWC2::Error::None;
+}
+
+bool HWCColorMatrix::ParseFloatValueByCommas(const string &values, uint32_t length,
+ std::vector<float> &elements) const {
+ std::istringstream data_stream(values);
+ string data;
+ uint32_t index = 0;
+ std::vector<float> temp_elements;
+ while (std::getline(data_stream, data, ',')) {
+ temp_elements.push_back(std::move(std::stof(data.c_str())));
+ index++;
+ }
+
+ if (index != length) {
+ DLOGW("Insufficient elements defined");
+ return false;
+ }
+ std::move(temp_elements.begin(), temp_elements.end(), elements.begin());
+ return true;
+}
+
+HWC2::Error WhiteCompensation::SetEnabled(bool enabled) {
+ // re-parse data when set enabled for retry calibration
+ if (enabled) {
+ if (!ConfigCoefficients() || !ParseWhitePointCalibrationData()) {
+ enabled_ = false;
+ DLOGE("Failed to WhiteCompensation Set");
+ return HWC2::Error::NotValidated;
+ }
+ CalculateRGBRatio();
+ }
+ enabled_ = enabled;
+ return HWC2::Error::None;
+}
+
+bool WhiteCompensation::ParseWhitePointCalibrationData() {
+ static constexpr char kWhitePointCalibrationDataPath[] = "/persist/display/calibrated_rgb";
+ FILE *fp = fopen(kWhitePointCalibrationDataPath, "r");
+ int ret;
+
+ if (!fp) {
+ DLOGW("Failed to open white point calibration data file");
+ return false;
+ }
+
+ ret = fscanf(fp, "%d %d %d", &compensated_red_, &compensated_green_, &compensated_blue_);
+ fclose(fp);
+
+ if ((ret == kNumOfCompensationData) && CheckCompensatedRGB(compensated_red_) &&
+ CheckCompensatedRGB(compensated_green_) && CheckCompensatedRGB(compensated_blue_)) {
+ DLOGD("Compensated RGB: %d %d %d", compensated_red_, compensated_green_, compensated_blue_);
+ return true;
+ } else {
+ compensated_red_ = kCompensatedMaxRGB;
+ compensated_green_ = kCompensatedMaxRGB;
+ compensated_blue_ = kCompensatedMaxRGB;
+ DLOGE("Wrong white compensated value");
+ return false;
+ }
+}
+
+bool WhiteCompensation::ConfigCoefficients() {
+ std::vector<float> CompensatedCoefficients(kCoefficientElements);
+ if (!ParseFloatValueByCommas(key_values_, kCoefficientElements, CompensatedCoefficients))
+ return false;
+ std::move(CompensatedCoefficients.begin(), CompensatedCoefficients.end(),
+ white_compensated_Coefficients_);
+ for (const auto &c : white_compensated_Coefficients_) {
+ DLOGD("white_compensated_Coefficients_=%f", c);
+ }
+ return true;
+}
+
+void WhiteCompensation::CalculateRGBRatio() {
+ // r = r_coeffient2 * R^2 + r_coeffient1 * R + r_coeffient0
+ // g = g_coeffient2 * G^2 + g_coeffient1 * G + g_coeffient0
+ // b = b_coeffient2 * B^2 + b_coeffient1 * B + b_coeffient0
+ // r_ratio = r/kCompensatedMaxRGB
+ // g_ratio = g/kCompensatedMaxRGB
+ // b_ratio = b/kCompensatedMaxRGB
+ auto rgb_ratio = [=](int rgb, float c2, float c1, float c0) {
+ float frgb = FLOAT(rgb);
+ return ((c2 * frgb * frgb + c1 * frgb + c0) / kCompensatedMaxRGB);
+ };
+
+ compensated_red_ratio_ =
+ rgb_ratio(compensated_red_, white_compensated_Coefficients_[0],
+ white_compensated_Coefficients_[1], white_compensated_Coefficients_[2]);
+ compensated_green_ratio_ =
+ rgb_ratio(compensated_green_, white_compensated_Coefficients_[3],
+ white_compensated_Coefficients_[4], white_compensated_Coefficients_[5]);
+ compensated_blue_ratio_ =
+ rgb_ratio(compensated_blue_, white_compensated_Coefficients_[6],
+ white_compensated_Coefficients_[7], white_compensated_Coefficients_[8]);
+ DLOGI("Compensated ratio %f %f %f", compensated_red_ratio_, compensated_green_ratio_,
+ compensated_blue_ratio_);
+}
+
+void WhiteCompensation::ApplyToMatrix(double *in) {
+ double matrix[kColorTransformMatrixCount] = {0};
+ for (uint32_t i = 0; i < kColorTransformMatrixCount; i++) {
+ if ((i % 4) == 0)
+ matrix[i] = compensated_red_ratio_ * in[i];
+ else if ((i % 4) == 1)
+ matrix[i] = compensated_green_ratio_ * in[i];
+ else if ((i % 4) == 2)
+ matrix[i] = compensated_blue_ratio_ * in[i];
+ else if ((i % 4) == 3)
+ matrix[i] = in[i];
+ }
+ std::move(&matrix[0], &matrix[kColorTransformMatrixCount - 1], in);
+}
+
+HWC2::Error SaturationCompensation::SetEnabled(bool enabled) {
+ if (enabled == enabled_)
+ return HWC2::Error::None;
+
+ if (enabled) {
+ if (!ConfigSaturationParameter()) {
+ enabled_ = false;
+ return HWC2::Error::NotValidated;
+ }
+ }
+ enabled_ = enabled;
+ return HWC2::Error::None;
+}
+
+bool SaturationCompensation::ConfigSaturationParameter() {
+ std::vector<float> SaturationParameter(kSaturationParameters);
+ if (!ParseFloatValueByCommas(key_values_, kSaturationParameters, SaturationParameter))
+ return false;
+
+ int32_t matrix_index = 0;
+ for (uint32_t i = 0; i < SaturationParameter.size(); i++) {
+ saturated_matrix_[matrix_index] = SaturationParameter.at(i);
+ // Put parameters to matrix and keep the last row/column identity
+ if ((i + 1) % 3 == 0) {
+ matrix_index += 2;
+ } else {
+ matrix_index++;
+ }
+ DLOGD("SaturationParameter[%d]=%f", i, SaturationParameter.at(i));
+ }
+ return true;
+}
+
+void SaturationCompensation::ApplyToMatrix(double *in) {
+ double matrix[kColorTransformMatrixCount] = {0};
+ // 4 x 4 matrix multiplication
+ for (uint32_t i = 0; i < kNumOfRows; i++) {
+ for (uint32_t j = 0; j < kColumnsPerRow; j++) {
+ for (uint32_t k = 0; k < kColumnsPerRow; k++) {
+ matrix[j + (i * kColumnsPerRow)] +=
+ saturated_matrix_[k + (i * kColumnsPerRow)] * in[j + (k * kColumnsPerRow)];
+ }
+ }
+ }
+ std::move(&matrix[0], &matrix[kColorTransformMatrixCount - 1], in);
+}
+
HWC2::Error HWCColorMode::SetColorTransform(const float *matrix,
android_color_transform_t /*hint*/) {
DTRACE_SCOPED();
auto status = HWC2::Error::None;
- double color_matrix[kColorTransformMatrixCount] = {0};
- CopyColorTransformMatrix(matrix, color_matrix);
-
- DisplayError error = display_intf_->SetColorTransform(kColorTransformMatrixCount, color_matrix);
+ double color_matrix_restore[kColorTransformMatrixCount] = {0};
+ CopyColorTransformMatrix(color_matrix_, color_matrix_restore);
+ CopyColorTransformMatrix(matrix, color_matrix_);
+ DisplayError error =
+ display_intf_->SetColorTransform(kColorTransformMatrixCount, PickTransferMatrix());
if (error != kErrorNone) {
+ CopyColorTransformMatrix(color_matrix_restore, color_matrix_);
DLOGE("Failed to set Color Transform Matrix");
status = HWC2::Error::Unsupported;
}
- CopyColorTransformMatrix(matrix, color_matrix_);
+
return status;
}
@@ -312,13 +523,23 @@
}
*os << "current mode: " << static_cast<uint32_t>(current_color_mode_) << std::endl;
*os << "current render_intent: " << static_cast<uint32_t>(current_render_intent_) << std::endl;
+ *os << "Need WhiteCompensation: "
+ << (current_render_intent_ == RenderIntent::ENHANCE && HasWhiteCompensation()) << std::endl;
+ *os << "Need SaturationCompensation: "
+ << (current_render_intent_ == RenderIntent::ENHANCE && HasSaturationCompensation())
+ << std::endl;
+
*os << "current transform: ";
+ double color_matrix[kColorTransformMatrixCount] = {0};
+
+ CopyColorTransformMatrix(PickTransferMatrix(), color_matrix);
+
for (uint32_t i = 0; i < kColorTransformMatrixCount; i++) {
if (i % 4 == 0) {
*os << std::endl;
}
- *os << std::fixed << std::setprecision(2) << std::setw(6) << std::setfill(' ')
- << color_matrix_[i] << " ";
+ *os << std::fixed << std::setprecision(4) << std::setw(8) << std::setfill(' ')
+ << color_matrix[i] << " ";
}
*os << std::endl;
}
@@ -462,6 +683,7 @@
uint32_t color_mode_count = 0;
display_intf_->GetColorModeCount(&color_mode_count);
+ hdr_largest_layer_px_ = 0.0f;
// Add one layer for fb target
// TODO(user): Add blit target layers
@@ -486,6 +708,10 @@
layer->flags.skip = true;
}
+ if (hwc_layer->IsColorTransformSet()) {
+ layer->flags.skip = true;
+ }
+
// set default composition as GPU for SDM
layer->composition = kCompositionGPU;
@@ -504,6 +730,8 @@
if (handle->buffer_type == BUFFER_TYPE_VIDEO) {
layer_stack_.flags.video_present = true;
is_video = true;
+ } else if (layer->transform.rotation != 0.0f) {
+ layer->flags.skip = true;
}
// TZ Protected Buffer - L1
// Gralloc Usage Protected Buffer - L3 - which needs to be treated as Secure & avoid fallback
@@ -544,9 +772,15 @@
// In such cases, we should not handle HDR as the HDR mode isn't applied
layer->input_buffer.flags.hdr = true;
layer_stack_.flags.hdr_present = true;
+
+ // HDR area
+ auto hdr_layer_area = (layer->dst_rect.right - layer->dst_rect.left) *
+ (layer->dst_rect.bottom - layer->dst_rect.top);
+ hdr_largest_layer_px_ = std::max(hdr_largest_layer_px_, hdr_layer_area);
}
- if (hwc_layer->IsNonIntegralSourceCrop() && !is_secure && !is_video) {
+ if (hwc_layer->IsNonIntegralSourceCrop() && !is_secure && !layer->flags.solid_fill &&
+ !is_video) {
layer->flags.skip = true;
}
@@ -573,8 +807,8 @@
layer_buffer->release_fence_fd = -1;
layer->src_rect.left = 0;
layer->src_rect.top = 0;
- layer->src_rect.right = layer_buffer->width;
- layer->src_rect.bottom = layer_buffer->height;
+ layer->src_rect.right = FLOAT(layer_buffer->width);
+ layer->src_rect.bottom = FLOAT(layer_buffer->height);
}
if (hwc_layer->HasMetaDataRefreshRate() && layer->frame_rate > metadata_refresh_rate_) {
@@ -1045,11 +1279,12 @@
break;
}
case kThermalEvent:
- case kIdlePowerCollapse:
case kPanelDeadEvent: {
SEQUENCE_WAIT_SCOPE_LOCK(HWCSession::locker_[type_]);
validated_ = false;
} break;
+ case kIdlePowerCollapse:
+ break;
default:
DLOGW("Unknown event: %d", event);
break;
@@ -2167,6 +2402,31 @@
return ((*out_num_types > 0) ? HWC2::Error::HasChanges : HWC2::Error::None);
}
+HWC2::Error HWCDisplay::SetDisplayedContentSamplingEnabledVndService(bool enabled) {
+ return HWC2::Error::Unsupported;
+}
+
+HWC2::Error HWCDisplay::SetDisplayedContentSamplingEnabled(int32_t enabled,
+ uint8_t component_mask, uint64_t max_frames) {
+
+ DLOGV("Request to start/stop histogram thread not supported on this display");
+ return HWC2::Error::Unsupported;
+}
+
+HWC2::Error HWCDisplay::GetDisplayedContentSamplingAttributes(int32_t* format,
+ int32_t* dataspace,
+ uint8_t* supported_components) {
+ return HWC2::Error::Unsupported;
+}
+
+HWC2::Error HWCDisplay::GetDisplayedContentSample(uint64_t max_frames,
+ uint64_t timestamp,
+ uint64_t* numFrames,
+ int32_t samples_size[NUM_HISTOGRAM_COLOR_COMPONENTS],
+ uint64_t* samples[NUM_HISTOGRAM_COLOR_COMPONENTS]) {
+ return HWC2::Error::Unsupported;
+}
+
void HWCDisplay::UpdateRefreshRate() {
for (auto hwc_layer : layer_set_) {
if (hwc_layer->HasMetaDataRefreshRate()) {
diff --git a/sdm/libs/hwc2/hwc_display.h b/sdm/libs/hwc2/hwc_display.h
index becc31a..1ff5504 100644
--- a/sdm/libs/hwc2/hwc_display.h
+++ b/sdm/libs/hwc2/hwc_display.h
@@ -37,6 +37,7 @@
#include "hwc_buffer_allocator.h"
#include "hwc_callbacks.h"
#include "hwc_layers.h"
+#include "histogram_collector.h"
using android::hardware::graphics::common::V1_1::ColorMode;
using android::hardware::graphics::common::V1_1::Dataspace;
@@ -46,6 +47,7 @@
class BlitEngine;
class HWCToneMapper;
+constexpr uint32_t kColorTransformMatrixCount = 16;
// Subclasses set this to their type. This has to be different from DisplayType.
// This is to avoid RTTI and dynamic_cast
@@ -56,6 +58,103 @@
DISPLAY_CLASS_NULL
};
+class HWCColorMatrix {
+ public:
+ HWCColorMatrix(const string &values) : key_values_(values){};
+ virtual ~HWCColorMatrix() = default;
+ virtual HWC2::Error SetEnabled(bool enabled);
+ bool GetEnabled() const { return enabled_; }
+ // Apply effect to input matrix
+ virtual void ApplyToMatrix(double *in) = 0;
+ bool ParseFloatValueByCommas(const string &values, uint32_t length,
+ std::vector<float> &elements) const;
+
+ protected:
+ bool enabled_ = false;
+ const string key_values_;
+};
+
+class WhiteCompensation : public HWCColorMatrix {
+ public:
+ WhiteCompensation(const string &values) : HWCColorMatrix(values){};
+ int GetCompensatedRed() const { return compensated_red_; }
+ int GetCompensatedGreen() const { return compensated_green_; }
+ int GetCompensatedBlue() const { return compensated_blue_; }
+ HWC2::Error SetEnabled(bool enabled) override;
+ /*
+ * Transform matrix is 4 x 4
+ * |r.r r.g r.b 0|
+ * |g.r g.g g.b 0|
+ * |b.r b.g b.b 0|
+ * |T.r T.g T.b 1|
+ * R_out = R_in * r.r + G_in * g.r + B_in * b.r + Tr
+ * G_out = R_in * r.g + G_in * g.g + B_in * b.g + Tg
+ * B_out = R_in * r.b + G_in * g.b + B_in * b.b + Tb
+ *
+ * Cr, Cg, Cb for white point compensation
+ * |r.r*Cr r.g*Cg r.b*Cb 0|
+ * |g.r*Cr g.g*Cg g.b*Cb 0|
+ * |b.r*Cr b.g*Cg b.b*Cb 0|
+ * |T.r*Cr T.g*Cg T.b*Cb 1|
+ * R_out = R_in * r.r * Cr + G_in * g.r * Cr + B_in * b.r * Cr + Tr * Cr
+ * G_out = R_in * r.g * Cg + G_in * g.g * Cg + B_in * b.g * Cg + Tg * Cg
+ * B_out = R_in * r.b * Cb + G_in * g.b * Cb + B_in * b.b * Cb + Tb * Cb
+ */
+ void ApplyToMatrix(double *in) override;
+
+ private:
+ static constexpr int kCompensatedMaxRGB = 255;
+ static constexpr int kCompensatedMinRGB = 230;
+ static constexpr int kNumOfCompensationData = 3;
+ int compensated_red_ = kCompensatedMaxRGB;
+ int compensated_green_ = kCompensatedMaxRGB;
+ int compensated_blue_ = kCompensatedMaxRGB;
+
+ double compensated_red_ratio_ = 1.0;
+ double compensated_green_ratio_ = 1.0;
+ double compensated_blue_ratio_ = 1.0;
+
+ static constexpr int kCoefficientElements = 9;
+ float white_compensated_Coefficients_[kCoefficientElements] = {0.0, 1.0, 0.0, 0.0, 1.0,
+ 0.0, 0.0, 1.0, 0.0};
+ bool ConfigCoefficients();
+ bool ParseWhitePointCalibrationData();
+ inline static constexpr bool CheckCompensatedRGB(int value) {
+ return ((value >= kCompensatedMinRGB) && (value <= kCompensatedMaxRGB));
+ }
+ void CalculateRGBRatio();
+};
+
+class SaturationCompensation : public HWCColorMatrix {
+ public:
+ SaturationCompensation(const string &values) : HWCColorMatrix(values){};
+ HWC2::Error SetEnabled(bool enabled) override;
+ /* Saturated matrix is 4 x 4
+ * | s0 s1 s2 s3|
+ * | s4 s5 s6 s7|
+ * | s8 s9 s10 s11|
+ * | s12 s13 s14 s15|
+ * Transform matrix is 4 x 4
+ * |a0 a1 a2 a3|
+ * |a4 a5 a6 a7|
+ * |a8 a9 a10 a11|
+ * |a12 a13 a14 a15|
+ *
+ * Saturated matrix[] X Transform matrix[]
+ */
+ void ApplyToMatrix(double *in) override;
+
+ private:
+ static constexpr int kSaturationParameters = 9;
+ static constexpr int kNumOfRows = 4;
+ static constexpr int kColumnsPerRow = 4;
+ static_assert(kNumOfRows * kColumnsPerRow == kColorTransformMatrixCount,
+ "Rows x Columns should be equal to matrix count");
+ float saturated_matrix_[kColorTransformMatrixCount] = {1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0};
+ bool ConfigSaturationParameter();
+};
+
class HWCColorMode {
public:
explicit HWCColorMode(DisplayInterface *display_intf);
@@ -73,9 +172,9 @@
HWC2::Error RestoreColorTransform();
PrimariesTransfer GetWorkingColorSpace();
ColorMode GetCurrentColorMode() { return current_color_mode_; }
+ HWC2::Error SetWhiteCompensation(bool enabled);
private:
- static const uint32_t kColorTransformMatrixCount = 16;
void PopulateColorModes();
void FindRenderIntent(const ColorMode &mode, const std::string &mode_string);
template <class T>
@@ -97,6 +196,19 @@
0.0, 1.0, 0.0, 0.0, \
0.0, 0.0, 1.0, 0.0, \
0.0, 0.0, 0.0, 1.0 };
+ void InitColorCompensation();
+ std::unique_ptr<WhiteCompensation> adaptive_white_;
+ std::unique_ptr<SaturationCompensation> adaptive_saturation_;
+ double compensated_color_matrix_[kColorTransformMatrixCount] = { 1.0, 0.0, 0.0, 0.0, \
+ 0.0, 1.0, 0.0, 0.0, \
+ 0.0, 0.0, 1.0, 0.0, \
+ 0.0, 0.0, 0.0, 1.0 };
+ bool HasWhiteCompensation() { return (adaptive_white_ && adaptive_white_->GetEnabled()); }
+ bool HasSaturationCompensation() {
+ return (adaptive_saturation_ && adaptive_saturation_->GetEnabled());
+ }
+
+ const double *PickTransferMatrix();
};
class HWCDisplay : public DisplayEventHandler {
@@ -205,6 +317,7 @@
virtual HWC2::Error SetClientTarget(buffer_handle_t target, int32_t acquire_fence,
int32_t dataspace, hwc_region_t damage);
virtual HWC2::Error SetColorMode(ColorMode mode) { return HWC2::Error::Unsupported; }
+ virtual HWC2::Error SetWhiteCompensation(bool enabled) { return HWC2::Error::Unsupported; }
virtual HWC2::Error SetColorModeWithRenderIntent(ColorMode mode, RenderIntent intent) {
return HWC2::Error::Unsupported;
}
@@ -262,10 +375,20 @@
return HWC2::Error::Unsupported;
}
+ virtual HWC2::Error SetDisplayedContentSamplingEnabledVndService(bool enabled);
+ virtual HWC2::Error SetDisplayedContentSamplingEnabled(int32_t enabled, uint8_t component_mask, uint64_t max_frames);
+ virtual HWC2::Error GetDisplayedContentSamplingAttributes(int32_t* format,
+ int32_t* dataspace,
+ uint8_t* supported_components);
+ virtual HWC2::Error GetDisplayedContentSample(uint64_t max_frames,
+ uint64_t timestamp,
+ uint64_t* numFrames,
+ int32_t samples_size[NUM_HISTOGRAM_COLOR_COMPONENTS],
+ uint64_t* samples[NUM_HISTOGRAM_COLOR_COMPONENTS]);
+
protected:
// Maximum number of layers supported by display manager.
static const uint32_t kMaxLayerCount = 32;
-
HWCDisplay(CoreInterface *core_intf, HWCCallbacks *callbacks, DisplayType type, hwc2_display_t id,
bool needs_blit, qService::QService *qservice, DisplayClass display_class,
BufferAllocator *buffer_allocator);
@@ -347,6 +470,7 @@
ColorMode current_color_mode_ = ColorMode::NATIVE;
ColorPrimaries working_primaries_ = ColorPrimaries_BT709_5;
GammaTransfer working_transfer_ = Transfer_sRGB;
+ float hdr_largest_layer_px_ = 0.0f;
private:
void DumpInputBuffers(void);
diff --git a/sdm/libs/hwc2/hwc_display_external.cpp b/sdm/libs/hwc2/hwc_display_external.cpp
index 21a9284..90dd214 100644
--- a/sdm/libs/hwc2/hwc_display_external.cpp
+++ b/sdm/libs/hwc2/hwc_display_external.cpp
@@ -166,8 +166,8 @@
return;
}
- uint32_t new_mixer_width = UINT32(mixer_width * FLOAT(1.0f - width_ratio));
- uint32_t new_mixer_height = UINT32(mixer_height * FLOAT(1.0f - height_ratio));
+ uint32_t new_mixer_width = UINT32(FLOAT(mixer_width) * (1.0f - width_ratio));
+ uint32_t new_mixer_height = UINT32(FLOAT(mixer_height) * (1.0f - height_ratio));
int x_offset = INT((FLOAT(mixer_width) * width_ratio) / 2.0f);
int y_offset = INT((FLOAT(mixer_height) * height_ratio) / 2.0f);
diff --git a/sdm/libs/hwc2/hwc_display_external_test.cpp b/sdm/libs/hwc2/hwc_display_external_test.cpp
index 8551854..a6fcb08 100644
--- a/sdm/libs/hwc2/hwc_display_external_test.cpp
+++ b/sdm/libs/hwc2/hwc_display_external_test.cpp
@@ -625,7 +625,8 @@
GetDisplayAttributesForConfig(INT32(active_config), &var_info);
layer->flags.updating = 1;
- layer->src_rect = LayerRect(0, 0, var_info.x_pixels, var_info.y_pixels);
+ layer->src_rect = LayerRect(0, 0, FLOAT(var_info.x_pixels),
+ FLOAT(var_info.y_pixels));
layer->dst_rect = layer->src_rect;
layer->frame_rate = var_info.fps;
layer->blending = kBlendingPremultiplied;
diff --git a/sdm/libs/hwc2/hwc_display_primary.cpp b/sdm/libs/hwc2/hwc_display_primary.cpp
index 1c548b6..7b01563 100644
--- a/sdm/libs/hwc2/hwc_display_primary.cpp
+++ b/sdm/libs/hwc2/hwc_display_primary.cpp
@@ -167,6 +167,8 @@
}
int HWCDisplayPrimary::Deinit() {
+ histogram.stop();
+
int status = HWCDisplay::Deinit();
if (status) {
return status;
@@ -177,6 +179,10 @@
return 0;
}
+std::string HWCDisplayPrimary::Dump() {
+ return HWCDisplay::Dump() + histogram.Dump();
+}
+
void HWCDisplayPrimary::ProcessBootAnimCompleted() {
uint32_t numBootUpLayers = 0;
// TODO(user): Remove this hack
@@ -310,6 +316,33 @@
}
}
+ if (CC_UNLIKELY(!has_init_light_server_)) {
+ using ILight = ::hardware::google::light::V1_0::ILight;
+ vendor_ILight_ = ILight::getService();
+ if (vendor_ILight_ != nullptr) {
+ vendor_ILight_->setHbm(false);
+ } else {
+ DLOGE("failed to get vendor light service");
+ }
+
+ uint32_t panel_x, panel_y;
+ GetPanelResolution(&panel_x, &panel_y);
+ hbm_threshold_px_ = float(panel_x * panel_y) * hbm_threshold_pct_;
+ DLOGI("Configure hbm_threshold_px_ to %f", hbm_threshold_px_);
+
+ has_init_light_server_ = true;
+ }
+
+ const bool enable_hbm(hdr_largest_layer_px_ > hbm_threshold_px_);
+ if (high_brightness_mode_ != enable_hbm && vendor_ILight_ != nullptr) {
+ using ::android::hardware::light::V2_0::Status;
+ if (Status::SUCCESS == vendor_ILight_->setHbm(enable_hbm)) {
+ high_brightness_mode_ = enable_hbm;
+ } else {
+ DLOGE("failed to setHbm to %d", enable_hbm);
+ }
+ }
+
CloseFd(&output_buffer_.acquire_fence_fd);
pending_commit_ = false;
return status;
@@ -339,6 +372,18 @@
return SetColorModeWithRenderIntent(mode, RenderIntent::COLORIMETRIC);
}
+HWC2::Error HWCDisplayPrimary::SetWhiteCompensation(bool enabled) {
+ auto status = color_mode_->SetWhiteCompensation(enabled);
+ if (status != HWC2::Error::None) {
+ DLOGE("failed for SetWhiteCompensation to %d", enabled);
+ return status;
+ }
+
+ callbacks_->Refresh(HWC_DISPLAY_PRIMARY);
+
+ return status;
+}
+
HWC2::Error HWCDisplayPrimary::SetColorModeWithRenderIntent(ColorMode mode, RenderIntent intent) {
auto status = color_mode_->SetColorModeWithRenderIntent(mode, intent);
if (status != HWC2::Error::None) {
@@ -787,6 +832,55 @@
return error;
}
+HWC2::Error HWCDisplayPrimary::SetDisplayedContentSamplingEnabledVndService(bool enabled) {
+ std::unique_lock<decltype(sampling_mutex)> lk(sampling_mutex);
+ vndservice_sampling_vote = enabled;
+ if (api_sampling_vote || vndservice_sampling_vote) {
+ histogram.start();
+ } else {
+ histogram.stop();
+ }
+ return HWC2::Error::None;
+}
+
+HWC2::Error HWCDisplayPrimary::SetDisplayedContentSamplingEnabled(int32_t enabled, uint8_t component_mask, uint64_t max_frames) {
+ if ((enabled != HWC2_DISPLAYED_CONTENT_SAMPLING_ENABLE) &&
+ (enabled != HWC2_DISPLAYED_CONTENT_SAMPLING_DISABLE))
+ return HWC2::Error::BadParameter;
+
+ std::unique_lock<decltype(sampling_mutex)> lk(sampling_mutex);
+ if (enabled == HWC2_DISPLAYED_CONTENT_SAMPLING_ENABLE) {
+ api_sampling_vote = true;
+ } else {
+ api_sampling_vote = false;
+ }
+
+ auto start = api_sampling_vote || vndservice_sampling_vote;
+ if (start && max_frames == 0) {
+ histogram.start();
+ } else if (start) {
+ histogram.start(max_frames);
+ } else {
+ histogram.stop();
+ }
+ return HWC2::Error::None;
+}
+
+HWC2::Error HWCDisplayPrimary::GetDisplayedContentSamplingAttributes(int32_t* format,
+ int32_t* dataspace,
+ uint8_t* supported_components) {
+ return histogram.getAttributes(format, dataspace, supported_components);
+}
+
+HWC2::Error HWCDisplayPrimary::GetDisplayedContentSample(uint64_t max_frames,
+ uint64_t timestamp,
+ uint64_t* numFrames,
+ int32_t samples_size[NUM_HISTOGRAM_COLOR_COMPONENTS],
+ uint64_t* samples[NUM_HISTOGRAM_COLOR_COMPONENTS])
+{
+ histogram.collect(max_frames, timestamp, samples_size, samples, numFrames);
+ return HWC2::Error::None;
+}
DisplayError HWCDisplayPrimary::SetMixerResolution(uint32_t width, uint32_t height) {
DisplayError error = display_intf_->SetMixerResolution(width, height);
diff --git a/sdm/libs/hwc2/hwc_display_primary.h b/sdm/libs/hwc2/hwc_display_primary.h
index f802879..4295f52 100644
--- a/sdm/libs/hwc2/hwc_display_primary.h
+++ b/sdm/libs/hwc2/hwc_display_primary.h
@@ -30,7 +30,10 @@
#ifndef __HWC_DISPLAY_PRIMARY_H__
#define __HWC_DISPLAY_PRIMARY_H__
+#include <hardware/google/light/1.0/ILight.h>
+#include <limits>
#include <string>
+#include <mutex>
#include "cpuhint.h"
#include "hwc_display.h"
@@ -53,11 +56,12 @@
HWCDisplay **hwc_display);
static void Destroy(HWCDisplay *hwc_display);
virtual int Init();
- virtual int Deinit();
+ virtual int Deinit() override;
virtual HWC2::Error Validate(uint32_t *out_num_types, uint32_t *out_num_requests);
virtual HWC2::Error Present(int32_t *out_retire_fence);
virtual HWC2::Error GetColorModes(uint32_t *out_num_modes, ColorMode *out_modes);
virtual HWC2::Error SetColorMode(ColorMode mode);
+ virtual HWC2::Error SetWhiteCompensation(bool enabled);
virtual HWC2::Error GetRenderIntents(ColorMode mode, uint32_t *out_num_intents,
RenderIntent *out_intents);
virtual HWC2::Error SetColorModeWithRenderIntent(ColorMode mode, RenderIntent intent);
@@ -81,6 +85,16 @@
virtual HWC2::Error ControlIdlePowerCollapse(bool enable, bool synchronous);
virtual DisplayError TeardownConcurrentWriteback(void);
+ virtual HWC2::Error SetDisplayedContentSamplingEnabledVndService(bool enabled);
+ virtual HWC2::Error SetDisplayedContentSamplingEnabled(int32_t enabled, uint8_t component_mask, uint64_t max_frames) override;
+ virtual HWC2::Error GetDisplayedContentSamplingAttributes(int32_t* format, int32_t* dataspace,
+ uint8_t* supported_components) override;
+ virtual HWC2::Error GetDisplayedContentSample(uint64_t max_frames,
+ uint64_t timestamp, uint64_t* numFrames,
+ int32_t samples_size[NUM_HISTOGRAM_COLOR_COMPONENTS],
+ uint64_t* samples[NUM_HISTOGRAM_COLOR_COMPONENTS]) override;
+ std::string Dump() override;
+
private:
HWCDisplayPrimary(CoreInterface *core_intf, BufferAllocator *buffer_allocator,
HWCCallbacks *callbacks, qService::QService *qservice);
@@ -123,6 +137,20 @@
BufferInfo output_buffer_info_ = {};
void *output_buffer_base_ = nullptr;
int default_mode_status_ = 0;
+
+ // Members for HBM feature
+ static constexpr float hbm_threshold_pct_ = 0.5f;
+ float hbm_threshold_px_ = std::numeric_limits<float>::max();
+ android::sp<hardware::google::light::V1_0::ILight> vendor_ILight_ = nullptr;
+ bool has_init_light_server_ = false;
+ bool high_brightness_mode_ = false;
+
+ // Members for Color sampling feature
+ histogram::HistogramCollector histogram;
+ std::mutex sampling_mutex;
+ bool api_sampling_vote = false;
+ bool vndservice_sampling_vote = false;
+
// PMIC interface to notify secure display start/end
PMICInterface *pmic_intf_ = nullptr;
bool pmic_notification_pending_ = false;
diff --git a/sdm/libs/hwc2/hwc_layers.cpp b/sdm/libs/hwc2/hwc_layers.cpp
index 028a3bc..dd287b1 100644
--- a/sdm/libs/hwc2/hwc_layers.cpp
+++ b/sdm/libs/hwc2/hwc_layers.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2018, 2020, The Linux Foundation. All rights reserved.
* Not a Contribution.
*
* Copyright 2015 The Android Open Source Project
@@ -110,9 +110,6 @@
case HAL_DATASPACE_TRANSFER_HLG:
*gamma_transfer = Transfer_HLG;
break;
- case HAL_DATASPACE_TRANSFER_LINEAR:
- *gamma_transfer = Transfer_Linear;
- break;
case HAL_DATASPACE_TRANSFER_GAMMA2_2:
*gamma_transfer = Transfer_Gamma2_2;
break;
@@ -148,6 +145,25 @@
}
}
+// Map the known color modes to dataspace.
+int32_t GetDataspace(ColorMode mode) {
+ switch (mode) {
+ case ColorMode::SRGB:
+ case ColorMode::NATIVE:
+ return HAL_DATASPACE_V0_SRGB;
+ case ColorMode::DCI_P3:
+ return HAL_DATASPACE_DCI_P3;
+ case ColorMode::DISPLAY_P3:
+ return HAL_DATASPACE_DISPLAY_P3;
+ case ColorMode::BT2100_PQ:
+ return HAL_DATASPACE_BT2020_PQ;
+ case ColorMode::BT2100_HLG:
+ return HAL_DATASPACE_BT2020_HLG;
+ default:
+ return HAL_DATASPACE_UNKNOWN;
+ }
+}
+
// Retrieve ColorMetaData from android_data_space_t (STANDARD|TRANSFER|RANGE)
bool GetSDMColorSpace(const int32_t &dataspace, ColorMetaData *color_metadata) {
bool valid = false;
@@ -520,6 +536,12 @@
return HWC2::Error::None;
}
+HWC2::Error HWCLayer::SetLayerColorTransform(const float *matrix) {
+ color_transform_matrix_set_ =
+ (std::memcmp(matrix, kIdentityMatrix, sizeof(kIdentityMatrix)) != 0);
+ return HWC2::Error::None;
+}
+
HWC2::Error HWCLayer::SetLayerPerFrameMetadata(uint32_t num_elements,
const PerFrameMetadataKey *keys,
const float *metadata) {
@@ -677,6 +699,7 @@
format = kFormatYCrCb420PlanarStride16;
break;
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ case HAL_PIXEL_FORMAT_NV21_ZSL:
format = kFormatYCrCb420SemiPlanar;
break;
case HAL_PIXEL_FORMAT_YCbCr_420_SP:
@@ -869,12 +892,24 @@
working_primaries <= ColorPrimaries_BT601_6_525) {
return true;
}
+
+ if ((working_primaries == ColorPrimaries_BT709_5 || working_primaries == ColorPrimaries_DCIP3) &&
+ layer_->input_buffer.flags.video &&
+ IsBT2020(layer_->input_buffer.color_metadata.colorPrimaries) &&
+ layer_->input_buffer.color_metadata.transfer == Transfer_SMPTE_170M) {
+ return true;
+ }
+
+ if (layer_->input_buffer.flags.secure)
+ return true;
+
return false;
}
bool HWCLayer::ValidateAndSetCSC() {
if (client_requested_ != HWC2::Composition::Device &&
- client_requested_ != HWC2::Composition::Cursor) {
+ client_requested_ != HWC2::Composition::Cursor &&
+ client_requested_ != HWC2::Composition::SolidColor) {
// Check the layers which are configured to Device
return true;
}
@@ -900,7 +935,7 @@
use_color_metadata = true;
}
- if (use_color_metadata) {
+ if (use_color_metadata && client_requested_ != HWC2::Composition::SolidColor) {
const private_handle_t *handle =
reinterpret_cast<const private_handle_t *>(layer_buffer->buffer_id);
if (sdm::SetCSC(handle, &layer_buffer->color_metadata) != kErrorNone) {
diff --git a/sdm/libs/hwc2/hwc_layers.h b/sdm/libs/hwc2/hwc_layers.h
index f485075..ce3bd1f 100644
--- a/sdm/libs/hwc2/hwc_layers.h
+++ b/sdm/libs/hwc2/hwc_layers.h
@@ -31,6 +31,7 @@
#include <hardware/hwcomposer2.h>
#undef HWC2_INCLUDE_STRINGIFICATION
#undef HWC2_USE_CPP11
+#include <android/hardware/graphics/common/1.1/types.h>
#include <android/hardware/graphics/composer/2.2/IComposerClient.h>
#include <deque>
#include <map>
@@ -38,6 +39,7 @@
#include "core/buffer_allocator.h"
#include "hwc_buffer_allocator.h"
+using android::hardware::graphics::common::V1_1::ColorMode;
using PerFrameMetadataKey =
android::hardware::graphics::composer::V2_2::IComposerClient::PerFrameMetadataKey;
@@ -49,6 +51,8 @@
void GetRange(const int32_t &dataspace, ColorRange *color_range);
bool GetSDMColorSpace(const int32_t &dataspace, ColorMetaData *color_metadata);
bool IsBT2020(const ColorPrimaries &color_primary);
+int32_t GetDataspace(ColorMode mode);
+
enum GeometryChanges {
kNone = 0x000,
kBlendMode = 0x001,
@@ -87,6 +91,7 @@
HWC2::Error SetLayerPerFrameMetadata(uint32_t num_elements, const PerFrameMetadataKey *keys,
const float *metadata);
HWC2::Error SetLayerZOrder(uint32_t z);
+ HWC2::Error SetLayerColorTransform(const float *matrix);
void SetComposition(const LayerComposition &sdm_composition);
HWC2::Composition GetClientRequestedCompositionType() { return client_requested_; }
void UpdateClientCompositionType(HWC2::Composition type) { client_requested_ = type; }
@@ -108,6 +113,7 @@
bool IsNonIntegralSourceCrop() { return non_integral_source_crop_; }
bool HasMetaDataRefreshRate() { return has_metadata_refresh_rate_; }
void SetPartialUpdate(bool enabled) { partial_update_enabled_ = enabled; }
+ bool IsColorTransformSet() const { return color_transform_matrix_set_; }
private:
Layer *layer_ = nullptr;
@@ -127,6 +133,7 @@
bool has_metadata_refresh_rate_ = false;
bool partial_update_enabled_ = false;
bool surface_updated_ = true;
+ bool color_transform_matrix_set_ = false;
// Composition requested by client(SF)
HWC2::Composition client_requested_ = HWC2::Composition::Device;
diff --git a/sdm/libs/hwc2/hwc_session.cpp b/sdm/libs/hwc2/hwc_session.cpp
index c758f61..b35beb3 100644
--- a/sdm/libs/hwc2/hwc_session.cpp
+++ b/sdm/libs/hwc2/hwc_session.cpp
@@ -53,6 +53,10 @@
#define HWC_UEVENT_GRAPHICS_FB0 "change@/devices/virtual/graphics/fb0"
#define HWC_UEVENT_DRM_EXT_HOTPLUG "mdss_mdp/drm/card"
+#define MAX_BRIGHTNESS 255
+#define BRIGHTNESS_FILE1 "/sys/class/leds/lcd-backlight/brightness"
+#define BRIGHTNESS_FILE2 "/sys/class/backlight/panel0-backlight/brightness"
+
static sdm::HWCSession::HWCModuleMethods g_hwc_module_methods;
hwc_module_t HAL_MODULE_INFO_SYM = {
@@ -75,25 +79,6 @@
Locker HWCSession::locker_[HWC_NUM_DISPLAY_TYPES];
static const int kSolidFillDelay = 100 * 1000;
-// Map the known color modes to dataspace.
-static int32_t GetDataspace(ColorMode mode) {
- switch (mode) {
- case ColorMode::SRGB:
- case ColorMode::NATIVE:
- return HAL_DATASPACE_V0_SRGB;
- case ColorMode::DCI_P3:
- return HAL_DATASPACE_DCI_P3;
- case ColorMode::DISPLAY_P3:
- return HAL_DATASPACE_DISPLAY_P3;
- case ColorMode::BT2100_PQ:
- return HAL_DATASPACE_BT2020_PQ;
- case ColorMode::BT2100_HLG:
- return HAL_DATASPACE_BT2020_HLG;
- default:
- return HAL_DATASPACE_UNKNOWN;
- }
-}
-
void HWCUEvent::UEventThread(HWCUEvent *hwc_uevent) {
const char *uevent_thread_name = "HWC_UeventThread";
@@ -230,6 +215,17 @@
}
}
+ char const *brightness_file;
+ if (access(BRIGHTNESS_FILE1, F_OK) == 0) {
+ brightness_file = BRIGHTNESS_FILE1;
+ } else {
+ brightness_file = BRIGHTNESS_FILE2;
+ }
+ brightness_fd_ = open(brightness_file, O_WRONLY);
+ if (brightness_fd_ == -1) {
+ DLOGW("Unable to open brightness file: [%d] %s", errno, strerror(errno));
+ }
+
return 0;
}
@@ -259,6 +255,8 @@
DLOGE("Display core de-initialization failed. Error = %d", error);
}
+ close(brightness_fd_);
+
return 0;
}
@@ -323,6 +321,13 @@
*outCount = count;
}
+int32_t HWCSession::GetDisplayBrightnessSupport(hwc2_device_t *device, hwc2_display_t display,
+ bool *out_support) {
+ HWCSession *hwc_session = static_cast<HWCSession *>(device);
+ *out_support = display == HWC_DISPLAY_PRIMARY && hwc_session->brightness_fd_ != -1;
+ return INT32(HWC2::Error::None);
+}
+
template <typename PFN, typename T>
static hwc2_function_pointer_t AsFP(T function) {
static_assert(std::is_same<PFN, T>::value, "Incompatible function pointer");
@@ -526,6 +531,39 @@
num_elements, keys, metadata);
}
+static int32_t SetDisplayedContentSamplingEnabled(hwc2_device_t* device,
+ hwc2_display_t display,
+ int32_t enabled, uint8_t component_mask,
+ uint64_t max_frames) {
+ static constexpr int32_t validComponentMask =
+ HWC2_FORMAT_COMPONENT_0 | HWC2_FORMAT_COMPONENT_1 |
+ HWC2_FORMAT_COMPONENT_2 | HWC2_FORMAT_COMPONENT_3;
+ if (component_mask & ~validComponentMask) return HWC2_ERROR_BAD_PARAMETER;
+ return HWCSession::CallDisplayFunction(device, display,
+ &HWCDisplay::SetDisplayedContentSamplingEnabled,
+ enabled, component_mask, max_frames);
+}
+
+static int32_t GetDisplayedContentSamplingAttributes(hwc2_device_t* device,
+ hwc2_display_t display,
+ int32_t* format,
+ int32_t* dataspace,
+ uint8_t* supported_components) {
+ return HWCSession::CallDisplayFunction(device, display,
+ &HWCDisplay::GetDisplayedContentSamplingAttributes,
+ format, dataspace, supported_components);
+}
+
+static int32_t GetDisplayedContentSample(
+ hwc2_device_t* device, hwc2_display_t display, uint64_t max_frames, uint64_t timestamp,
+ uint64_t* numFrames,
+ int32_t samples_size[NUM_HISTOGRAM_COLOR_COMPONENTS],
+ uint64_t* samples[NUM_HISTOGRAM_COLOR_COMPONENTS]) {
+
+ return HWCSession::CallDisplayFunction(device, display, &HWCDisplay::GetDisplayedContentSample,
+ max_frames, timestamp, numFrames, samples_size, samples);
+}
+
static int32_t GetDisplayAttribute(hwc2_device_t *device, hwc2_display_t display,
hwc2_config_t config, int32_t int_attribute,
int32_t *out_value) {
@@ -812,6 +850,12 @@
return HWCSession::CallDisplayFunction(device, display, &HWCDisplay::SetLayerZOrder, layer, z);
}
+static int32_t SetLayerColorTransform(hwc2_device_t *device, hwc2_display_t display,
+ hwc2_layer_t layer, const float *matrix) {
+ return HWCSession::CallLayerFunction(device, display, layer, &HWCLayer::SetLayerColorTransform,
+ matrix);
+}
+
int32_t HWCSession::SetOutputBuffer(hwc2_device_t *device, hwc2_display_t display,
buffer_handle_t buffer, int32_t releaseFence) {
if (!device) {
@@ -911,6 +955,86 @@
return INT32(status);
}
+int32_t HWCSession::GetDisplayCapabilities(hwc2_device_t* device, hwc2_display_t display,
+ uint32_t* outNumCapabilities, uint32_t* outCapabilities) {
+ if (outNumCapabilities == nullptr) {
+ return INT32(HWC2::Error::None);
+ }
+
+ bool brightness_support = false;
+ auto status = GetDisplayBrightnessSupport(device, display, &brightness_support);
+ if (status != HWC2_ERROR_NONE) {
+ DLOGE("Failed to get display brightness support Error = %d", status);
+ return INT32(status);
+ }
+ int doze_support = 0;
+ status = GetDozeSupport(device, display, &doze_support);
+ if (status != HWC2_ERROR_NONE) {
+ DLOGE("Failed to get doze support Error = %d", status);
+ return INT32(status);
+ }
+
+ uint32_t count = 1 + static_cast<uint32_t>(doze_support) + (brightness_support ? 1 : 0);
+ int index = 0;
+ if (outCapabilities != nullptr && (*outNumCapabilities >= count)) {
+ outCapabilities[index++] = HWC2_DISPLAY_CAPABILITY_SKIP_CLIENT_COLOR_TRANSFORM;
+ if (doze_support == 1) {
+ outCapabilities[index++] = HWC2_DISPLAY_CAPABILITY_DOZE;
+ }
+ if (brightness_support) {
+ outCapabilities[index++] = HWC2_DISPLAY_CAPABILITY_BRIGHTNESS;
+ }
+ }
+
+ *outNumCapabilities = count;
+ return INT32(HWC2::Error::None);
+}
+
+int32_t HWCSession::SetDisplayBrightness(hwc2_device_t *device, hwc2_display_t display,
+ float brightness) {
+ bool brightness_support = false;
+ auto status = GetDisplayBrightnessSupport(device, display, &brightness_support);
+ if (status != HWC2_ERROR_NONE) {
+ return INT32(status);
+ }
+ if (!brightness_support) {
+ return HWC2_ERROR_UNSUPPORTED;
+ }
+ int backlight = -1;
+ if (brightness == -1.0f) {
+ backlight = 0;
+ } else if (brightness < 0.0f || brightness > 1.0f) {
+ return INT32(HWC2::Error::BadParameter);
+ } else {
+ // 0 is reserved for "backlight off", so we scale the brightness from 1 to MAX_BRIGHTNESS.
+ backlight = (int) ((MAX_BRIGHTNESS - 1.0f) * brightness + 1.0f);
+ }
+ char buff[20];
+ int n = snprintf(buff, sizeof(buff), "%d\n", backlight);
+ if (n < 0 || n >= sizeof(buff)) {
+ return INT32(HWC2::Error::BadParameter);
+ }
+
+ HWCSession *hwc_session = static_cast<HWCSession *>(device);
+ long error = lseek(hwc_session->brightness_fd_, 0, SEEK_SET);
+ if (error == -1) {
+ DLOGW("Failed to rewind brightness file: [%d] %s", errno, strerror(errno));
+ return INT32(HWC2::Error::NoResources);
+ }
+ error = write(hwc_session->brightness_fd_, buff, (size_t) n);
+ if (error == -1) {
+ DLOGW("Failed to write to brightness file: [%d] %s", errno, strerror(errno));
+ return INT32(HWC2::Error::NoResources);
+ }
+ error = fsync(hwc_session->brightness_fd_);
+ if (error == -1) {
+ DLOGW("Failed to flush brightness file: [%d] %s", errno, strerror(errno));
+ return INT32(HWC2::Error::NoResources);
+ }
+
+ return INT32(HWC2::Error::None);
+}
+
hwc2_function_pointer_t HWCSession::GetFunction(struct hwc2_device *device,
int32_t int_descriptor) {
auto descriptor = static_cast<HWC2::FunctionDescriptor>(int_descriptor);
@@ -994,6 +1118,8 @@
return AsFP<HWC2_PFN_SET_LAYER_VISIBLE_REGION>(SetLayerVisibleRegion);
case HWC2::FunctionDescriptor::SetLayerZOrder:
return AsFP<HWC2_PFN_SET_LAYER_Z_ORDER>(SetLayerZOrder);
+ case HWC2::FunctionDescriptor::SetLayerColorTransform:
+ return AsFP<HWC2_PFN_SET_LAYER_COLOR_TRANSFORM>(SetLayerColorTransform);
case HWC2::FunctionDescriptor::SetOutputBuffer:
return AsFP<HWC2_PFN_SET_OUTPUT_BUFFER>(SetOutputBuffer);
case HWC2::FunctionDescriptor::SetPowerMode:
@@ -1019,6 +1145,16 @@
return AsFP<HWC2_PFN_GET_PER_FRAME_METADATA_KEYS>(GetPerFrameMetadataKeys);
case HWC2::FunctionDescriptor::SetLayerPerFrameMetadata:
return AsFP<HWC2_PFN_SET_LAYER_PER_FRAME_METADATA>(SetLayerPerFrameMetadata);
+ case HWC2::FunctionDescriptor::SetDisplayedContentSamplingEnabled:
+ return AsFP<HWC2_PFN_SET_DISPLAYED_CONTENT_SAMPLING_ENABLED>(SetDisplayedContentSamplingEnabled);
+ case HWC2::FunctionDescriptor::GetDisplayedContentSamplingAttributes:
+ return AsFP<HWC2_PFN_GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES>(GetDisplayedContentSamplingAttributes);
+ case HWC2::FunctionDescriptor::GetDisplayedContentSample:
+ return AsFP<HWC2_PFN_GET_DISPLAYED_CONTENT_SAMPLE>(GetDisplayedContentSample);
+ case HWC2::FunctionDescriptor::GetDisplayCapabilities:
+ return AsFP<HWC2_PFN_GET_DISPLAY_CAPABILITIES>(GetDisplayCapabilities);
+ case HWC2::FunctionDescriptor::SetDisplayBrightness:
+ return AsFP<HWC2_PFN_SET_DISPLAY_BRIGHTNESS>(SetDisplayBrightness);
default:
DLOGD("Unknown/Unimplemented function descriptor: %d (%s)", int_descriptor,
to_string(descriptor).c_str());
@@ -1362,6 +1498,21 @@
status = SetIdlePC(input_parcel);
break;
+ case qService::IQService::SET_COLOR_SAMPLING_ENABLED:
+ if (!input_parcel) {
+ DLOGE("QService command = %d: input_parcel needed.", command);
+ break;
+ }
+ status = setColorSamplingEnabled(input_parcel);
+ break;
+
+ case qService::IQService::SET_WHITE_COMPENSATION:
+ if (!input_parcel) {
+ DLOGE("QService command = %d: input_parcel needed.", command);
+ break;
+ }
+ status = SetWhiteCompensation(input_parcel);
+ break;
default:
DLOGW("QService command = %d is not supported.", command);
break;
@@ -1374,6 +1525,25 @@
return is_composer_up_;
}
+android::status_t HWCSession::setColorSamplingEnabled(const android::Parcel* input_parcel)
+{
+ int dpy = input_parcel->readInt32();
+ int enabled_cmd = input_parcel->readInt32();
+ if (dpy < HWC_DISPLAY_PRIMARY || dpy >= HWC_NUM_DISPLAY_TYPES ||
+ enabled_cmd < 0 || enabled_cmd > 1) {
+ return android::BAD_VALUE;
+ }
+
+ SEQUENCE_WAIT_SCOPE_LOCK(locker_[dpy]);
+ if (!hwc_display_[dpy]) {
+ DLOGW("No display id %i active to enable histogram event", dpy);
+ return android::BAD_VALUE;
+ }
+
+ auto error = hwc_display_[dpy]->SetDisplayedContentSamplingEnabledVndService(enabled_cmd);
+ return (error == HWC2::Error::None) ? android::OK : android::BAD_VALUE;
+}
+
android::status_t HWCSession::HandleGetDisplayAttributesForConfig(const android::Parcel
*input_parcel,
android::Parcel *output_parcel) {
@@ -1558,6 +1728,18 @@
return 0;
}
+android::status_t HWCSession::SetWhiteCompensation(const android::Parcel *input_parcel) {
+ auto display = static_cast<hwc2_display_t>(input_parcel->readInt32());
+ auto enabled = static_cast<bool>(input_parcel->readInt32());
+ auto device = static_cast<hwc2_device_t *>(this);
+
+ auto err = CallDisplayFunction(device, display, &HWCDisplay::SetWhiteCompensation, enabled);
+ if (err != HWC2_ERROR_NONE)
+ return -EINVAL;
+
+ return 0;
+}
+
android::status_t HWCSession::SetColorModeWithRenderIntentOverride(
const android::Parcel *input_parcel) {
auto display = static_cast<hwc2_display_t>(input_parcel->readInt32());
diff --git a/sdm/libs/hwc2/hwc_session.h b/sdm/libs/hwc2/hwc_session.h
index 5b94b57..303743b 100644
--- a/sdm/libs/hwc2/hwc_session.h
+++ b/sdm/libs/hwc2/hwc_session.h
@@ -179,6 +179,10 @@
int32_t *release_fence);
static int32_t GetDozeSupport(hwc2_device_t *device, hwc2_display_t display,
int32_t *out_support);
+ static int32_t GetDisplayCapabilities(hwc2_device_t* device, hwc2_display_t display,
+ uint32_t* outNumCapabilities, uint32_t* outCapabilities);
+ static int32_t SetDisplayBrightness(hwc2_device_t *device, hwc2_display_t display,
+ float brightness);
static Locker locker_[HWC_NUM_DISPLAY_TYPES];
@@ -191,6 +195,8 @@
static int Close(hw_device_t *device);
static void GetCapabilities(struct hwc2_device *device, uint32_t *outCount,
int32_t *outCapabilities);
+ static int32_t GetDisplayBrightnessSupport(hwc2_device_t *device, hwc2_display_t display,
+ bool *out_support);
static hwc2_function_pointer_t GetFunction(struct hwc2_device *device, int32_t descriptor);
// Uevent handler
@@ -271,11 +277,14 @@
android::status_t SetMixerResolution(const android::Parcel *input_parcel);
android::status_t SetColorModeOverride(const android::Parcel *input_parcel);
android::status_t SetColorModeWithRenderIntentOverride(const android::Parcel *input_parcel);
+ android::status_t SetWhiteCompensation(const android::Parcel *input_parcel);
android::status_t SetColorModeById(const android::Parcel *input_parcel);
android::status_t getComposerStatus();
android::status_t SetIdlePC(const android::Parcel *input_parcel);
+ android::status_t setColorSamplingEnabled(const android::Parcel *input_parcel);
+
void Refresh(hwc2_display_t display);
void HotPlug(hwc2_display_t display, HWC2::Connection state);
HWC2::Error ValidateDisplayInternal(hwc2_display_t display, uint32_t *out_num_types,
@@ -302,6 +311,7 @@
int hpd_bpp_ = 0;
int hpd_pattern_ = 0;
uint32_t idle_pc_ref_cnt_ = 0;
+ int brightness_fd_ = -1;
};
} // namespace sdm
diff --git a/sdm/libs/utils/Android.mk b/sdm/libs/utils/Android.mk
index a9d705c..6dddae6 100644
--- a/sdm/libs/utils/Android.mk
+++ b/sdm/libs/utils/Android.mk
@@ -3,6 +3,8 @@
include $(LOCAL_PATH)/../../../common.mk
LOCAL_MODULE := libsdmutils
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-BSD
+LOCAL_LICENSE_CONDITIONS := notice
LOCAL_VENDOR_MODULE := true
LOCAL_MODULE_TAGS := optional
LOCAL_C_INCLUDES := $(common_includes)