hwc2: Handle multiple displays concurrency

Allow and limit concurrent displays to below combinations:
- BuiltIn + BuiltIn + External
- BuiltIn + BuiltIn + Virtual
- BuiltIn + External + External
- BuiltIn + External
- BuiltIn + Virtual

CRs-Fixed: 2324149
Change-Id: I87471585f73c411fb93fbe4fcc51e4191d636dc8
diff --git a/libqdutils/display_config.h b/libqdutils/display_config.h
index 8073bb1..8373c90 100644
--- a/libqdutils/display_config.h
+++ b/libqdutils/display_config.h
@@ -54,6 +54,7 @@
 
     // Additional displays only for vendor client (e.g. pp) reference
     DISPLAY_BUILTIN_2 = 3,
+    DISPLAY_EXTERNAL_2 = 4,
 };
 
 // External Display states - used in setSecondaryDisplayStatus()
diff --git a/sdm/libs/hwc2/hwc_display_pluggable.cpp b/sdm/libs/hwc2/hwc_display_pluggable.cpp
index 43de395..e418537 100644
--- a/sdm/libs/hwc2/hwc_display_pluggable.cpp
+++ b/sdm/libs/hwc2/hwc_display_pluggable.cpp
@@ -329,7 +329,7 @@
     return status;
   }
 
-  callbacks_->Refresh(HWC_DISPLAY_EXTERNAL);
+  callbacks_->Refresh(id_);
   validated_ = false;
 
   return status;
diff --git a/sdm/libs/hwc2/hwc_session.cpp b/sdm/libs/hwc2/hwc_session.cpp
index 0cfcb16..c552137 100644
--- a/sdm/libs/hwc2/hwc_session.cpp
+++ b/sdm/libs/hwc2/hwc_session.cpp
@@ -70,6 +70,8 @@
 
 static HWCUEvent g_hwc_uevent_;
 Locker HWCSession::locker_[kNumDisplays];
+bool HWCSession::power_on_pending_[kNumDisplays];
+
 static const int kSolidFillDelay = 100 * 1000;
 
 // Map the known color modes to dataspace.
@@ -289,15 +291,22 @@
     case qdutils::DISPLAY_EXTERNAL:
       map_info = map_info_pluggable_.size() ? &map_info_pluggable_[0] : nullptr;
       break;
+    case qdutils::DISPLAY_EXTERNAL_2:
+      map_info = (map_info_pluggable_.size() > 1) ? &map_info_pluggable_[1] : nullptr;
+      break;
     case qdutils::DISPLAY_VIRTUAL:
       map_info = map_info_virtual_.size() ? &map_info_virtual_[0] : nullptr;
       break;
     case qdutils::DISPLAY_BUILTIN_2:
       map_info = map_info_builtin_.size() ? &map_info_builtin_[0] : nullptr;
       break;
+    default:
+      DLOGW("Unknown display %d.", dpy);
+      break;
   }
 
   if (!map_info) {
+    DLOGE("Display index not found for display %d.", dpy);
     return -1;
   }
 
