diff options
Diffstat (limited to 'hwc2_device/DrmHwcTwo.cpp')
-rw-r--r-- | hwc2_device/DrmHwcTwo.cpp | 220 |
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 |