Merge "sdm:hwc2: Correct frame capture buffer stride value"
diff --git a/libdrmutils/drm_interface.h b/libdrmutils/drm_interface.h
index 35285ee..5dc5e4b 100644
--- a/libdrmutils/drm_interface.h
+++ b/libdrmutils/drm_interface.h
@@ -318,6 +318,13 @@
   V3,
 };
 
+/* QSEED3 Step version */
+enum struct QSEEDStepVersion {
+  V2,
+  V3,
+  V4,
+};
+
 enum struct SmartDMARevision {
   V1,
   V2,
@@ -372,6 +379,7 @@
   uint32_t max_vertical_deci;
   uint64_t max_pipe_bandwidth;
   uint32_t cache_size;  // cache size in bytes for inline rotation support.
+  QSEEDStepVersion qseed3_version;
 };
 
 // All DRM Planes as map<Plane_id , plane_type_info> listed from highest to lowest priority
@@ -422,6 +430,7 @@
   bool roi_merge;
   DRMRotation panel_orientation;
   drm_panel_hdr_properties panel_hdr_prop;
+  uint32_t transfer_time_us;
 };
 
 /* Identifier token for a display */
diff --git a/libgralloc1/gr_adreno_info.cpp b/libgralloc1/gr_adreno_info.cpp
index ecac238..f71f1d2 100644
--- a/libgralloc1/gr_adreno_info.cpp
+++ b/libgralloc1/gr_adreno_info.cpp
@@ -48,16 +48,12 @@
   lock_guard<mutex> obj(s_lock);
   if (!s_instance) {
     s_instance = new AdrenoMemInfo();
-    if (!s_instance->Init()) {
-      delete s_instance;
-      s_instance = nullptr;
-    }
   }
 
   return s_instance;
 }
 
-bool AdrenoMemInfo::Init() {
+AdrenoMemInfo::AdrenoMemInfo() {
   libadreno_utils_ = ::dlopen("libadreno_utils.so", RTLD_NOW);
   if (libadreno_utils_) {
     *reinterpret_cast<void **>(&LINK_adreno_compute_aligned_width_and_height) =
@@ -74,7 +70,6 @@
         ::dlsym(libadreno_utils_, "get_gpu_pixel_alignment");
   } else {
     ALOGE(" Failed to load libadreno_utils.so");
-    return false;
   }
 
   // Check if the overriding property debug.gralloc.gfx_ubwc_disable
@@ -91,8 +86,6 @@
        (!strncasecmp(property, "true", PROPERTY_VALUE_MAX)))) {
     map_fb_ = true;
   }
-
-  return true;
 }
 
 AdrenoMemInfo::~AdrenoMemInfo() {
@@ -170,6 +163,8 @@
         width, height, format, 0, raster_mode, padding_threshold,
         reinterpret_cast<int *>(aligned_w), reinterpret_cast<int *>(aligned_h), &bytesPerPixel);
   } else {
+    *aligned_w = (unsigned int)ALIGN(width, 32);
+    *aligned_h = (unsigned int)ALIGN(height, 32);
     ALOGW("%s: Warning!! compute_compressedfmt_aligned_width_and_height not found", __FUNCTION__);
   }
 }
