sdm: Define client interface to enable/disable idle pc

1. Define client interface to enable/disable idle power collapse
2. Maintain refcount to handle concurrent enable/disable
3. Trigger the screen refresh and wait for the next frame commit
   done event before returning the control to the client on disable
   idle pc
4. Enable idle pc on suspend and reset the refcount
5. Add binder support to enable/disable idle pc

CRs-Fixed: 2255316
Change-Id:Ibcaf9d4edca502cc91e9b201be822bd48313a635
diff --git a/sdm/libs/core/display_base.h b/sdm/libs/core/display_base.h
index 0f77ca5..e4efb92 100644
--- a/sdm/libs/core/display_base.h
+++ b/sdm/libs/core/display_base.h
@@ -127,6 +127,9 @@
   virtual DisplayError SetQSyncMode(QSyncMode qsync_mode) { return kErrorNotSupported; }
   virtual std::string Dump();
   virtual DisplayError InitializeColorModes();
+  virtual DisplayError ControlIdlePowerCollapse(bool enable, bool synchronous) {
+    return kErrorNotSupported;
+  }
 
  protected:
   const char *kBt2020Pq = "bt2020_pq";
diff --git a/sdm/libs/core/display_builtin.cpp b/sdm/libs/core/display_builtin.cpp
index 79e79f6..02c534d 100644
--- a/sdm/libs/core/display_builtin.cpp
+++ b/sdm/libs/core/display_builtin.cpp
@@ -568,4 +568,18 @@
   return kErrorNone;
 }
 
+DisplayError DisplayBuiltIn::ControlIdlePowerCollapse(bool enable, bool synchronous) {
+  lock_guard<recursive_mutex> obj(recursive_mutex_);
+  if (!active_) {
+    DLOGW("Invalid display state = %d. Panel must be on.", state_);
+    return kErrorPermission;
+  }
+  if (hw_panel_info_.mode == kModeVideo) {
+    DLOGW("Idle power collapse not supported for video mode panel.");
+    return kErrorNotSupported;
+  }
+  return hw_intf_->ControlIdlePowerCollapse(enable, synchronous);
+}
+
+
 }  // namespace sdm
diff --git a/sdm/libs/core/display_builtin.h b/sdm/libs/core/display_builtin.h
index f1de42a..d4295fa 100644
--- a/sdm/libs/core/display_builtin.h
+++ b/sdm/libs/core/display_builtin.h
@@ -71,6 +71,7 @@
   virtual DisplayError GetPanelBrightness(int *level);
   virtual DisplayError HandleSecureEvent(SecureEvent secure_event);
   virtual DisplayError SetQSyncMode(QSyncMode qsync_mode);
+  virtual DisplayError ControlIdlePowerCollapse(bool enable, bool synchronous);
 
   // Implement the HWEventHandlers
   virtual DisplayError VSync(int64_t timestamp);