@@ -422,7 +431,7 @@
   auto *hwc_session = static_cast<HWCSession *>(device);
 
   if (hwc_session->hwc_display_[HWC_DISPLAY_PRIMARY]) {
-    std :: bitset < kSecureMax > secure_sessions = 0;
+    std::bitset<kSecureMax> secure_sessions = 0;
     hwc_session->hwc_display_[HWC_DISPLAY_PRIMARY]->GetActiveSecureSession(&secure_sessions);
     if (secure_sessions.any()) {
       DLOGI("Destroying virtual display id:%" PRIu64, display);
@@ -660,7 +669,9 @@
     }
 
     // TODO(user): Handle virtual display/HDMI concurrency
-    if (hwc_session->hwc_display_[display]) {
+    if (power_on_pending_[display]) {
+      status = HWC2::Error::None;
+    } else if (hwc_session->hwc_display_[display]) {
       status = hwc_session->PresentDisplayInternal(display, out_retire_fence);
     }
   }
@@ -933,7 +944,7 @@
 
   HWCSession *hwc_session = static_cast<HWCSession *>(device);
   *out_support = 0;
-  if (display == HWC_DISPLAY_PRIMARY || display == hwc_session->GetNextBuiltinIndex()) {
+  if (hwc_session->GetDisplayClass(display) == DISPLAY_CLASS_BUILTIN) {
     *out_support = 1;
   }
 
@@ -950,7 +961,6 @@
   if (display >= kNumDisplays) {
     return HWC2_ERROR_BAD_DISPLAY;
   }
-
   DTRACE_SCOPED();
   HWCSession *hwc_session = static_cast<HWCSession *>(device);
   // TODO(user): Handle secure session, handle QDCM solid fill
@@ -958,7 +968,9 @@
   hwc_session->HandleSecureSession(display);
   {
     SEQUENCE_ENTRY_SCOPE_LOCK(locker_[display]);
-    if (hwc_session->hwc_display_[display]) {
+    if (power_on_pending_[display]) {
+      status = HWC2::Error::None;
+    } else if (hwc_session->hwc_display_[display]) {
       status = hwc_session->ValidateDisplayInternal(display, out_num_types, out_num_requests);
     }
   }
@@ -1089,16 +1101,6 @@
 
 HWC2::Error HWCSession::CreateVirtualDisplayObj(uint32_t width, uint32_t height, int32_t *format,
                                                 hwc2_display_t *out_display_id) {
-  if (hwc_display_[HWC_DISPLAY_PRIMARY]) {
-    SEQUENCE_WAIT_SCOPE_LOCK(locker_[HWC_DISPLAY_PRIMARY]);
-    std :: bitset < kSecureMax > secure_sessions = 0;
-    hwc_display_[HWC_DISPLAY_PRIMARY]->GetActiveSecureSession(&secure_sessions);
-    if (secure_sessions.any()) {
-      DLOGE("Secure session is active, cannot create virtual display");
-      return HWC2::Error::Unsupported;
-    }
-  }
-
   if (!client_connected_) {
     DLOGE("Client is not ready yet.");
     return HWC2::Error::BadDisplay;
@@ -1110,6 +1112,19 @@
     return HWC2::Error::NoResources;
   }
 
+  if (hwc_display_[HWC_DISPLAY_PRIMARY]) {
+    SEQUENCE_WAIT_SCOPE_LOCK(locker_[HWC_DISPLAY_PRIMARY]);
+    std::bitset<kSecureMax> secure_sessions = 0;
+    hwc_display_[HWC_DISPLAY_PRIMARY]->GetActiveSecureSession(&secure_sessions);
+    if (secure_sessions.any()) {
+      DLOGE("Secure session is active, cannot create virtual display.");
+      return HWC2::Error::Unsupported;
+    } else if (IsPluggableDisplayConnected()) {
+      DLOGE("External session is active, cannot create virtual display.");
+      return HWC2::Error::Unsupported;
+    }
+  }
+
   HWDisplaysInfo hw_displays_info = {};
   DisplayError error = core_intf_->GetDisplaysStatus(&hw_displays_info);
   if (error != kErrorNone) {
@@ -1160,14 +1175,45 @@
   return HWC2::Error::None;
 }
 
-hwc2_display_t HWCSession::GetNextBuiltinIndex() {
+bool HWCSession::GetFirstNonPrimaryBuiltinStatus() {
+  hwc2_display_t builtin_id = HWC_DISPLAY_PRIMARY;
+
   for (auto &map_info : map_info_builtin_) {
     if (hwc_display_[map_info.client_id]) {
-        return map_info.client_id;
+      builtin_id = map_info.client_id;
+      break;
     }
   }
 
-  return 0;
+  if (HWC_DISPLAY_PRIMARY == builtin_id) {
+    // No non-primary builtin.
+    return false;
+  }
+  auto &hwc_display = hwc_display_[builtin_id];
+  if (hwc_display) {
+    return hwc_display->GetLastPowerMode() != HWC2::PowerMode::Off;
+  }
+  return false;
+}
+
+DisplayClass HWCSession::GetDisplayClass(hwc2_display_t display) {
+  if (display >= kNumDisplays) {
+    DLOGE("display = %d beyond max supported display slots = %d", display, kNumDisplays);
+    return DISPLAY_CLASS_NULL;
+  } else if (!hwc_display_[display]) {
+    DLOGE("display = %d slot empty.", display);
+    return DISPLAY_CLASS_NULL;
+  }
+  return hwc_display_[display]->GetDisplayClass();
+}
+
+bool HWCSession::IsPluggableDisplayConnected() {
+  for (auto &map_info : map_info_pluggable_) {
+    if (hwc_display_[map_info.client_id]) {
+      return true;
+    }
+  }
+  return false;
 }
 
 // Qclient methods
@@ -2019,13 +2065,28 @@
     hpd_pattern_ = GetEventValue(uevent_data, length, "pattern=");
     DLOGI("Uevent = %s, status = %s, MST_HOTPLUG = %s, bpp = %d, pattern = %d", uevent_data,
           str_status ? str_status : "NULL", str_mst ? str_mst : "NULL", hpd_bpp_, hpd_pattern_);
-    if (HandlePluggableDisplays(true)) {
+
+    hwc2_display_t virtual_display_index =
+        (hwc2_display_t)GetDisplayIndex(qdutils::DISPLAY_VIRTUAL);
+
+    std::bitset<kSecureMax> secure_sessions = 0;
+    {
+      Locker::ScopeLock lock_p(locker_[HWC_DISPLAY_PRIMARY]);
+      hwc_display_[HWC_DISPLAY_PRIMARY]->GetActiveSecureSession(&secure_sessions);
+    }
+    if (secure_sessions[kSecureDisplay] || hwc_display_[virtual_display_index]) {
+      // Defer hotplug events.
+      hotplug_pending_event_ = kHotPlugEvent;
+      DLOGI("%s session in progress. Deferring display hotplug...",
+            secure_sessions[kSecureDisplay] ? "Secure" : "Virtual");
+    } else if (HandlePluggableDisplays(true)) {
       DLOGE("Could not handle hotplug. Event dropped.");
     }
 
     if (str_status) {
       bool connected = (strncmp(str_status, "connected", strlen("connected")) == 0);
       DLOGI("Connected = %d", connected);
+      // Pass on legacy HDMI hot-plug event.
       qservice_->onHdmiHotplug(INT(connected));
     }
   }
@@ -2233,8 +2294,6 @@
 int HWCSession::HandleConnectedDisplays(HWDisplaysInfo *hw_displays_info, bool delay_hotplug) {
   int status = 0;
   std::vector<hwc2_display_t> pending_hotplugs = {};
-  std :: bitset <kSecureMax> secure_sessions = 0;
-  hwc_display_[HWC_DISPLAY_PRIMARY]->GetActiveSecureSession(&secure_sessions);
 
   for (auto &iter : *hw_displays_info) {
     auto &info = iter.second;
@@ -2254,10 +2313,29 @@
       continue;
     }
 
+    bool sec_builtin_active = GetFirstNonPrimaryBuiltinStatus();
+
     // find an empty slot to create display.
     for (auto &map_info : map_info_pluggable_) {
       hwc2_display_t client_id = map_info.client_id;
 
+      if (client_id > HWC_DISPLAY_EXTERNAL_2 ||
+          ((kPluggable == map_info_primary_.disp_type) && (client_id > HWC_DISPLAY_EXTERNAL)) ||
+          (sec_builtin_active && (client_id > HWC_DISPLAY_EXTERNAL))) {
+        // A maximum of 2 external displays supported. Remaining slots unused on this target.
+        // If HDMI is primary, 1 external display allowed.
+        // If secondary display is present, only 1 external display allowed.
+        // Valid Concurrencies on this target:
+        //   BuiltIn + BuiltIn + External
+        //   BuiltIn + BuiltIn + Virtual
+        //   BuiltIn + External + External
+        //   BuiltIn + External
+        //   BuiltIn + Virtual
+        DLOGW("Pluggable display instance limit reached. Breaking at pluggable display slot %d.",
+              client_id);
+        break;
+      }
+
       // Lock confined to this scope
       {
         SCOPE_LOCK(locker_[client_id]);
@@ -2272,11 +2350,9 @@
         // Test pattern generation ?
         map_info.test_pattern = (hpd_bpp_ > 0) && (hpd_pattern_ > 0);
         if (!map_info.test_pattern) {
-          if (!secure_sessions[kSecureDisplay]) {
-            status = HWCDisplayPluggable::Create(core_intf_, &buffer_allocator_, &callbacks_, this,
+          status = HWCDisplayPluggable::Create(core_intf_, &buffer_allocator_, &callbacks_, this,
                                                qservice_, client_id, info.display_id, 0, 0, false,
                                                &hwc_display);
-          }
         } else {
           status = HWCDisplayPluggableTest::Create(core_intf_, &buffer_allocator_, &callbacks_,
                                                    this, qservice_, client_id, info.display_id,
@@ -2333,8 +2409,6 @@
 
 int HWCSession::HandleDisconnectedDisplays(HWDisplaysInfo *hw_displays_info) {
   // Destroy pluggable displays which were connected earlier but got disconnected now.
-  std :: bitset <kSecureMax> secure_sessions = 0;
-  hwc_display_[HWC_DISPLAY_PRIMARY]->GetActiveSecureSession(&secure_sessions);
   for (auto &map_info : map_info_pluggable_) {
     bool disconnect = true;   // disconnect in case display id is not found in list.
 
@@ -2350,12 +2424,7 @@
     }
 
     if (disconnect) {
-      if (secure_sessions[kSecureDisplay]) {
-        hotplug_pending_event_ = kHotPlugDisconnect;
-      } else {
-        DestroyDisplay(&map_info);
-        hotplug_pending_event_ = kHotPlugNone;
-      }
+      DestroyDisplay(&map_info);
     }
   }
 
@@ -2447,7 +2516,9 @@
 
 void HWCSession::DisplayPowerReset() {
   Locker::ScopeLock lock_p(locker_[HWC_DISPLAY_PRIMARY]);
+  Locker::ScopeLock lock_b2(locker_[HWC_DISPLAY_BUILTIN_2]);
   Locker::ScopeLock lock_e(locker_[HWC_DISPLAY_EXTERNAL]);
+  Locker::ScopeLock lock_e2(locker_[HWC_DISPLAY_EXTERNAL_2]);
   Locker::ScopeLock lock_v(locker_[HWC_DISPLAY_VIRTUAL]);
 
   HWC2::Error status = HWC2::Error::None;
@@ -2479,30 +2550,23 @@
 }
 
 void HWCSession::HandleSecureSession(hwc2_display_t disp_id) {
-  Locker::ScopeLock lock_p(locker_[HWC_DISPLAY_PRIMARY]);
-  Locker::ScopeLock lock_e(locker_[HWC_DISPLAY_EXTERNAL]);
-  Locker::ScopeLock lock_v(locker_[HWC_DISPLAY_VIRTUAL]);
-  std :: bitset < kSecureMax > secure_sessions = 0;
-  if (hwc_display_[HWC_DISPLAY_PRIMARY]) {
-    hwc_display_[HWC_DISPLAY_PRIMARY]->GetActiveSecureSession(&secure_sessions);
+  std::bitset<kSecureMax> secure_sessions = 0;
+  {
+    Locker::ScopeLock lock_p(locker_[HWC_DISPLAY_PRIMARY]);
+    if (hwc_display_[HWC_DISPLAY_PRIMARY]) {
+      hwc_display_[HWC_DISPLAY_PRIMARY]->GetActiveSecureSession(&secure_sessions);
+    }
   }
 
   // If it is called during primary prepare/commit, we need to pause any onging commit on
   // external/virtual display.
-  if (disp_id == HWC_DISPLAY_PRIMARY) {
-    if (hwc_display_[HWC_DISPLAY_EXTERNAL]) {
-      hwc_display_[HWC_DISPLAY_EXTERNAL]->HandleSecureSession(secure_sessions,
-                                           &power_on_pending_[HWC_DISPLAY_EXTERNAL]);
+  std::vector<int> disps{HWC_DISPLAY_PRIMARY, HWC_DISPLAY_BUILTIN_2, HWC_DISPLAY_EXTERNAL,
+                         HWC_DISPLAY_EXTERNAL_2, HWC_DISPLAY_VIRTUAL};
+  for (auto disp : disps) {
+    Locker::ScopeLock lock_d(locker_[disp]);
+    if (hwc_display_[disp]) {
+      hwc_display_[disp]->HandleSecureSession(secure_sessions, &power_on_pending_[disp]);
     }
-    if (hwc_display_[HWC_DISPLAY_VIRTUAL]) {
-      hwc_display_[HWC_DISPLAY_VIRTUAL]->HandleSecureSession(secure_sessions,
-                                           &power_on_pending_[HWC_DISPLAY_VIRTUAL]);
-    }
-  }
-
-  if (hwc_display_[disp_id]) {
-    hwc_display_[disp_id]->HandleSecureSession(secure_sessions,
-                                           &power_on_pending_[disp_id]);
   }
 }
 
@@ -2510,49 +2574,49 @@
   if (disp_id != HWC_DISPLAY_PRIMARY) {
     return;
   }
+
   Locker::ScopeLock lock_p(locker_[HWC_DISPLAY_PRIMARY]);
-  Locker::ScopeLock lock_e(locker_[HWC_DISPLAY_EXTERNAL]);
-  Locker::ScopeLock lock_v(locker_[HWC_DISPLAY_VIRTUAL]);
+  std::vector<int> disps{HWC_DISPLAY_BUILTIN_2, HWC_DISPLAY_EXTERNAL, HWC_DISPLAY_EXTERNAL_2,
+                         HWC_DISPLAY_VIRTUAL};
 
-
-  if (!hwc_display_[HWC_DISPLAY_PRIMARY]->IsDisplayCommandMode()) {
-    if (power_on_pending_[HWC_DISPLAY_EXTERNAL] || power_on_pending_[HWC_DISPLAY_VIRTUAL]) {
-      // retire fence is set only after successful primary commit, So check for retire fence to know
-      // non secure commit went through to notify driver to change the CRTC mode to non secure.
-      // Otherwise any commit to external display would fail.
-      if (retire_fence < 0) {
-        return;
-      }
-      int error = sync_wait(retire_fence, 1000);
-      if (error < 0) {
-        DLOGE("sync_wait error errno = %d, desc = %s", errno,  strerror(errno));
-      }
+  bool power_on_pending = false;
+  for (auto disp : disps) {
+    Locker::ScopeLock lock_d(locker_[disp]);
+    if (power_on_pending_[disp]) {
+      power_on_pending = true;
+      break;
     }
   }
-
-  if (power_on_pending_[HWC_DISPLAY_EXTERNAL]) {
-    if (hwc_display_[HWC_DISPLAY_EXTERNAL]) {
-      HWC2::Error status = hwc_display_[HWC_DISPLAY_EXTERNAL]->SetPowerMode(HWC2::PowerMode::On,
-                                                                            false /* teardown */);
-      if (status == HWC2::Error::None) {
-        power_on_pending_[HWC_DISPLAY_EXTERNAL] = false;
-      }
+  if (power_on_pending) {
+    // retire fence is set only after successful primary commit, So check for retire fence to know
+    // non secure commit went through to notify driver to change the CRTC mode to non secure.
+    // Otherwise any commit to non-primary display would fail.
+    if (retire_fence < 0) {
+      return;
     }
+    int error = sync_wait(retire_fence, 1000);
+    if (error < 0) {
+      DLOGE("sync_wait error errno = %d, desc = %s", errno, strerror(errno));
+    }
+  } else {
+    return;
   }
 
-  if (power_on_pending_[HWC_DISPLAY_VIRTUAL]) {
-    if (hwc_display_[HWC_DISPLAY_VIRTUAL]) {
-      HWC2::Error status = hwc_display_[HWC_DISPLAY_VIRTUAL]->SetPowerMode(HWC2::PowerMode::On,
-                                                                           false /* teardown */);
+  for (auto disp : disps) {
+    Locker::ScopeLock lock_d(locker_[disp]);
+    if (power_on_pending_[disp] && hwc_display_[disp]) {
+      HWC2::Error status =
+          hwc_display_[disp]->SetPowerMode(HWC2::PowerMode::On, false /* teardown */);
       if (status == HWC2::Error::None) {
-        power_on_pending_[HWC_DISPLAY_VIRTUAL] = false;
+        power_on_pending_[disp] = false;
       }
     }
   }
 }
 
 void HWCSession::HandleHotplugPending(hwc2_display_t disp_id, int retire_fence) {
-  if (disp_id != HWC_DISPLAY_PRIMARY || hotplug_pending_event_ == kHotPlugNone) {
+  if (disp_id != HWC_DISPLAY_PRIMARY ||
+      (kHotPlugNone == hotplug_pending_event_ && !destroy_virtual_disp_pending_)) {
     return;
   }
 
@@ -2561,7 +2625,7 @@
     Locker::ScopeLock lock_p(locker_[HWC_DISPLAY_PRIMARY]);
     if (hwc_display_[HWC_DISPLAY_PRIMARY]) {
       if (!hwc_display_[HWC_DISPLAY_PRIMARY]->IsDisplayCommandMode()) {
-        if (destroy_virtual_disp_pending_ || hotplug_pending_event_ == kHotPlugDisconnect) {
+        if (destroy_virtual_disp_pending_ || kHotPlugEvent == hotplug_pending_event_) {
           if (retire_fence >= 0) {
             int error = sync_wait(retire_fence, 1000);
             if (error < 0) {
@@ -2572,15 +2636,23 @@
       }
       hwc_display_[HWC_DISPLAY_PRIMARY]->GetActiveSecureSession(&secure_sessions);
     }
-
-    // Destroy the pending virtual display if secure session not present.
-    if (!secure_sessions.any() && destroy_virtual_disp_pending_) {
-      for (auto &map_info : map_info_virtual_) {
-        DestroyDisplay(&map_info);
-        destroy_virtual_disp_pending_ = false;
-      }
+  }
+  // Destroy the pending virtual display if secure session not present.
+  if (!secure_sessions.any() && destroy_virtual_disp_pending_) {
+    for (auto &map_info : map_info_virtual_) {
+      DestroyDisplay(&map_info);
+      destroy_virtual_disp_pending_ = false;
     }
   }
+  // Handle connect/disconnect hotplugs if secure session is not present.
+  hwc2_display_t virtual_display_index = (hwc2_display_t)GetDisplayIndex(qdutils::DISPLAY_VIRTUAL);
+  if (!secure_sessions.any() && !hwc_display_[virtual_display_index] &&
+      kHotPlugEvent == hotplug_pending_event_) {
+    if (HandlePluggableDisplays(true)) {
+      DLOGE("Could not handle hotplug. Event dropped.");
+    }
+    hotplug_pending_event_ = kHotPlugNone;
+  }
 }
 
 int32_t HWCSession::GetReadbackBufferAttributes(hwc2_device_t *device, hwc2_display_t display,
diff --git a/sdm/libs/hwc2/hwc_session.h b/sdm/libs/hwc2/hwc_session.h
index 31110a6..20c537b 100644
--- a/sdm/libs/hwc2/hwc_session.h
+++ b/sdm/libs/hwc2/hwc_session.h
@@ -112,8 +112,7 @@
 
   enum HotPlugEvent {
     kHotPlugNone,
-    kHotPlugConnect,
-    kHotPlugDisconnect,
+    kHotPlugEvent,
   };
 
   explicit HWCSession(const hw_module_t *module);
@@ -341,6 +340,8 @@
 
   void Refresh(hwc2_display_t display);
   void HotPlug(hwc2_display_t display, HWC2::Connection state);
+
+  // Internal methods
   HWC2::Error ValidateDisplayInternal(hwc2_display_t display, uint32_t *out_num_types,
                                       uint32_t *out_num_requests);
   HWC2::Error PresentDisplayInternal(hwc2_display_t display, int32_t *out_retire_fence);
@@ -348,8 +349,10 @@
   void HandlePowerOnPending(hwc2_display_t display, int retire_fence);
   void HandleHotplugPending(hwc2_display_t disp_id, int retire_fence);
   void UpdateVsyncSource();
-  hwc2_display_t GetNextBuiltinIndex();
   hwc2_display_t GetNextVsyncSource();
+  bool GetFirstNonPrimaryBuiltinStatus();
+  DisplayClass GetDisplayClass(hwc2_display_t display_id);
+  bool IsPluggableDisplayConnected();
 
   CoreInterface *core_intf_ = nullptr;
   HWCDisplay *hwc_display_[kNumDisplays] = {nullptr};
@@ -375,7 +378,7 @@
   int hpd_bpp_ = 0;
   int hpd_pattern_ = 0;
   int null_display_mode_ = 0;
-  bool power_on_pending_[kNumDisplays] = {false};
+  static bool power_on_pending_[kNumDisplays];
   HotPlugEvent hotplug_pending_event_ = kHotPlugNone;
   bool destroy_virtual_disp_pending_ = false;
   uint32_t idle_pc_ref_cnt_ = 0;