drm_hwcomposer: Make single atomic function for all atomic commit ops.

... to allow precise control on atomic commit operations.

This should also help to implement dynamic modechange, readback and
other useful features.

Signed-off-by: Roman Stratiienko <roman.o.stratiienko@globallogic.com>
diff --git a/DrmHwcTwo.cpp b/DrmHwcTwo.cpp
index 071f3bf..dada168 100644
--- a/DrmHwcTwo.cpp
+++ b/DrmHwcTwo.cpp
@@ -253,7 +253,8 @@
 }
 
 void DrmHwcTwo::HwcDisplay::ClearDisplay() {
-  compositor_.ClearDisplay();
+  AtomicCommitArgs a_args = {.clear_active_composition = true};
+  compositor_.ExecuteAtomicCommit(a_args);
 }
 
 HWC2::Error DrmHwcTwo::HwcDisplay::Init(std::vector<DrmPlane *> *planes) {
@@ -711,7 +712,7 @@
     composition_layers.emplace_back(std::move(layer));
   }
 
-  auto composition = std::make_unique<DrmDisplayComposition>(crtc_,
+  auto composition = std::make_shared<DrmDisplayComposition>(crtc_,
                                                              planner_.get());
 
   // TODO(nobody): Don't always assume geometry changed
@@ -740,16 +741,21 @@
     i = overlay_planes.erase(i);
   }
 
-  if (test) {
-    ret = compositor_.TestComposition(composition.get());
-  } else {
-    ret = compositor_.ApplyComposition(std::move(composition));
-    AddFenceToPresentFence(compositor_.TakeOutFence());
-  }
+  AtomicCommitArgs a_args = {
+      .test_only = test,
+      .composition = composition,
+  };
+
+  ret = compositor_.ExecuteAtomicCommit(a_args);
+
   if (ret) {
     if (!test)
       ALOGE("Failed to apply the frame composition ret=%d", ret);
     return HWC2::Error::BadParameter;
+  } else {
+    if (!test) {
+      AddFenceToPresentFence(std::move(a_args.out_fence));
+    }
   }
   return HWC2::Error::None;
 }
@@ -793,11 +799,12 @@
     return HWC2::Error::BadConfig;
   }
 