diff --git a/sdm/libs/core/drm/hw_device_drm.cpp b/sdm/libs/core/drm/hw_device_drm.cpp
index d6f8c43..4cb06c7 100644
--- a/sdm/libs/core/drm/hw_device_drm.cpp
+++ b/sdm/libs/core/drm/hw_device_drm.cpp
@@ -842,16 +842,6 @@
 }
 
 DisplayError HWDeviceDRM::PowerOn(const HWQosData &qos_data, int *release_fence) {
-  DTRACE_SCOPED();
-  if (!drm_atomic_intf_) {
-    DLOGE("DRM Atomic Interface is null!");
-    return kErrorUndefined;
-  }
-
-  if (first_cycle_) {
-    return kErrorNone;
-  }
-
   SetQOSData(qos_data);
 
   int64_t release_fence_t = -1;
@@ -859,7 +849,6 @@
   drm_atomic_intf_->Perform(DRMOps::CRTC_SET_ACTIVE, token_.crtc_id, 1);
   drm_atomic_intf_->Perform(DRMOps::CONNECTOR_SET_POWER_MODE, token_.conn_id, DRMPowerMode::ON);
   drm_atomic_intf_->Perform(DRMOps::CRTC_GET_RELEASE_FENCE, token_.crtc_id, &release_fence_t);
-
   int ret = NullCommit(true /* synchronous */, true /* retain_planes */);
   if (ret) {
     DLOGE("Failed with error: %d", ret);
@@ -1282,7 +1271,7 @@
   DTRACE_SCOPED();
   SetupAtomic(hw_layers, false /* validate */);
 
-  int ret = drm_atomic_intf_->Commit(false /* synchronous */, false /* retain_planes*/);
+  int ret = drm_atomic_intf_->Commit(synchronous_commit_, false /* retain_planes*/);
   if (ret) {
     DLOGE("%s failed with error %d crtc %d", __FUNCTION__, ret, token_.crtc_id);
     vrefresh_ = 0;
diff --git a/sdm/libs/core/drm/hw_device_drm.h b/sdm/libs/core/drm/hw_device_drm.h
index a8bb5b3..b89391b 100644
--- a/sdm/libs/core/drm/hw_device_drm.h
+++ b/sdm/libs/core/drm/hw_device_drm.h
@@ -112,6 +112,9 @@
   virtual DisplayError SetDppsFeature(void *payload, size_t size) { return kErrorNotSupported; }
   virtual DisplayError GetDppsFeatureInfo(void *payload, size_t size) { return kErrorNotSupported; }
   virtual DisplayError HandleSecureEvent(SecureEvent secure_event) { return kErrorNotSupported; }
+  virtual DisplayError ControlIdlePowerCollapse(bool enable, bool synchronous) {
+    return kErrorNotSupported;
+  }
 
   enum {
     kHWEventVSync,
@@ -207,9 +210,9 @@
   std::vector<sde_drm::DRMSolidfillStage> solid_fills_ {};
   bool secure_display_active_ = false;
   uint64_t debug_dump_count_ = 0;
+  bool synchronous_commit_ = false;
 
  private:
-  bool synchronous_commit_ = false;
   std::string interface_str_ = "DSI";
   bool resolution_switch_enabled_ = false;
   uint32_t vrefresh_ = 0;
diff --git a/sdm/libs/core/drm/hw_peripheral_drm.cpp b/sdm/libs/core/drm/hw_peripheral_drm.cpp
index 0249396..df8b462 100644
--- a/sdm/libs/core/drm/hw_peripheral_drm.cpp
+++ b/sdm/libs/core/drm/hw_peripheral_drm.cpp
@@ -68,6 +68,7 @@
   HWLayersInfo &hw_layer_info = hw_layers->info;
   SetDestScalarData(hw_layer_info);
   SetupConcurrentWriteback(hw_layer_info, true);
+  SetIdlePCState();
 
   return HWDeviceDRM::Validate(hw_layers);
 }
@@ -76,13 +77,20 @@
   HWLayersInfo &hw_layer_info = hw_layers->info;
   SetDestScalarData(hw_layer_info);
   SetupConcurrentWriteback(hw_layer_info, false);
+  SetIdlePCState();
 
   DisplayError error = HWDeviceDRM::Commit(hw_layers);
+  if (error != kErrorNone) {
+    return error;
+  }
 
   if (cwb_config_.enabled && (error == kErrorNone)) {
     PostCommitConcurrentWriteback(hw_layer_info.stack->output_buffer);
   }
 
+  // Initialize to default after successful commit
+  synchronous_commit_ = false;
+
   return error;
 }
 
@@ -314,4 +322,38 @@
   }
 }
 
