aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoman Stratiienko <roman.o.stratiienko@globallogic.com>2022-01-11 19:18:34 +0200
committerRoman Stratiienko <roman.o.stratiienko@globallogic.com>2022-02-02 12:57:32 +0200
commit3dacd47d13e3b739cf6909a23b9a5f40152e5eea (patch)
treef43fd0c28e18c221003c452cdb22126d72ba8fec
parent19c162fe790ce9da373686512f451c6ed276bca8 (diff)
downloaddrm_hwcomposer-3dacd47d13e3b739cf6909a23b9a5f40152e5eea.tar.gz
drm_hwcomposer: Dynamic DrmDisplayPipeline to HwcDisplay bindings
The following use scenarios are now possible: 1. When no display connected, primary HwcDisplay is created in headless mode. 2. When user connects first display, it binds to primary slot, replacing headless HwcDisplay. 3. When user connects another display it binds to the new HwcDisplay slot, creating new display for the framework. 4. When user disconnects first (Primary) display, drm_hwc detaches second display and attaches it to the Primary slot. In this case framework is notified as Primary display resolution updated (Plugged->Plugged transition). And second display is disconnected (Plugged->Unplugged transition). DrmDisplayPipeline is now created on demand (after hotplug event). HwcDisplay class is now destructed on connector unplug, which will give us ability to destroy any resource caches (will be required for FB caching logic). Signed-off-by: Roman Stratiienko <roman.o.stratiienko@globallogic.com>
-rw-r--r--backend/Backend.cpp2
-rw-r--r--drm/DrmDevice.cpp56
-rw-r--r--drm/DrmDevice.h15
-rw-r--r--drm/ResourceManager.cpp121
-rw-r--r--drm/ResourceManager.h43
-rw-r--r--hwc2_device/DrmHwcTwo.cpp164
-rw-r--r--hwc2_device/DrmHwcTwo.h32
-rw-r--r--hwc2_device/HwcDisplay.cpp94
-rw-r--r--hwc2_device/HwcDisplay.h31
-rw-r--r--hwc2_device/HwcDisplayConfigs.cpp16
-rw-r--r--hwc2_device/HwcDisplayConfigs.h4
-rw-r--r--hwc2_device/hwc2_device.cpp6
12 files changed, 346 insertions, 238 deletions
diff --git a/backend/Backend.cpp b/backend/Backend.cpp
index 7d82eef..d707192 100644
--- a/backend/Backend.cpp
+++ b/backend/Backend.cpp
@@ -86,7 +86,7 @@ bool Backend::IsClientLayer(HwcDisplay *display, HwcLayer *layer) {
!BufferInfoGetter::GetInstance()->IsHandleUsable(layer->GetBuffer()) ||
display->color_transform_hint() != HAL_COLOR_TRANSFORM_IDENTITY ||
(layer->RequireScalingOrPhasing() &&
- display->resource_manager()->ForcedScalingWithGpu());
+ display->GetHwc2()->GetResMan().ForcedScalingWithGpu());
}
bool Backend::HardwareSupportsLayerType(HWC2::Composition comp_type) {
diff --git a/drm/DrmDevice.cpp b/drm/DrmDevice.cpp
index d1ae7c9..e5f41e8 100644
--- a/drm/DrmDevice.cpp
+++ b/drm/DrmDevice.cpp
@@ -22,12 +22,8 @@
#include <xf86drm.h>
#include <xf86drmMode.h>
-#include <algorithm>
-#include <array>
-#include <cerrno>
#include <cinttypes>
#include <cstdint>
-#include <sstream>
#include <string>
#include "compositor/DrmDisplayCompositor.h"
@@ -41,26 +37,25 @@ DrmDevice::DrmDevice() {
drm_fb_importer_ = std::make_unique<DrmFbImporter>(*this);
}
-// NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme
-std::tuple<int, int> DrmDevice::Init(const char *path, int num_displays) {
+auto DrmDevice::Init(const char *path) -> int {
/* TODO: Use drmOpenControl here instead */
fd_ = UniqueFd(open(path, O_RDWR | O_CLOEXEC));
if (!fd_) {
// NOLINTNEXTLINE(concurrency-mt-unsafe): Fixme
ALOGE("Failed to open dri %s: %s", path, strerror(errno));
- return std::make_tuple(-ENODEV, 0);
+ return -ENODEV;
}
int ret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
if (ret != 0) {
ALOGE("Failed to set universal plane cap %d", ret);
- return std::make_tuple(ret, 0);
+ return ret;
}
ret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_ATOMIC, 1);
if (ret != 0) {
ALOGE("Failed to set atomic cap %d", ret);
- return std::make_tuple(ret, 0);
+ return ret;
}
#ifdef DRM_CLIENT_CAP_WRITEBACK_CONNECTORS
@@ -80,13 +75,13 @@ std::tuple<int, int> DrmDevice::Init(const char *path, int num_displays) {
drmSetMaster(GetFd());
if (drmIsMaster(GetFd()) == 0) {
ALOGE("DRM/KMS master access required");
- return std::make_tuple(-EACCES, 0);
+ return -EACCES;
}
auto res = MakeDrmModeResUnique(GetFd());
if (!res) {
ALOGE("Failed to get DrmDevice resources");
- return std::make_tuple(-ENODEV, 0);
+ return -ENODEV;
}
min_resolution_ = std::pair<uint32_t, uint32_t>(res->min_width,
@@ -128,7 +123,7 @@ std::tuple<int, int> DrmDevice::Init(const char *path, int num_displays) {
auto plane_res = MakeDrmModePlaneResUnique(GetFd());
if (!plane_res) {
ALOGE("Failed to get plane resources");
- return std::make_tuple(-ENOENT, 0);
+ return -ENOENT;
}
for (uint32_t i = 0; i < plane_res->count_planes; ++i) {
@@ -140,42 +135,7 @@ std::tuple<int, int> DrmDevice::Init(const char *path, int num_displays) {
}
}
- auto add_displays = [this, &num_displays](bool internal, bool connected) {
- for (auto &conn : connectors_) {
- bool is_connected = conn->IsConnected();
- if ((internal ? conn->IsInternal() : conn->IsExternal()) &&
- (connected ? is_connected : !is_connected)) {
- auto pipe = DrmDisplayPipeline::CreatePipeline(*conn);
- if (pipe) {
- pipelines_[num_displays] = std::move(pipe);
- ++num_displays;
- }
- }
- }
- };
-
- /* Put internal first to ensure Primary display will be internal
- * in case at least 1 internal is available
- */
- add_displays(/*internal = */ true, /*connected = */ true);
- add_displays(/*internal = */ false, /*connected = */ true);
- add_displays(/*internal = */ true, /*connected = */ false);
- add_displays(/*internal = */ false, /*connected = */ false);
-
- return std::make_tuple(0, pipelines_.size());
-}
-
-bool DrmDevice::HandlesDisplay(int display) const {
- return pipelines_.count(display) != 0;
-}
-
-auto DrmDevice::GetDisplayId(DrmConnector *conn) -> int {
- for (auto &dpipe : pipelines_) {
- if (dpipe.second->connector->Get() == conn) {
- return dpipe.first;
- }
- }
- return -1;
+ return 0;
}
auto DrmDevice::RegisterUserPropertyBlob(void *data, size_t length) const
diff --git a/drm/DrmDevice.h b/drm/DrmDevice.h
index 5220760..f2530ee 100644
--- a/drm/DrmDevice.h
+++ b/drm/DrmDevice.h
@@ -37,7 +37,7 @@ class DrmDevice {
DrmDevice();
~DrmDevice() = default;
- std::tuple<int, int> Init(const char *path, int num_displays);
+ auto Init(const char *path) -> int;
auto GetFd() const {
return fd_.Get();
@@ -56,19 +56,12 @@ class DrmDevice {
return max_resolution_;
}
- auto *GetPipelineForDisplay(int display) {
- return pipelines_.count(display) != 0 ? pipelines_.at(display).get()
- : nullptr;
- }
-
std::string GetName() const;
auto RegisterUserPropertyBlob(void *data, size_t length) const
-> DrmModeUserPropertyBlobUnique;
- bool HandlesDisplay(int display) const;
-
- bool HasAddFb2ModifiersSupport() const {
+ auto HasAddFb2ModifiersSupport() const {
return HasAddFb2ModifiersSupport_;
}
@@ -98,8 +91,6 @@ class DrmDevice {
return nullptr;
}
- auto GetDisplayId(DrmConnector *conn) -> int;
-
int GetProperty(uint32_t obj_id, uint32_t obj_type, const char *prop_name,
DrmProperty *property) const;
@@ -115,8 +106,6 @@ class DrmDevice {
std::pair<uint32_t, uint32_t> min_resolution_;
std::pair<uint32_t, uint32_t> max_resolution_;
- std::map<int /*display*/, std::unique_ptr<DrmDisplayPipeline>> pipelines_;
-
bool HasAddFb2ModifiersSupport_{};
std::unique_ptr<DrmFbImporter> drm_fb_importer_;
diff --git a/drm/ResourceManager.cpp b/drm/ResourceManager.cpp
index a7d99ee..789eca3 100644
--- a/drm/ResourceManager.cpp
+++ b/drm/ResourceManager.cpp
@@ -33,25 +33,34 @@
namespace android {
-ResourceManager::ResourceManager() : num_displays_(0) {
+ResourceManager::ResourceManager(
+ PipelineToFrontendBindingInterface *p2f_bind_interface)
+ : frontend_interface_(p2f_bind_interface) {
+ if (uevent_listener_.Init() != 0) {
+ ALOGE("Can't initialize event listener");
+ }
}
ResourceManager::~ResourceManager() {
uevent_listener_.Exit();
}
-int ResourceManager::Init() {
+void ResourceManager::Init() {
+ if (initialized_) {
+ ALOGE("Already initialized");
+ return;
+ }
+
char path_pattern[PROPERTY_VALUE_MAX];
// Could be a valid path or it can have at the end of it the wildcard %
// which means that it will try open all devices until an error is met.
int path_len = property_get("vendor.hwc.drm.device", path_pattern,
"/dev/dri/card%");
- int ret = 0;
if (path_pattern[path_len - 1] != '%') {
- ret = AddDrmDevice(std::string(path_pattern));
+ AddDrmDevice(std::string(path_pattern));
} else {
path_pattern[path_len - 1] = '\0';
- for (int idx = 0; ret == 0; ++idx) {
+ for (int idx = 0;; ++idx) {
std::ostringstream path;
path << path_pattern << idx;
@@ -59,51 +68,109 @@ int ResourceManager::Init() {
if (stat(path.str().c_str(), &buf) != 0)
break;
- if (DrmDevice::IsKMSDev(path.str().c_str()))
- ret = AddDrmDevice(path.str());
+ if (DrmDevice::IsKMSDev(path.str().c_str())) {
+ AddDrmDevice(path.str());
+ }
}
}
- if (num_displays_ == 0) {
- ALOGE("Failed to initialize any displays");
- return ret != 0 ? -EINVAL : ret;
- }
-
char scale_with_gpu[PROPERTY_VALUE_MAX];
property_get("vendor.hwc.drm.scale_with_gpu", scale_with_gpu, "0");
scale_with_gpu_ = bool(strncmp(scale_with_gpu, "0", 1));
if (BufferInfoGetter::GetInstance() == nullptr) {
ALOGE("Failed to initialize BufferInfoGetter");
- return -EINVAL;
+ return;
}
- ret = uevent_listener_.Init();
- if (ret != 0) {
- ALOGE("Can't initialize event listener %d", ret);
- return ret;
+ uevent_listener_.RegisterHotplugHandler([this] {
+ const std::lock_guard<std::mutex> lock(GetMainLock());
+ UpdateFrontendDisplays();
+ });
+
+ UpdateFrontendDisplays();
+
+ initialized_ = true;
+}
+
+void ResourceManager::DeInit() {
+ if (!initialized_) {
+ ALOGE("Not initialized");
+ return;
}
- return 0;
+ uevent_listener_.RegisterHotplugHandler([] {});
+
+ DetachAllFrontendDisplays();
+ drms_.clear();
+
+ initialized_ = false;
}
int ResourceManager::AddDrmDevice(const std::string &path) {
auto drm = std::make_unique<DrmDevice>();
- int displays_added = 0;
- int ret = 0;
- std::tie(ret, displays_added) = drm->Init(path.c_str(), num_displays_);
+ int ret = drm->Init(path.c_str());
drms_.push_back(std::move(drm));
- num_displays_ += displays_added;
return ret;
}
-DrmDisplayPipeline *ResourceManager::GetPipeline(int display) {
+void ResourceManager::UpdateFrontendDisplays() {
+ auto ordered_connectors = GetOrderedConnectors();
+
+ for (auto *conn : ordered_connectors) {
+ conn->UpdateModes();
+ bool connected = conn->IsConnected();
+ bool attached = attached_pipelines_.count(conn) != 0;
+
+ if (connected != attached) {
+ ALOGI("%s connector %s", connected ? "Attaching" : "Detaching",
+ conn->GetName().c_str());
+
+ if (connected) {
+ auto pipeline = DrmDisplayPipeline::CreatePipeline(*conn);
+ frontend_interface_->BindDisplay(pipeline.get());
+ attached_pipelines_[conn] = std::move(pipeline);
+ } else {
+ auto &pipeline = attached_pipelines_[conn];
+ frontend_interface_->UnbindDisplay(pipeline.get());
+ attached_pipelines_.erase(conn);
+ }
+ }
+ }
+ frontend_interface_->FinalizeDisplayBinding();
+}
+
+void ResourceManager::DetachAllFrontendDisplays() {
+ for (auto &p : attached_pipelines_) {
+ frontend_interface_->UnbindDisplay(p.second.get());
+ }
+ attached_pipelines_.clear();
+ frontend_interface_->FinalizeDisplayBinding();
+}
+
+auto ResourceManager::GetOrderedConnectors() -> std::vector<DrmConnector *> {
+ /* Put internal displays first then external to
+ * ensure Internal will take Primary slot
+ */
+
+ std::vector<DrmConnector *> ordered_connectors;
+
+ for (auto &drm : drms_) {
+ for (const auto &conn : drm->GetConnectors()) {
+ if (conn->IsInternal()) {
+ ordered_connectors.emplace_back(conn.get());
+ }
+ }
+ }
+
for (auto &drm : drms_) {
- auto *pipe = drm->GetPipelineForDisplay(display);
- if (pipe != nullptr) {
- return pipe;
+ for (const auto &conn : drm->GetConnectors()) {
+ if (conn->IsExternal()) {
+ ordered_connectors.emplace_back(conn.get());
+ }
}
}
- return nullptr;
+
+ return ordered_connectors;
}
} // namespace android
diff --git a/drm/ResourceManager.h b/drm/ResourceManager.h
index caeb098..c4c3edd 100644
--- a/drm/ResourceManager.h
+++ b/drm/ResourceManager.h
@@ -20,42 +20,48 @@
#include <cstring>
#include "DrmDevice.h"
+#include "DrmDisplayPipeline.h"
#include "DrmFbImporter.h"
#include "UEventListener.h"
namespace android {
+class PipelineToFrontendBindingInterface {
+ public:
+ virtual ~PipelineToFrontendBindingInterface() = default;
+ virtual bool BindDisplay(DrmDisplayPipeline *);
+ virtual bool UnbindDisplay(DrmDisplayPipeline *);
+ virtual void FinalizeDisplayBinding();
+};
+
class ResourceManager {
public:
- ResourceManager();
+ explicit ResourceManager(
+ PipelineToFrontendBindingInterface *p2f_bind_interface);
ResourceManager(const ResourceManager &) = delete;
ResourceManager &operator=(const ResourceManager &) = delete;
+ ResourceManager(const ResourceManager &&) = delete;
+ ResourceManager &&operator=(const ResourceManager &&) = delete;
~ResourceManager();
- int Init();
- auto GetPipeline(int display) -> DrmDisplayPipeline *;
- auto &GetDrmDevices() const {
- return drms_;
- }
- int GetDisplayCount() const {
- return num_displays_;
- }
+ void Init();
+
+ void DeInit();
+
bool ForcedScalingWithGpu() const {
return scale_with_gpu_;
}
- UEventListener *GetUEventListener() {
- return &uevent_listener_;
- }
-
auto &GetMainLock() {
return main_lock_;
}
private:
- int AddDrmDevice(std::string const &path);
+ auto AddDrmDevice(std::string const &path) -> int;
+ auto GetOrderedConnectors() -> std::vector<DrmConnector *>;
+ void UpdateFrontendDisplays();
+ void DetachAllFrontendDisplays();
- int num_displays_;
std::vector<std::unique_ptr<DrmDevice>> drms_;
bool scale_with_gpu_{};
@@ -63,6 +69,13 @@ class ResourceManager {
UEventListener uevent_listener_;
std::mutex main_lock_;
+
+ std::map<DrmConnector *, std::unique_ptr<DrmDisplayPipeline>>
+ attached_pipelines_;
+
+ PipelineToFrontendBindingInterface *const frontend_interface_;
+
+ bool initialized_{};
};
} // namespace android
diff --git a/hwc2_device/DrmHwcTwo.cpp b/hwc2_device/DrmHwcTwo.cpp
index 4cb0fc3..2002b85 100644
--- a/hwc2_device/DrmHwcTwo.cpp
+++ b/hwc2_device/DrmHwcTwo.cpp
@@ -18,51 +18,96 @@
#include "DrmHwcTwo.h"
+#include <cinttypes>
+
#include "backend/Backend.h"
#include "utils/log.h"
namespace android {
-DrmHwcTwo::DrmHwcTwo() = default;
+DrmHwcTwo::DrmHwcTwo() : resource_manager_(this){};
+
+/* Must be called after every display attach/detach cycle */
+void DrmHwcTwo::FinalizeDisplayBinding() {
+ if (displays_.count(kPrimaryDisplay) == 0) {
+ /* Create/update new headless display if no other displays exists
+ * or reattach different display to make it primary
+ */
-HWC2::Error DrmHwcTwo::CreateDisplay(hwc2_display_t displ,
- HWC2::DisplayType type) {
- auto *pipe = resource_manager_.GetPipeline(static_cast<int>(displ));
- if (!pipe) {
- ALOGE("Failed to get a valid drmresource");
- return HWC2::Error::NoResources;
+ if (display_handles_.empty()) {
+ /* Enable headless mode */
+ ALOGI("No pipelines available. Creating null-display for headless mode");
+ displays_[kPrimaryDisplay] = std::make_unique<
+ HwcDisplay>(nullptr, kPrimaryDisplay, HWC2::DisplayType::Physical,
+ this);
+ } else {
+ 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);
+ if (displays_.count(kPrimaryDisplay) == 0) {
+ ALOGE("FIXME!!! Still no primary display after reattaching...");
+ }
+ }
}
- displays_.emplace(std::piecewise_construct, std::forward_as_tuple(displ),
- std::forward_as_tuple(&resource_manager_, pipe, displ, type,
- this));
- displays_.at(displ).Init();
- return HWC2::Error::None;
+ // Finally, send hotplug events to the client
+ for (auto &dhe : deferred_hotplug_events_) {
+ SendHotplugEventToClient(dhe.first, dhe.second);
+ }
+ deferred_hotplug_events_.clear();
}
-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_;
+ }
+
+ auto disp = std::make_unique<HwcDisplay>(pipeline, disp_handle,
+ HWC2::DisplayType::Physical, this);
+
+ if (disp_handle == kPrimaryDisplay) {
+ displays_.erase(disp_handle);
}
- resource_manager_.GetUEventListener()->RegisterHotplugHandler([this] {
- const std::lock_guard<std::mutex> lock(GetResMan().GetMainLock());
+ 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] = std::move(disp);
+ display_handles_[pipeline] = disp_handle;
+
+ 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];
- HandleHotplugUEvent();
- });
+ ALOGI("Detaching the pipeline '%s' from the display #%i%s",
+ pipeline->connector->Get()->GetName().c_str(), (int)handle,
+ handle == kPrimaryDisplay ? " (Primary)" : "");
- return ret;
+ display_handles_.erase(pipeline);
+ if (displays_.count(handle) == 0) {
+ ALOGE("%s, can't find the display, handle: %" PRIu64, __func__, handle);
+ return false;
+ }
+ displays_.erase(handle);
+ return true;
}
HWC2::Error DrmHwcTwo::CreateVirtualDisplay(uint32_t /*width*/,
@@ -89,8 +134,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());
@@ -107,9 +152,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: {
@@ -132,7 +181,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__);
@@ -147,50 +197,10 @@ 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->GetConnectors()) {
- int display_id = drmDevice->GetDisplayId(conn.get());
- auto &display = displays_.at(display_id);
-
- if (!conn->IsConnected() && !display.IsInHeadlessMode())
- continue;
- HandleDisplayHotplug(display_id, display.IsInHeadlessMode()
- ? 1
- : (conn->IsConnected() ? 1 : 0));
- }
-}
-
-void DrmHwcTwo::HandleHotplugUEvent() {
- for (const auto &drm : resource_manager_.GetDrmDevices()) {
- for (const auto &conn : drm->GetConnectors()) {
- int display_id = drm->GetDisplayId(conn.get());
-
- bool old_state = conn->IsConnected();
- bool cur_state = conn->UpdateModes() ? false : conn->IsConnected();
- if (cur_state == old_state)
- continue;
-
- ALOGI("%s event for connector %u on display %d",
- cur_state == DRM_MODE_CONNECTED ? "Plug" : "Unplug", conn->GetId(),
- display_id);
-
- auto &display = displays_.at(display_id);
- display.ChosePreferredConfig();
- if (cur_state) {
- display.ClearDisplay();
- }
-
- HandleDisplayHotplug(display_id, display.IsInHeadlessMode()
- ? DRM_MODE_CONNECTED
- : (cur_state ? 1 : 0));
- }
- }
-}
-
} // namespace android
diff --git a/hwc2_device/DrmHwcTwo.h b/hwc2_device/DrmHwcTwo.h
index fa13170..83fbd79 100644
--- a/hwc2_device/DrmHwcTwo.h
+++ b/hwc2_device/DrmHwcTwo.h
@@ -24,11 +24,10 @@
namespace android {
-class DrmHwcTwo {
+class DrmHwcTwo : public PipelineToFrontendBindingInterface {
public:
DrmHwcTwo();
-
- HWC2::Error Init();
+ ~DrmHwcTwo() override = default;
std::pair<HWC2_PFN_HOTPLUG, hwc2_callback_data_t> hotplug_callback_{};
std::pair<HWC2_PFN_VSYNC, hwc2_callback_data_t> vsync_callback_{};
@@ -45,27 +44,38 @@ class DrmHwcTwo {
uint32_t GetMaxVirtualDisplayCount();
HWC2::Error RegisterCallback(int32_t descriptor, hwc2_callback_data_t data,
hwc2_function_pointer_t function);
- HWC2::Error CreateDisplay(hwc2_display_t displ, HWC2::DisplayType type);
auto GetDisplay(hwc2_display_t display_handle) {
- return displays_.count(display_handle) != 0 ? &displays_.at(display_handle)
- : nullptr;
+ return displays_.count(display_handle) != 0
+ ? displays_[display_handle].get()
+ : nullptr;
}
auto &GetResMan() {
return resource_manager_;
}
- private:
- void HandleDisplayHotplug(hwc2_display_t displayid, int state);
- void HandleInitialHotplugState(DrmDevice *drmDevice);
+ void ScheduleHotplugEvent(hwc2_display_t displayid, bool connected) {
+ deferred_hotplug_events_[displayid] = connected;
+ }
- void HandleHotplugUEvent();
+ // PipelineToFrontendBindingInterface
+ bool BindDisplay(DrmDisplayPipeline *pipeline) override;
+ bool UnbindDisplay(DrmDisplayPipeline *pipeline) override;
+ void FinalizeDisplayBinding() override;
+
+ private:
+ void SendHotplugEventToClient(hwc2_display_t displayid, bool connected);
ResourceManager resource_manager_;
- std::map<hwc2_display_t, HwcDisplay> displays_;
+ std::map<hwc2_display_t, std::unique_ptr<HwcDisplay>> displays_;
+ std::map<DrmDisplayPipeline *, hwc2_display_t> display_handles_;
std::string mDumpString;
+
+ std::map<hwc2_display_t, bool> deferred_hotplug_events_;
+
+ uint32_t last_display_handle_ = kPrimaryDisplay;
};
} // namespace android
diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp
index ac5d196..7ca4549 100644
--- a/hwc2_device/HwcDisplay.cpp
+++ b/hwc2_device/HwcDisplay.cpp
@@ -27,6 +27,9 @@
namespace android {
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+uint32_t HwcDisplay::layer_idx_ = 2; /* Start from 2. See destroyLayer() */
+
std::string HwcDisplay::DumpDelta(HwcDisplay::Stats delta) {
if (delta.total_pixops_ == 0)
return "No stats yet";
@@ -84,11 +87,9 @@ std::string HwcDisplay::Dump() {
return ss.str();
}
-HwcDisplay::HwcDisplay(ResourceManager *resource_manager,
- DrmDisplayPipeline *pipeline, hwc2_display_t handle,
+HwcDisplay::HwcDisplay(DrmDisplayPipeline *pipeline, hwc2_display_t handle,
HWC2::DisplayType type, DrmHwcTwo *hwc2)
: hwc2_(hwc2),
- resource_manager_(resource_manager),
pipeline_(pipeline),
handle_(handle),
type_(type),
@@ -99,6 +100,26 @@ HwcDisplay::HwcDisplay(ResourceManager *resource_manager,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0};
// clang-format on
+
+ ChosePreferredConfig();
+ Init();
+
+ hwc2_->ScheduleHotplugEvent(handle_, /*connected = */ true);
+}
+
+HwcDisplay::~HwcDisplay() {
+ if (handle_ != kPrimaryDisplay) {
+ hwc2_->ScheduleHotplugEvent(handle_, /*connected = */ false);
+ }
+
+ auto &main_lock = hwc2_->GetResMan().GetMainLock();
+ /* Unlock to allow pending vsync callbacks to finish */
+ main_lock.unlock();
+ flattening_vsync_worker_.VSyncControl(false);
+ flattening_vsync_worker_.Exit();
+ vsync_worker_.VSyncControl(false);
+ vsync_worker_.Exit();
+ main_lock.lock();
}
void HwcDisplay::ClearDisplay() {
@@ -108,7 +129,7 @@ void HwcDisplay::ClearDisplay() {
}
AtomicCommitArgs a_args = {.clear_active_composition = true};
- GetPipe().compositor->ExecuteAtomicCommit(a_args);
+ pipeline_->compositor->ExecuteAtomicCommit(a_args);
}
HWC2::Error HwcDisplay::Init() {
@@ -152,21 +173,29 @@ HWC2::Error HwcDisplay::Init() {
return HWC2::Error::BadDisplay;
}
- ret = BackendManager::GetInstance().SetBackendForDisplay(this);
- if (ret) {
- ALOGE("Failed to set backend for d=%d %d\n", int(handle_), ret);
- return HWC2::Error::BadDisplay;
+ if (!IsInHeadlessMode()) {
+ ret = BackendManager::GetInstance().SetBackendForDisplay(this);
+ if (ret) {
+ ALOGE("Failed to set backend for d=%d %d\n", int(handle_), ret);
+ return HWC2::Error::BadDisplay;
+ }
}
client_layer_.SetLayerBlendMode(HWC2_BLEND_MODE_PREMULTIPLIED);
- return ChosePreferredConfig();
+ return HWC2::Error::None;
}
HWC2::Error HwcDisplay::ChosePreferredConfig() {
- HWC2::Error err = configs_.Update(*GetPipe().connector->Get());
- if (!IsInHeadlessMode() && err != HWC2::Error::None)
+ HWC2::Error err{};
+ if (!IsInHeadlessMode()) {
+ err = configs_.Update(*pipeline_->connector->Get());
+ } else {
+ configs_.FillHeadless();
+ }
+ if (!IsInHeadlessMode() && err != HWC2::Error::None) {
return HWC2::Error::BadDisplay;
+ }
return SetActiveConfig(configs_.preferred_config_id);
}
@@ -185,8 +214,24 @@ HWC2::Error HwcDisplay::CreateLayer(hwc2_layer_t *layer) {
}
HWC2::Error HwcDisplay::DestroyLayer(hwc2_layer_t layer) {
- if (!get_layer(layer))
+ if (!get_layer(layer)) {
+ /* Primary display don't send unplug event, instead it replaces
+ * display to headless or to another one and sends Plug event to the
+ * SF. SF can't distinguish this case from virtualized display size
+ * change case and will destroy previously used layers. If we will return
+ * BadLayer, service will print errors to the logcat.
+ *
+ * Nevertheless VTS is trying to destroy 1st layer without adding any
+ * layers prior to that, than it checks for BadLayer result. So we
+ * numbering the layers starting from 2, and use index 1 to catch VTS client
+ * to return BadLayer, making VTS pass.
+ */
+ if (layers_.empty() && layer != 1) {
+ return HWC2::Error::None;
+ }
+
return HWC2::Error::BadLayer;
+ }
layers_.erase(layer);
return HWC2::Error::None;
@@ -226,12 +271,13 @@ HWC2::Error HwcDisplay::GetChangedCompositionTypes(uint32_t *num_elements,
HWC2::Error HwcDisplay::GetClientTargetSupport(uint32_t width, uint32_t height,
int32_t /*format*/,
int32_t dataspace) {
- std::pair<uint32_t, uint32_t> min = GetPipe().device->GetMinResolution();
- std::pair<uint32_t, uint32_t> max = GetPipe().device->GetMaxResolution();
if (IsInHeadlessMode()) {
return HWC2::Error::None;
}
+ std::pair<uint32_t, uint32_t> min = pipeline_->device->GetMinResolution();
+ std::pair<uint32_t, uint32_t> max = pipeline_->device->GetMaxResolution();
+
if (width < min.first || height < min.second)
return HWC2::Error::Unsupported;
@@ -261,7 +307,7 @@ HWC2::Error HwcDisplay::GetDisplayAttribute(hwc2_config_t config,
int conf = static_cast<int>(config);
if (configs_.hwc_configs.count(conf) == 0) {
- ALOGE("Could not find active mode for %d", conf);
+ ALOGE("Could not find mode #%d", conf);
return HWC2::Error::BadConfig;
}
@@ -331,7 +377,11 @@ HWC2::Error HwcDisplay::GetDisplayConfigs(uint32_t *num_configs,
HWC2::Error HwcDisplay::GetDisplayName(uint32_t *size, char *name) {
std::ostringstream stream;
- stream << "display-" << GetPipe().connector->Get()->GetId();
+ if (IsInHeadlessMode()) {
+ stream << "null-display";
+ } else {
+ stream << "display-" << GetPipe().connector->Get()->GetId();
+ }
std::string string = stream.str();
size_t length = string.length();
if (!name) {
@@ -745,11 +795,18 @@ HWC2::Error HwcDisplay::SetContentType(int32_t contentType) {
HWC2::Error HwcDisplay::GetDisplayIdentificationData(uint8_t *outPort,
uint32_t *outDataSize,
uint8_t *outData) {
+ if (IsInHeadlessMode()) {
+ return HWC2::Error::None;
+ }
auto blob = GetPipe().connector->Get()->GetEdidBlob();
+ *outPort = handle_ - 1;
+
if (!blob) {
- ALOGE("Failed to get edid property value.");
- return HWC2::Error::Unsupported;
+ if (outData == nullptr) {
+ *outDataSize = 0;
+ }
+ return HWC2::Error::None;
}
if (outData) {
@@ -758,7 +815,6 @@ HWC2::Error HwcDisplay::GetDisplayIdentificationData(uint8_t *outPort,
} else {
*outDataSize = blob->length;
}
- *outPort = GetPipe().connector->Get()->GetId();
return HWC2::Error::None;
}
diff --git a/hwc2_device/HwcDisplay.h b/hwc2_device/HwcDisplay.h
index b73404a..42b000a 100644
--- a/hwc2_device/HwcDisplay.h
+++ b/hwc2_device/HwcDisplay.h
@@ -37,10 +37,10 @@ inline constexpr uint32_t kPrimaryDisplay = 0;
class HwcDisplay {
public:
- HwcDisplay(ResourceManager *resource_manager, DrmDisplayPipeline *pipeline,
- hwc2_display_t handle, HWC2::DisplayType type, DrmHwcTwo *hwc2);
+ HwcDisplay(DrmDisplayPipeline *pipeline, hwc2_display_t handle,
+ HWC2::DisplayType type, DrmHwcTwo *hwc2);
HwcDisplay(const HwcDisplay &) = delete;
- HWC2::Error Init();
+ ~HwcDisplay();
HWC2::Error CreateComposition(AtomicCommitArgs &a_args);
std::vector<HwcLayer *> GetOrderLayersByZPos();
@@ -144,6 +144,10 @@ class HwcDisplay {
const Backend *backend() const;
void set_backend(std::unique_ptr<Backend> backend);
+ auto GetHwc2() {
+ return hwc2_;
+ }
+
std::map<hwc2_layer_t, HwcLayer> &layers() {
return layers_;
}
@@ -152,10 +156,6 @@ class HwcDisplay {
return *pipeline_;
}
- ResourceManager *resource_manager() const {
- return resource_manager_;
- }
-
android_color_transform_t &color_transform_hint() {
return color_transform_hint_;
}
@@ -193,8 +193,7 @@ class HwcDisplay {
* https://source.android.com/devices/graphics/hotplug#handling-common-scenarios
*/
bool IsInHeadlessMode() {
- return handle_ == kPrimaryDisplay &&
- !GetPipe().connector->Get()->IsConnected();
+ return !pipeline_;
}
private:
@@ -213,20 +212,22 @@ class HwcDisplay {
HwcDisplayConfigs configs_;
- DrmHwcTwo *hwc2_;
+ DrmHwcTwo *const hwc2_;
std::optional<DrmMode> staged_mode;
- ResourceManager *resource_manager_;
-
DrmDisplayPipeline *const pipeline_;
std::unique_ptr<Backend> backend_;
VSyncWorker vsync_worker_;
- hwc2_display_t handle_;
+
+ const hwc2_display_t handle_;
HWC2::DisplayType type_;
- uint32_t layer_idx_ = 0;
+
+ // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+ static uint32_t layer_idx_;
+
std::map<hwc2_layer_t, HwcLayer> layers_;
HwcLayer client_layer_;
int32_t color_mode_{};
@@ -237,6 +238,8 @@ class HwcDisplay {
Stats total_stats_;
Stats prev_stats_;
std::string DumpDelta(HwcDisplay::Stats delta);
+
+ HWC2::Error Init();
};
} // namespace android
diff --git a/hwc2_device/HwcDisplayConfigs.cpp b/hwc2_device/HwcDisplayConfigs.cpp
index f6ccdb7..c28ec9f 100644
--- a/hwc2_device/HwcDisplayConfigs.cpp
+++ b/hwc2_device/HwcDisplayConfigs.cpp
@@ -31,10 +31,10 @@ constexpr uint32_t kHeadlessModeDisplayVRefresh = 60;
namespace android {
-// NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme
-HWC2::Error HwcDisplayConfigs::Update(DrmConnector &connector) {
- /* In case UpdateModes will fail we will still have one mode for headless
- * mode*/
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+int HwcDisplayConfigs::last_config_id = 1;
+
+void HwcDisplayConfigs::FillHeadless() {
hwc_configs.clear();
last_config_id++;
@@ -53,7 +53,13 @@ HWC2::Error HwcDisplayConfigs::Update(DrmConnector &connector) {
mm_width = kHeadlessModeDisplayWidthMm;
mm_height = kHeadlessModeDisplayHeightMm;
+}
+// NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme
+HWC2::Error HwcDisplayConfigs::Update(DrmConnector &connector) {
+ /* In case UpdateModes will fail we will still have one mode for headless
+ * mode*/
+ FillHeadless();
/* Read real configs */
int ret = connector.UpdateModes();
if (ret != 0) {
@@ -190,8 +196,6 @@ HWC2::Error HwcDisplayConfigs::Update(DrmConnector &connector) {
}
}
- /* Set active mode to be valid mode */
- active_config_id = preferred_config_id;
return HWC2::Error::None;
}
diff --git a/hwc2_device/HwcDisplayConfigs.h b/hwc2_device/HwcDisplayConfigs.h
index 5bcf696..75852a6 100644
--- a/hwc2_device/HwcDisplayConfigs.h
+++ b/hwc2_device/HwcDisplayConfigs.h
@@ -40,13 +40,15 @@ struct HwcDisplayConfig {
struct HwcDisplayConfigs {
HWC2::Error Update(DrmConnector &conn);
+ void FillHeadless();
std::map<int /*config_id*/, struct HwcDisplayConfig> hwc_configs;
int active_config_id = 0;
int preferred_config_id = 0;
- int last_config_id = 1;
+ // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+ static int last_config_id;
uint32_t mm_width = 0;
uint32_t mm_height = 0;
diff --git a/hwc2_device/hwc2_device.cpp b/hwc2_device/hwc2_device.cpp
index 57bc205..a6dedb4 100644
--- a/hwc2_device/hwc2_device.cpp
+++ b/hwc2_device/hwc2_device.cpp
@@ -390,12 +390,6 @@ static int HookDevOpen(const struct hw_module_t *module, const char *name,
ctx->getCapabilities = HookDevGetCapabilities;
ctx->getFunction = HookDevGetFunction;
- HWC2::Error err = ctx->drmhwctwo.Init();
- if (err != HWC2::Error::None) {
- ALOGE("Failed to initialize DrmHwcTwo err=%d\n", err);
- return -EINVAL;
- }
-
*dev = &ctx.release()->common;
return 0;