hwc2: Add support to set powerstate asynchronously.

-- Create dummy displays for all displays that support async update.
-- Map real and dummy displays as part of HWC Init.
-- Upon call from client route all calls to dummy displays. This includes
   layerstack operations ie; create, destroy, get/set params etc.
-- Post powerstate, get the current layerstack from dummy display
   and update it onto real display.

Change-Id: Ib6ab3b31e28cbc9f609933eaece940a6b9eee3ba
CRs-fixed: 2462204
diff --git a/include/display_properties.h b/include/display_properties.h
index d78da9d..0c1448e 100644
--- a/include/display_properties.h
+++ b/include/display_properties.h
@@ -123,5 +123,6 @@
 
 #define ZERO_SWAP_INTERVAL                   "vendor.debug.egl.swapinterval"
 #define ENABLE_DROP_REFRESH                  DISPLAY_PROP("enable_drop_refresh")
+#define ENABLE_ASYNC_POWERMODE               DISPLAY_PROP("enable_async_powermode")
 
 #endif  // __DISPLAY_PROPERTIES_H__
diff --git a/sdm/libs/hwc2/hwc_callbacks.h b/sdm/libs/hwc2/hwc_callbacks.h
index c3a760f..2e4bdba 100644
--- a/sdm/libs/hwc2/hwc_callbacks.h
+++ b/sdm/libs/hwc2/hwc_callbacks.h
@@ -44,7 +44,11 @@
   static const int kNumPluggable = 4;
   static const int kNumVirtual = 4;
   // Add 1 primary display which can be either a builtin or pluggable.
-  static const int kNumDisplays = 1 + kNumBuiltIn + kNumPluggable + kNumVirtual;
+  // Async powermode update requires dummy hwc displays.
+  // Limit dummy displays to builtin/pluggable type for now.
+  static const int kNumRealDisplays = 1 + kNumBuiltIn + kNumPluggable + kNumVirtual;
+  static const int kNumDisplays = 1 + kNumBuiltIn + kNumPluggable + kNumVirtual +
+                                    1 + kNumBuiltIn + kNumPluggable;
 
   HWC2::Error Hotplug(hwc2_display_t display, HWC2::Connection state);
   HWC2::Error Refresh(hwc2_display_t display);
diff --git a/sdm/libs/hwc2/hwc_display.cpp b/sdm/libs/hwc2/hwc_display.cpp
index 8c88a1c..694c008 100644
--- a/sdm/libs/hwc2/hwc_display.cpp
+++ b/sdm/libs/hwc2/hwc_display.cpp
@@ -869,6 +869,29 @@
   return HWC2::Error::None;
 }
 
+void HWCDisplay::PostPowerMode() {
+  if (release_fence_ < 0) {
+    return;
+  }
+
+  for (auto hwc_layer : layer_set_) {
+    auto fence = hwc_layer->PopBackReleaseFence();
+    auto merged_fence = -1;
+    if (fence >= 0) {
+      merged_fence = sync_merge("sync_merge", release_fence_, fence);
+      ::close(fence);
+    } else {
+      merged_fence = ::dup(release_fence_);
+    }
+    hwc_layer->PushBackReleaseFence(merged_fence);
+  }
+
+  // Add this release fence onto fbt_release fence.
+  CloseFd(&fbt_release_fence_);
+  fbt_release_fence_ = release_fence_;
+  release_fence_ = -1;
+}
+
 HWC2::Error HWCDisplay::SetPowerMode(HWC2::PowerMode mode, bool teardown) {
   DLOGV("display = %d, mode = %s", id_, to_string(mode).c_str());
   DisplayState state = kStateOff;
@@ -917,23 +940,8 @@
     return HWC2::Error::BadParameter;
   }
 