+DisplayError HWPeripheralDRM::ControlIdlePowerCollapse(bool enable, bool synchronous) {
+  sde_drm::DRMIdlePCState idle_pc_state =
+    enable ? sde_drm::DRMIdlePCState::ENABLE : sde_drm::DRMIdlePCState::DISABLE;
+  if (idle_pc_state == idle_pc_state_) {
+    return kErrorNone;
+  }
+  // As idle PC is disabled after subsequent commit, Make sure to have synchrounous commit and
+  // ensure TA accesses the display_cc registers after idle PC is disabled.
+  idle_pc_state_ = idle_pc_state;
+  synchronous_commit_ = !enable ? synchronous : false;
+  return kErrorNone;
+}
+
+DisplayError HWPeripheralDRM::PowerOn(const HWQosData &qos_data, int *release_fence) {
+  DTRACE_SCOPED();
+  if (!drm_atomic_intf_) {
+    DLOGE("DRM Atomic Interface is null!");
+    return kErrorUndefined;
+  }
+
+  if (first_cycle_) {
+    return kErrorNone;
+  }
+  drm_atomic_intf_->Perform(sde_drm::DRMOps::CRTC_SET_IDLE_PC_STATE, token_.crtc_id,
+                            sde_drm::DRMIdlePCState::ENABLE);
+  DisplayError err = HWDeviceDRM::PowerOn(qos_data, release_fence);
+  if (err != kErrorNone) {
+    return err;
+  }
+  idle_pc_state_ = sde_drm::DRMIdlePCState::ENABLE;
+
+  return kErrorNone;
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/core/drm/hw_peripheral_drm.h b/sdm/libs/core/drm/hw_peripheral_drm.h
index 923d805..0ded3e1 100644
--- a/sdm/libs/core/drm/hw_peripheral_drm.h
+++ b/sdm/libs/core/drm/hw_peripheral_drm.h
@@ -54,6 +54,8 @@
   virtual DisplayError SetDppsFeature(void *payload, size_t size);
   virtual DisplayError GetDppsFeatureInfo(void *payload, size_t size);
   virtual DisplayError HandleSecureEvent(SecureEvent secure_event);
+  virtual DisplayError ControlIdlePowerCollapse(bool enable, bool synchronous);
+  virtual DisplayError PowerOn(const HWQosData &qos_data, int *release_fence);
 
  private:
   void SetDestScalarData(HWLayersInfo hw_layer_info);
@@ -62,10 +64,15 @@
   void SetupConcurrentWriteback(const HWLayersInfo &hw_layer_info, bool validate);
   void ConfigureConcurrentWriteback(LayerStack *stack);
   void PostCommitConcurrentWriteback(LayerBuffer *output_buffer);
+  void SetIdlePCState() {
+    drm_atomic_intf_->Perform(sde_drm::DRMOps::CRTC_SET_IDLE_PC_STATE, token_.crtc_id,
+                              idle_pc_state_);
+  }
 
   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;
 };
 
 }  // namespace sdm
diff --git a/sdm/libs/core/drm/hw_tv_drm.cpp b/sdm/libs/core/drm/hw_tv_drm.cpp
index caac575..6606623 100644
--- a/sdm/libs/core/drm/hw_tv_drm.cpp
+++ b/sdm/libs/core/drm/hw_tv_drm.cpp
@@ -291,5 +291,19 @@
         hdr_metadata_.white_point_x, hdr_metadata_.white_point_y, hdr_metadata_.eotf);
 }
 
+DisplayError HWTVDRM::PowerOn(const HWQosData &qos_data, int *release_fence) {
+  DTRACE_SCOPED();
+  if (!drm_atomic_intf_) {
+    DLOGE("DRM Atomic Interface is null!");
+    return kErrorUndefined;
+  }
+
+  if (first_cycle_) {
+    return kErrorNone;
+  }
+
+  return HWDeviceDRM::PowerOn(qos_data, release_fence);
+}
+
 }  // namespace sdm
 
diff --git a/sdm/libs/core/drm/hw_tv_drm.h b/sdm/libs/core/drm/hw_tv_drm.h
index ec670f5..e1dc6f1 100644
--- a/sdm/libs/core/drm/hw_tv_drm.h
+++ b/sdm/libs/core/drm/hw_tv_drm.h
@@ -48,6 +48,7 @@
   virtual DisplayError Standby();
   virtual DisplayError Commit(HWLayers *hw_layers);
   virtual void PopulateHWPanelInfo();
+  virtual DisplayError PowerOn(const HWQosData &qos_data, int *release_fence);
 
  private:
   DisplayError UpdateHDRMetaData(HWLayers *hw_layers);