-  if (!compositor_.SetDisplayMode(*mode)) {
-    return HWC2::Error::BadConfig;
-  }
-  int err = compositor_.ApplyComposition(
-      compositor_.CreateInitializedComposition());
+  AtomicCommitArgs a_args = {
+      .display_mode = *mode,
+      .clear_active_composition = true,
+  };
+
+  int err = compositor_.ExecuteAtomicCommit(a_args);
   if (err != 0) {
     ALOGE("Failed to queue mode changing commit %d", err);
     return HWC2::Error::BadConfig;
@@ -882,12 +889,14 @@
 HWC2::Error DrmHwcTwo::HwcDisplay::SetPowerMode(int32_t mode_in) {
   supported(__func__);
   auto mode = static_cast<HWC2::PowerMode>(mode_in);
+  AtomicCommitArgs a_args{};
+
   switch (mode) {
     case HWC2::PowerMode::Off:
-      compositor_.SetDisplayActive(false);
+      a_args.active = false;
       break;
     case HWC2::PowerMode::On:
-      compositor_.SetDisplayActive(true);
+      a_args.active = true;
       break;
     case HWC2::PowerMode::Doze:
     case HWC2::PowerMode::DozeSuspend:
@@ -897,10 +906,9 @@
       return HWC2::Error::BadParameter;
   };
 
-  int ret = compositor_.ApplyComposition(
-      compositor_.CreateInitializedComposition());
-  if (ret) {
-    ALOGE("Failed to apply the dpms composition ret=%d", ret);
+  int err = compositor_.ExecuteAtomicCommit(a_args);
+  if (err) {
+    ALOGE("Failed to apply the dpms composition err=%d", err);
     return HWC2::Error::BadParameter;
   }
   return HWC2::Error::None;
diff --git a/compositor/DrmDisplayComposition.h b/compositor/DrmDisplayComposition.h
index 7b7e668..f1958d7 100644
--- a/compositor/DrmDisplayComposition.h
+++ b/compositor/DrmDisplayComposition.h
@@ -102,8 +102,6 @@
     return planner_;
   }
 
-  UniqueFd out_fence_;
-
  private:
   DrmCrtc *crtc_ = NULL;
   Planner *planner_ = NULL;
diff --git a/compositor/DrmDisplayCompositor.cpp b/compositor/DrmDisplayCompositor.cpp
index bb6a33b..447d75e 100644
--- a/compositor/DrmDisplayCompositor.cpp
+++ b/compositor/DrmDisplayCompositor.cpp
@@ -40,20 +40,6 @@
 
 namespace android {
 
-DrmDisplayCompositor::DrmDisplayCompositor()
-    : resource_manager_(nullptr),
-      display_(-1),
-      initialized_(false),
-      active_(false) {
-}
-
-DrmDisplayCompositor::~DrmDisplayCompositor() {
-  if (!initialized_)
-    return;
-
-  active_composition_.reset();
-}
-
 auto DrmDisplayCompositor::Init(ResourceManager *resource_manager, int display)
     -> int {
   resource_manager_ = resource_manager;
@@ -81,42 +67,30 @@
   return std::make_unique<DrmDisplayComposition>(crtc, planner_.get());
 }
 
-int DrmDisplayCompositor::DisablePlanes(DrmDisplayComposition *display_comp) {
-  auto pset = MakeDrmModeAtomicReqUnique();
-  if (!pset) {
-    ALOGE("Failed to allocate property set");
-    return -ENOMEM;
-  }
-
-  int ret = 0;
-  std::vector<DrmCompositionPlane> &comp_planes = display_comp
-                                                      ->composition_planes();
-  for (DrmCompositionPlane &comp_plane : comp_planes) {
-    if (comp_plane.plane()->AtomicDisablePlane(*pset) != 0) {
-      return -EINVAL;
-    }
-  }
-  DrmDevice *drm = resource_manager_->GetDrmDevice(display_);
-  ret = drmModeAtomicCommit(drm->fd(), pset.get(), 0, drm);
-  if (ret) {
-    ALOGE("Failed to commit pset ret=%d\n", ret);
-    return ret;
-  }
-
-  return 0;
-}
-
-int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
-                                      bool test_only) {
+auto DrmDisplayCompositor::CommitFrame(AtomicCommitArgs &args) -> int {
   ATRACE_CALL();
 
-  int ret = 0;
+  if (args.active && *args.active == active_kms_data.active_state) {
+    /* Don't set the same state twice */
+    args.active.reset();
+  }
 
-  std::vector<DrmHwcLayer> &layers = display_comp->layers();
-  std::vector<DrmCompositionPlane> &comp_planes = display_comp
-                                                      ->composition_planes();
+  if (!args.HasInputs()) {
+    /* nothing to do */
+    return 0;
+  }
+
+  if (!active_kms_data.active_state) {
+    /* Force activate display */
+    args.active = true;
+  }
+
+  if (args.clear_active_composition && args.composition) {
+    ALOGE("%s: Invalid arguments", __func__);
+    return -EINVAL;
+  }
+
   DrmDevice *drm = resource_manager_->GetDrmDevice(display_);
-  uint64_t out_fences[drm->crtcs().size()];
 
   DrmConnector *connector = drm->GetConnectorForDisplay(display_);
   if (!connector) {
@@ -135,113 +109,133 @@
     return -ENOMEM;
   }
 
+  int64_t out_fence = -1;
   if (crtc->out_fence_ptr_property() &&
-      !crtc->out_fence_ptr_property()
-           .AtomicSet(*pset, (uint64_t)&out_fences[crtc->pipe()])) {
+      !crtc->out_fence_ptr_property().AtomicSet(*pset, (uint64_t)&out_fence)) {
     return -EINVAL;
   }
 
-  if (mode_.blob &&
-      (!crtc->active_property().AtomicSet(*pset, 1) ||
-       !crtc->mode_property().AtomicSet(*pset, *mode_.blob) ||
-       !connector->crtc_id_property().AtomicSet(*pset, crtc->id()))) {
-    return -EINVAL;
+  DrmModeUserPropertyBlobUnique mode_blob;
+
+  if (args.active) {
+    if (!crtc->active_property().AtomicSet(*pset, *args.active) ||
+        !connector->crtc_id_property().AtomicSet(*pset, crtc->id())) {
+      return -EINVAL;
+    }
   }
 
-  for (DrmCompositionPlane &comp_plane : comp_planes) {
-    DrmPlane *plane = comp_plane.plane();
-    std::vector<size_t> &source_layers = comp_plane.source_layers();
+  if (args.display_mode) {
+    mode_blob = args.display_mode.value().CreateModeBlob(
+        *resource_manager_->GetDrmDevice(display_));
 
-    if (comp_plane.type() != DrmCompositionPlane::Type::kDisable) {
-      if (source_layers.size() > 1) {
-        ALOGE("Can't handle more than one source layer sz=%zu type=%d",
-              source_layers.size(), comp_plane.type());
-        continue;
-      }
+    if (!mode_blob) {
+      ALOGE("Failed to create mode_blob");
+      return -EINVAL;
+    }
 
-      if (source_layers.empty() || source_layers.front() >= layers.size()) {
-        ALOGE("Source layer index %zu out of bounds %zu type=%d",
-              source_layers.front(), layers.size(), comp_plane.type());
-        return -EINVAL;
-      }
-      DrmHwcLayer &layer = layers[source_layers.front()];
+    if (!crtc->mode_property().AtomicSet(*pset, *mode_blob)) {
+      return -EINVAL;
+    }
+  }
 
-      if (plane->AtomicSetState(*pset, layer, source_layers.front(),
-                                crtc->id()) != 0) {
-        return -EINVAL;
+  if (args.composition) {
+    std::vector<DrmHwcLayer> &layers = args.composition->layers();
+    std::vector<DrmCompositionPlane> &comp_planes = args.composition
+                                                        ->composition_planes();
+
+    for (DrmCompositionPlane &comp_plane : comp_planes) {
+      DrmPlane *plane = comp_plane.plane();
+      std::vector<size_t> &source_layers = comp_plane.source_layers();
+
+      if (comp_plane.type() != DrmCompositionPlane::Type::kDisable) {
+        if (source_layers.size() > 1) {
+          ALOGE("Can't handle more than one source layer sz=%zu type=%d",
+                source_layers.size(), comp_plane.type());
+          continue;
+        }
+
+        if (source_layers.empty() || source_layers.front() >= layers.size()) {
+          ALOGE("Source layer index %zu out of bounds %zu type=%d",
+                source_layers.front(), layers.size(), comp_plane.type());
+          return -EINVAL;
+        }
+        DrmHwcLayer &layer = layers[source_layers.front()];
+
+        if (plane->AtomicSetState(*pset, layer, source_layers.front(),
+                                  crtc->id()) != 0) {
+          return -EINVAL;
+        }
+      } else {
+        if (plane->AtomicDisablePlane(*pset) != 0) {
+          return -EINVAL;
+        }
       }
-    } else {
-      if (plane->AtomicDisablePlane(*pset) != 0) {
+    }
+  }
+
+  if (args.clear_active_composition && active_kms_data.composition) {
+    auto &comp_planes = active_kms_data.composition->composition_planes();
+    for (auto &comp_plane : comp_planes) {
+      if (comp_plane.plane()->AtomicDisablePlane(*pset) != 0) {
         return -EINVAL;
       }
     }
   }
 
-  if (!ret) {
-    uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
-    if (test_only)
-      flags |= DRM_MODE_ATOMIC_TEST_ONLY;
+  uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
+  if (args.test_only)
+    flags |= DRM_MODE_ATOMIC_TEST_ONLY;
 
-    ret = drmModeAtomicCommit(drm->fd(), pset.get(), flags, drm);
-    if (ret) {
-      if (!test_only)
-        ALOGE("Failed to commit pset ret=%d\n", ret);
-      return ret;
-    }
+  int err = drmModeAtomicCommit(drm->fd(), pset.get(), flags, drm);
+  if (err) {
+    if (!args.test_only)
+      ALOGE("Failed to commit pset ret=%d\n", err);
+    return err;
   }
 
-  if (!test_only) {
-    if (mode_.blob) {
-      connector->set_active_mode(mode_.mode);
-      mode_.old_blob = std::move(mode_.blob);
+  if (!args.test_only) {
+    if (args.display_mode) {
+      connector->set_active_mode(*args.display_mode);
+      active_kms_data.mode_blob = std::move(mode_blob);
     }
-    active_changed_ = false;
+
+    if (args.clear_active_composition) {
+      active_kms_data.composition.reset();
+    }
+
+    if (args.composition) {
+      active_kms_data.composition = args.composition;
+    }
+
+    if (args.active) {
+      active_kms_data.active_state = *args.active;
+    }
 
     if (crtc->out_fence_ptr_property()) {
-      display_comp->out_fence_ = UniqueFd((int)out_fences[crtc->pipe()]);
+      args.out_fence = UniqueFd((int)out_fence);
     }
   }
 
-  return ret;
+  return 0;
 }
 
-void DrmDisplayCompositor::ClearDisplay() {
-  if (!active_composition_)
-    return;
+auto DrmDisplayCompositor::ExecuteAtomicCommit(AtomicCommitArgs &args) -> int {
+  int err = CommitFrame(args);
 
-  if (DisablePlanes(active_composition_.get()))
-    return;
-
-  active_composition_.reset(nullptr);
-}
-
-int DrmDisplayCompositor::ApplyComposition(
-    std::unique_ptr<DrmDisplayComposition> composition) {
-  int ret = CommitFrame(composition.get(), false);
-
-  if (ret) {
-    ALOGE("Composite failed for display %d", display_);
-    // Disable the hw used by the last active composition. This allows us to
-    // signal the release fences from that composition to avoid hanging.
-    ClearDisplay();
-    return ret;
+  if (!args.test_only) {
+    if (err) {
+      ALOGE("Composite failed for display %d", display_);
+      // Disable the hw used by the last active composition. This allows us to
+      // signal the release fences from that composition to avoid hanging.
+      AtomicCommitArgs cl_args = {.clear_active_composition = true};
+      if (CommitFrame(cl_args)) {
+        ALOGE("Failed to clean-up active composition for display %d", display_);
+      }
+      return err;
+    }
   }
 
-  if (composition) {
-    active_composition_.swap(composition);
-  }
-
-  return ret;
-}
-
-int DrmDisplayCompositor::TestComposition(DrmDisplayComposition *composition) {
-  return CommitFrame(composition, true);
-}
-
-auto DrmDisplayCompositor::SetDisplayMode(const DrmMode &display_mode) -> bool {
-  mode_.mode = display_mode;
-  mode_.blob = mode_.mode.CreateModeBlob(*resource_manager_->GetDrmDevice(display_));
-  return !!mode_.blob;
-}
+  return err;
+}  // namespace android
 
 }  // namespace android
diff --git a/compositor/DrmDisplayCompositor.h b/compositor/DrmDisplayCompositor.h
index 434668f..55fe77d 100644
--- a/compositor/DrmDisplayCompositor.h
+++ b/compositor/DrmDisplayCompositor.h
@@ -34,53 +34,49 @@
 
 namespace android {
 
+struct AtomicCommitArgs {
+  /* inputs. All fields are optional, but at least one has to be specified */
+  bool test_only = false;
+  std::optional<DrmMode> display_mode;
+  std::optional<bool> active;
+  std::shared_ptr<DrmDisplayComposition> composition;
+  /* 'clear' should never be used together with 'composition' */
+  bool clear_active_composition = false;
+
+  /* out */
+  UniqueFd out_fence;
+
+  /* helpers */
+  auto HasInputs() -> bool {
+    return display_mode || active || composition || clear_active_composition;
+  }
+};
+
 class DrmDisplayCompositor {
  public:
-  DrmDisplayCompositor();
-  ~DrmDisplayCompositor();
-
+  DrmDisplayCompositor() = default;
+  ~DrmDisplayCompositor() = default;
   auto Init(ResourceManager *resource_manager, int display) -> int;
 
   std::unique_ptr<DrmDisplayComposition> CreateInitializedComposition() const;
-  int ApplyComposition(std::unique_ptr<DrmDisplayComposition> composition);
-  int TestComposition(DrmDisplayComposition *composition);
-  void ClearDisplay();
-  auto SetDisplayMode(const DrmMode &display_mode) -> bool;
-  auto SetDisplayActive(bool state) -> void {
-    active_ = state;
-    active_changed_ = true;
-  }
 
-  UniqueFd TakeOutFence() {
-    if (!active_composition_) {
-      return UniqueFd();
-    }
-    return std::move(active_composition_->out_fence_);
-  }
+  auto ExecuteAtomicCommit(AtomicCommitArgs &args) -> int;
 
  private:
-  struct ModeState {
-    DrmMode mode;
-    DrmModeUserPropertyBlobUnique blob;
-    DrmModeUserPropertyBlobUnique old_blob;
-  };
-
   DrmDisplayCompositor(const DrmDisplayCompositor &) = delete;
 
-  int CommitFrame(DrmDisplayComposition *display_comp, bool test_only);
-  int DisablePlanes(DrmDisplayComposition *display_comp);
+  auto CommitFrame(AtomicCommitArgs &args) -> int;
 
-  ResourceManager *resource_manager_;
-  int display_;
+  struct {
+    std::shared_ptr<DrmDisplayComposition> composition;
+    DrmModeUserPropertyBlobUnique mode_blob;
+    bool active_state{};
+  } active_kms_data;
 
-  std::unique_ptr<DrmDisplayComposition> active_composition_;
-
-  bool initialized_;
-  bool active_{true}, active_changed_{true};
-
-  ModeState mode_;
-
+  ResourceManager *resource_manager_ = nullptr;
   std::unique_ptr<Planner> planner_;
+  bool initialized_{};
+  int display_ = -1;
 };
 }  // namespace android