| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "hwc-drm-two" |
| |
| #include "DrmHwcTwo.h" |
| |
| #include <cinttypes> |
| |
| #include "backend/Backend.h" |
| #include "utils/log.h" |
| |
| namespace android { |
| |
| DrmHwcTwo::DrmHwcTwo() : resource_manager_(this){}; |
| |
| /* 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({}); |
| } |
| |
| 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); |
| } |
| |
| // Finally, send hotplug events to the client |
| for (auto &dhe : deferred_hotplug_events_) { |
| SendHotplugEventToClient(dhe.first, dhe.second); |
| } |
| 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(); |
| for (auto handle : displays_for_removal_list_) { |
| displays_.erase(handle); |
| } |
| } |
| |
| bool DrmHwcTwo::BindDisplay(std::shared_ptr<DrmDisplayPipeline> pipeline) { |
| if (display_handles_.count(pipeline) != 0) { |
| ALOGE("%s, pipeline is already used by another display, FIXME!!!: %p", |
| __func__, pipeline.get()); |
| return false; |
| } |
| |
| uint32_t disp_handle = kPrimaryDisplay; |
| |
| if (displays_.count(kPrimaryDisplay) != 0 && |
| !displays_[kPrimaryDisplay]->IsInHeadlessMode()) { |
| disp_handle = ++last_display_handle_; |
| } |
| |
| 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)" : ""); |
| |
| displays_[disp_handle]->SetPipeline(pipeline); |
| display_handles_[pipeline] = disp_handle; |
| |
| return true; |
| } |
| |
| bool DrmHwcTwo::UnbindDisplay(std::shared_ptr<DrmDisplayPipeline> pipeline) { |
| if (display_handles_.count(pipeline) == 0) { |
| ALOGE("%s, can't find the display, pipeline: %p", __func__, pipeline.get()); |
| 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({}); |
| |
| /* 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, uint32_t height, |
| int32_t *format, // NOLINT(readability-non-const-parameter) |
| hwc2_display_t *display) { |
| ALOGI("Creating virtual display %dx%d format %d", width, height, *format); |
| |
| auto virtual_pipeline = resource_manager_.GetVirtualDisplayPipeline(); |
| if (!virtual_pipeline) |
| return HWC2::Error::Unsupported; |
| |
| *display = ++last_display_handle_; |
| auto disp = std::make_unique<HwcDisplay>(*display, HWC2::DisplayType::Virtual, |
| this); |
| |
| disp->SetVirtualDisplayResolution(width, height); |
| disp->SetPipeline(virtual_pipeline); |
| displays_[*display] = std::move(disp); |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::DestroyVirtualDisplay(hwc2_display_t display) { |
| ALOGI("Destroying virtual display %" PRIu64, display); |
| |
| if (displays_.count(display) == 0) { |
| ALOGE("Trying to destroy non-existent display %" PRIu64, display); |
| return HWC2::Error::BadDisplay; |
| } |
| |
| displays_[display]->SetPipeline({}); |
| |
| /* 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(); |
| |
| displays_.erase(display); |
| |
| return HWC2::Error::None; |
| } |
| |
| void DrmHwcTwo::Dump(uint32_t *outSize, char *outBuffer) { |
| if (outBuffer != nullptr) { |
| auto copied_bytes = mDumpString.copy(outBuffer, *outSize); |
| *outSize = static_cast<uint32_t>(copied_bytes); |
| return; |
| } |
| |
| std::stringstream output; |
| |
| output << "-- drm_hwcomposer --\n\n"; |
| |
| for (auto &disp : displays_) |
| output << disp.second->Dump(); |
| |
| mDumpString = output.str(); |
| *outSize = static_cast<uint32_t>(mDumpString.size()); |
| } |
| |
| uint32_t DrmHwcTwo::GetMaxVirtualDisplayCount() { |
| auto writeback_count = resource_manager_.GetWritebackConnectorsCount(); |
| writeback_count = std::min(writeback_count, 1U); |
| /* Currently, only 1 virtual display is supported. Other cases need testing */ |
| ALOGI("Max virtual display count: %d", writeback_count); |
| return writeback_count; |
| } |
| |
| HWC2::Error DrmHwcTwo::RegisterCallback(int32_t descriptor, |
| hwc2_callback_data_t data, |
| hwc2_function_pointer_t function) { |
| switch (static_cast<HWC2::Callback>(descriptor)) { |
| case HWC2::Callback::Hotplug: { |
| hotplug_callback_ = std::make_pair(HWC2_PFN_HOTPLUG(function), data); |
| if (function != nullptr) { |
| resource_manager_.Init(); |
| } else { |
| resource_manager_.DeInit(); |
| /* Headless display may still be here. Remove it! */ |
| if (displays_.count(kPrimaryDisplay) != 0) { |
| displays_[kPrimaryDisplay]->Deinit(); |
| displays_.erase(kPrimaryDisplay); |
| } |
| } |
| break; |
| } |
| case HWC2::Callback::Refresh: { |
| refresh_callback_ = std::make_pair(HWC2_PFN_REFRESH(function), data); |
| break; |
| } |
| case HWC2::Callback::Vsync: { |
| vsync_callback_ = std::make_pair(HWC2_PFN_VSYNC(function), data); |
| break; |
| } |
| #if __ANDROID_API__ > 29 |
| case HWC2::Callback::Vsync_2_4: { |
| 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; |
| } |
| return HWC2::Error::None; |
| } |
| |
| void DrmHwcTwo::SendHotplugEventToClient(hwc2_display_t displayid, |
| bool connected) const { |
| auto hc = hotplug_callback_; |
| if (hc.first != nullptr && hc.second != nullptr) { |
| /* For some reason HWC Service will call HWC2 API in hotplug callback |
| * handler. This is the reason we're using recursive mutex. |
| */ |
| hc.first(hc.second, displayid, |
| connected == DRM_MODE_CONNECTED ? HWC2_CONNECTION_CONNECTED |
| : HWC2_CONNECTION_DISCONNECTED); |
| } |
| } |
| |
| void DrmHwcTwo::SendVsyncEventToClient( |
| hwc2_display_t displayid, int64_t timestamp, |
| [[maybe_unused]] uint32_t vsync_period) const { |
| /* vsync callback */ |
| #if __ANDROID_API__ > 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::SendVsyncPeriodTimingChangedEventToClient( |
| [[maybe_unused]] hwc2_display_t displayid, |
| [[maybe_unused]] int64_t timestamp) const { |
| #if __ANDROID_API__ > 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 |