Snap for 5450365 from 71b5571bcbd349571e7bfb18e68d702f055c8f61 to pi-platform-release

Change-Id: I51993d92b44a8c195616eec1affa7b0dbaee3dde
diff --git a/gralloc/gr_buf_mgr.cpp b/gralloc/gr_buf_mgr.cpp
index c830952..985dd3e 100644
--- a/gralloc/gr_buf_mgr.cpp
+++ b/gralloc/gr_buf_mgr.cpp
@@ -376,6 +376,7 @@
 }
 
 Error BufferManager::Dump(std::ostringstream *os) {
+  std::lock_guard<std::mutex> buffer_lock(buffer_lock_);
   for (auto it : handles_map_) {
     auto buf = it.second;
     auto hnd = buf->handle;
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/libqservice/IQService.h b/libqservice/IQService.h
index b201256..e9af5a1 100644
--- a/libqservice/IQService.h
+++ b/libqservice/IQService.h
@@ -79,6 +79,7 @@
         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
+        SET_WHITE_POINT_COMPENSATION = 399, // Enable/disable white point compensation
         COMMAND_LIST_END = 400,
     };
 
diff --git a/sdm/libs/core/display_base.cpp b/sdm/libs/core/display_base.cpp
index 67f7ac8..e5f61c1 100644
--- a/sdm/libs/core/display_base.cpp
+++ b/sdm/libs/core/display_base.cpp
@@ -1299,6 +1299,7 @@
     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;
   }
 
   return;
diff --git a/sdm/libs/core/drm/hw_device_drm.cpp b/sdm/libs/core/drm/hw_device_drm.cpp
index 627e008..40e0b22 100644
--- a/sdm/libs/core/drm/hw_device_drm.cpp
+++ b/sdm/libs/core/drm/hw_device_drm.cpp
@@ -1047,6 +1047,11 @@
   }
 }
 
