drm_hwcomposer: Support assigning planes in ValidateDisplay

In order to assign planes to layers in ValidateDisplay, testing
compositing with a DRM atomic modeset test is needed as PresentDisplay
is too late. This means most of PresentDisplay needs to be run from
ValidateDisplay, so refactor PresentDisplay to a common function adding
a test only option.

Signed-off-by: Rob Herring <robh@kernel.org>
diff --git a/drmdisplaycompositor.cpp b/drmdisplaycompositor.cpp
index 2f9f6c6..defede6 100644
--- a/drmdisplaycompositor.cpp
+++ b/drmdisplaycompositor.cpp
@@ -510,6 +510,10 @@
   return ret;
 }
 
+int DrmDisplayCompositor::TestComposition(DrmDisplayComposition *composition) {
+  return CommitFrame(composition, true);
+}
+
 void DrmDisplayCompositor::Dump(std::ostringstream *out) const {
   int ret = pthread_mutex_lock(&lock_);
   if (ret)
diff --git a/drmdisplaycompositor.h b/drmdisplaycompositor.h
index ed46873..0d85949 100644
--- a/drmdisplaycompositor.h
+++ b/drmdisplaycompositor.h
@@ -44,6 +44,7 @@
 
   std::unique_ptr<DrmDisplayComposition> CreateComposition() const;
   int ApplyComposition(std::unique_ptr<DrmDisplayComposition> composition);
+  int TestComposition(DrmDisplayComposition *composition);
   int Composite();
   void Dump(std::ostringstream *out) const;
 
diff --git a/drmhwctwo.cpp b/drmhwctwo.cpp
index 3c22484..cbe6df7 100644
--- a/drmhwctwo.cpp
+++ b/drmhwctwo.cpp
@@ -467,8 +467,7 @@
   }
 }
 
-HWC2::Error DrmHwcTwo::HwcDisplay::PresentDisplay(int32_t *retire_fence) {
-  supported(__func__);
+HWC2::Error DrmHwcTwo::HwcDisplay::CreateComposition(bool test) {
   std::vector<DrmCompositionDisplayLayersMap> layers_map;
   layers_map.emplace_back();
   DrmCompositionDisplayLayersMap &map = layers_map.back();
@@ -481,7 +480,13 @@
   uint32_t client_z_order = 0;
   std::map<uint32_t, DrmHwcTwo::HwcLayer *> z_map;
   for (std::pair<const hwc2_layer_t, DrmHwcTwo::HwcLayer> &l : layers_) {
-    switch (l.second.validated_type()) {
+    HWC2::Composition comp_type;
+    if (test)
+      comp_type = l.second.sf_type();
+    else
+      comp_type = l.second.validated_type();
+
+    switch (comp_type) {
       case HWC2::Composition::Device:
         z_map.emplace(std::make_pair(l.second.z_order(), &l.second));
         break;
@@ -497,6 +502,9 @@
   if (use_client_layer)
     z_map.emplace(std::make_pair(client_z_order, &client_layer_));
 
+  if (z_map.empty())
+    return HWC2::Error::BadLayer;
+
   // now that they're ordered by z, add them to the composition
   for (std::pair<const uint32_t, DrmHwcTwo::HwcLayer *> &l : z_map) {
     DrmHwcLayer layer;
@@ -508,10 +516,6 @@
     }
     map.layers.emplace_back(std::move(layer));
   }
-  if (map.layers.empty()) {
-    *retire_fence = -1;
-    return HWC2::Error::None;
-  }
 
   std::unique_ptr<DrmDisplayComposition> composition =
       compositor_.CreateComposition();
@@ -542,13 +546,31 @@
     i = overlay_planes.erase(i);
   }
 
-  AddFenceToRetireFence(composition->take_out_fence());
-
-  ret = compositor_.ApplyComposition(std::move(composition));
+  if (test) {
+    ret = compositor_.TestComposition(composition.get());
+  } else {
+    AddFenceToRetireFence(composition->take_out_fence());
+    ret = compositor_.ApplyComposition(std::move(composition));
+  }
   if (ret) {
     ALOGE("Failed to apply the frame composition ret=%d", ret);
     return HWC2::Error::BadParameter;
   }
+  return HWC2::Error::None;
+}
+
+HWC2::Error DrmHwcTwo::HwcDisplay::PresentDisplay(int32_t *retire_fence) {
+  supported(__func__);
+  HWC2::Error ret;
+
+  ret = CreateComposition(false);
+  if (ret == HWC2::Error::BadLayer) {
+    // Can we really have no client or device layers?
+    *retire_fence = -1;
+    return HWC2::Error::None;
+  }
+  if (ret != HWC2::Error::None)
+    return ret;
 
   // The retire fence returned here is for the last frame, so return it and
   // promote the next retire fence
@@ -670,11 +692,47 @@
 HWC2::Error DrmHwcTwo::HwcDisplay::ValidateDisplay(uint32_t *num_types,
                                                    uint32_t *num_requests) {
   supported(__func__);
+  size_t plane_count = 0;
   *num_types = 0;
   *num_requests = 0;
+  size_t avail_planes = primary_planes_.size() + overlay_planes_.size();
+
+  HWC2::Error ret;
+
+  for (std::pair<const hwc2_layer_t, DrmHwcTwo::HwcLayer> &l : layers_)
+    l.second.set_validated_type(HWC2::Composition::Invalid);
+
+  ret = CreateComposition(true);
+  if (ret != HWC2::Error::None)
+    // Assume the test failed due to overlay planes
+    avail_planes = 1;
+
+  std::map<uint32_t, DrmHwcTwo::HwcLayer *> z_map;
+  for (std::pair<const hwc2_layer_t, DrmHwcTwo::HwcLayer> &l : layers_) {
+    if (l.second.sf_type() == HWC2::Composition::Device)
+      z_map.emplace(std::make_pair(l.second.z_order(), &l.second));
+  }
+
+  /*
+   * If more layers then planes, save one plane
+   * for client composited layers
+   */
+  if (avail_planes < layers_.size())
+    avail_planes--;
+
+  for (std::pair<const uint32_t, DrmHwcTwo::HwcLayer *> &l : z_map) {
+    if (!avail_planes--)
+      break;
+    l.second->set_validated_type(HWC2::Composition::Device);
+  }
+
   for (std::pair<const hwc2_layer_t, DrmHwcTwo::HwcLayer> &l : layers_) {
     DrmHwcTwo::HwcLayer &layer = l.second;
     switch (layer.sf_type()) {
+      case HWC2::Composition::Device:
+        if (layer.validated_type() == HWC2::Composition::Device)
+          break;
+      // fall thru
       case HWC2::Composition::SolidColor:
       case HWC2::Composition::Cursor:
       case HWC2::Composition::Sideband:
diff --git a/drmhwctwo.h b/drmhwctwo.h
index aa9de3f..82a9768 100644
--- a/drmhwctwo.h
+++ b/drmhwctwo.h
@@ -185,6 +185,7 @@
     }
 
    private:
+    HWC2::Error CreateComposition(bool test);
     void AddFenceToRetireFence(int fd);
 
     DrmResources *drm_;