drm_hwcomposer: Virtual display acceleration support

Some platforms like RaspberryPI-4 can benefit from Display Blender IP Core's
ability to write back the composition into RAM, offloading the GPU in cases
where the display content needs to be used (screen record, remote display,
etc.).

To enable this feature the following system property must be enabled:

    PRODUCT_VENDOR_PROPERTIES += debug.sf.enable_hwc_vds=1

The feature was requested by the Tesla Android project to improve UI
performance.

Closes: https://gitlab.freedesktop.org/drm-hwcomposer/drm-hwcomposer/-/issues/4
Change-Id: I643f94551408bf218a0b889f1a031598646242f1
Signed-off-by: Roman Stratiienko <r.stratiienko@gmail.com>
diff --git a/drm/DrmAtomicStateManager.cpp b/drm/DrmAtomicStateManager.cpp
index 4ff16e2..b1f8257 100644
--- a/drm/DrmAtomicStateManager.cpp
+++ b/drm/DrmAtomicStateManager.cpp
@@ -79,8 +79,26 @@
   }
 
   int out_fence = -1;
-  if (!crtc->GetOutFencePtrProperty().AtomicSet(*pset, uint64_t(&out_fence))) {
-    return -EINVAL;
+  if (!args.writeback_fb) {
+    if (!crtc->GetOutFencePtrProperty().  //
+         AtomicSet(*pset, uint64_t(&out_fence))) {
+      return -EINVAL;
+    }
+  } else {
+    if (!connector->GetWritebackOutFenceProperty().  //
+         AtomicSet(*pset, uint64_t(&out_fence))) {
+      return -EINVAL;
+    }
+
+    if (!connector->GetWritebackFbIdProperty().  //
+         AtomicSet(*pset, args.writeback_fb->GetFbId())) {
+      return -EINVAL;
+    }
+
+    if (args.writeback_release_fence) {
+      sync_wait(*args.writeback_release_fence, -1);
+      args.writeback_release_fence.reset();
+    }
   }
 
   bool nonblock = true;
diff --git a/drm/DrmAtomicStateManager.h b/drm/DrmAtomicStateManager.h
index 6e32a37..e5a0945 100644
--- a/drm/DrmAtomicStateManager.h
+++ b/drm/DrmAtomicStateManager.h
@@ -37,6 +37,9 @@
   std::shared_ptr<DrmKmsPlan> composition;
   std::shared_ptr<drm_color_ctm> color_matrix;
 
+  std::shared_ptr<DrmFbIdHandle> writeback_fb;
+  SharedFd writeback_release_fence;
+
   /* out */
   SharedFd out_fence;
 
diff --git a/drm/DrmConnector.h b/drm/DrmConnector.h
index f21f598..018c615 100644
--- a/drm/DrmConnector.h
+++ b/drm/DrmConnector.h
@@ -94,6 +94,14 @@
     return edid_property_;
   }
 
+  auto &GetWritebackFbIdProperty() const {
+    return writeback_fb_id_;
+  }
+
+  auto &GetWritebackOutFenceProperty() const {
+    return writeback_out_fence_;
+  }
+
   auto IsConnected() const {
     return connector_->connection == DRM_MODE_CONNECTED;
   }
diff --git a/drm/DrmDevice.cpp b/drm/DrmDevice.cpp
index 1d6b62e..f6f0b01 100644
--- a/drm/DrmDevice.cpp
+++ b/drm/DrmDevice.cpp
@@ -242,6 +242,11 @@
   return connectors_;
 }
 
+auto DrmDevice::GetWritebackConnectors()
+    -> const std::vector<std::unique_ptr<DrmConnector>> & {
+  return writeback_connectors_;
+}
+
 auto DrmDevice::GetPlanes() -> const std::vector<std::unique_ptr<DrmPlane>> & {
   return planes_;
 }
diff --git a/drm/DrmDevice.h b/drm/DrmDevice.h
index 39d0c88..cbaa536 100644
--- a/drm/DrmDevice.h
+++ b/drm/DrmDevice.h
@@ -47,6 +47,8 @@
   }
 
   auto GetConnectors() -> const std::vector<std::unique_ptr<DrmConnector>> &;
+  auto GetWritebackConnectors()
+      -> const std::vector<std::unique_ptr<DrmConnector>> &;
   auto GetPlanes() -> const std::vector<std::unique_ptr<DrmPlane>> &;
   auto GetCrtcs() -> const std::vector<std::unique_ptr<DrmCrtc>> &;
   auto GetEncoders() -> const std::vector<std::unique_ptr<DrmEncoder>> &;
diff --git a/drm/ResourceManager.cpp b/drm/ResourceManager.cpp
index 634ccb7..a6e9fc2 100644
--- a/drm/ResourceManager.cpp
+++ b/drm/ResourceManager.cpp
@@ -187,4 +187,30 @@
 
   return ordered_connectors;
 }
+
+auto ResourceManager::GetVirtualDisplayPipeline()
+    -> std::shared_ptr<DrmDisplayPipeline> {
+  for (auto &drm : drms_) {
+    for (const auto &conn : drm->GetWritebackConnectors()) {
+      auto pipeline = DrmDisplayPipeline::CreatePipeline(*conn);
+      if (!pipeline) {
+        ALOGE("Failed to create pipeline for writeback connector %s",
+              conn->GetName().c_str());
+      }
+      if (pipeline) {
+        return pipeline;
+      }
+    }
+  }
+  return {};
+}
+
+auto ResourceManager::GetWritebackConnectorsCount() -> uint32_t {
+  uint32_t count = 0;
+  for (auto &drm : drms_) {
+    count += drm->GetWritebackConnectors().size();
+  }
+  return count;
+}
+
 }  // namespace android
diff --git a/drm/ResourceManager.h b/drm/ResourceManager.h
index 72ee3e2..20e84a9 100644
--- a/drm/ResourceManager.h
+++ b/drm/ResourceManager.h
@@ -65,6 +65,9 @@
     return main_lock_;
   }
 
+  auto GetVirtualDisplayPipeline() -> std::shared_ptr<DrmDisplayPipeline>;
+  auto GetWritebackConnectorsCount() -> uint32_t;
+
   static auto GetTimeMonotonicNs() -> int64_t;
 
  private: