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;