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, &reg->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, &current_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)