drm_hwcomposer: Introduce DrmDisplayPipeline class

Create systematic way of binding DRM objects (Crtc,Encoder,Planes...)
to the pipeline using RAII. Use it to create the pipeline.

+ Allow pipeline creation to fail.

Closes: https://gitlab.freedesktop.org/drm-hwcomposer/drm-hwcomposer/-/issues/14
Signed-off-by: Roman Stratiienko <roman.o.stratiienko@globallogic.com>
diff --git a/drm/DrmDisplayPipeline.cpp b/drm/DrmDisplayPipeline.cpp
new file mode 100644
index 0000000..8a490f8
--- /dev/null
+++ b/drm/DrmDisplayPipeline.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hwc-drm-display-pipeline"
+
+#include "DrmDisplayPipeline.h"
+
+#include "DrmConnector.h"
+#include "DrmCrtc.h"
+#include "DrmDevice.h"
+#include "DrmEncoder.h"
+#include "DrmPlane.h"
+#include "compositor/DrmDisplayCompositor.h"
+#include "utils/log.h"
+
+namespace android {
+
+template <class O>
+auto PipelineBindable<O>::BindPipeline(DrmDisplayPipeline *pipeline,
+                                       bool return_object_if_bound)
+    -> std::shared_ptr<BindingOwner<O>> {
+  auto owner_object = owner_object_.lock();
+  if (owner_object) {
+    if (bound_pipeline_ == pipeline && return_object_if_bound) {
+      return owner_object;
+    }
+
+    return {};
+  }
+  owner_object = std::make_shared<BindingOwner<O>>(static_cast<O *>(this));
+
+  bound_pipeline_ = pipeline;
+  return owner_object;
+}
+
+static auto TryCreatePipeline(DrmDevice &dev, DrmConnector &connector,
+                              DrmEncoder &enc, DrmCrtc &crtc)
+    -> std::unique_ptr<DrmDisplayPipeline> {
+  /* Check if resources are available */
+
+  auto pipe = std::make_unique<DrmDisplayPipeline>();
+  pipe->device = &dev;
+
+  pipe->connector = connector.BindPipeline(pipe.get());
+  pipe->encoder = enc.BindPipeline(pipe.get());
+  pipe->crtc = crtc.BindPipeline(pipe.get());
+
+  if (!pipe->connector || !pipe->encoder || !pipe->crtc) {
+    return {};
+  }
+
+  std::vector<DrmPlane *> primary_planes;
+  std::vector<DrmPlane *> overlay_planes;
+
+  /* Attach necessary resources */
+  auto display_planes = std::vector<DrmPlane *>();
+  for (const auto &plane : dev.GetPlanes()) {
+    if (plane->IsCrtcSupported(crtc)) {
+      if (plane->GetType() == DRM_PLANE_TYPE_PRIMARY) {
+        primary_planes.emplace_back(plane.get());
+      } else if (plane->GetType() == DRM_PLANE_TYPE_OVERLAY) {
+        overlay_planes.emplace_back(plane.get());
+      } else {
+        ALOGI("Ignoring cursor plane %d", plane->GetId());
+      }
+    }
+  }
+
+  if (primary_planes.empty()) {
+    ALOGE("Primary plane for CRTC %d not found", crtc.GetId());
+    return {};
+  }
+
+  if (primary_planes.size() > 1) {
+    ALOGE("Found more than 1 primary plane for CRTC %d", crtc.GetId());
+    return {};
+  }
+
+  pipe->primary_plane = primary_planes[0]->BindPipeline(pipe.get());
+  if (!pipe->primary_plane) {
+    ALOGE("Primary plane %d is already owned. Internal error.",
+          primary_planes[0]->GetId());
+    return {};
+  }
+
+  bool use_overlay_planes = true;  // TODO(rsglobal): restore
+                                   // strtol(use_overlay_planes_prop, nullptr,
+                                   // 10);
+  if (use_overlay_planes) {
+    for (auto *plane : overlay_planes) {
+      auto op = plane->BindPipeline(pipe.get());
+      if (op) {
+        pipe->overlay_planes.emplace_back(op);
+      }
+    }
+  }
+
+  return pipe;
+}
+
+static auto TryCreatePipelineUsingEncoder(DrmDevice &dev, DrmConnector &conn,
+                                          DrmEncoder &enc)
+    -> std::unique_ptr<DrmDisplayPipeline> {
+  /* First try to use the currently-bound crtc */
+  auto *crtc = dev.FindCrtcById(enc.GetCurrentCrtcId());
+  if (crtc != nullptr) {
+    auto pipeline = TryCreatePipeline(dev, conn, enc, *crtc);
+    if (pipeline) {
+      return pipeline;
+    }
+  }
+
+  /* Try to find a possible crtc which will work */
+  for (const auto &crtc : dev.GetCrtcs()) {
+    if (enc.SupportsCrtc(*crtc)) {
+      auto pipeline = TryCreatePipeline(dev, conn, enc, *crtc);
+      if (pipeline) {
+        return pipeline;
+      }
+    }
+  }
+
+  /* We can't use this encoder, but nothing went wrong, try another one */
+  return {};
+}
+
+auto DrmDisplayPipeline::CreatePipeline(DrmConnector &connector)
+    -> std::unique_ptr<DrmDisplayPipeline> {
+  auto &dev = connector.GetDev();
+  /* Try to use current setup first */
+  auto *encoder = dev.FindEncoderById(connector.GetCurrentEncoderId());
+
+  if (encoder != nullptr) {
+    auto pipeline = TryCreatePipelineUsingEncoder(dev, connector, *encoder);
+    if (pipeline) {
+      return pipeline;
+    }
+  }
+
+  for (const auto &enc : dev.GetEncoders()) {
+    if (connector.SupportsEncoder(*enc)) {
+      auto pipeline = TryCreatePipelineUsingEncoder(dev, connector, *enc);
+      if (pipeline) {
+        return pipeline;
+      }
+    }
+  }
+
+  ALOGE("Could not find a suitable encoder/crtc for connector %s",
+        connector.GetName().c_str());
+
+  return {};
+}
+
+}  // namespace android