sdm: hwc2: Add support for HDMI as primary.

- Create external display if HDMI is already
  connected on boot.
- Wait for hotplug connect event if HDMI is not
  connected on boot.
- Notify surfaceflinger only once when primary
  display is created for first time.
- Destroy and recreate SDM external display on
  successive disconnect/connect. Do not notify
  surfaceflinger any subsequent hotplug events.
- Add null display implementation to route any
  incoming calls from surfaceflinger when display
  is disconnected.

CRs-Fixed: 2037067
Change-Id: I649756452d714e538c313cf80c0144934ad494f9
diff --git a/sdm/libs/hwc2/Android.mk b/sdm/libs/hwc2/Android.mk
index 592dc02..9f1f3c7 100644
--- a/sdm/libs/hwc2/Android.mk
+++ b/sdm/libs/hwc2/Android.mk
@@ -39,7 +39,8 @@
                                  hwc_callbacks.cpp \
                                  ../hwc/cpuhint.cpp \
                                  ../hwc/hwc_socket_handler.cpp \
-                                 hwc_tonemapper.cpp
+                                 hwc_tonemapper.cpp \
+                                 display_null.cpp
 
 ifneq ($(TARGET_USES_GRALLOC1), true)
     LOCAL_SRC_FILES += ../hwc/hwc_buffer_allocator.cpp