-  if (release_fence >= 0) {
-    for (auto hwc_layer : layer_set_) {
-      auto fence = hwc_layer->PopBackReleaseFence();
-      auto merged_fence = -1;
-      if (fence >= 0) {
-        merged_fence = sync_merge("sync_merge", release_fence, fence);
-        ::close(fence);
-      } else {
-        merged_fence = ::dup(release_fence);
-      }
-      hwc_layer->PushBackReleaseFence(merged_fence);
-    }
-
-    // Add this release fence onto fbt_release fence.
-    CloseFd(&fbt_release_fence_);
-    fbt_release_fence_ = release_fence;
-  }
+  // Update release fence.
+  release_fence_ = release_fence;
   current_power_mode_ = mode;
   return HWC2::Error::None;
 }
@@ -2351,4 +2359,16 @@
   }
 }
 
+void HWCDisplay::GetLayerStack(HWCLayerStack *stack) {
+  stack->client_target = client_target_;
+  stack->layer_map = layer_map_;
+  stack->layer_set = layer_set_;
+}
+
+void HWCDisplay::SetLayerStack(HWCLayerStack *stack) {
+  client_target_ = stack->client_target;
+  layer_map_ = stack->layer_map;
+  layer_set_ = stack->layer_set;
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/hwc2/hwc_display.h b/sdm/libs/hwc2/hwc_display.h
index 11d2613..5d54e99 100644
--- a/sdm/libs/hwc2/hwc_display.h
+++ b/sdm/libs/hwc2/hwc_display.h
@@ -130,6 +130,12 @@
     kSkipValidate,
   };
 
+  struct HWCLayerStack {
+    HWCLayer *client_target = nullptr;                   // Also known as framebuffer target
+    std::map<hwc2_layer_t, HWCLayer *> layer_map;        // Look up by Id - TODO
+    std::multiset<HWCLayer *, SortLayersByZ> layer_set;  // Maintain a set sorted by Z
+  };
+
   virtual ~HWCDisplay() {}
   virtual int Init();
   virtual int Deinit();
@@ -295,6 +301,9 @@
   virtual HWC2::Error SetCursorPosition(hwc2_layer_t layer, int x, int y);
   virtual HWC2::Error SetVsyncEnabled(HWC2::Vsync enabled);
   virtual HWC2::Error SetPowerMode(HWC2::PowerMode mode, bool teardown);
+  virtual HWC2::Error UpdatePowerMode(HWC2::PowerMode mode) {
+    return HWC2::Error::None;
+  }
   virtual HWC2::Error CreateLayer(hwc2_layer_t *out_layer_id);
   virtual HWC2::Error DestroyLayer(hwc2_layer_t layer_id);
   virtual HWC2::Error SetLayerZOrder(hwc2_layer_t layer_id, uint32_t z);
@@ -323,6 +332,9 @@
   }
   virtual HWC2::Error GetDisplayIdentificationData(uint8_t *out_port, uint32_t *out_data_size,
                                                    uint8_t *out_data);
+  virtual void GetLayerStack(HWCLayerStack *stack);
+  virtual void SetLayerStack(HWCLayerStack *stack);
+  virtual void PostPowerMode();
 
  protected:
   static uint32_t throttling_refresh_rate_;
@@ -428,6 +440,7 @@
   bool fast_path_enabled_ = true;
   bool first_cycle_ = true;  // false if a display commit has succeeded on the device.
   int fbt_release_fence_ = -1;
+  int release_fence_ = -1;
 };
 
 inline int HWCDisplay::Perform(uint32_t operation, ...) {
diff --git a/sdm/libs/hwc2/hwc_display_builtin.cpp b/sdm/libs/hwc2/hwc_display_builtin.cpp
index cacbc7a..0ff54f4 100644
--- a/sdm/libs/hwc2/hwc_display_builtin.cpp
+++ b/sdm/libs/hwc2/hwc_display_builtin.cpp
@@ -871,4 +871,10 @@
   return HWC2::Error::None;
 }
 