diff --git a/sdm/libs/core/drm/hw_virtual_drm.cpp b/sdm/libs/core/drm/hw_virtual_drm.cpp
index afbdc19..b6a1c13 100644
--- a/sdm/libs/core/drm/hw_virtual_drm.cpp
+++ b/sdm/libs/core/drm/hw_virtual_drm.cpp
@@ -212,5 +212,24 @@
   }
 }
 
+DisplayError HWVirtualDRM::PowerOn(const HWQosData &qos_data, int *release_fence) {
+  DTRACE_SCOPED();
+  if (!drm_atomic_intf_) {
+    DLOGE("DRM Atomic Interface is null!");
+    return kErrorUndefined;
+  }
+
+  if (first_cycle_) {
+    return kErrorNone;
+  }
+
+  DisplayError err = HWDeviceDRM::PowerOn(qos_data, release_fence);
+  if (err != kErrorNone) {
+    return err;
+  }
+
+  return kErrorNone;
+}
+
 }  // namespace sdm
 
diff --git a/sdm/libs/core/drm/hw_virtual_drm.h b/sdm/libs/core/drm/hw_virtual_drm.h
index 30ed84c..9dfc409 100644
--- a/sdm/libs/core/drm/hw_virtual_drm.h
+++ b/sdm/libs/core/drm/hw_virtual_drm.h
@@ -52,6 +52,7 @@
   virtual DisplayError Validate(HWLayers *hw_layers);
   virtual DisplayError Commit(HWLayers *hw_layers);
   virtual DisplayError GetPPFeaturesVersion(PPFeatureVersion *vers);
+  virtual DisplayError PowerOn(const HWQosData &qos_data, int *release_fence);
 
  private:
   void ConfigureWbConnectorFbId(uint32_t fb_id);
diff --git a/sdm/libs/core/fb/hw_device.h b/sdm/libs/core/fb/hw_device.h
index c92535e..cf77e09 100644
--- a/sdm/libs/core/fb/hw_device.h
+++ b/sdm/libs/core/fb/hw_device.h
@@ -102,6 +102,9 @@
   virtual DisplayError GetDppsFeatureInfo(void *payload, size_t size) { return kErrorNotSupported; }
   virtual DisplayError DumpDebugData(DisplayType type) { return kErrorNone; }
   virtual DisplayError HandleSecureEvent(SecureEvent secure_event) { return kErrorNotSupported; }
+  virtual DisplayError ControlIdlePowerCollapse(bool enable, bool synchronous) {
+    return kErrorNotSupported;
+  }
 
   enum {
     kHWEventVSync,
diff --git a/sdm/libs/core/hw_interface.h b/sdm/libs/core/hw_interface.h
index 5ab880d..b5223b5 100644
--- a/sdm/libs/core/hw_interface.h
+++ b/sdm/libs/core/hw_interface.h
@@ -117,6 +117,7 @@
   virtual DisplayError SetDppsFeature(void *payload, size_t size) = 0;
   virtual DisplayError GetDppsFeatureInfo(void *payload, size_t size) = 0;
   virtual DisplayError HandleSecureEvent(SecureEvent secure_event) = 0;
+  virtual DisplayError ControlIdlePowerCollapse(bool enable, bool synchronous) = 0;
 
  protected:
   virtual ~HWInterface() { }
diff --git a/sdm/libs/hwc2/Android.mk b/sdm/libs/hwc2/Android.mk
index a8f259b..6edbf5f 100644
--- a/sdm/libs/hwc2/Android.mk
+++ b/sdm/libs/hwc2/Android.mk
@@ -33,6 +33,11 @@
 ifeq ($(display_config_version), DISPLAY_CONFIG_1_2)
 LOCAL_SHARED_LIBRARIES        += vendor.display.config@1.2 vendor.display.config@1.1
 endif
+ifeq ($(display_config_version), DISPLAY_CONFIG_1_3)
+LOCAL_SHARED_LIBRARIES        += vendor.display.config@1.1
+LOCAL_SHARED_LIBRARIES        += vendor.display.config@1.2
+LOCAL_SHARED_LIBRARIES        += vendor.display.config@1.3
+endif
 
 LOCAL_SRC_FILES               := hwc_session.cpp \
                                  hwc_session_services.cpp \
diff --git a/sdm/libs/hwc2/display_null.h b/sdm/libs/hwc2/display_null.h
index 3dc8761..82b7ed4 100644
--- a/sdm/libs/hwc2/display_null.h
+++ b/sdm/libs/hwc2/display_null.h
@@ -96,6 +96,7 @@
                                     const ColorMetaData &))
   MAKE_NO_OP(HandleSecureEvent(SecureEvent))
   MAKE_NO_OP(SetQSyncMode(QSyncMode))
