aboutsummaryrefslogtreecommitdiff
path: root/hwc2_device/DrmHwcTwo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'hwc2_device/DrmHwcTwo.cpp')
-rw-r--r--hwc2_device/DrmHwcTwo.cpp220
1 files changed, 139 insertions, 81 deletions
diff --git a/hwc2_device/DrmHwcTwo.cpp b/hwc2_device/DrmHwcTwo.cpp
index a2a093f..e689419 100644
--- a/hwc2_device/DrmHwcTwo.cpp
+++ b/hwc2_device/DrmHwcTwo.cpp
@@ -18,61 +18,117 @@
#include "DrmHwcTwo.h"
+#include <cinttypes>
+
#include "backend/Backend.h"
#include "utils/log.h"
namespace android {
-DrmHwcTwo::DrmHwcTwo() = default;
+DrmHwcTwo::DrmHwcTwo() : resource_manager_(this){};
-HWC2::Error DrmHwcTwo::CreateDisplay(hwc2_display_t displ,
- HWC2::DisplayType type) {
- DrmDevice *drm = resource_manager_.GetDrmDevice(static_cast<int>(displ));
- if (!drm) {
- ALOGE("Failed to get a valid drmresource");
- return HWC2::Error::NoResources;
+/* Must be called after every display attach/detach cycle */
+void DrmHwcTwo::FinalizeDisplayBinding() {
+ if (displays_.count(kPrimaryDisplay) == 0) {
+ /* Primary display MUST always exist */
+ ALOGI("No pipelines available. Creating null-display for headless mode");
+ displays_[kPrimaryDisplay] = std::make_unique<
+ HwcDisplay>(kPrimaryDisplay, HWC2::DisplayType::Physical, this);
+ /* Initializes null-display */
+ displays_[kPrimaryDisplay]->SetPipeline(nullptr);
}
- displays_.emplace(std::piecewise_construct, std::forward_as_tuple(displ),
- std::forward_as_tuple(&resource_manager_, drm, displ, type,
- this));
- DrmCrtc *crtc = drm->GetCrtcForDisplay(static_cast<int>(displ));
- if (!crtc) {
- ALOGE("Failed to get crtc for display %d", static_cast<int>(displ));
- return HWC2::Error::BadDisplay;
+ if (displays_[kPrimaryDisplay]->IsInHeadlessMode() &&
+ !display_handles_.empty()) {
+ /* Reattach first secondary display to take place of the primary */
+ auto *pipe = display_handles_.begin()->first;
+ ALOGI("Primary display was disconnected, reattaching '%s' as new primary",
+ pipe->connector->Get()->GetName().c_str());
+ UnbindDisplay(pipe);
+ BindDisplay(pipe);
}
- auto display_planes = std::vector<DrmPlane *>();
- for (const auto &plane : drm->planes()) {
- if (plane->GetCrtcSupported(*crtc))
- display_planes.push_back(plane.get());
+
+ // Finally, send hotplug events to the client
+ for (auto &dhe : deferred_hotplug_events_) {
+ SendHotplugEventToClient(dhe.first, dhe.second);
}
- displays_.at(displ).Init(&display_planes);
- return HWC2::Error::None;
+ deferred_hotplug_events_.clear();
+
+ /* Wait 0.2s before removing the displays to flush pending HWC2 transactions
+ */
+ auto &mutex = GetResMan().GetMainLock();
+ mutex.unlock();
+ const int kTimeForSFToDisposeDisplayUs = 200000;
+ usleep(kTimeForSFToDisposeDisplayUs);
+ mutex.lock();
+ std::vector<std::unique_ptr<HwcDisplay>> for_disposal;
+ for (auto handle : displays_for_removal_list_) {
+ for_disposal.emplace_back(
+ std::unique_ptr<HwcDisplay>(displays_[handle].release()));
+ displays_.erase(handle);
+ }
+ /* Destroy HwcDisplays while unlocked to avoid vsyncworker deadlocks */
+ mutex.unlock();
+ for_disposal.clear();
+ mutex.lock();
}
-HWC2::Error DrmHwcTwo::Init() {
- int rv = resource_manager_.Init();
- if (rv) {
- ALOGE("Can't initialize the resource manager %d", rv);
- return HWC2::Error::NoResources;
+bool DrmHwcTwo::BindDisplay(DrmDisplayPipeline *pipeline) {
+ if (display_handles_.count(pipeline) != 0) {
+ ALOGE("%s, pipeline is already used by another display, FIXME!!!: %p",
+ __func__, pipeline);
+ return false;
}
- HWC2::Error ret = HWC2::Error::None;
- for (int i = 0; i < resource_manager_.GetDisplayCount(); i++) {
- ret = CreateDisplay(i, HWC2::DisplayType::Physical);
- if (ret != HWC2::Error::None) {
- ALOGE("Failed to create display %d with error %d", i, ret);
- return ret;
- }
+ uint32_t disp_handle = kPrimaryDisplay;
+
+ if (displays_.count(kPrimaryDisplay) != 0 &&
+ !displays_[kPrimaryDisplay]->IsInHeadlessMode()) {
+ disp_handle = ++last_display_handle_;
}
- resource_manager_.GetUEventListener()->RegisterHotplugHandler([this] {
- const std::lock_guard<std::mutex> lock(GetResMan().GetMainLock());
+ if (displays_.count(disp_handle) == 0) {
+ auto disp = std::make_unique<HwcDisplay>(disp_handle,
+ HWC2::DisplayType::Physical, this);
+ displays_[disp_handle] = std::move(disp);
+ }
+
+ ALOGI("Attaching pipeline '%s' to the display #%d%s",
+ pipeline->connector->Get()->GetName().c_str(), (int)disp_handle,
+ disp_handle == kPrimaryDisplay ? " (Primary)" : "");
- HandleHotplugUEvent();
- });
+ displays_[disp_handle]->SetPipeline(pipeline);
+ display_handles_[pipeline] = disp_handle;
- return ret;
+ return true;
+}
+
+bool DrmHwcTwo::UnbindDisplay(DrmDisplayPipeline *pipeline) {
+ if (display_handles_.count(pipeline) == 0) {
+ ALOGE("%s, can't find the display, pipeline: %p", __func__, pipeline);
+ return false;
+ }
+ auto handle = display_handles_[pipeline];
+ display_handles_.erase(pipeline);
+
+ ALOGI("Detaching the pipeline '%s' from the display #%i%s",
+ pipeline->connector->Get()->GetName().c_str(), (int)handle,
+ handle == kPrimaryDisplay ? " (Primary)" : "");
+
+ if (displays_.count(handle) == 0) {
+ ALOGE("%s, can't find the display, handle: %" PRIu64, __func__, handle);
+ return false;
+ }
+ displays_[handle]->SetPipeline(nullptr);
+
+ /* We must defer display disposal and removal, since it may still have pending
+ * HWC_API calls scheduled and waiting until ueventlistener thread releases
+ * main lock, otherwise transaction may fail and SF may crash
+ */
+ if (handle != kPrimaryDisplay) {
+ displays_for_removal_list_.emplace_back(handle);
+ }
+ return true;
}
HWC2::Error DrmHwcTwo::CreateVirtualDisplay(uint32_t /*width*/,
@@ -99,8 +155,8 @@ void DrmHwcTwo::Dump(uint32_t *outSize, char *outBuffer) {
output << "-- drm_hwcomposer --\n\n";
- for (std::pair<const hwc2_display_t, HwcDisplay> &dp : displays_)
- output << dp.second.Dump();
+ for (auto &disp : displays_)
+ output << disp.second->Dump();
mDumpString = output.str();
*outSize = static_cast<uint32_t>(mDumpString.size());
@@ -117,9 +173,13 @@ HWC2::Error DrmHwcTwo::RegisterCallback(int32_t descriptor,
switch (static_cast<HWC2::Callback>(descriptor)) {
case HWC2::Callback::Hotplug: {
hotplug_callback_ = std::make_pair(HWC2_PFN_HOTPLUG(function), data);
- const auto &drm_devices = resource_manager_.GetDrmDevices();
- for (const auto &device : drm_devices)
- HandleInitialHotplugState(device.get());
+ if (function != nullptr) {
+ resource_manager_.Init();
+ } else {
+ resource_manager_.DeInit();
+ /* Headless display may still be here, remove it */
+ displays_.erase(kPrimaryDisplay);
+ }
break;
}
case HWC2::Callback::Refresh: {
@@ -135,6 +195,11 @@ HWC2::Error DrmHwcTwo::RegisterCallback(int32_t descriptor,
vsync_2_4_callback_ = std::make_pair(HWC2_PFN_VSYNC_2_4(function), data);
break;
}
+ case HWC2::Callback::VsyncPeriodTimingChanged: {
+ period_timing_changed_callback_ = std::
+ make_pair(HWC2_PFN_VSYNC_PERIOD_TIMING_CHANGED(function), data);
+ break;
+ }
#endif
default:
break;
@@ -142,7 +207,8 @@ HWC2::Error DrmHwcTwo::RegisterCallback(int32_t descriptor,
return HWC2::Error::None;
}
-void DrmHwcTwo::HandleDisplayHotplug(hwc2_display_t displayid, int state) {
+void DrmHwcTwo::SendHotplugEventToClient(hwc2_display_t displayid,
+ bool connected) {
auto &mutex = GetResMan().GetMainLock();
if (mutex.try_lock()) {
ALOGE("FIXME!!!: Main mutex must be locked in %s", __func__);
@@ -157,52 +223,44 @@ void DrmHwcTwo::HandleDisplayHotplug(hwc2_display_t displayid, int state) {
*/
mutex.unlock();
hc.first(hc.second, displayid,
- state == DRM_MODE_CONNECTED ? HWC2_CONNECTION_CONNECTED
- : HWC2_CONNECTION_DISCONNECTED);
+ connected == DRM_MODE_CONNECTED ? HWC2_CONNECTION_CONNECTED
+ : HWC2_CONNECTION_DISCONNECTED);
mutex.lock();
}
}
-void DrmHwcTwo::HandleInitialHotplugState(DrmDevice *drmDevice) {
- for (const auto &conn : drmDevice->connectors()) {
- int display_id = conn->display();
- auto &display = displays_.at(display_id);
-
- if (conn->state() != DRM_MODE_CONNECTED && !display.IsInHeadlessMode())
- continue;
- HandleDisplayHotplug(conn->display(), display.IsInHeadlessMode()
- ? DRM_MODE_CONNECTED
- : conn->state());
+void DrmHwcTwo::SendVsyncEventToClient(
+ hwc2_display_t displayid, int64_t timestamp,
+ [[maybe_unused]] uint32_t vsync_period) const {
+ /* vsync callback */
+#if PLATFORM_SDK_VERSION > 29
+ if (vsync_2_4_callback_.first != nullptr &&
+ vsync_2_4_callback_.second != nullptr) {
+ vsync_2_4_callback_.first(vsync_2_4_callback_.second, displayid, timestamp,
+ vsync_period);
+ } else
+#endif
+ if (vsync_callback_.first != nullptr &&
+ vsync_callback_.second != nullptr) {
+ vsync_callback_.first(vsync_callback_.second, displayid, timestamp);
}
}
-void DrmHwcTwo::HandleHotplugUEvent() {
- for (const auto &drm : resource_manager_.GetDrmDevices()) {
- for (const auto &conn : drm->connectors()) {
- drmModeConnection old_state = conn->state();
- drmModeConnection cur_state = conn->UpdateModes()
- ? DRM_MODE_UNKNOWNCONNECTION
- : conn->state();
-
- if (cur_state == old_state)
- continue;
-
- ALOGI("%s event for connector %u on display %d",
- cur_state == DRM_MODE_CONNECTED ? "Plug" : "Unplug", conn->id(),
- conn->display());
-
- int display_id = conn->display();
- auto &display = displays_.at(display_id);
- display.ChosePreferredConfig();
- if (cur_state != DRM_MODE_CONNECTED) {
- display.ClearDisplay();
- }
-
- HandleDisplayHotplug(display_id, display.IsInHeadlessMode()
- ? DRM_MODE_CONNECTED
- : cur_state);
- }
+void DrmHwcTwo::SendVsyncPeriodTimingChangedEventToClient(
+ [[maybe_unused]] hwc2_display_t displayid,
+ [[maybe_unused]] int64_t timestamp) const {
+#if PLATFORM_SDK_VERSION > 29
+ hwc_vsync_period_change_timeline_t timeline = {
+ .newVsyncAppliedTimeNanos = timestamp,
+ .refreshRequired = false,
+ .refreshTimeNanos = 0,
+ };
+ if (period_timing_changed_callback_.first != nullptr &&
+ period_timing_changed_callback_.second != nullptr) {
+ period_timing_changed_callback_
+ .first(period_timing_changed_callback_.second, displayid, &timeline);
}
+#endif
}
} // namespace android