+HWC2::Error HWCDisplayBuiltIn::UpdatePowerMode(HWC2::PowerMode mode) {
+  current_power_mode_ = mode;
+  validated_ = false;
+  return HWC2::Error::None;
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/hwc2/hwc_display_builtin.h b/sdm/libs/hwc2/hwc_display_builtin.h
index 0b5cd99..63234f6 100644
--- a/sdm/libs/hwc2/hwc_display_builtin.h
+++ b/sdm/libs/hwc2/hwc_display_builtin.h
@@ -94,6 +94,7 @@
   virtual void SetFastPathComposition(bool enable) {
     fast_path_composition_ = enable && !readback_buffer_queued_;
   }
+  virtual HWC2::Error UpdatePowerMode(HWC2::PowerMode mode);
 
  private:
   HWCDisplayBuiltIn(CoreInterface *core_intf, BufferAllocator *buffer_allocator,
diff --git a/sdm/libs/hwc2/hwc_display_dummy.cpp b/sdm/libs/hwc2/hwc_display_dummy.cpp
index 4c937f7..358eefd 100644
--- a/sdm/libs/hwc2/hwc_display_dummy.cpp
+++ b/sdm/libs/hwc2/hwc_display_dummy.cpp
@@ -28,6 +28,7 @@
 */
 
 #include "hwc_display_dummy.h"
+#include "utils/debug.h"
 
 #define __CLASS__ "HWCDisplayDummy"
 
@@ -43,12 +44,18 @@
   return kErrorNone;
 }
 
+void HWCDisplayDummy::Destroy(HWCDisplay *hwc_display) {
+  delete hwc_display;
+}
 
 HWC2::Error HWCDisplayDummy::Validate(uint32_t *out_num_types, uint32_t *out_num_requests) {
   return HWC2::Error::None;
 }
 
 HWC2::Error HWCDisplayDummy::Present(int32_t *out_retire_fence) {
+  for (auto hwc_layer : layer_set_) {
+    hwc_layer->PushBackReleaseFence(-1);
+  }
   return HWC2::Error::None;
 }
 
@@ -70,4 +77,9 @@
   display_intf_ = &display_null_;
 }
 
+HWC2::Error HWCDisplayDummy::GetActiveConfig(hwc2_config_t *out_config) {
+  *out_config = 0;
+  return HWC2::Error::None;
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/hwc2/hwc_display_dummy.h b/sdm/libs/hwc2/hwc_display_dummy.h
index 869c41d..29e04f4 100644
--- a/sdm/libs/hwc2/hwc_display_dummy.h
+++ b/sdm/libs/hwc2/hwc_display_dummy.h
@@ -41,10 +41,10 @@
                     HWCCallbacks *callbacks, HWCDisplayEventHandler *event_handler,
                     qService::QService *qservice, hwc2_display_t id, int32_t sdm_id,
                     HWCDisplay **hwc_display);
-
+  static void Destroy(HWCDisplay *hwc_display);
   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 GetActiveConfig(hwc2_config_t *out_config);
 
  private:
   HWCDisplayDummy(CoreInterface *core_intf, BufferAllocator *buffer_allocator,
diff --git a/sdm/libs/hwc2/hwc_display_pluggable.cpp b/sdm/libs/hwc2/hwc_display_pluggable.cpp
index 3a4f0b6..d30a637 100644
--- a/sdm/libs/hwc2/hwc_display_pluggable.cpp
+++ b/sdm/libs/hwc2/hwc_display_pluggable.cpp
@@ -328,4 +328,10 @@
   return status;
 }
 
+HWC2::Error HWCDisplayPluggable::UpdatePowerMode(HWC2::PowerMode mode) {
+  current_power_mode_ = mode;
+  validated_ = false;
+  return HWC2::Error::None;
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/hwc2/hwc_display_pluggable.h b/sdm/libs/hwc2/hwc_display_pluggable.h
index 1b35b98..7c24c1d 100644
--- a/sdm/libs/hwc2/hwc_display_pluggable.h
+++ b/sdm/libs/hwc2/hwc_display_pluggable.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2019, The Linux Foundation. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -54,6 +54,7 @@
                                        RenderIntent *out_intents);
   virtual HWC2::Error SetColorMode(ColorMode mode);
   virtual HWC2::Error SetColorModeWithRenderIntent(ColorMode mode, RenderIntent intent);