+  MAKE_NO_OP(ControlIdlePowerCollapse(bool, bool))
 
   DisplayConfigVariableInfo default_variable_config_ = {};
   DisplayConfigFixedInfo default_fixed_config_ = {};
diff --git a/sdm/libs/hwc2/hwc_display.h b/sdm/libs/hwc2/hwc_display.h
index 27f48d1..4f558bc 100644
--- a/sdm/libs/hwc2/hwc_display.h
+++ b/sdm/libs/hwc2/hwc_display.h
@@ -269,6 +269,9 @@
   virtual HWC2::Error SetQSyncMode(QSyncMode qsync_mode) {
     return HWC2::Error::Unsupported;
   }
+  virtual HWC2::Error ControlIdlePowerCollapse(bool enable, bool synchronous) {
+    return HWC2::Error::Unsupported;
+  }
 
  protected:
   // Maximum number of layers supported by display manager.
diff --git a/sdm/libs/hwc2/hwc_display_builtin.cpp b/sdm/libs/hwc2/hwc_display_builtin.cpp
index c502c0e..cc02ad6 100644
--- a/sdm/libs/hwc2/hwc_display_builtin.cpp
+++ b/sdm/libs/hwc2/hwc_display_builtin.cpp
@@ -706,4 +706,15 @@
   return HWC2::Error::None;
 }
 
+HWC2::Error HWCDisplayBuiltIn::ControlIdlePowerCollapse(bool enable, bool synchronous) {
+  DisplayError error = kErrorNone;
+
+  if (display_intf_) {
+    error = display_intf_->ControlIdlePowerCollapse(enable, synchronous);
+    validated_ = false;
+  }
+
+  return (error != kErrorNone) ?  HWC2::Error::Unsupported : HWC2::Error::None;
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/hwc2/hwc_display_builtin.h b/sdm/libs/hwc2/hwc_display_builtin.h
index 04658d6..1f01723 100644
--- a/sdm/libs/hwc2/hwc_display_builtin.h
+++ b/sdm/libs/hwc2/hwc_display_builtin.h
@@ -79,6 +79,7 @@
                                         bool post_processed_output);
   virtual HWC2::Error GetReadbackBufferFence(int32_t *release_fence);
   virtual HWC2::Error SetQSyncMode(QSyncMode qsync_mode);
+  virtual HWC2::Error ControlIdlePowerCollapse(bool enable, bool synchronous);
 
  private:
   HWCDisplayBuiltIn(CoreInterface *core_intf, BufferAllocator *buffer_allocator,
diff --git a/sdm/libs/hwc2/hwc_session.cpp b/sdm/libs/hwc2/hwc_session.cpp
index 2ea8633..4237b56 100644
--- a/sdm/libs/hwc2/hwc_session.cpp
+++ b/sdm/libs/hwc2/hwc_session.cpp
@@ -879,6 +879,10 @@
   if (error != HWC2_ERROR_NONE) {
     return error;
   }
+  // Reset idle pc ref count on suspend, as we enable idle pc during suspend.
+  if (mode == HWC2::PowerMode::Off) {
+    hwc_session->idle_pc_ref_cnt_ = 0;
+  }
 
   hwc_session->UpdateVsyncSource(display);
 
@@ -1382,6 +1386,14 @@
       status = SetQSyncMode(input_parcel);
       break;
 
+    case qService::IQService::SET_IDLE_PC:
+      if (!input_parcel) {
+        DLOGE("QService command = %d: input_parcel needed.", command);
+        break;
+      }
+      status = SetIdlePC(input_parcel);
+      break;
+
     default:
       DLOGW("QService command = %d is not supported.", command);
       break;
@@ -2540,4 +2552,35 @@
   }
 }
 