diff --git a/sdm/libs/hwc2/display_null.cpp b/sdm/libs/hwc2/display_null.cpp
new file mode 100644
index 0000000..16f4da6
--- /dev/null
+++ b/sdm/libs/hwc2/display_null.cpp
@@ -0,0 +1,68 @@
+/*
+* Copyright (c) 2017, 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
+* met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above
+*       copyright notice, this list of conditions and the following
+*       disclaimer in the documentation and/or other materials provided
+*       with the distribution.
+*     * Neither the name of The Linux Foundation nor the names of its
+*       contributors may be used to endorse or promote products derived
+*       from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "display_null.h"
+
+#define __CLASS__ "DisplayNull"
+
+namespace sdm {
+
+DisplayError DisplayNull::Commit(LayerStack *layer_stack) {
+  for (Layer *layer : layer_stack->layers) {
+    if (layer->composition != kCompositionGPUTarget) {
+      layer->composition = kCompositionSDE;
+      layer->input_buffer.release_fence_fd = -1;
+    }
+  }
+  layer_stack->retire_fence_fd = -1;
+
+  return kErrorNone;
+}
+
+DisplayError DisplayNull::GetDisplayState(DisplayState *state) {
+  *state = state_;
+  return kErrorNone;
+}
+
+DisplayError DisplayNull::SetDisplayState(DisplayState state) {
+  state_ = state;
+  return kErrorNone;
+}
+
+DisplayError DisplayNull::SetFrameBufferConfig(const DisplayConfigVariableInfo &variable_info) {
+  fb_config_ = variable_info;
+  return kErrorNone;
+}
+
+DisplayError DisplayNull::GetFrameBufferConfig(DisplayConfigVariableInfo *variable_info) {
+  *variable_info = fb_config_;
+  return kErrorNone;
+}
+
+}  // namespace sdm
diff --git a/sdm/libs/hwc2/display_null.h b/sdm/libs/hwc2/display_null.h
new file mode 100644
index 0000000..fa88c28
--- /dev/null
+++ b/sdm/libs/hwc2/display_null.h
@@ -0,0 +1,107 @@
+/*
+* Copyright (c) 2017, 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
+* met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above
+*       copyright notice, this list of conditions and the following
+*       disclaimer in the documentation and/or other materials provided
+*       with the distribution.
+*     * Neither the name of The Linux Foundation nor the names of its
+*       contributors may be used to endorse or promote products derived
+*       from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef __DISPLAY_NULL_H__
+#define __DISPLAY_NULL_H__
+
+#include <core/display_interface.h>
+#include <string>
+#include <vector>
+
+namespace sdm {
+
+#define MAKE_NO_OP(virtual_method_name) \
+      virtual DisplayError virtual_method_name { return kErrorNone; }
+
+class DisplayNull : public DisplayInterface {
+ public:
+  virtual ~DisplayNull() { }
+  virtual DisplayError Commit(LayerStack *layer_stack);
+  virtual DisplayError GetDisplayState(DisplayState *state);
+  virtual DisplayError SetDisplayState(DisplayState state);
+  virtual DisplayError SetFrameBufferConfig(const DisplayConfigVariableInfo &variable_info);
+  virtual DisplayError GetFrameBufferConfig(DisplayConfigVariableInfo *variable_info);
+  virtual bool IsUnderscanSupported() { return true; }
+  virtual void SetIdleTimeoutMs(uint32_t active_ms) { }
+  virtual bool IsPrimaryDisplay() { return true; }
+
+  void SetActive(bool active) {
+    active_ = active;
+  }
+
+  bool IsActive() {
+    return active_;
+  }
+
+  MAKE_NO_OP(Prepare(LayerStack *))
+  MAKE_NO_OP(Flush())
+  MAKE_NO_OP(GetNumVariableInfoConfigs(uint32_t *))
+  MAKE_NO_OP(GetConfig(uint32_t, DisplayConfigVariableInfo *))
+  MAKE_NO_OP(GetConfig(DisplayConfigFixedInfo *))
+  MAKE_NO_OP(GetActiveConfig(uint32_t *))
+  MAKE_NO_OP(GetVSyncState(bool *))
+  MAKE_NO_OP(SetActiveConfig(uint32_t))
+  MAKE_NO_OP(SetActiveConfig(DisplayConfigVariableInfo *))
+  MAKE_NO_OP(SetMaxMixerStages(uint32_t))
+  MAKE_NO_OP(ControlPartialUpdate(bool, uint32_t *))
+  MAKE_NO_OP(DisablePartialUpdateOneFrame())
+  MAKE_NO_OP(SetDisplayMode(uint32_t))
+  MAKE_NO_OP(SetPanelBrightness(int))
+  MAKE_NO_OP(CachePanelBrightness(int))
+  MAKE_NO_OP(OnMinHdcpEncryptionLevelChange(uint32_t))
+  MAKE_NO_OP(ColorSVCRequestRoute(const PPDisplayAPIPayload &, PPDisplayAPIPayload *,
+                                  PPPendingParams *))
+  MAKE_NO_OP(GetColorModeCount(uint32_t *))
+  MAKE_NO_OP(GetColorModes(uint32_t *, std::vector<std::string> *))
+  MAKE_NO_OP(GetColorModeAttr(const std::string &, AttrVal *))
+  MAKE_NO_OP(SetColorMode(const std::string &))
+  MAKE_NO_OP(SetColorModeById(int32_t))
+  MAKE_NO_OP(SetColorTransform(const uint32_t, const double *))
+  MAKE_NO_OP(GetDefaultColorMode(std::string *))
+  MAKE_NO_OP(ApplyDefaultDisplayMode())
+  MAKE_NO_OP(SetCursorPosition(int, int))
+  MAKE_NO_OP(GetRefreshRateRange(uint32_t *, uint32_t *))
+  MAKE_NO_OP(SetRefreshRate(uint32_t, bool))
+  MAKE_NO_OP(GetPanelBrightness(int *))
+  MAKE_NO_OP(SetVSyncState(bool))
+  MAKE_NO_OP(SetMixerResolution(uint32_t, uint32_t))
+  MAKE_NO_OP(GetMixerResolution(uint32_t *, uint32_t *))
+  MAKE_NO_OP(SetDetailEnhancerData(const DisplayDetailEnhancerData &))
+  MAKE_NO_OP(GetDisplayPort(DisplayPort *))
+  MAKE_NO_OP(SetCompositionState(LayerComposition, bool))
+
+ private:
+  bool active_ = false;
+  DisplayState state_ = kStateOff;
+  DisplayConfigVariableInfo fb_config_ = {};
+};
+
+}  // namespace sdm
+
+#endif  // __DISPLAY_NULL_H__
diff --git a/sdm/libs/hwc2/hwc_display.cpp b/sdm/libs/hwc2/hwc_display.cpp
index 69b0acd..f3c81f2 100644
--- a/sdm/libs/hwc2/hwc_display.cpp
+++ b/sdm/libs/hwc2/hwc_display.cpp
@@ -1598,6 +1598,7 @@
     auto layer = hwc_layer->GetSDMLayer();
     layer->composition = kCompositionSDE;
   }
+  validated_ = true;
 }
 
 void HWCDisplay::MarkLayersForClientComposition() {
diff --git a/sdm/libs/hwc2/hwc_display.h b/sdm/libs/hwc2/hwc_display.h
index 9ee7811..b70f160 100644
--- a/sdm/libs/hwc2/hwc_display.h
+++ b/sdm/libs/hwc2/hwc_display.h
@@ -159,6 +159,9 @@
     return INT32(status);
   }
 
+  virtual int SetState(bool connected) {
+    return kErrorNotSupported;
+  }
   int SetPanelBrightness(int level);
   int GetPanelBrightness(int *level);
   int ToggleScreenUpdates(bool enable);
diff --git a/sdm/libs/hwc2/hwc_display_external.cpp b/sdm/libs/hwc2/hwc_display_external.cpp
index a8f8480..e89451d 100644
--- a/sdm/libs/hwc2/hwc_display_external.cpp
+++ b/sdm/libs/hwc2/hwc_display_external.cpp
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2014 - 2016, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014-2017, 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
@@ -64,6 +64,7 @@
 
   error = hwc_display_external->GetMixerResolution(&external_width, &external_height);
   if (error != kErrorNone) {
+    Destroy(hwc_display_external);
     return -EINVAL;
   }
 
@@ -215,4 +216,65 @@
   }
 }
 
+int HWCDisplayExternal::SetState(bool connected) {
+  DisplayError error = kErrorNone;
+  DisplayState state = kStateOff;
+  DisplayConfigVariableInfo fb_config = {};
+
+  if (connected) {
+    if (display_null_.IsActive()) {
+      error = core_intf_->CreateDisplay(type_, this, &display_intf_);
+      if (error != kErrorNone) {
+        DLOGE("Display create failed. Error = %d display_type %d event_handler %p disp_intf %p",
+              error, type_, this, &display_intf_);
+        return -EINVAL;
+      }
+
+      // Restore HDMI attributes when display is reconnected.
+      // This is to ensure that surfaceflinger & sdm are in sync.
+      display_null_.GetFrameBufferConfig(&fb_config);
+      int status = SetFrameBufferResolution(fb_config.x_pixels, fb_config.y_pixels);
+      if (status) {
+        DLOGW("Set frame buffer config failed. Error = %d", error);
+        return -1;
+      }
+
+      display_null_.GetDisplayState(&state);
+      display_intf_->SetDisplayState(state);
+
+      SetVsyncEnabled(HWC2::Vsync::Enable);
+
+      display_null_.SetActive(false);
+      DLOGI("Display is connected successfully.");
+    } else {
+      DLOGI("Display is already connected.");
+    }
+  } else {
+    if (!display_null_.IsActive()) {
+      // Preserve required attributes of HDMI display that surfaceflinger sees.
+      // Restore HDMI attributes when display is reconnected.
+      display_intf_->GetDisplayState(&state);
+      display_null_.SetDisplayState(state);
+
+      error = display_intf_->GetFrameBufferConfig(&fb_config);
+      if (error != kErrorNone) {
+        DLOGW("Get frame buffer config failed. Error = %d", error);
+        return -1;
+      }
+      display_null_.SetFrameBufferConfig(fb_config);
+
+      SetVsyncEnabled(HWC2::Vsync::Disable);
+      core_intf_->DestroyDisplay(display_intf_);
+      display_intf_ = &display_null_;
+
+      display_null_.SetActive(true);
+      DLOGI("Display is disconnected successfully.");
+    } else {
+      DLOGI("Display is already disconnected.");
+    }
+  }
+
+  return 0;
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/hwc2/hwc_display_external.h b/sdm/libs/hwc2/hwc_display_external.h
index 802a77c..7aa84b2 100644
--- a/sdm/libs/hwc2/hwc_display_external.h
+++ b/sdm/libs/hwc2/hwc_display_external.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2017, 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
@@ -31,6 +31,7 @@
 #define __HWC_DISPLAY_EXTERNAL_H__
 
 #include "hwc_display.h"
+#include "display_null.h"
 
 namespace sdm {
 
@@ -47,6 +48,7 @@
   virtual HWC2::Error Validate(uint32_t *out_num_types, uint32_t *out_num_requests);
   virtual HWC2::Error Present(int32_t *out_retire_fence);
   virtual void SetSecureDisplay(bool secure_display_active);
+  virtual int SetState(bool connected);
 
  private:
   HWCDisplayExternal(CoreInterface *core_intf, HWCBufferAllocator *buffer_allocator,
@@ -54,6 +56,8 @@
   void ApplyScanAdjustment(hwc_rect_t *display_frame);
   static void GetDownscaleResolution(uint32_t primary_width, uint32_t primary_height,
                                      uint32_t *virtual_width, uint32_t *virtual_height);
+
+  DisplayNull display_null_;
 };
 
 }  // namespace sdm
diff --git a/sdm/libs/hwc2/hwc_session.cpp b/sdm/libs/hwc2/hwc_session.cpp
index a6739b2..b305a9a 100644
--- a/sdm/libs/hwc2/hwc_session.cpp
+++ b/sdm/libs/hwc2/hwc_session.cpp
@@ -96,9 +96,7 @@
 
   StartServices();
 
-  buffer_allocator_ = new HWCBufferAllocator();
-
-  DisplayError error = CoreInterface::CreateCore(HWCDebugHandler::Get(), buffer_allocator_,
+  DisplayError error = CoreInterface::CreateCore(HWCDebugHandler::Get(), &buffer_allocator_,
                                                  &buffer_sync_handler_, &socket_handler_,
                                                  &core_intf_);
   if (error != kErrorNone) {
@@ -106,20 +104,31 @@
     return -EINVAL;
   }
 
-  // Read which display is first, and create it and store it in primary slot
-  // TODO(user): This will need to be redone for HWC2 - right now we validate only
-  // the primary physical path
-  HWDisplayInterfaceInfo hw_disp_info;
+  // 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 && hw_disp_info.type == kHDMI && hw_disp_info.is_connected) {
-    // HDMI is primary display. If already connected, then create it and store in
-    // primary display slot. If not connected, create a NULL display for now.
-    status = HWCDisplayExternal::Create(core_intf_, buffer_allocator_, &callbacks_, qservice_,
-                                        &hwc_display_[HWC_DISPLAY_PRIMARY]);
+  if (error != kErrorNone) {
+    CoreInterface::DestroyCore();
+    DLOGE("Primary display type not recognized. Error = %d", error);
+    return -EINVAL;
+  }
+
+  if (hw_disp_info.type == kHDMI) {
+    status = 0;
+    hdmi_is_primary_ = true;
+    // Create display if it is connected, else wait for hotplug connect event.
+    if (hw_disp_info.is_connected) {
+      status = HWCDisplayExternal::Create(core_intf_, &buffer_allocator_, &callbacks_, qservice_,
+                                          &hwc_display_[HWC_DISPLAY_PRIMARY]);
+    }
   } else {
     // Create and power on primary display
-    status = HWCDisplayPrimary::Create(core_intf_, buffer_allocator_, &callbacks_, qservice_,
+    status = HWCDisplayPrimary::Create(core_intf_, &buffer_allocator_, &callbacks_, qservice_,
                                        &hwc_display_[HWC_DISPLAY_PRIMARY]);
+    color_mgr_ = HWCColorManager::CreateColorManager(&buffer_allocator_);
+    if (!color_mgr_) {
+      DLOGW("Failed to load HWCColorManager.");
+    }
   }
 
   if (status) {
@@ -127,19 +136,6 @@
     return status;
   }
 
-  color_mgr_ = HWCColorManager::CreateColorManager(buffer_allocator_);
-  if (!color_mgr_) {
-    DLOGW("Failed to load HWCColorManager.");
-  }
-
-  if (pthread_create(&uevent_thread_, NULL, &HWCUeventThread, this) < 0) {
-    DLOGE("Failed to start = %s, error = %s", uevent_thread_name_, strerror(errno));
-    HWCDisplayPrimary::Destroy(hwc_display_[HWC_DISPLAY_PRIMARY]);
-    hwc_display_[HWC_DISPLAY_PRIMARY] = 0;
-    CoreInterface::DestroyCore();
-    return -errno;
-  }
-
   struct rlimit fd_limit = {};
   getrlimit(RLIMIT_NOFILE, &fd_limit);
   fd_limit.rlim_cur = fd_limit.rlim_cur * 2;
@@ -149,31 +145,38 @@
       DLOGW("Unable to increase fd limit -  err:%d, %s", errno, strerror(errno));
     }
   }
+
+  std::thread uevent_thread(HWCUeventThread, this);
+  uevent_thread_.swap(uevent_thread);
+
   return 0;
 }
 
 int HWCSession::Deinit() {
-  HWCDisplayPrimary::Destroy(hwc_display_[HWC_DISPLAY_PRIMARY]);
-  hwc_display_[HWC_DISPLAY_PRIMARY] = 0;
+  HWCDisplay *primary_display = hwc_display_[HWC_DISPLAY_PRIMARY];
+  if (primary_display) {
+    if (hdmi_is_primary_) {
+      HWCDisplayExternal::Destroy(primary_display);
+    } else {
+      HWCDisplayPrimary::Destroy(primary_display);
+    }
+  }
+  hwc_display_[HWC_DISPLAY_PRIMARY] = nullptr;
+
   if (color_mgr_) {
     color_mgr_->DestroyColorManager();
   }
-  uevent_thread_exit_ = true;
+
   DLOGD("Terminating uevent thread");
-  // TODO(user): on restarting HWC in the same process, the uevent thread does not restart
-  // cleanly.
-  Sys::pthread_cancel_(uevent_thread_);
+  uevent_thread_exit_ = true;
+  // todo(user): add a local event to interrupt thread execution and join.
+  uevent_thread_.detach();
 
   DisplayError error = CoreInterface::DestroyCore();
   if (error != kErrorNone) {
     DLOGE("Display core de-initialization failed. Error = %d", error);
   }
 
-  if (buffer_allocator_ != nullptr) {
-    delete buffer_allocator_;
-  }
-  buffer_allocator_ = nullptr;
-
   return 0;
 }
 
@@ -436,8 +439,13 @@
   auto desc = static_cast<HWC2::Callback>(descriptor);
   auto error = hwc_session->callbacks_.Register(desc, callback_data, pointer);
   DLOGD("Registering callback: %s", to_string(desc).c_str());
-  if (descriptor == HWC2_CALLBACK_HOTPLUG)
-    hwc_session->callbacks_.Hotplug(HWC_DISPLAY_PRIMARY, HWC2::Connection::Connected);
+  if (descriptor == HWC2_CALLBACK_HOTPLUG) {
+    // If primary display (HDMI) is not created yet, wait for it to be hotplugged.
+    if (hwc_session->hwc_display_[HWC_DISPLAY_PRIMARY]) {
+      hwc_session->callbacks_.Hotplug(HWC_DISPLAY_PRIMARY, HWC2::Connection::Connected);
+    }
+  }
+
   return INT32(error);
 }
 
@@ -719,7 +727,7 @@
   if (hwc_display_[HWC_DISPLAY_VIRTUAL]) {
     return HWC2::Error::NoResources;
   }
-  auto status = HWCDisplayVirtual::Create(core_intf_, buffer_allocator_, &callbacks_, width,
+  auto status = HWCDisplayVirtual::Create(core_intf_, &buffer_allocator_, &callbacks_, width,
                                           height, format, &hwc_display_[HWC_DISPLAY_VIRTUAL]);
   // TODO(user): validate width and height support
   if (status)
@@ -738,7 +746,7 @@
   hwc_display_[HWC_DISPLAY_PRIMARY]->GetFrameBufferResolution(&primary_width, &primary_height);
 
   if (disp == HWC_DISPLAY_EXTERNAL) {
-    status = HWCDisplayExternal::Create(core_intf_, buffer_allocator_, &callbacks_, primary_width,
+    status = HWCDisplayExternal::Create(core_intf_, &buffer_allocator_, &callbacks_, primary_width,
                                         primary_height, qservice_, false, &hwc_display_[disp]);
   } else {
     DLOGE("Invalid display type");
@@ -1341,34 +1349,35 @@
 int HWCSession::HotPlugHandler(bool connected) {
   int status = 0;
   bool notify_hotplug = false;
-  bool hdmi_primary = false;
 
   // To prevent sending events to client while a lock is held, acquire scope locks only within
   // below scope so that those get automatically unlocked after the scope ends.
-  {
+  do {
     SEQUENCE_WAIT_SCOPE_LOCK(locker_);
 
+    // If HDMI is primary but not created yet (first time), create it and notify surfaceflinger.
+    //    if it is already created, but got disconnected/connected again,
+    //    just toggle display status and do not notify surfaceflinger.
+    // If HDMI is not primary, create/destroy external display normally.
+    if (hdmi_is_primary_) {
+      if (hwc_display_[HWC_DISPLAY_PRIMARY]) {
+        status = hwc_display_[HWC_DISPLAY_PRIMARY]->SetState(connected);
+      } else {
+        status = HWCDisplayExternal::Create(core_intf_, &buffer_allocator_, &callbacks_,
+                                            qservice_, &hwc_display_[HWC_DISPLAY_PRIMARY]);
+        notify_hotplug = true;
+      }
+
+      break;
+    }
+
+    // Primary display must be connected for HDMI as secondary cases.
     if (!hwc_display_[HWC_DISPLAY_PRIMARY]) {
       DLOGE("Primary display is not connected.");
       return -1;
     }
 
-    HWCDisplay *primary_display = hwc_display_[HWC_DISPLAY_PRIMARY];
-    HWCDisplay *external_display = NULL;
-
-    if (primary_display->GetDisplayClass() == DISPLAY_CLASS_EXTERNAL) {
-      external_display = static_cast<HWCDisplayExternal *>(hwc_display_[HWC_DISPLAY_PRIMARY]);
-      hdmi_primary = true;
-    }
-
-    // If primary display connected is a NULL display, then replace it with the external display
     if (connected) {
-      // If we are in HDMI as primary and the primary display just got plugged in
-      if (hwc_display_[HWC_DISPLAY_EXTERNAL]) {
-        DLOGE("HDMI is already connected");
-        return -1;
-      }
-
       // Connect external display if virtual display is not connected.
       // Else, defer external display connection and process it when virtual display
       // tears down; Do not notify SurfaceFlinger since connection is deferred now.
@@ -1386,39 +1395,28 @@
       // Do not return error if external display is not in connected status.
       // Due to virtual display concurrency, external display connection might be still pending
       // but hdmi got disconnected before pending connection could be processed.
-
-      if (hdmi_primary) {
-        assert(external_display != NULL);
-        uint32_t x_res, y_res;
-        external_display->GetFrameBufferResolution(&x_res, &y_res);
-        // Need to manually disable VSYNC as SF is not aware of connect/disconnect cases
-        // for HDMI as primary
-        external_display->SetVsyncEnabled(HWC2::Vsync::Disable);
-        HWCDisplayExternal::Destroy(external_display);
-
-        // In HWC2, primary displays can be hotplugged out
+      if (hwc_display_[HWC_DISPLAY_EXTERNAL]) {
+        status = DisconnectDisplay(HWC_DISPLAY_EXTERNAL);
         notify_hotplug = true;
-      } else {
-        if (hwc_display_[HWC_DISPLAY_EXTERNAL]) {
-          status = DisconnectDisplay(HWC_DISPLAY_EXTERNAL);
-          notify_hotplug = true;
-        }
-        external_pending_connect_ = false;
       }
+      external_pending_connect_ = false;
+    }
+  } while (0);
+
+  if (connected) {
+    callbacks_.Refresh(0);
+
+    if (!hdmi_is_primary_) {
+      // wait for sufficient time to ensure sufficient resources are available to process new
+      // new display connection.
+      uint32_t vsync_period = UINT32(GetVsyncPeriod(HWC_DISPLAY_PRIMARY));
+      usleep(vsync_period * 2 / 1000);
     }
   }
 
-  if (connected && notify_hotplug) {
-    // trigger screen refresh to ensure sufficient resources are available to process new
-    // new display connection.
-    callbacks_.Refresh(0);
-    uint32_t vsync_period = UINT32(GetVsyncPeriod(HWC_DISPLAY_PRIMARY));
-    usleep(vsync_period * 2 / 1000);
-  }
   // notify client
-  // Handle HDMI as primary here
   if (notify_hotplug) {
-    callbacks_.Hotplug(HWC_DISPLAY_EXTERNAL,
+    callbacks_.Hotplug(hdmi_is_primary_ ? HWC_DISPLAY_PRIMARY : HWC_DISPLAY_EXTERNAL,
                        connected ? HWC2::Connection::Connected : HWC2::Connection::Disconnected);
   }
 
diff --git a/sdm/libs/hwc2/hwc_session.h b/sdm/libs/hwc2/hwc_session.h
index be4494e..cd9831d 100644
--- a/sdm/libs/hwc2/hwc_session.h
+++ b/sdm/libs/hwc2/hwc_session.h
@@ -23,6 +23,7 @@
 #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"
@@ -188,23 +189,24 @@
   android::status_t SetColorModeById(const android::Parcel *input_parcel);
 
   static Locker locker_;
-  CoreInterface *core_intf_ = NULL;
-  HWCDisplay *hwc_display_[HWC_NUM_DISPLAY_TYPES] = {NULL};
+  CoreInterface *core_intf_ = nullptr;
+  HWCDisplay *hwc_display_[HWC_NUM_DISPLAY_TYPES] = {nullptr};
   HWCCallbacks callbacks_;
-  pthread_t uevent_thread_;
+  std::thread uevent_thread_;
   bool uevent_thread_exit_ = false;
   const char *uevent_thread_name_ = "HWC_UeventThread";
-  HWCBufferAllocator *buffer_allocator_;
+  HWCBufferAllocator buffer_allocator_;
   HWCBufferSyncHandler buffer_sync_handler_;
-  HWCColorManager *color_mgr_ = NULL;
+  HWCColorManager *color_mgr_ = nullptr;
   bool reset_panel_ = false;
   bool secure_display_active_ = false;
   bool external_pending_connect_ = false;
   bool new_bw_mode_ = false;
   bool need_invalidate_ = false;
   int bw_mode_release_fd_ = -1;
-  qService::QService *qservice_ = NULL;
+  qService::QService *qservice_ = nullptr;
   HWCSocketHandler socket_handler_;
+  bool hdmi_is_primary_ = false;
 };
 
 }  // namespace sdm