+  virtual HWC2::Error UpdatePowerMode(HWC2::PowerMode mode);
 
  private:
   HWCDisplayPluggable(CoreInterface *core_intf, HWCBufferAllocator *buffer_allocator,
diff --git a/sdm/libs/hwc2/hwc_session.cpp b/sdm/libs/hwc2/hwc_session.cpp
index 674f666..5e8748a 100644
--- a/sdm/libs/hwc2/hwc_session.cpp
+++ b/sdm/libs/hwc2/hwc_session.cpp
@@ -72,7 +72,8 @@
 static HWCUEvent g_hwc_uevent_;
 Locker HWCSession::locker_[HWCCallbacks::kNumDisplays];
 bool HWCSession::power_on_pending_[HWCCallbacks::kNumDisplays];
-
+Locker HWCSession::power_state_[HWCCallbacks::kNumDisplays];
+Locker HWCSession::display_config_locker_;
 static const int kSolidFillDelay = 100 * 1000;
 int HWCSession::null_display_mode_ = 0;
 
@@ -213,6 +214,11 @@
     g_hwc_uevent_.Register(this);
   }
 
+  int value = 0;
+  Debug::Get()->GetProperty(ENABLE_ASYNC_POWERMODE, &value);
+  async_powermode_ = (value == 1);
+  DLOGI("builtin_powermode_override: %d", async_powermode_);
+
   InitSupportedDisplaySlots();
   // Create primary display here. Remaining builtin displays will be created after client has set
   // display indexes which may happen sometime before callback is registered.
@@ -344,6 +350,19 @@
 
   // resize HDR supported map to total number of displays.
   is_hdr_display_.resize(UINT32(base_id));
