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>
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 @@
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) {
- for (auto &drm : drms_) {
- auto *pipe = drm->GetPipelineForDisplay(display);
- if (pipe != nullptr) {
- return pipe;
+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);
+ }
}
}
- return nullptr;
+ 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_) {
+ for (const auto &conn : drm->GetConnectors()) {
+ if (conn->IsExternal()) {
+ ordered_connectors.emplace_back(conn.get());
+ }
+ }
+ }
+
+ return ordered_connectors;
}
} // namespace android