+android::status_t HWCSession::SetIdlePC(const android::Parcel *input_parcel) {
+  auto enable = input_parcel->readInt32();
+  auto synchronous = input_parcel->readInt32();
+
+#ifdef DISPLAY_CONFIG_1_3
+  return static_cast<android::status_t>(controlIdlePowerCollapse(enable, synchronous));
+#else
+  {
+    SEQUENCE_WAIT_SCOPE_LOCK(locker_[HWC_DISPLAY_PRIMARY]);
+    if (hwc_display_[HWC_DISPLAY_PRIMARY]) {
+      DLOGE("Primary display is not ready");
+      return -EINVAL;
+    }
+    auto error = hwc_display_[HWC_DISPLAY_PRIMARY]->ControlIdlePowerCollapse(enable, synchronous);
+    if (error != HWC2::Error::None) {
+      return -EINVAL;
+    }
+    if (!enable) {
+      Refresh(HWC_DISPLAY_PRIMARY);
+      int32_t error = locker_[HWC_DISPLAY_PRIMARY].WaitFinite(kCommitDoneTimeoutMs);
+      if (error == ETIMEDOUT) {
+        DLOGE("Timed out!! Next frame commit done event not received!!");
+        return error;
+      }
+    }
+    DLOGI("Idle PC %s!!", enable ? "enabled" : "disabled");
+  }
+  return 0;
+#endif
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/hwc2/hwc_session.h b/sdm/libs/hwc2/hwc_session.h
index 2a18b5f..6387cd6 100644
--- a/sdm/libs/hwc2/hwc_session.h
+++ b/sdm/libs/hwc2/hwc_session.h
@@ -20,7 +20,9 @@
 #ifndef __HWC_SESSION_H__
 #define __HWC_SESSION_H__
 
-#ifdef DISPLAY_CONFIG_1_2
+#ifdef DISPLAY_CONFIG_1_3
+#include <vendor/display/config/1.3/IDisplayConfig.h>
+#elif DISPLAY_CONFIG_1_2
 #include <vendor/display/config/1.2/IDisplayConfig.h>
 #elif DISPLAY_CONFIG_1_1
 #include <vendor/display/config/1.1/IDisplayConfig.h>
@@ -47,7 +49,9 @@
 
 namespace sdm {
 
-#ifdef DISPLAY_CONFIG_1_2
+#ifdef DISPLAY_CONFIG_1_3
+using vendor::display::config::V1_3::IDisplayConfig;
+#elif DISPLAY_CONFIG_1_2
 using vendor::display::config::V1_2::IDisplayConfig;
 #elif DISPLAY_CONFIG_1_1
 using vendor::display::config::V1_1::IDisplayConfig;
@@ -216,7 +220,7 @@
   };
 
   static const int kExternalConnectionTimeoutMs = 500;
-  static const int kPartialUpdateControlTimeoutMs = 100;
+  static const int kCommitDoneTimeoutMs = 100;
 
   // hwc methods
   static int Open(const hw_module_t *module, const char *name, hw_device_t **device);
@@ -275,8 +279,6 @@
                                   getHDRCapabilities_cb _hidl_cb) override;
   Return<int32_t> setCameraLaunchStatus(uint32_t on) override;
   Return<void> displayBWTransactionPending(displayBWTransactionPending_cb _hidl_cb) override;
-
-  // Methods from ::android::hardware::display::config::V1_1::IDisplayConfig follow.
 #ifdef DISPLAY_CONFIG_1_1
   Return<int32_t> setDisplayAnimating(uint64_t display_id, bool animating) override;
 #endif