+
+  if (!async_powermode_) {
+    return;
+  }
+
+  int start_index = HWCCallbacks::kNumRealDisplays;
+  std::vector<DisplayMapInfo> map_info = {map_info_primary_};
+  std::copy(map_info_builtin_.begin(), map_info_builtin_.end(), std::back_inserter(map_info));
+  std::copy(map_info_pluggable_.begin(), map_info_pluggable_.end(), std::back_inserter(map_info));
+  for (auto &map : map_info) {
+    DLOGI("Display Pairs: map.client_id: %d, start_index: %d", map.client_id, start_index);
+    map_hwc_display_.insert(std::make_pair(map.client_id, start_index++));
+  }
 }
 
 int HWCSession::GetDisplayIndex(int dpy) {
@@ -528,7 +547,7 @@
     *out_size = max_dump_size;
   } else {
     std::string s {};
-    for (int id = 0; id < HWCCallbacks::kNumDisplays; id++) {
+    for (int id = 0; id < HWCCallbacks::kNumRealDisplays; id++) {
       SCOPE_LOCK(locker_[id]);
       if (hwc_session->hwc_display_[id]) {
         s += hwc_session->hwc_display_[id]->Dump();
@@ -726,6 +745,14 @@
 
   hwc_session->HandleSecureSession();
   {
+    SCOPE_LOCK(power_state_[display]);
+    if (hwc_session->power_state_transition_[display]) {
+      // Route all interactions with client to dummy display.
+      display = hwc_session->map_hwc_display_.find(display)->second;
+    }
+  }
+
+  {
     SEQUENCE_EXIT_SCOPE_LOCK(locker_[display]);
     if (!hwc_session->hwc_display_[display]) {
       DLOGW("Removed Display : display = %" PRIu64, display);
@@ -757,6 +784,7 @@
 
   hwc_session->HandlePowerOnPending(display, *out_retire_fence);
   hwc_session->HandleHotplugPending(display, *out_retire_fence);
+  hwc_session->display_ready_.set(UINT32(display));
   hwc_session->HandlePendingRefresh();
 
   return INT32(status);
@@ -1003,10 +1031,18 @@
     return HWC2_ERROR_UNSUPPORTED;
   }
 
-  auto error = CallDisplayFunction(device, display, &HWCDisplay::SetPowerMode, mode,
+  bool override_mode = hwc_session->async_powermode_ &&
+                       hwc_session->display_ready_.test(UINT32(display));
+  if (!override_mode) {
+    auto error = CallDisplayFunction(device, display, &HWCDisplay::SetPowerMode, mode,
                                    false /* teardown */);
-  if (error != HWC2_ERROR_NONE) {
-    return error;
+    if (error != HWC2_ERROR_NONE) {
+      return error;
+    }
+  } else {
+    SCOPE_LOCK(locker_[display]);
+    // Update hwc state for now. Actual poweron will handled through DisplayConfig.
+    hwc_session->hwc_display_[display]->UpdatePowerMode(mode);
   }
   // Reset idle pc ref count on suspend, as we enable idle pc during suspend.
   if (mode == HWC2::PowerMode::Off) {
@@ -1075,8 +1111,17 @@
   if (display >= HWCCallbacks::kNumDisplays) {
     return HWC2_ERROR_BAD_DISPLAY;
   }
-  DTRACE_SCOPED();
+
   HWCSession *hwc_session = static_cast<HWCSession *>(device);
+
+  {
+    SCOPE_LOCK(power_state_[display]);
+    if (hwc_session->power_state_transition_[display]) {
+      // Route all interactions with client to dummy display.
+      display = hwc_session->map_hwc_display_.find(display)->second;
+    }
+  }
+  DTRACE_SCOPED();
   // TODO(user): Handle secure session, handle QDCM solid fill
   auto status = HWC2::Error::BadDisplay;
   hwc_session->HandleSecureSession();
@@ -2457,6 +2502,7 @@
       map_info_primary_.disp_type = info.display_type;
       map_info_primary_.sdm_id = info.display_id;
 
+      CreateDummyDisplay(HWC_DISPLAY_PRIMARY);
       color_mgr_ = HWCColorManager::CreateColorManager(&buffer_allocator_);
       if (!color_mgr_) {
         DLOGW("Failed to load HWCColorManager.");
@@ -2472,6 +2518,20 @@
   return status;
 }
 
+void HWCSession::CreateDummyDisplay(hwc2_display_t client_id) {
+  if (!async_powermode_) {
+    return;
+  }
+
+  hwc2_display_t dummy_disp_id = map_hwc_display_.find(client_id)->second;
+  auto hwc_display_dummy = &hwc_display_[dummy_disp_id];
+  HWCDisplayDummy::Create(core_intf_, &buffer_allocator_, &callbacks_, this, qservice_,
+                    0, 0, hwc_display_dummy);
+  if (!*hwc_display_dummy) {
+    DLOGE("Dummy display creation failed for %d display\n", client_id);
+  }
+}
+
 int HWCSession::HandleBuiltInDisplays() {
   if (null_display_mode_) {
     DLOGW("Skipped BuiltIn display handling in null-display mode");
@@ -2516,6 +2576,7 @@
         DLOGI("Builtin display created: sdm id = %d, client id = %d", info.display_id, client_id);
         map_info.disp_type = info.display_type;
         map_info.sdm_id = info.display_id;
+        CreateDummyDisplay(client_id);
       }
 
       DLOGI("Hotplugging builtin display, sdm id = %d, client id = %d", info.display_id, client_id);
@@ -2665,6 +2726,7 @@
         is_hdr_display_[UINT32(client_id)] = HasHDRSupport(hwc_display);
         DLOGI("Created pluggable display successfully: sdm id = %d, client id = %d",
               info.display_id, client_id);
+        CreateDummyDisplay(client_id);
       }
 
       map_info.disp_type = info.display_type;
@@ -2788,6 +2850,16 @@
       HWCDisplayPluggableTest::Destroy(hwc_display);
     }
 
+    if (async_powermode_) {
+      hwc2_display_t dummy_disp_id = map_hwc_display_.find(client_id)->second;
+      auto &hwc_display_dummy = hwc_display_[dummy_disp_id];
+      display_ready_.reset(UINT32(dummy_disp_id));
+      if (hwc_display_dummy) {
+        HWCDisplayDummy::Destroy(hwc_display_dummy);
+        hwc_display_dummy = nullptr;
+      }
+    }
+    display_ready_.reset(UINT32(client_id));
     hwc_display = nullptr;
     map_info->Reset();
   }
@@ -2813,7 +2885,17 @@
       break;
     }
 
+    if (async_powermode_ && map_info->disp_type == kBuiltIn) {
+      hwc2_display_t dummy_disp_id = map_hwc_display_.find(client_id)->second;
+      auto &hwc_display_dummy = hwc_display_[dummy_disp_id];
+      display_ready_.reset(UINT32(dummy_disp_id));
+      if (hwc_display_dummy) {
+        HWCDisplayDummy::Destroy(hwc_display_dummy);
+        hwc_display_dummy = nullptr;
+      }
+    }
     hwc_display = nullptr;
+    display_ready_.reset(UINT32(client_id));
     map_info->Reset();
 }
 
diff --git a/sdm/libs/hwc2/hwc_session.h b/sdm/libs/hwc2/hwc_session.h
index a1bd80a..a0995df 100644
--- a/sdm/libs/hwc2/hwc_session.h
+++ b/sdm/libs/hwc2/hwc_session.h
@@ -48,12 +48,14 @@
 #include <display_config.h>
 #include <vector>
 #include <utility>
+#include <map>
 
 #include "hwc_callbacks.h"
 #include "hwc_layers.h"
 #include "hwc_display.h"
 #include "hwc_display_builtin.h"
 #include "hwc_display_pluggable.h"
+#include "hwc_display_dummy.h"
 #include "hwc_display_virtual.h"
 #include "hwc_display_pluggable_test.h"
 #include "hwc_color_manager.h"
@@ -147,8 +149,16 @@
       return HWC2_ERROR_BAD_DISPLAY;
     }
 
-    SCOPE_LOCK(locker_[display]);
     HWCSession *hwc_session = static_cast<HWCSession *>(device);
+    {
+      // Power state transition start.
+      SCOPE_LOCK(power_state_[display]);
+      if (hwc_session->power_state_transition_[display]) {
+        display = hwc_session->map_hwc_display_.find(display)->second;
+      }
+    }
+
+    SCOPE_LOCK(locker_[display]);
     auto status = HWC2::Error::BadDisplay;
     if (hwc_session->hwc_display_[display]) {
       auto hwc_display = hwc_session->hwc_display_[display];
@@ -169,8 +179,16 @@
       return HWC2_ERROR_BAD_DISPLAY;
     }
 
-    SCOPE_LOCK(locker_[display]);
     HWCSession *hwc_session = static_cast<HWCSession *>(device);
+    {
+      // Power state transition start.
+      SCOPE_LOCK(power_state_[display]);
+      if (hwc_session->power_state_transition_[display]) {
+        display = hwc_session->map_hwc_display_.find(display)->second;
+      }
+    }
+
+    SCOPE_LOCK(locker_[display]);
     auto status = HWC2::Error::BadDisplay;
     if (hwc_session->hwc_display_[display]) {
       status = HWC2::Error::BadLayer;
@@ -236,6 +254,8 @@
                                 int32_t *out_support);
 
   static Locker locker_[HWCCallbacks::kNumDisplays];
+  static Locker power_state_[HWCCallbacks::kNumDisplays];
+  static Locker display_config_locker_;
 
  private:
   struct DisplayMapInfo {
@@ -269,6 +289,7 @@
   void InitSupportedDisplaySlots();
   int GetDisplayIndex(int dpy);
   int CreatePrimaryDisplay();
+  void CreateDummyDisplay(hwc2_display_t client_id);
   int HandleBuiltInDisplays();
   int HandlePluggableDisplays(bool delay_hotplug);
   int HandleConnectedDisplays(HWDisplaysInfo *hw_displays_info, bool delay_hotplug);
@@ -421,6 +442,7 @@
   std::vector<DisplayMapInfo> map_info_pluggable_;  // Pluggable displays excluding primary
   std::vector<DisplayMapInfo> map_info_virtual_;    // Virtual displays
   std::vector<bool> is_hdr_display_;    // info on HDR supported
+  std::map <hwc2_display_t, hwc2_display_t> map_hwc_display_;  // Real and dummy display pairs.
   bool reset_panel_ = false;
   bool client_connected_ = false;
   bool new_bw_mode_ = false;
@@ -445,6 +467,9 @@
   float set_max_lum_ = -1.0;
   float set_min_lum_ = -1.0;
   std::bitset<HWCCallbacks::kNumDisplays> pending_refresh_;
+  bool async_powermode_ = false;
+  bool power_state_transition_[HWCCallbacks::kNumDisplays] = {};  // +1 to account for primary.
+  std::bitset<HWCCallbacks::kNumDisplays> display_ready_;
 };
 
 }  // namespace sdm
diff --git a/sdm/libs/hwc2/hwc_session_services.cpp b/sdm/libs/hwc2/hwc_session_services.cpp
index 60786e7..dd12ade 100644
--- a/sdm/libs/hwc2/hwc_session_services.cpp
+++ b/sdm/libs/hwc2/hwc_session_services.cpp
@@ -666,12 +666,60 @@
 #endif
 
 #ifdef DISPLAY_CONFIG_1_7
-Return<int32_t> HWCSession::setPowerMode(uint32_t disp_id, PowerMode power_mode) {
-  return 0;
+Return<bool> HWCSession::isPowerModeOverrideSupported(uint32_t disp_id) {
+  if (!async_powermode_ || (disp_id > HWCCallbacks::kNumRealDisplays)) {
+    return false;
+  }
+
+  return true;
 }
 
-Return<bool> HWCSession::isPowerModeOverrideSupported(uint32_t disp_id) {
-  return false;
+Return<int32_t> HWCSession::setPowerMode(uint32_t disp_id, PowerMode power_mode) {
+  SCOPE_LOCK(display_config_locker_);
+
+  if (!isPowerModeOverrideSupported(disp_id)) {
+    return 0;
+  }
+
+  DLOGI("disp_id: %d power_mode: %d", disp_id, power_mode);
+  HWCDisplay::HWCLayerStack stack = {};
+  hwc2_display_t dummy_disp_id = map_hwc_display_.at(disp_id);
+
+  {
+    // Power state transition start.
+    Locker::ScopeLock lock_power(power_state_[disp_id]);
+    Locker::ScopeLock lock_primary(locker_[disp_id]);
+    Locker::ScopeLock lock_dummy(locker_[dummy_disp_id]);
+
+    power_state_transition_[disp_id] = true;
+    // Pass on the complete stack to dummy display.
+    hwc_display_[disp_id]->GetLayerStack(&stack);
+    // Update the same stack onto dummy display.
+    hwc_display_[dummy_disp_id]->SetLayerStack(&stack);
+  }
+
+  {
+    SCOPE_LOCK(locker_[disp_id]);
+    auto mode = static_cast<HWC2::PowerMode>(power_mode);
+    hwc_display_[disp_id]->SetPowerMode(mode, false /* teardown */);
+  }
+
+  {
+    // Power state transition end.
+    Locker::ScopeLock lock_power(power_state_[disp_id]);
+    Locker::ScopeLock lock_primary(locker_[disp_id]);
+    Locker::ScopeLock lock_dummy(locker_[dummy_disp_id]);
+    // Pass on the layer stack to real display.
+    hwc_display_[dummy_disp_id]->GetLayerStack(&stack);
+    // Update the same stack onto real display.
+    hwc_display_[disp_id]->SetLayerStack(&stack);
+    // Read display has got layerstack. Update the fences.
+    hwc_display_[disp_id]->PostPowerMode();
+
+    power_state_transition_[disp_id] = false;
+  }
+
+  return 0;
 }
 
 Return<bool> HWCSession::isHDRSupported(uint32_t disp_id) {