+void HWDeviceDRM::ClearSolidfillStages() {
+  solid_fills_.clear();
+  SetSolidfillStages();
+}
+
 DisplayError HWDeviceDRM::Validate(HWLayers *hw_layers) {
   DTRACE_SCOPED();
 
@@ -1184,6 +1189,7 @@
 
 DisplayError HWDeviceDRM::Flush() {
   DTRACE_SCOPED();
+  ClearSolidfillStages();
   int ret = drm_atomic_intf_->Commit(false /* synchronous */, false /* retain_planes*/);
   if (ret) {
     DLOGE("failed with error %d", ret);
diff --git a/sdm/libs/core/drm/hw_device_drm.h b/sdm/libs/core/drm/hw_device_drm.h
index 63853c4..c4f06c3 100644
--- a/sdm/libs/core/drm/hw_device_drm.h
+++ b/sdm/libs/core/drm/hw_device_drm.h
@@ -126,6 +126,7 @@
   void UpdateMixerAttributes();
   void SetSolidfillStages();
   void AddSolidfillStage(const HWSolidfillStage &sf, uint32_t plane_alpha);
+  void ClearSolidfillStages();
   void SetBlending(const LayerBlending &source, sde_drm::DRMBlendType *target);
   void SetSrcConfig(const LayerBuffer &input_buffer, const HWRotatorMode &mode, uint32_t *config);
   void SelectCscType(const LayerBuffer &input_buffer, sde_drm::DRMCscType *type);
diff --git a/sdm/libs/core/drm/hw_peripheral_drm.cpp b/sdm/libs/core/drm/hw_peripheral_drm.cpp
index 03403e5..e4da108 100644
--- a/sdm/libs/core/drm/hw_peripheral_drm.cpp
+++ b/sdm/libs/core/drm/hw_peripheral_drm.cpp
@@ -165,6 +165,13 @@
     if (enable) {
       // Set DRM properties for Concurrent Writeback.
       ConfigureConcurrentWriteback(hw_layer_info.stack);
+
+      if (!validate) {
+        // Set GET_RETIRE_FENCE property to get Concurrent Writeback fence.
+        int *fence = &hw_layer_info.stack->output_buffer->release_fence_fd;
+        drm_atomic_intf_->Perform(DRMOps::CONNECTOR_GET_RETIRE_FENCE,
+                                  cwb_config_.token.conn_id, fence);
+      }
     } else {
       // Tear down the Concurrent Writeback topology.
       drm_atomic_intf_->Perform(DRMOps::CONNECTOR_SET_CRTC, cwb_config_.token.conn_id, 0);
@@ -239,11 +246,7 @@
 void HWPeripheralDRM::PostCommitConcurrentWriteback(LayerBuffer *output_buffer) {
   bool enabled = hw_resource_.has_concurrent_writeback && output_buffer;
 
-  if (enabled) {
-    // Get Concurrent Writeback fence
-    int *fence = &output_buffer->release_fence_fd;
-    drm_atomic_intf_->Perform(DRMOps::CONNECTOR_GET_RETIRE_FENCE, cwb_config_.token.conn_id, fence);
-  } else {
+  if (!enabled) {
     drm_mgr_intf_->UnregisterDisplay(cwb_config_.token);
     cwb_config_.enabled = false;
   }
diff --git a/sdm/libs/hwc2/hwc_display.cpp b/sdm/libs/hwc2/hwc_display.cpp
index f06de99..5940dc2 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();
 }
 
@@ -121,11 +122,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 +145,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 +155,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 Point 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() || !ParseWhiteCompensatedData()) {
+      enabled_ = false;
+      DLOGE("Failed to WhiteCompensation Set");
+      return HWC2::Error::NotValidated;
+    }
+    CalculateRGBRatio();
+  }
+  enabled_ = enabled;
+  return HWC2::Error::None;
+}
+
+bool WhiteCompensation::ParseWhiteCompensatedData() {
+  static constexpr char kWhitePointCalibrationDataPath[] = "/persist/display/calibrated_rgb";
+  FILE *fp = fopen(kWhitePointCalibrationDataPath, "r");
+  int ret;
+
+  if (!fp) {
+    DLOGW("Failed to open white compensated 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("Failed to get white compensated data");
+    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) {
+    return ((c2 * rgb * rgb + c1 * rgb + 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);
+  double color_matrix_restore[kColorTransformMatrixCount] = {0};
+  CopyColorTransformMatrix(color_matrix_, color_matrix_restore);
 
-  DisplayError error = display_intf_->SetColorTransform(kColorTransformMatrixCount, color_matrix);
+  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 +525,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;
 }
@@ -377,7 +600,7 @@
 
   DisplayConfigFixedInfo fixed_info = {};
   display_intf_->GetConfig(&fixed_info);
-  partial_update_enabled_ = fixed_info.partial_update;
+  partial_update_enabled_ = fixed_info.partial_update || (!fixed_info.is_cmdmode);
   client_target_->SetPartialUpdate(partial_update_enabled_);
 
   DLOGI("Display created with id: %d", id_);
@@ -1784,11 +2007,15 @@
   switch (display_status) {
     case kDisplayStatusResume:
       display_paused_ = false;
+      status = INT32(SetPowerMode(HWC2::PowerMode::On));
+      break;
     case kDisplayStatusOnline:
       status = INT32(SetPowerMode(HWC2::PowerMode::On));
       break;
     case kDisplayStatusPause:
       display_paused_ = true;
+      status = INT32(SetPowerMode(HWC2::PowerMode::Off));
+      break;
     case kDisplayStatusOffline:
       status = INT32(SetPowerMode(HWC2::PowerMode::Off));
       break;
diff --git a/sdm/libs/hwc2/hwc_display.h b/sdm/libs/hwc2/hwc_display.h
index 4940678..4523cb5 100644
--- a/sdm/libs/hwc2/hwc_display.h
+++ b/sdm/libs/hwc2/hwc_display.h
@@ -46,6 +46,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 +57,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 ParseWhiteCompensatedData();
+  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 +171,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 +195,18 @@
                                                        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 {
@@ -202,6 +312,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;
   }
diff --git a/sdm/libs/hwc2/hwc_display_primary.cpp b/sdm/libs/hwc2/hwc_display_primary.cpp
index 1a6944b..4d70d7f 100644
--- a/sdm/libs/hwc2/hwc_display_primary.cpp
+++ b/sdm/libs/hwc2/hwc_display_primary.cpp
@@ -73,6 +73,10 @@
 
 DisplayError HWCDisplayPrimary::PMICInterface::Notify(bool secure_display_start) {
   std::string str_sd_start = secure_display_start ? std::to_string(1) : std::to_string(0);
+  if (fd_lcd_bias_ < 0 || fd_wled_ < 0) {
+    DLOGI("Ignore notify because fd_lcd_bias_ or fd_wled_ open failed");
+    return kErrorNotSupported;
+  }
   ssize_t err = ::pwrite(fd_lcd_bias_, str_sd_start.c_str(), str_sd_start.length(), 0);
   if (err <= 0) {
     DLOGE("Write failed for lcd_bias, Error = %s", strerror(errno));
@@ -256,6 +260,7 @@
     // here in a subsequent draw round. Readback is not allowed for any secure use case.
     readback_configured_ = !layer_stack_.flags.secure_present;
     if (readback_configured_) {
+      DisablePartialUpdateOneFrame();
       layer_stack_.output_buffer = &output_buffer_;
       layer_stack_.flags.post_processed_output = post_processed_output_;
     }
@@ -338,6 +343,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) {
@@ -422,7 +439,6 @@
   readback_configured_ = false;
   validated_ = false;
 
-  DisablePartialUpdateOneFrame();
   return HWC2::Error::None;
 }
 
diff --git a/sdm/libs/hwc2/hwc_display_primary.h b/sdm/libs/hwc2/hwc_display_primary.h
index 40f0f48..0794b94 100644
--- a/sdm/libs/hwc2/hwc_display_primary.h
+++ b/sdm/libs/hwc2/hwc_display_primary.h
@@ -58,6 +58,7 @@
   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);
diff --git a/sdm/libs/hwc2/hwc_session.cpp b/sdm/libs/hwc2/hwc_session.cpp
index e440464..570e0fe 100644
--- a/sdm/libs/hwc2/hwc_session.cpp
+++ b/sdm/libs/hwc2/hwc_session.cpp
@@ -1355,6 +1355,14 @@
       status = SetIdlePC(input_parcel);
       break;
 
+    case qService::IQService::SET_WHITE_POINT_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;
@@ -1551,6 +1559,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..6271e61 100644
--- a/sdm/libs/hwc2/hwc_session.h
+++ b/sdm/libs/hwc2/hwc_session.h
@@ -271,6 +271,7 @@
   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();
diff --git a/sdm/libs/hwc2/hwc_session_services.cpp b/sdm/libs/hwc2/hwc_session_services.cpp
index 519346f..90ed00b 100644
--- a/sdm/libs/hwc2/hwc_session_services.cpp
+++ b/sdm/libs/hwc2/hwc_session_services.cpp
@@ -524,8 +524,8 @@
       if (!idle_pc_ref_cnt_) {
         HWC2::Error err =
             hwc_display_[HWC_DISPLAY_PRIMARY]->ControlIdlePowerCollapse(enable, synchronous);
-        if (err != HWC2::Error::None) {
-          return -EINVAL;
+        if (err == HWC2::Error::Unsupported) {
+          return 0;
         }
         Refresh(HWC_DISPLAY_PRIMARY);
         int32_t error = locker_[HWC_DISPLAY_PRIMARY].WaitFinite(kCommitDoneTimeoutMs);
@@ -540,8 +540,8 @@
       if (!(idle_pc_ref_cnt_ - 1)) {
         HWC2::Error err =
             hwc_display_[HWC_DISPLAY_PRIMARY]->ControlIdlePowerCollapse(enable, synchronous);
-        if (err != HWC2::Error::None) {
-          return -EINVAL;
+        if (err == HWC2::Error::Unsupported) {
+          return 0;
         }
         DLOGI("Idle PC enabled!!");
       }