drm_hwcomposer: Add support for color transform matrix

1. Add and wire-up CRTC CTM property support.
2. Add custom Android property to select behavior for cases
   where DRM can't handle color transform matrix.

The "vendor.hwc.drm.ctm" property can be set to:
 - DRM_OR_GPU (default) - Use GPU if CTM is not supported by DRM.
 - DRM_OR_IGNORE - Ignore CTM if DRM doesn't support it.

The last option is useful for Android 13 and later where default
color transformation matrix is not an identity matrix.

At the moment I do not have any devices with CTM support, therefore
I can test only DRM_OR_IGNORE option.

Signed-off-by: Roman Stratiienko <r.stratiienko@gmail.com>
diff --git a/drm/DrmAtomicStateManager.cpp b/drm/DrmAtomicStateManager.cpp
index 58a5523..4ff16e2 100644
--- a/drm/DrmAtomicStateManager.cpp
+++ b/drm/DrmAtomicStateManager.cpp
@@ -107,6 +107,20 @@
     }
   }
 
+  if (args.color_matrix && crtc->GetCtmProperty()) {
+    auto blob = drm->RegisterUserPropertyBlob(args.color_matrix.get(),
+                                              sizeof(drm_color_ctm));
+    new_frame_state.ctm_blob = std::move(blob);
+
+    if (!new_frame_state.ctm_blob) {
+      ALOGE("Failed to create CTM blob");
+      return -EINVAL;
+    }
+
+    if (!crtc->GetCtmProperty().AtomicSet(*pset, *new_frame_state.ctm_blob))
+      return -EINVAL;
+  }
+
   auto unused_planes = new_frame_state.used_planes;
 
   if (args.composition) {
diff --git a/drm/DrmAtomicStateManager.h b/drm/DrmAtomicStateManager.h
index e456a91..6e32a37 100644
--- a/drm/DrmAtomicStateManager.h
+++ b/drm/DrmAtomicStateManager.h
@@ -35,6 +35,7 @@
   std::optional<DrmMode> display_mode;
   std::optional<bool> active;
   std::shared_ptr<DrmKmsPlan> composition;
+  std::shared_ptr<drm_color_ctm> color_matrix;
 
   /* out */
   SharedFd out_fence;
@@ -75,6 +76,7 @@
     std::vector<std::shared_ptr<DrmFbIdHandle>> used_framebuffers;
 
     DrmModeUserPropertyBlobUnique mode_blob;
+    DrmModeUserPropertyBlobUnique ctm_blob;
 
     int release_fence_pt_index{};
 
diff --git a/drm/DrmCrtc.cpp b/drm/DrmCrtc.cpp
index 3b749b1..948a9ac 100644
--- a/drm/DrmCrtc.cpp
+++ b/drm/DrmCrtc.cpp
@@ -61,6 +61,11 @@
     return {};
   }
 
+  ret = GetCrtcProperty(dev, *c, "CTM", &c->ctm_property_);
+  if (ret != 0) {
+    ALOGV("Missing optional CTM property");
+  }
+
   return c;
 }
 
diff --git a/drm/DrmCrtc.h b/drm/DrmCrtc.h
index 64fb56f..96443cd 100644
--- a/drm/DrmCrtc.h
+++ b/drm/DrmCrtc.h
@@ -58,6 +58,10 @@
     return out_fence_ptr_property_;
   }
 
+  auto &GetCtmProperty() const {
+    return ctm_property_;
+  }
+
  private:
   DrmCrtc(DrmModeCrtcUnique crtc, uint32_t index)
       : crtc_(std::move(crtc)), index_in_res_array_(index){};
@@ -66,6 +70,8 @@
 
   const uint32_t index_in_res_array_;
 
+  DrmProperty ctm_property_;
+
   DrmProperty active_property_;
   DrmProperty mode_property_;
   DrmProperty out_fence_ptr_property_;
diff --git a/drm/ResourceManager.cpp b/drm/ResourceManager.cpp
index dd26c17..577d86c 100644
--- a/drm/ResourceManager.cpp
+++ b/drm/ResourceManager.cpp
@@ -72,9 +72,21 @@
     }
   }
 
-  char scale_with_gpu[PROPERTY_VALUE_MAX];
-  property_get("vendor.hwc.drm.scale_with_gpu", scale_with_gpu, "0");
-  scale_with_gpu_ = bool(strncmp(scale_with_gpu, "0", 1));
+  char proptext[PROPERTY_VALUE_MAX];
+  property_get("vendor.hwc.drm.scale_with_gpu", proptext, "0");
+  scale_with_gpu_ = bool(strncmp(proptext, "0", 1));
+
+  constexpr char kDrmOrGpu[] = "DRM_OR_GPU";
+  constexpr char kDrmOrIgnore[] = "DRM_OR_IGNORE";
+  property_get("vendor.hwc.drm.ctm", proptext, kDrmOrGpu);
+  if (strncmp(proptext, kDrmOrGpu, sizeof(kDrmOrGpu)) == 0) {
+    ctm_handling_ = CtmHandling::kDrmOrGpu;
+  } else if (strncmp(proptext, kDrmOrIgnore, sizeof(kDrmOrIgnore)) == 0) {
+    ctm_handling_ = CtmHandling::kDrmOrIgnore;
+  } else {
+    ALOGE("Invalid value for vendor.hwc.drm.ctm: %s", proptext);
+    ctm_handling_ = CtmHandling::kDrmOrGpu;
+  }
 
   if (BufferInfoGetter::GetInstance() == nullptr) {
     ALOGE("Failed to initialize BufferInfoGetter");
diff --git a/drm/ResourceManager.h b/drm/ResourceManager.h
index 60b6b16..7fa3fc6 100644
--- a/drm/ResourceManager.h
+++ b/drm/ResourceManager.h
@@ -26,6 +26,11 @@
 
 namespace android {
 
+enum class CtmHandling {
+  kDrmOrGpu,    /* Handled by DRM is possible, otherwise by GPU */
+  kDrmOrIgnore, /* Handled by DRM is possible, otherwise displayed as is */
+};
+
 class PipelineToFrontendBindingInterface {
  public:
   virtual ~PipelineToFrontendBindingInterface() = default;
@@ -52,6 +57,10 @@
     return scale_with_gpu_;
   }
 
+  auto &GetCtmHandling() const {
+    return ctm_handling_;
+  }
+
   auto &GetMainLock() {
     return main_lock_;
   }
@@ -65,7 +74,9 @@
 
   std::vector<std::unique_ptr<DrmDevice>> drms_;
 
+  // Android properties:
   bool scale_with_gpu_{};
+  CtmHandling ctm_handling_{};
 
   std::shared_ptr<UEventListener> uevent_listener_;