diff --git a/libgralloc1/gr_adreno_info.h b/libgralloc1/gr_adreno_info.h
index 75ba507..b19256a 100644
--- a/libgralloc1/gr_adreno_info.h
+++ b/libgralloc1/gr_adreno_info.h
@@ -71,8 +71,6 @@
 
 class AdrenoMemInfo {
  public:
-  bool Init();
-
   /*
    * Function to compute aligned width and aligned height based on
    * width, height, format and usage flags.
@@ -123,6 +121,7 @@
   static AdrenoMemInfo *GetInstance();
 
  private:
+  AdrenoMemInfo();
   ~AdrenoMemInfo();
   // link(s)to adreno surface padding library.
   int (*LINK_adreno_compute_padding)(int width, int bpp, int surface_tile_height,
diff --git a/libgralloc1/gr_utils.cpp b/libgralloc1/gr_utils.cpp
index b3056e1..560bb08 100644
--- a/libgralloc1/gr_utils.cpp
+++ b/libgralloc1/gr_utils.cpp
@@ -509,7 +509,9 @@
     // Query GPU for UBWC only if buffer is intended to be used by GPU.
     if ((cons_usage & GRALLOC1_CONSUMER_USAGE_GPU_TEXTURE) ||
         (prod_usage & GRALLOC1_PRODUCER_USAGE_GPU_RENDER_TARGET)) {
-      enable = AdrenoMemInfo::GetInstance()->IsUBWCSupportedByGPU(format);
+      if (AdrenoMemInfo::GetInstance()) {
+        enable = AdrenoMemInfo::GetInstance()->IsUBWCSupportedByGPU(format);
+      }
     }
 
     // Allow UBWC, only if CPU usage flags are not set
@@ -675,8 +677,10 @@
   int tile = ubwc_enabled;
 
   if (IsUncompressedRGBFormat(format)) {
-    AdrenoMemInfo::GetInstance()->AlignUnCompressedRGB(width, height, format, tile, alignedw,
-                                                       alignedh);
+    if (AdrenoMemInfo::GetInstance()) {
+      AdrenoMemInfo::GetInstance()->AlignUnCompressedRGB(width, height, format, tile, alignedw,
+                                                         alignedh);
+    }
     return;
   }
 
@@ -686,7 +690,9 @@
   }
 
   if (IsCompressedRGBFormat(format)) {
-    AdrenoMemInfo::GetInstance()->AlignCompressedRGB(width, height, format, alignedw, alignedh);
+    if (AdrenoMemInfo::GetInstance()) {
+      AdrenoMemInfo::GetInstance()->AlignCompressedRGB(width, height, format, alignedw, alignedh);
+    }
     return;
   }
 
@@ -698,6 +704,9 @@
   switch (format) {
     case HAL_PIXEL_FORMAT_YCrCb_420_SP:
     case HAL_PIXEL_FORMAT_YCbCr_420_SP:
+      if (AdrenoMemInfo::GetInstance() == nullptr) {
+        return;
+      }
       alignment = AdrenoMemInfo::GetInstance()->GetGpuPixelAlignment();
       aligned_w = ALIGN(width, alignment);
       break;
diff --git a/sdm/include/private/hw_info_types.h b/sdm/include/private/hw_info_types.h
index 752d0c4..421277a 100644
--- a/sdm/include/private/hw_info_types.h
+++ b/sdm/include/private/hw_info_types.h
@@ -88,6 +88,7 @@
 enum HWAlphaInterpolation {
   kInterpolationPixelRepeat,
   kInterpolationBilinear,
+  kInterpolation2D,
   kInterpolationMax,
 };
 
@@ -136,6 +137,12 @@
   void Reset() { *this = HWRotatorInfo(); }
 };
 
+enum HWQseedStepVersion {
+  kQseed3v2,
+  kQseed3v3,
+  kQseed3v4,
+};
+
 struct HWDestScalarInfo {
   uint32_t count = 0;
   uint32_t max_input_width = 0;
@@ -205,6 +212,7 @@
   CompRatioMap comp_ratio_rt_map;
   CompRatioMap comp_ratio_nrt_map;
   uint32_t cache_size = 0;  // cache size in bytes
+  HWQseedStepVersion pipe_qseed3_version = kQseed3v2;  // only valid when has_qseed3=true
 
   void Reset() { *this = HWResourceInfo(); }
 };
@@ -274,6 +282,7 @@
   uint32_t blackness_level = 0;       // Panel's blackness level
   HWColorPrimaries primaries = {};    // WRGB color primaries
   HWPanelOrientation panel_orientation = {};  // Panel Orientation
+  uint32_t transfer_time_us = 0;      // transfer time in micro seconds to panel's active region
 
   bool operator !=(const HWPanelInfo &panel_info) {
     return ((port != panel_info.port) || (mode != panel_info.mode) ||
@@ -289,7 +298,8 @@
             (max_fps != panel_info.max_fps) || (is_primary_panel != panel_info.is_primary_panel) ||
             (split_info != panel_info.split_info) || (s3d_mode != panel_info.s3d_mode) ||
             (left_roi_count != panel_info.left_roi_count) ||
-            (right_roi_count != panel_info.right_roi_count));
+            (right_roi_count != panel_info.right_roi_count) ||
+            (transfer_time_us != panel_info.transfer_time_us));
   }
 
   bool operator ==(const HWPanelInfo &panel_info) {
diff --git a/sdm/libs/core/drm/hw_device_drm.cpp b/sdm/libs/core/drm/hw_device_drm.cpp
index 1ed9042..a5ed8a2 100644
--- a/sdm/libs/core/drm/hw_device_drm.cpp
+++ b/sdm/libs/core/drm/hw_device_drm.cpp
@@ -494,6 +494,7 @@
   hw_panel_info_.primaries.green[1] = connector_info_.panel_hdr_prop.display_primaries[5];
   hw_panel_info_.primaries.blue[0] = connector_info_.panel_hdr_prop.display_primaries[6];
   hw_panel_info_.primaries.blue[1] = connector_info_.panel_hdr_prop.display_primaries[7];
+  hw_panel_info_.transfer_time_us = connector_info_.transfer_time_us;
 
   // no supprt for 90 rotation only flips or 180 supported
   hw_panel_info_.panel_orientation.rotation = 0;
@@ -519,6 +520,7 @@
   DLOGI("FPS: min = %d, max =%d", hw_panel_info_.min_fps, hw_panel_info_.max_fps);
   DLOGI("Left Split = %d, Right Split = %d", hw_panel_info_.split_info.left_split,
         hw_panel_info_.split_info.right_split);
+  DLOGI("Panel Transfer time = %d us", hw_panel_info_.transfer_time_us);
 }
 
 void HWDeviceDRM::GetHWDisplayPortAndMode() {
@@ -792,6 +794,7 @@
   drm_atomic_intf_->Perform(DRMOps::CRTC_SET_DRAM_AB, token_.crtc_id, qos_data.dram_ab_bps);
   drm_atomic_intf_->Perform(DRMOps::CRTC_SET_DRAM_IB, token_.crtc_id, qos_data.dram_ib_bps);
   drm_atomic_intf_->Perform(DRMOps::CRTC_SET_ROT_CLK, token_.crtc_id, qos_data.rot_clock_hz);
+  drm_atomic_intf_->Perform(DRMOps::CRTC_SET_SECURITY_LEVEL, token_.crtc_id, crtc_security_level);
 
   DLOGI_IF(kTagDriverConfig, "System Clock=%d Hz, Core: AB=%llu Bps, IB=%llu Bps, " \
            "LLCC: AB=%llu Bps, IB=%llu Bps, DRAM AB=%llu Bps, IB=%llu Bps Rot Clock=%d",
diff --git a/sdm/libs/core/drm/hw_info_drm.cpp b/sdm/libs/core/drm/hw_info_drm.cpp
index 69af1bd..b8eb564 100644
--- a/sdm/libs/core/drm/hw_info_drm.cpp
+++ b/sdm/libs/core/drm/hw_info_drm.cpp
@@ -101,6 +101,23 @@
   char buf_[1024] = {};
 };
 
+static HWQseedStepVersion GetQseedStepVersion(sde_drm::QSEEDStepVersion drm_version) {
+  HWQseedStepVersion sdm_version;
+  switch (drm_version) {
+    case sde_drm::QSEEDStepVersion::V2:
+    default:
+      sdm_version = kQseed3v2;
+      break;
+    case sde_drm::QSEEDStepVersion::V3:
+      sdm_version = kQseed3v3;
+      break;
+    case sde_drm::QSEEDStepVersion::V4:
+      sdm_version = kQseed3v4;
+      break;
+  }
+  return sdm_version;
+}
+
 HWResourceInfo *HWInfoDRM::hw_resource_ = nullptr;
 
 HWInfoDRM::HWInfoDRM() {
@@ -374,6 +391,7 @@
   hw_resource->has_decimation = info.max_horizontal_deci > 1 && info.max_vertical_deci > 1;
   hw_resource->max_pipe_bw = info.max_pipe_bandwidth / kKiloUnit;
   hw_resource->cache_size = info.cache_size;
+  hw_resource->pipe_qseed3_version = GetQseedStepVersion(info.qseed3_version);
 }
 
 void HWInfoDRM::PopulateSupportedFmts(HWSubBlockType sub_blk_type,
diff --git a/sdm/libs/core/drm/hw_info_drm.h b/sdm/libs/core/drm/hw_info_drm.h
index 5d92c41..7883592 100644
--- a/sdm/libs/core/drm/hw_info_drm.h
+++ b/sdm/libs/core/drm/hw_info_drm.h
@@ -64,7 +64,6 @@
                              HWResourceInfo *hw_resource);
   void PopulatePipeCaps(const sde_drm::DRMPlaneTypeInfo &info, HWResourceInfo *hw_resource);
 
-
   sde_drm::DRMManagerInterface *drm_mgr_intf_ = {};
   bool default_mode_ = false;
 
diff --git a/sdm/libs/core/drm/hw_scale_drm.cpp b/sdm/libs/core/drm/hw_scale_drm.cpp
index 96f084e..de0ccb2 100644
--- a/sdm/libs/core/drm/hw_scale_drm.cpp
+++ b/sdm/libs/core/drm/hw_scale_drm.cpp
@@ -58,6 +58,8 @@
       return FILTER_ALPHA_DROP_REPEAT;
     case kInterpolationBilinear:
       return FILTER_ALPHA_BILINEAR;
+    case kInterpolation2D:
+      return FILTER_ALPHA_2D;
     default:
       DLOGE("Invalid Alpha Interpolation");
       return kInterpolationMax;
diff --git a/sdm/libs/hwc2/hwc_buffer_allocator.cpp b/sdm/libs/hwc2/hwc_buffer_allocator.cpp
index 3c8d460..4865e08 100644
--- a/sdm/libs/hwc2/hwc_buffer_allocator.cpp
+++ b/sdm/libs/hwc2/hwc_buffer_allocator.cpp
@@ -41,23 +41,43 @@
 
 namespace sdm {
 
-HWCBufferAllocator::HWCBufferAllocator() {
+DisplayError HWCBufferAllocator::Init() {
   int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module_);
   if (err != 0) {
-    DLOGE("FATAL: can not open GRALLOC module");
-  } else {
-    gralloc1_open(module_, &gralloc_device_);
+    DLOGE("FATAL: can not get GRALLOC module");
+    return kErrorResources;
   }
+
+  err = gralloc1_open(module_, &gralloc_device_);
+  if (err != 0) {
+    DLOGE("FATAL: can not open GRALLOC device");
+    return kErrorResources;
+  }
+
+  if (gralloc_device_ == nullptr) {
+    DLOGE("FATAL: gralloc device is null");
+    return kErrorResources;
+  }
+
   ReleaseBuffer_ = reinterpret_cast<GRALLOC1_PFN_RELEASE>(
       gralloc_device_->getFunction(gralloc_device_, GRALLOC1_FUNCTION_RELEASE));
   Perform_ = reinterpret_cast<GRALLOC1_PFN_PERFORM>(
       gralloc_device_->getFunction(gralloc_device_, GRALLOC1_FUNCTION_PERFORM));
+  Lock_ = reinterpret_cast<GRALLOC1_PFN_LOCK>(
+      gralloc_device_->getFunction(gralloc_device_, GRALLOC1_FUNCTION_LOCK));
+
+  return kErrorNone;
 }
 
-HWCBufferAllocator::~HWCBufferAllocator() {
+DisplayError HWCBufferAllocator::Deinit() {
   if (gralloc_device_ != nullptr) {
-    gralloc1_close(gralloc_device_);
+    int err = gralloc1_close(gralloc_device_);
+    if (err != 0) {
+      DLOGE("FATAL: can not close GRALLOC device");
+      return kErrorResources;
+    }
   }
+  return kErrorNone;
 }
 
 DisplayError HWCBufferAllocator::AllocateBuffer(BufferInfo *buffer_info) {
@@ -354,4 +374,21 @@
   return kErrorNone;
 }
 
+DisplayError HWCBufferAllocator::MapBuffer(const private_handle_t *handle, int acquire_fence) {
+  void* buffer_ptr = NULL;
+  const gralloc1_rect_t accessRegion = {
+        .left = 0,
+        .top = 0,
+        .width = 0,
+        .height = 0
+  };
+  Lock_(gralloc_device_, handle, GRALLOC1_PRODUCER_USAGE_CPU_READ, GRALLOC1_CONSUMER_USAGE_NONE,
+        &accessRegion, &buffer_ptr, acquire_fence);
+  if (!buffer_ptr) {
+    return kErrorUndefined;
+  }
+
+  return kErrorNone;
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/hwc2/hwc_buffer_allocator.h b/sdm/libs/hwc2/hwc_buffer_allocator.h
index d574401..8101d85 100644
--- a/sdm/libs/hwc2/hwc_buffer_allocator.h
+++ b/sdm/libs/hwc2/hwc_buffer_allocator.h
@@ -45,9 +45,8 @@
 
 class HWCBufferAllocator : public BufferAllocator {
  public:
-  HWCBufferAllocator();
-  ~HWCBufferAllocator();
-
+  DisplayError Init();
+  DisplayError Deinit();
   DisplayError AllocateBuffer(BufferInfo *buffer_info);
   DisplayError FreeBuffer(BufferInfo *buffer_info);
   uint32_t GetBufferSize(BufferInfo *buffer_info);
@@ -61,12 +60,14 @@
                                uint32_t stride[4], uint32_t offset[4],
                                uint32_t *num_planes);
   int SetBufferInfo(LayerBufferFormat format, int *target, uint64_t *flags);
+  DisplayError MapBuffer(const private_handle_t *handle, int acquire_fence);
 
  private:
   gralloc1_device_t *gralloc_device_ = nullptr;
   const hw_module_t *module_;
   GRALLOC1_PFN_RELEASE ReleaseBuffer_ = nullptr;
   GRALLOC1_PFN_PERFORM Perform_ = nullptr;
+  GRALLOC1_PFN_LOCK Lock_ = nullptr;
 };
 
 }  // namespace sdm
diff --git a/sdm/libs/hwc2/hwc_session.cpp b/sdm/libs/hwc2/hwc_session.cpp
index 7f39d7f..293ffce 100644
--- a/sdm/libs/hwc2/hwc_session.cpp
+++ b/sdm/libs/hwc2/hwc_session.cpp
@@ -35,6 +35,8 @@
 #include <algorithm>
 #include <string>
 #include <bitset>
+#include <thread>
+#include <memory>
 
 #include "hwc_buffer_allocator.h"
 #include "hwc_buffer_sync_handler.h"
@@ -66,8 +68,63 @@
 };
 
 namespace sdm {
+
+static HWCUEvent g_hwc_uevent_;
 Locker HWCSession::locker_;
 
+void HWCUEvent::UEventThread(HWCUEvent *hwc_uevent) {
+  const char *uevent_thread_name = "HWC_UeventThread";
+
+  prctl(PR_SET_NAME, uevent_thread_name, 0, 0, 0);
+  setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
+
+  int status = uevent_init();
+  if (!status) {
+    std::unique_lock<std::mutex> caller_lock(hwc_uevent->mutex_);
+    hwc_uevent->caller_cv_.notify_one();
+    DLOGE("Failed to init uevent with err %d", status);
+    return;
+  }
+
+  {
+    // Signal caller thread that worker thread is ready to listen to events.
+    std::unique_lock<std::mutex> caller_lock(hwc_uevent->mutex_);
+    hwc_uevent->init_done_ = true;
+    hwc_uevent->caller_cv_.notify_one();
+  }
+
+  while (1) {
+    char uevent_data[PAGE_SIZE] = {};
+
+    // keep last 2 zeroes to ensure double 0 termination
+    int length = uevent_next_event(uevent_data, INT32(sizeof(uevent_data)) - 2);
+
+    // scope of lock to this block only, so that caller is free to set event handler to nullptr;
+    {
+      std::lock_guard<std::mutex> guard(hwc_uevent->mutex_);
+      if (hwc_uevent->uevent_listener_) {
+        hwc_uevent->uevent_listener_->UEventHandler(uevent_data, length);
+      } else {
+        DLOGW("UEvent dropped. No uevent listener.");
+      }
+    }
+  }
+}
+
+HWCUEvent::HWCUEvent() {
+  std::unique_lock<std::mutex> caller_lock(mutex_);
+  std::thread thread(HWCUEvent::UEventThread, this);
+  thread.detach();
+  caller_cv_.wait(caller_lock);
+}
+
+void HWCUEvent::Register(HWCUEventListener *uevent_listener) {
+  DLOGI("Set uevent listener = %p", uevent_listener);
+
+  std::lock_guard<std::mutex> obj(mutex_);
+  uevent_listener_ = uevent_listener;
+}
+
 HWCSession::HWCSession(const hw_module_t *module) {
   hwc2_device_t::common.tag = HARDWARE_DEVICE_TAG;
   hwc2_device_t::common.version = HWC_DEVICE_API_VERSION_2_0;
@@ -81,6 +138,10 @@
   int status = -EINVAL;
   const char *qservice_name = "display.qservice";
 
+  if (!g_hwc_uevent_.InitDone()) {
+    return status;
+  }
+
   // Start QService and connect to it.
   qService::QService::init();
   android::sp<qService::IQService> iqservice = android::interface_cast<qService::IQService>(
@@ -96,19 +157,29 @@
 
   StartServices();
 
-  DisplayError error = CoreInterface::CreateCore(HWCDebugHandler::Get(), &buffer_allocator_,
-                                                 &buffer_sync_handler_, &socket_handler_,
-                                                 &core_intf_);
+  DisplayError error = buffer_allocator_.Init();
   if (error != kErrorNone) {
+    DLOGE("Buffer allocaor initialization failed. Error = %d", error);
+    return -EINVAL;
+  }
+
+  error = CoreInterface::CreateCore(HWCDebugHandler::Get(), &buffer_allocator_,
+                                    &buffer_sync_handler_, &socket_handler_, &core_intf_);
+  if (error != kErrorNone) {
+    buffer_allocator_.Deinit();
     DLOGE("Display core initialization failed. Error = %d", error);
     return -EINVAL;
   }
 
+  g_hwc_uevent_.Register(this);
+
   // If HDMI display is primary display, defer display creation until hotplug event is received.
   HWDisplayInterfaceInfo hw_disp_info = {};
   error = core_intf_->GetFirstDisplayInterfaceType(&hw_disp_info);
   if (error != kErrorNone) {
+    g_hwc_uevent_.Register(nullptr);
     CoreInterface::DestroyCore();
+    buffer_allocator_.Deinit();
     DLOGE("Primary display type not recognized. Error = %d", error);
     return -EINVAL;
   }
@@ -132,7 +203,9 @@
   }
 
   if (status) {
+    g_hwc_uevent_.Register(nullptr);
     CoreInterface::DestroyCore();
+    buffer_allocator_.Deinit();
     return status;
   }
 
@@ -146,9 +219,6 @@
     }
   }
 
-  std::thread uevent_thread(HWCUeventThread, this);
-  uevent_thread_.swap(uevent_thread);
-
   return 0;
 }
 
@@ -167,10 +237,7 @@
     color_mgr_->DestroyColorManager();
   }
 
-  DLOGD("Terminating uevent thread");
-  uevent_thread_exit_ = true;
-  // todo(user): add a local event to interrupt thread execution and join.
-  uevent_thread_.detach();
+  g_hwc_uevent_.Register(nullptr);
 
   DisplayError error = CoreInterface::DestroyCore();
   if (error != kErrorNone) {
@@ -1238,52 +1305,26 @@
   return (ret ? -EINVAL : 0);
 }
 
-void *HWCSession::HWCUeventThread(void *context) {
-  if (context) {
-    return reinterpret_cast<HWCSession *>(context)->HWCUeventThreadHandler();
-  }
-
-  return NULL;
-}
-
-void *HWCSession::HWCUeventThreadHandler() {
-  static char uevent_data[PAGE_SIZE];
-  int length = 0;
-  prctl(PR_SET_NAME, uevent_thread_name_, 0, 0, 0);
-  setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
-  if (!uevent_init()) {
-    DLOGE("Failed to init uevent");
-    pthread_exit(0);
-    return NULL;
-  }
-
-  while (!uevent_thread_exit_) {
-    // keep last 2 zeroes to ensure double 0 termination
-    length = uevent_next_event(uevent_data, INT32(sizeof(uevent_data)) - 2);
-
-    if (strcasestr(HWC_UEVENT_SWITCH_HDMI, uevent_data)) {
-      DLOGI("Uevent HDMI = %s", uevent_data);
-      int connected = GetEventValue(uevent_data, length, "SWITCH_STATE=");
-      if (connected >= 0) {
-        DLOGI("HDMI = %s", connected ? "connected" : "disconnected");
-        if (HotPlugHandler(connected) == -1) {
-          DLOGE("Failed handling Hotplug = %s", connected ? "connected" : "disconnected");
-        }
+void HWCSession::UEventHandler(const char *uevent_data, int length) {
+  if (strcasestr(uevent_data, HWC_UEVENT_SWITCH_HDMI)) {
+    DLOGI("Uevent HDMI = %s", uevent_data);
+    int connected = GetEventValue(uevent_data, length, "SWITCH_STATE=");
+    if (connected >= 0) {
+      DLOGI("HDMI = %s", connected ? "connected" : "disconnected");
+      if (HotPlugHandler(connected) == -1) {
+        DLOGE("Failed handling Hotplug = %s", connected ? "connected" : "disconnected");
       }
-    } else if (strcasestr(HWC_UEVENT_GRAPHICS_FB0, uevent_data)) {
-      DLOGI("Uevent FB0 = %s", uevent_data);
-      int panel_reset = GetEventValue(uevent_data, length, "PANEL_ALIVE=");
-      if (panel_reset == 0) {
-        callbacks_.Refresh(0);
-        reset_panel_ = true;
-      }
-    } else if (strcasestr(uevent_data, HWC_UEVENT_DRM_EXT_HOTPLUG)) {
-      HandleExtHPD(uevent_data, length);
     }
+  } else if (strcasestr(uevent_data, HWC_UEVENT_GRAPHICS_FB0)) {
+    DLOGI("Uevent FB0 = %s", uevent_data);
+    int panel_reset = GetEventValue(uevent_data, length, "PANEL_ALIVE=");
+    if (panel_reset == 0) {
+      callbacks_.Refresh(0);
+      reset_panel_ = true;
+    }
+  } else if (strcasestr(uevent_data, HWC_UEVENT_DRM_EXT_HOTPLUG)) {
+    HandleExtHPD(uevent_data, length);
   }
-  pthread_exit(0);
-
-  return NULL;
 }
 
 void HWCSession::HandleExtHPD(const char *uevent_data, int length) {
diff --git a/sdm/libs/hwc2/hwc_session.h b/sdm/libs/hwc2/hwc_session.h
index cd9831d..7d2a30c 100644
--- a/sdm/libs/hwc2/hwc_session.h
+++ b/sdm/libs/hwc2/hwc_session.h
@@ -23,7 +23,6 @@
 #include <vendor/display/config/1.0/IDisplayConfig.h>
 #include <core/core_interface.h>
 #include <utils/locker.h>
-#include <thread>
 
 #include "hwc_callbacks.h"
 #include "hwc_layers.h"
@@ -39,7 +38,32 @@
 using ::vendor::display::config::V1_0::IDisplayConfig;
 using ::android::hardware::Return;
 
-class HWCSession : hwc2_device_t, public IDisplayConfig, public qClient::BnQClient {
+// Create a singleton uevent listener thread valid for life of hardware composer process.
+// This thread blocks on uevents poll inside uevent library implementation. This poll exits
+// only when there is a valid uevent, it can not be interrupted otherwise. Tieing life cycle
+// of this thread with HWC session cause HWC deinitialization to wait infinitely for the
+// thread to exit.
+class HWCUEventListener {
+ public:
+  virtual ~HWCUEventListener() {}
+  virtual void UEventHandler(const char *uevent_data, int length) = 0;
+};
+
+class HWCUEvent {
+ public:
+  HWCUEvent();
+  static void UEventThread(HWCUEvent *hwc_event);
+  void Register(HWCUEventListener *uevent_listener);
+  inline bool InitDone() { return init_done_; }
+
+ private:
+  std::mutex mutex_;
+  std::condition_variable caller_cv_;
+  HWCUEventListener *uevent_listener_ = nullptr;
+  bool init_done_ = false;
+};
+
+class HWCSession : hwc2_device_t, HWCUEventListener, IDisplayConfig, public qClient::BnQClient {
  public:
   struct HWCModuleMethods : public hw_module_methods_t {
     HWCModuleMethods() { hw_module_methods_t::open = HWCSession::Open; }
@@ -120,9 +144,8 @@
                               int32_t *outCapabilities);
   static hwc2_function_pointer_t GetFunction(struct hwc2_device *device, int32_t descriptor);
 
-  // Uevent thread
-  static void *HWCUeventThread(void *context);
-  void *HWCUeventThreadHandler();
+  // Uevent handler
+  virtual void UEventHandler(const char *uevent_data, int length);
   int GetEventValue(const char *uevent_data, int length, const char *event_info);
   void HandleExtHPD(const char *uevent_data, int length);
   int HotPlugHandler(bool connected);
@@ -192,9 +215,6 @@
   CoreInterface *core_intf_ = nullptr;
   HWCDisplay *hwc_display_[HWC_NUM_DISPLAY_TYPES] = {nullptr};
   HWCCallbacks callbacks_;
-  std::thread uevent_thread_;
-  bool uevent_thread_exit_ = false;
-  const char *uevent_thread_name_ = "HWC_UeventThread";
   HWCBufferAllocator buffer_allocator_;
   HWCBufferSyncHandler buffer_sync_handler_;
   HWCColorManager *color_mgr_ = nullptr;
diff --git a/sdm/libs/hwc2/hwc_tonemapper.cpp b/sdm/libs/hwc2/hwc_tonemapper.cpp
index 2626d93..a224e8d 100644
--- a/sdm/libs/hwc2/hwc_tonemapper.cpp
+++ b/sdm/libs/hwc2/hwc_tonemapper.cpp
@@ -289,6 +289,7 @@
 }
 
 void HWCToneMapper::DumpToneMapOutput(ToneMapSession *session, int *acquire_fd) {
+  DisplayError error = kErrorNone;
   if (!dump_frame_count_) {
     return;
   }
@@ -304,6 +305,12 @@
     }
   }
 
+  error = buffer_allocator_->MapBuffer(target_buffer, *acquire_fd);
+  if (error != kErrorNone) {
+    DLOGE("MapBuffer failed, base addr = %x", target_buffer->base);
+    return;
+  }
+
   size_t result = 0;
   char dump_file_name[PATH_MAX];
   snprintf(dump_file_name, sizeof(dump_file_name), "%s/frame_dump_primary"