@@ -285,6 +287,9 @@
   Return<int32_t> setDisplayIndex(IDisplayConfig::DisplayTypeExt disp_type,
                                   uint32_t base, uint32_t count) override;
 #endif
+#ifdef DISPLAY_CONFIG_1_3
+  Return<int32_t> controlIdlePowerCollapse(bool enable, bool synchronous) override;
+#endif
 
   // QClient methods
   virtual android::status_t notifyCallback(uint32_t command, const android::Parcel *input_parcel,
@@ -307,6 +312,7 @@
   android::status_t SetColorModeById(const android::Parcel *input_parcel);
   android::status_t getComposerStatus();
   android::status_t SetQSyncMode(const android::Parcel *input_parcel);
+  android::status_t SetIdlePC(const android::Parcel *input_parcel);
 
   void Refresh(hwc2_display_t display);
   void HotPlug(hwc2_display_t display, HWC2::Connection state);
@@ -345,6 +351,7 @@
   bool power_on_pending_[HWC_NUM_DISPLAY_TYPES] = {false};
   HotPlugEvent hotplug_pending_event_ = kHotPlugNone;
   bool destroy_virtual_disp_pending_ = false;
+  uint32_t idle_pc_ref_cnt_ = 0;
 };
 
 }  // namespace sdm
diff --git a/sdm/libs/hwc2/hwc_session_services.cpp b/sdm/libs/hwc2/hwc_session_services.cpp
index d1fb02a..467da12 100644
--- a/sdm/libs/hwc2/hwc_session_services.cpp
+++ b/sdm/libs/hwc2/hwc_session_services.cpp
@@ -371,7 +371,7 @@
   Refresh(HWC_DISPLAY_PRIMARY);
 
   // Wait until partial update control is complete
-  int32_t error = locker_[disp_id].WaitFinite(kPartialUpdateControlTimeoutMs);
+  int32_t error = locker_[disp_id].WaitFinite(kCommitDoneTimeoutMs);
 
   return error;
 }
@@ -591,6 +591,46 @@
 
   return 0;
 }
-#endif
+#endif  // DISPLAY_CONFIG_1_2
+
+#ifdef DISPLAY_CONFIG_1_3
+Return<int32_t> HWCSession::controlIdlePowerCollapse(bool enable, bool synchronous) {
+  SEQUENCE_WAIT_SCOPE_LOCK(locker_[HWC_DISPLAY_PRIMARY]);
+
+  if (hwc_display_[HWC_DISPLAY_PRIMARY]) {
+    if (!enable) {
+      if (!idle_pc_ref_cnt_) {
+        HWC2::Error err =
+            hwc_display_[HWC_DISPLAY_PRIMARY]->ControlIdlePowerCollapse(enable, synchronous);
+        if (err != HWC2::Error::None) {
+          return -EINVAL;
+        }
+        Refresh(HWC_DISPLAY_PRIMARY);
+        int32_t error = locker_[HWC_DISPLAY_PRIMARY].WaitFinite(kCommitDoneTimeoutMs);
+        if (error == ETIMEDOUT) {
+          DLOGE("Timed out!! Next frame commit done event not received!!");
+          return error;
+        }
+        DLOGI("Idle PC disabled!!");
+      }
+      idle_pc_ref_cnt_++;
+    } else if (idle_pc_ref_cnt_ > 0) {
+      if (!(idle_pc_ref_cnt_ - 1)) {
+        HWC2::Error err =
+            hwc_display_[HWC_DISPLAY_PRIMARY]->ControlIdlePowerCollapse(enable, synchronous);
+        if (err != HWC2::Error::None) {
+          return -EINVAL;
+        }
+        DLOGI("Idle PC enabled!!");
+      }
+      idle_pc_ref_cnt_--;
+    }
+    return 0;
+  }
+
+  DLOGW("Display = %d is not connected.", HWC_DISPLAY_PRIMARY);
+  return -ENODEV;
+}
+#endif  // DISPLAY_CONFIG_1_3
 
 }  // namespace sdm