Merge "drm_hwcomposer: Merge remote-tracking branch 'aosp/upstream-main' into HEAD" am: bc74646e90 am: b52fbe0ff4

Original change: https://android-review.googlesource.com/c/platform/external/drm_hwcomposer/+/2007868

Change-Id: Ib5da17b7bcc3634a702eac2a3ee1deaec4c8b65b
diff --git a/.ci/Makefile b/.ci/Makefile
index 7f43a8a..a0e4b73 100644
--- a/.ci/Makefile
+++ b/.ci/Makefile
@@ -21,21 +21,11 @@
     bufferinfo/legacy/BufferInfoMaliMediatek.cpp:COARSE \
     bufferinfo/legacy/BufferInfoMaliMeson.cpp:COARSE    \
     bufferinfo/legacy/BufferInfoMinigbm.cpp:COARSE      \
-    compositor/DrmDisplayComposition.cpp:COARSE         \
-    compositor/DrmDisplayComposition.h:COARSE           \
-    compositor/DrmDisplayCompositor.cpp:COARSE          \
     drm/DrmFbImporter.h:FINE                            \
     drm/DrmMode.h:COARSE                                \
-    drm/DrmDevice.h:COARSE                              \
     drm/DrmProperty.h:COARSE                            \
-    drm/DrmConnector.h:COARSE                           \
-    drm/DrmCrtc.h:COARSE                                \
     drm/DrmUnique.h:FINE                                \
-    drm/DrmEncoder.h:COARSE                             \
-    drm/DrmConnector.cpp:COARSE                         \
-    drm/DrmDevice.cpp:COARSE                            \
     drm/DrmProperty.cpp:COARSE                          \
-    drm/UEventListener.cpp:COARSE                       \
     drm/VSyncWorker.cpp:COARSE                          \
     hwc2_device/DrmHwcTwo.cpp:COARSE                    \
     hwc2_device/DrmHwcTwo.h:COARSE                      \
@@ -94,7 +84,7 @@
 
 # Build
 
-BUILD_FILES_AUTO := $(shell find -L $(SRC_DIR) -not -path '*/\.*' -not -path '*/tests/*' -path '*.cpp')
+BUILD_FILES_AUTO := $(shell find -L $(SRC_DIR) -not -path '*/\.*' -not -path '*/tests/test_include/*' -path '*.cpp')
 SKIP_FILES_path := $(foreach file,$(SKIP_FILES),$(SRC_DIR)/$(file))
 
 BUILD_FILES := $(subst ./,,$(filter-out $(SKIP_FILES_path),$(BUILD_FILES_AUTO)))
@@ -117,7 +107,7 @@
 	$(CLANG) $(CXXARGS) $< -MM -MT $(OUT_DIR)/$(patsubst %.cpp,%.o,$<) -o $@
 
 # TIDY
-TIDY_FILES_AUTO := $(shell find -L $(SRC_DIR) -not -path '*/\.*' -not -path '*/tests/*' \( -path '*.cpp' -o -path '*.h' \))
+TIDY_FILES_AUTO := $(shell find -L $(SRC_DIR) -not -path '*/\.*' -not -path '*/tests/test_include/*' \( -path '*.cpp' -o -path '*.h' \))
 
 TIDY_FILES_AUTO_filtered := $(filter-out $(SKIP_FILES_path),$(TIDY_FILES_AUTO))
 
@@ -154,7 +144,7 @@
 $$(_TARG): TIDY_ARGS := $$(TIDY_ARGS)
 $$(_TARG): $$(_DEP)
 	mkdir -p $$(dir $$(_TARG))
-	$$(CLANG_TIDY) $$(_DEP) $$(TIDY_ARGS) -- -x c++ $$(CXXARGS)
+	$$(CLANG_TIDY) $$(_DEP) $$(TIDY_ARGS) -- -x c++ $$(CXXARGS) -Wno-pragma-once-outside-header
 	touch $$(_TARG)
 
 endef
diff --git a/Android.bp b/Android.bp
index 1c3030c..c9c94af 100644
--- a/Android.bp
+++ b/Android.bp
@@ -83,6 +83,7 @@
     cppflags: [
         "-DHWC2_INCLUDE_STRINGIFICATION",
         "-DHWC2_USE_CPP11",
+        "-std=c++17",
     ],
 
     product_variables: {
@@ -101,13 +102,13 @@
         "bufferinfo/BufferInfoGetter.cpp",
         "bufferinfo/BufferInfoMapperMetadata.cpp",
 
-        "compositor/DrmDisplayComposition.cpp",
-        "compositor/DrmDisplayCompositor.cpp",
-        "compositor/Planner.cpp",
+        "compositor/DrmKmsPlan.cpp",
 
+        "drm/DrmAtomicStateManager.cpp",
         "drm/DrmConnector.cpp",
         "drm/DrmCrtc.cpp",
         "drm/DrmDevice.cpp",
+        "drm/DrmDisplayPipeline.cpp",
         "drm/DrmEncoder.cpp",
         "drm/DrmFbImporter.cpp",
         "drm/DrmMode.cpp",
@@ -122,7 +123,6 @@
         "backend/Backend.cpp",
         "backend/BackendClient.cpp",
         "backend/BackendManager.cpp",
-        "backend/BackendRCarDu.cpp",
 
         "hwc2_device/DrmHwcTwo.cpp",
         "hwc2_device/HwcDisplay.cpp",
diff --git a/backend/Backend.cpp b/backend/Backend.cpp
index 98862ba..f6d9c18 100644
--- a/backend/Backend.cpp
+++ b/backend/Backend.cpp
@@ -86,7 +86,7 @@
          !BufferInfoGetter::GetInstance()->IsHandleUsable(layer->GetBuffer()) ||
          display->color_transform_hint() != HAL_COLOR_TRANSFORM_IDENTITY ||
          (layer->RequireScalingOrPhasing() &&
-          display->resource_manager()->ForcedScalingWithGpu());
+          display->GetHwc2()->GetResMan().ForcedScalingWithGpu());
 }
 
 bool Backend::HardwareSupportsLayerType(HWC2::Composition comp_type) {
@@ -119,8 +119,8 @@
 std::tuple<int, int> Backend::GetExtraClientRange(
     HwcDisplay *display, const std::vector<HwcLayer *> &layers,
     int client_start, size_t client_size) {
-  size_t avail_planes = display->primary_planes().size() +
-                        display->overlay_planes().size();
+  auto planes = display->GetPipe().GetUsablePlanes();
+  size_t avail_planes = planes.size();
 
   /*
    * If more layers then planes, save one plane
diff --git a/backend/BackendManager.cpp b/backend/BackendManager.cpp
index aadef36..9bf6324 100644
--- a/backend/BackendManager.cpp
+++ b/backend/BackendManager.cpp
@@ -42,7 +42,7 @@
 }
 
 int BackendManager::SetBackendForDisplay(HwcDisplay *display) {
-  std::string driver_name(display->drm()->GetName());
+  std::string driver_name(display->GetPipe().device->GetName());
   char backend_override[PROPERTY_VALUE_MAX];
   property_get("vendor.hwc.backend_override", backend_override,
                driver_name.c_str());
@@ -51,13 +51,15 @@
   display->set_backend(GetBackendByName(backend_name));
   if (display->backend() == nullptr) {
     ALOGE("Failed to set backend '%s' for '%s' and driver '%s'",
-          backend_name.c_str(), display->connector()->name().c_str(),
+          backend_name.c_str(),
+          display->GetPipe().connector->Get()->GetName().c_str(),
           driver_name.c_str());
     return -EINVAL;
   }
 
   ALOGI("Backend '%s' for '%s' and driver '%s' was successfully set",
-        backend_name.c_str(), display->connector()->name().c_str(),
+        backend_name.c_str(),
+        display->GetPipe().connector->Get()->GetName().c_str(),
         driver_name.c_str());
 
   return 0;
diff --git a/backend/BackendRCarDu.cpp b/backend/BackendRCarDu.cpp
deleted file mode 100644
index 0750ee4..0000000
--- a/backend/BackendRCarDu.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-#include "BackendRCarDu.h"
-
-#include "BackendManager.h"
-#include "bufferinfo/BufferInfoGetter.h"
-#include "drm_fourcc.h"
-
-namespace android {
-
-bool BackendRCarDu::IsClientLayer(HwcDisplay *display, HwcLayer *layer) {
-  hwc_drm_bo_t bo;
-
-  int ret = BufferInfoGetter::GetInstance()->ConvertBoInfo(layer->GetBuffer(),
-                                                           &bo);
-  if (ret != 0)
-    return true;
-
-  if (bo.format == DRM_FORMAT_ABGR8888)
-    return true;
-
-  if (layer->RequireScalingOrPhasing())
-    return true;
-
-  return Backend::IsClientLayer(display, layer);
-}
-
-// clang-format off
-// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables, cert-err58-cpp)
-REGISTER_BACKEND("rcar-du", BackendRCarDu);
-// clang-format on
-
-}  // namespace android
\ No newline at end of file
diff --git a/backend/BackendRCarDu.h b/backend/BackendRCarDu.h
deleted file mode 100644
index 1259c9f..0000000
--- a/backend/BackendRCarDu.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-#ifndef HWC_DISPLAY_BACKEND_RCAR_DU_H
-#define HWC_DISPLAY_BACKEND_RCAR_DU_H
-
-#include "Backend.h"
-
-namespace android {
-
-class BackendRCarDu : public Backend {
- public:
-  bool IsClientLayer(HwcDisplay *display, HwcLayer *layer) override;
-};
-}  // namespace android
-
-#endif
diff --git a/bufferinfo/legacy/BufferInfoLibdrm.cpp b/bufferinfo/legacy/BufferInfoLibdrm.cpp
index 4f942fe..6baf6bb 100644
--- a/bufferinfo/legacy/BufferInfoLibdrm.cpp
+++ b/bufferinfo/legacy/BufferInfoLibdrm.cpp
@@ -47,6 +47,11 @@
   int fourcc; /* DRM_FORMAT_ */
 };
 
+#ifndef DRM_FORMAT_XYUV8888
+#define DRM_FORMAT_XYUV8888 \
+  fourcc_code('X', 'Y', 'U', 'V') /* [31:0] X:Y:Cb:Cr 8:8:8:8 little endian */
+#endif
+
 /* The following table is used to look up a DRI image FourCC based
  * on native format and information contained in android_ycbcr struct. */
 static const struct DroidYuvFormat kDroidYuvFormats[] = {
diff --git a/build_deploy.sh b/build_deploy.sh
index ba9732b..ef25e5c 100755
--- a/build_deploy.sh
+++ b/build_deploy.sh
@@ -14,10 +14,10 @@
 adb root && adb remount && adb sync vendor
 
 adb shell stop
-adb shell stop vendor.hwcomposer-2-1 || true
-adb shell stop vendor.hwcomposer-2-2 || true
-adb shell stop vendor.hwcomposer-2-3 || true
-adb shell stop vendor.hwcomposer-2-4 || true
+adb shell stop vendor.hwcomposer-2-1 && adb shell start vendor.hwcomposer-2-1 || true
+adb shell stop vendor.hwcomposer-2-2 && adb shell start vendor.hwcomposer-2-2 || true
+adb shell stop vendor.hwcomposer-2-3 && adb shell start vendor.hwcomposer-2-3 || true
+adb shell stop vendor.hwcomposer-2-4 && adb shell start vendor.hwcomposer-2-4 || true
 
 [ $HWCLOG -eq "1" ] && adb logcat -c
 
diff --git a/compositor/DrmDisplayComposition.cpp b/compositor/DrmDisplayComposition.cpp
deleted file mode 100644
index e571b26..0000000
--- a/compositor/DrmDisplayComposition.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2015 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-composition"
-
-#include "DrmDisplayComposition.h"
-
-#include <sync/sync.h>
-#include <xf86drmMode.h>
-
-#include <algorithm>
-#include <cstdlib>
-#include <unordered_set>
-
-#include "DrmDisplayCompositor.h"
-#include "Planner.h"
-#include "drm/DrmDevice.h"
-#include "utils/log.h"
-
-namespace android {
-
-DrmDisplayComposition::DrmDisplayComposition(DrmCrtc *crtc)
-    : crtc_(crtc)  // Can be NULL if we haven't modeset yet
-{
-}
-
-int DrmDisplayComposition::SetLayers(DrmHwcLayer *layers, size_t num_layers) {
-  for (size_t layer_index = 0; layer_index < num_layers; layer_index++) {
-    layers_.emplace_back(std::move(layers[layer_index]));
-  }
-
-  return 0;
-}
-
-int DrmDisplayComposition::AddPlaneComposition(DrmCompositionPlane plane) {
-  composition_planes_.emplace_back(std::move(plane));
-  return 0;
-}
-
-int DrmDisplayComposition::Plan(std::vector<DrmPlane *> *primary_planes,
-                                std::vector<DrmPlane *> *overlay_planes) {
-  std::map<size_t, DrmHwcLayer *> to_composite;
-
-  for (size_t i = 0; i < layers_.size(); ++i)
-    to_composite.emplace(std::make_pair(i, &layers_[i]));
-
-  int ret = 0;
-  std::tie(ret, composition_planes_) = Planner::ProvisionPlanes(to_composite,
-                                                                crtc_,
-                                                                primary_planes,
-                                                                overlay_planes);
-  if (ret) {
-    ALOGV("Planner failed provisioning planes ret=%d", ret);
-    return ret;
-  }
-
-  // Remove the planes we used from the pool before returning. This ensures they
-  // won't be reused by another display in the composition.
-  for (auto &i : composition_planes_) {
-    if (!i.plane())
-      continue;
-
-    std::vector<DrmPlane *> *container = nullptr;
-    if (i.plane()->GetType() == DRM_PLANE_TYPE_PRIMARY)
-      container = primary_planes;
-    else
-      container = overlay_planes;
-    for (auto j = container->begin(); j != container->end(); ++j) {
-      if (*j == i.plane()) {
-        container->erase(j);
-        break;
-      }
-    }
-  }
-
-  return 0;
-}
-
-}  // namespace android
diff --git a/compositor/DrmDisplayComposition.h b/compositor/DrmDisplayComposition.h
deleted file mode 100644
index dcfd96e..0000000
--- a/compositor/DrmDisplayComposition.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#ifndef ANDROID_DRM_DISPLAY_COMPOSITION_H_
-#define ANDROID_DRM_DISPLAY_COMPOSITION_H_
-
-#include <hardware/hardware.h>
-#include <hardware/hwcomposer.h>
-
-#include <sstream>
-#include <vector>
-
-#include "drm/DrmCrtc.h"
-#include "drm/DrmPlane.h"
-#include "drmhwcomposer.h"
-
-namespace android {
-
-class Importer;
-
-constexpr size_t kUndefinedSourceLayer = UINT16_MAX;
-
-class DrmCompositionPlane {
- public:
-  DrmCompositionPlane() = default;
-  DrmCompositionPlane(DrmCompositionPlane &&rhs) = default;
-  DrmCompositionPlane &operator=(DrmCompositionPlane &&other) = default;
-  DrmCompositionPlane(DrmPlane *plane, size_t source_layer)
-      : plane_(plane), source_layer_(source_layer) {
-  }
-
-  DrmPlane *plane() const {
-    return plane_;
-  }
-
-  size_t source_layer() const {
-    return source_layer_;
-  }
-
- private:
-  DrmPlane *plane_ = nullptr;
-  size_t source_layer_ = kUndefinedSourceLayer;
-};
-
-class DrmDisplayComposition {
- public:
-  DrmDisplayComposition(const DrmDisplayComposition &) = delete;
-  explicit DrmDisplayComposition(DrmCrtc *crtc);
-  ~DrmDisplayComposition() = default;
-
-  int SetLayers(DrmHwcLayer *layers, size_t num_layers);
-  int AddPlaneComposition(DrmCompositionPlane plane);
-
-  int Plan(std::vector<DrmPlane *> *primary_planes,
-           std::vector<DrmPlane *> *overlay_planes);
-
-  std::vector<DrmHwcLayer> &layers() {
-    return layers_;
-  }
-
-  std::vector<DrmCompositionPlane> &composition_planes() {
-    return composition_planes_;
-  }
-
-  DrmCrtc *crtc() const {
-    return crtc_;
-  }
-
- private:
-  DrmCrtc *crtc_ = nullptr;
-
-  std::vector<DrmHwcLayer> layers_;
-  std::vector<DrmCompositionPlane> composition_planes_;
-};
-}  // namespace android
-
-#endif  // ANDROID_DRM_DISPLAY_COMPOSITION_H_
diff --git a/compositor/DrmDisplayCompositor.cpp b/compositor/DrmDisplayCompositor.cpp
deleted file mode 100644
index c2e51ee..0000000
--- a/compositor/DrmDisplayCompositor.cpp
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (C) 2015 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
-#define LOG_TAG "hwc-drm-display-compositor"
-
-#include "DrmDisplayCompositor.h"
-
-#include <drm/drm_mode.h>
-#include <pthread.h>
-#include <sched.h>
-#include <sync/sync.h>
-#include <utils/Trace.h>
-
-#include <array>
-#include <cstdlib>
-#include <ctime>
-#include <sstream>
-#include <vector>
-
-#include "drm/DrmCrtc.h"
-#include "drm/DrmDevice.h"
-#include "drm/DrmPlane.h"
-#include "drm/DrmUnique.h"
-#include "utils/log.h"
-
-namespace android {
-
-auto DrmDisplayCompositor::Init(ResourceManager *resource_manager, int display)
-    -> int {
-  resource_manager_ = resource_manager;
-  display_ = display;
-  DrmDevice *drm = resource_manager_->GetDrmDevice(display);
-  if (!drm) {
-    ALOGE("Could not find drmdevice for display");
-    return -EINVAL;
-  }
-
-  initialized_ = true;
-  return 0;
-}
-
-// NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme
-auto DrmDisplayCompositor::CommitFrame(AtomicCommitArgs &args) -> int {
-  ATRACE_CALL();
-
-  if (args.active && *args.active == active_frame_state_.crtc_active_state) {
-    /* Don't set the same state twice */
-    args.active.reset();
-  }
-
-  if (!args.HasInputs()) {
-    /* nothing to do */
-    return 0;
-  }
-
-  if (!active_frame_state_.crtc_active_state) {
-    /* Force activate display */
-    args.active = true;
-  }
-
-  if (args.clear_active_composition && args.composition) {
-    ALOGE("%s: Invalid arguments", __func__);
-    return -EINVAL;
-  }
-
-  auto new_frame_state = NewFrameState();
-
-  DrmDevice *drm = resource_manager_->GetDrmDevice(display_);
-
-  DrmConnector *connector = drm->GetConnectorForDisplay(display_);
-  if (!connector) {
-    ALOGE("Could not locate connector for display %d", display_);
-    return -ENODEV;
-  }
-  DrmCrtc *crtc = drm->GetCrtcForDisplay(display_);
-  if (!crtc) {
-    ALOGE("Could not locate crtc for display %d", display_);
-    return -ENODEV;
-  }
-
-  auto pset = MakeDrmModeAtomicReqUnique();
-  if (!pset) {
-    ALOGE("Failed to allocate property set");
-    return -ENOMEM;
-  }
-
-  int64_t out_fence = -1;
-  if (crtc->out_fence_ptr_property() &&
-      !crtc->out_fence_ptr_property().AtomicSet(*pset, (uint64_t)&out_fence)) {
-    return -EINVAL;
-  }
-
-  if (args.active) {
-    new_frame_state.crtc_active_state = *args.active;
-    if (!crtc->active_property().AtomicSet(*pset, *args.active) ||
-        !connector->crtc_id_property().AtomicSet(*pset, crtc->id())) {
-      return -EINVAL;
-    }
-  }
-
-  if (args.display_mode) {
-    new_frame_state.mode_blob = args.display_mode.value().CreateModeBlob(
-        *resource_manager_->GetDrmDevice(display_));
-
-    if (!new_frame_state.mode_blob) {
-      ALOGE("Failed to create mode_blob");
-      return -EINVAL;
-    }
-
-    if (!crtc->mode_property().AtomicSet(*pset, *new_frame_state.mode_blob)) {
-      return -EINVAL;
-    }
-  }
-
-  auto unused_planes = new_frame_state.used_planes;
-
-  if (args.composition) {
-    new_frame_state.used_framebuffers.clear();
-    new_frame_state.used_planes.clear();
-
-    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();
-      size_t source_layer = comp_plane.source_layer();
-
-      if (source_layer >= layers.size()) {
-        ALOGE("Source layer index %zu out of bounds %zu", source_layer,
-              layers.size());
-        return -EINVAL;
-      }
-      DrmHwcLayer &layer = layers[source_layer];
-
-      new_frame_state.used_framebuffers.emplace_back(layer.fb_id_handle);
-      new_frame_state.used_planes.emplace_back(plane);
-
-      /* Remove from 'unused' list, since plane is re-used */
-      auto &v = unused_planes;
-      v.erase(std::remove(v.begin(), v.end(), plane), v.end());
-
-      if (plane->AtomicSetState(*pset, layer, source_layer, crtc->id()) != 0) {
-        return -EINVAL;
-      }
-    }
-  }
-
-  if (args.clear_active_composition) {
-    new_frame_state.used_framebuffers.clear();
-    new_frame_state.used_planes.clear();
-  }
-
-  if (args.clear_active_composition || args.composition) {
-    for (auto *plane : unused_planes) {
-      if (plane->AtomicDisablePlane(*pset) != 0) {
-        return -EINVAL;
-      }
-    }
-  }
-
-  uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
-  if (args.test_only)
-    flags |= DRM_MODE_ATOMIC_TEST_ONLY;
-
-  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 (!args.test_only) {
-    if (args.display_mode) {
-      /* TODO(nobody): we still need this for synthetic vsync, remove after
-       * vsync reworked */
-      connector->set_active_mode(*args.display_mode);
-    }
-
-    active_frame_state_ = std::move(new_frame_state);
-
-    if (crtc->out_fence_ptr_property()) {
-      args.out_fence = UniqueFd((int)out_fence);
-    }
-  }
-
-  return 0;
-}
-
-auto DrmDisplayCompositor::ExecuteAtomicCommit(AtomicCommitArgs &args) -> int {
-  int err = CommitFrame(args);
-
-  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;
-    }
-  }
-
-  return err;
-}  // namespace android
-
-auto DrmDisplayCompositor::ActivateDisplayUsingDPMS() -> int {
-  auto *drm = resource_manager_->GetDrmDevice(display_);
-  auto *connector = drm->GetConnectorForDisplay(display_);
-  if (connector == nullptr) {
-    ALOGE("Could not locate connector for display %d", display_);
-    return -ENODEV;
-  }
-
-  if (connector->dpms_property()) {
-    drmModeConnectorSetProperty(drm->fd(), connector->id(),
-                                connector->dpms_property().id(),
-                                DRM_MODE_DPMS_ON);
-  }
-  return 0;
-}
-
-}  // namespace android
diff --git a/compositor/DrmKmsPlan.cpp b/compositor/DrmKmsPlan.cpp
new file mode 100644
index 0000000..966bd4e
--- /dev/null
+++ b/compositor/DrmKmsPlan.cpp
@@ -0,0 +1,59 @@
+/*
+ * 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-composition-drm-kms-plan"
+
+#include "DrmKmsPlan.h"
+
+#include "drm/DrmDevice.h"
+#include "drm/DrmPlane.h"
+#include "utils/log.h"
+
+namespace android {
+auto DrmKmsPlan::CreateDrmKmsPlan(DrmDisplayPipeline &pipe,
+                                  std::vector<DrmHwcLayer> composition)
+    -> std::unique_ptr<DrmKmsPlan> {
+  auto plan = std::make_unique<DrmKmsPlan>();
+
+  auto avail_planes = pipe.GetUsablePlanes();
+
+  int z_pos = 0;
+  for (auto &dhl : composition) {
+    std::shared_ptr<BindingOwner<DrmPlane>> plane;
+
+    /* Skip unsupported planes */
+    do {
+      if (avail_planes.empty()) {
+        return {};
+      }
+
+      plane = *avail_planes.begin();
+      avail_planes.erase(avail_planes.begin());
+    } while (!plane->Get()->IsValidForLayer(&dhl));
+
+    LayerToPlaneJoining joining = {
+        .layer = std::move(dhl),
+        .plane = plane,
+        .z_pos = z_pos++,
+    };
+
+    plan->plan.emplace_back(std::move(joining));
+  }
+
+  return plan;
+}
+
+}  // namespace android
diff --git a/compositor/DrmKmsPlan.h b/compositor/DrmKmsPlan.h
new file mode 100644
index 0000000..35e66e9
--- /dev/null
+++ b/compositor/DrmKmsPlan.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_DRM_KMS_PLAN_H_
+#define ANDROID_DRM_KMS_PLAN_H_
+
+#include <memory>
+#include <vector>
+
+#include "drmhwcomposer.h"
+
+namespace android {
+
+class DrmDevice;
+
+struct DrmKmsPlan {
+  struct LayerToPlaneJoining {
+    DrmHwcLayer layer;
+    std::shared_ptr<BindingOwner<DrmPlane>> plane;
+    int z_pos;
+  };
+
+  std::vector<LayerToPlaneJoining> plan;
+
+  static auto CreateDrmKmsPlan(DrmDisplayPipeline &pipe,
+                               std::vector<DrmHwcLayer> composition)
+      -> std::unique_ptr<DrmKmsPlan>;
+};
+
+}  // namespace android
+#endif
diff --git a/compositor/Planner.cpp b/compositor/Planner.cpp
deleted file mode 100644
index f43e314..0000000
--- a/compositor/Planner.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2016 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-platform"
-
-#include "Planner.h"
-
-#include <algorithm>
-
-#include "drm/DrmDevice.h"
-#include "utils/log.h"
-
-namespace android {
-
-std::vector<DrmPlane *> Planner::GetUsablePlanes(
-    DrmCrtc *crtc, std::vector<DrmPlane *> *primary_planes,
-    std::vector<DrmPlane *> *overlay_planes) {
-  std::vector<DrmPlane *> usable_planes;
-  std::copy_if(primary_planes->begin(), primary_planes->end(),
-               std::back_inserter(usable_planes),
-               [=](DrmPlane *plane) { return plane->GetCrtcSupported(*crtc); });
-  std::copy_if(overlay_planes->begin(), overlay_planes->end(),
-               std::back_inserter(usable_planes),
-               [=](DrmPlane *plane) { return plane->GetCrtcSupported(*crtc); });
-  return usable_planes;
-}
-
-std::tuple<int, std::vector<DrmCompositionPlane>> Planner::ProvisionPlanes(
-    std::map<size_t, DrmHwcLayer *> &layers, DrmCrtc *crtc,
-    std::vector<DrmPlane *> *primary_planes,
-    std::vector<DrmPlane *> *overlay_planes) {
-  std::vector<DrmCompositionPlane> composition;
-  std::vector<DrmPlane *> planes = GetUsablePlanes(crtc, primary_planes,
-                                                   overlay_planes);
-  if (planes.empty()) {
-    ALOGE("Display %d has no usable planes", crtc->display());
-    return std::make_tuple(-ENODEV, std::vector<DrmCompositionPlane>());
-  }
-
-  // Go through the provisioning stages and provision planes
-  int ret = ProvisionPlanesInternal(&composition, layers, &planes);
-  if (ret != 0) {
-    ALOGV("Failed provision stage with ret %d", ret);
-    return std::make_tuple(ret, std::vector<DrmCompositionPlane>());
-  }
-
-  return std::make_tuple(0, std::move(composition));
-}
-
-int Planner::ProvisionPlanesInternal(
-    std::vector<DrmCompositionPlane> *composition,
-    std::map<size_t, DrmHwcLayer *> &layers, std::vector<DrmPlane *> *planes) {
-  // Fill up the remaining planes
-  for (auto i = layers.begin(); i != layers.end(); i = layers.erase(i)) {
-    int ret = Emplace(composition, planes, std::make_pair(i->first, i->second));
-    // We don't have any planes left
-    if (ret == -ENOENT)
-      break;
-
-    if (ret != 0) {
-      ALOGV("Failed to emplace layer %zu, dropping it", i->first);
-      return ret;
-    }
-  }
-
-  return 0;
-}
-}  // namespace android
diff --git a/compositor/Planner.h b/compositor/Planner.h
deleted file mode 100644
index 7802d0c..0000000
--- a/compositor/Planner.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#ifndef ANDROID_DRM_PLATFORM_H_
-#define ANDROID_DRM_PLATFORM_H_
-
-#include <hardware/hardware.h>
-#include <hardware/hwcomposer.h>
-
-#include <map>
-#include <memory>
-#include <vector>
-
-#include "compositor/DrmDisplayComposition.h"
-#include "drmhwcomposer.h"
-
-namespace android {
-
-class DrmDevice;
-
-class Planner {
- private:
-  // Removes and returns the next available plane from planes
-  static DrmPlane *PopPlane(std::vector<DrmPlane *> *planes) {
-    if (planes->empty())
-      return nullptr;
-    DrmPlane *plane = planes->front();
-    planes->erase(planes->begin());
-    return plane;
-  }
-
-  // Inserts the given layer:plane in the composition at the back
-  static int Emplace(std::vector<DrmCompositionPlane> *composition,
-                     std::vector<DrmPlane *> *planes,
-                     std::pair<size_t, DrmHwcLayer *> layer) {
-    DrmPlane *plane = PopPlane(planes);
-    std::vector<DrmPlane *> unused_planes;
-    int ret = -ENOENT;
-    while (plane != nullptr) {
-      ret = plane->IsValidForLayer(layer.second) ? 0 : -EINVAL;
-      if (ret == 0)
-        break;
-      if (!plane->GetZPosProperty().is_immutable())
-        unused_planes.push_back(plane);
-      plane = PopPlane(planes);
-    }
-
-    if (ret == 0) {
-      composition->emplace_back(plane, layer.first);
-      planes->insert(planes->begin(), unused_planes.begin(),
-                     unused_planes.end());
-    }
-
-    return ret;
-  }
-
-  static int ProvisionPlanesInternal(
-      std::vector<DrmCompositionPlane> *composition,
-      std::map<size_t, DrmHwcLayer *> &layers, std::vector<DrmPlane *> *planes);
-
- public:
-  // Takes a stack of layers and provisions hardware planes for them. If the
-  // entire stack can't fit in hardware, FIXME
-  //
-  // @layers: a map of index:layer of layers to composite
-  // @primary_planes: a vector of primary planes available for this frame
-  // @overlay_planes: a vector of overlay planes available for this frame
-  //
-  // Returns: A tuple with the status of the operation (0 for success) and
-  //          a vector of the resulting plan (ie: layer->plane mapping).
-  static std::tuple<int, std::vector<DrmCompositionPlane>> ProvisionPlanes(
-      std::map<size_t, DrmHwcLayer *> &layers, DrmCrtc *crtc,
-      std::vector<DrmPlane *> *primary_planes,
-      std::vector<DrmPlane *> *overlay_planes);
-
- private:
-  static std::vector<DrmPlane *> GetUsablePlanes(
-      DrmCrtc *crtc, std::vector<DrmPlane *> *primary_planes,
-      std::vector<DrmPlane *> *overlay_planes);
-};
-}  // namespace android
-#endif
diff --git a/drm/DrmAtomicStateManager.cpp b/drm/DrmAtomicStateManager.cpp
new file mode 100644
index 0000000..65fb19e
--- /dev/null
+++ b/drm/DrmAtomicStateManager.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2015 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+#define LOG_TAG "hwc-drm-atomic-state-manager"
+
+#include "DrmAtomicStateManager.h"
+
+#include <drm/drm_mode.h>
+#include <pthread.h>
+#include <sched.h>
+#include <sync/sync.h>
+#include <utils/Trace.h>
+
+#include <array>
+#include <cstdlib>
+#include <ctime>
+#include <sstream>
+#include <vector>
+
+#include "drm/DrmCrtc.h"
+#include "drm/DrmDevice.h"
+#include "drm/DrmPlane.h"
+#include "drm/DrmUnique.h"
+#include "utils/log.h"
+
+namespace android {
+
+// NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme
+auto DrmAtomicStateManager::CommitFrame(AtomicCommitArgs &args) -> int {
+  ATRACE_CALL();
+
+  if (args.active && *args.active == active_frame_state_.crtc_active_state) {
+    /* Don't set the same state twice */
+    args.active.reset();
+  }
+
+  if (!args.HasInputs()) {
+    /* nothing to do */
+    return 0;
+  }
+
+  if (!active_frame_state_.crtc_active_state) {
+    /* Force activate display */
+    args.active = true;
+  }
+
+  auto new_frame_state = NewFrameState();
+
+  auto *drm = pipe_->device;
+  auto *connector = pipe_->connector->Get();
+  auto *crtc = pipe_->crtc->Get();
+
+  auto pset = MakeDrmModeAtomicReqUnique();
+  if (!pset) {
+    ALOGE("Failed to allocate property set");
+    return -ENOMEM;
+  }
+
+  int64_t out_fence = -1;
+  if (crtc->GetOutFencePtrProperty() &&
+      !crtc->GetOutFencePtrProperty().AtomicSet(*pset, uint64_t(&out_fence))) {
+    return -EINVAL;
+  }
+
+  if (args.active) {
+    new_frame_state.crtc_active_state = *args.active;
+    if (!crtc->GetActiveProperty().AtomicSet(*pset, *args.active ? 1 : 0) ||
+        !connector->GetCrtcIdProperty().AtomicSet(*pset, crtc->GetId())) {
+      return -EINVAL;
+    }
+  }
+
+  if (args.display_mode) {
+    new_frame_state.mode_blob = args.display_mode.value().CreateModeBlob(*drm);
+
+    if (!new_frame_state.mode_blob) {
+      ALOGE("Failed to create mode_blob");
+      return -EINVAL;
+    }
+
+    if (!crtc->GetModeProperty().AtomicSet(*pset, *new_frame_state.mode_blob)) {
+      return -EINVAL;
+    }
+  }
+
+  auto unused_planes = new_frame_state.used_planes;
+
+  if (args.composition) {
+    new_frame_state.used_framebuffers.clear();
+    new_frame_state.used_planes.clear();
+
+    for (auto &joining : args.composition->plan) {
+      DrmPlane *plane = joining.plane->Get();
+      DrmHwcLayer &layer = joining.layer;
+
+      new_frame_state.used_framebuffers.emplace_back(layer.fb_id_handle);
+      new_frame_state.used_planes.emplace_back(joining.plane);
+
+      /* Remove from 'unused' list, since plane is re-used */
+      auto &v = unused_planes;
+      v.erase(std::remove(v.begin(), v.end(), joining.plane), v.end());
+
+      if (plane->AtomicSetState(*pset, layer, joining.z_pos, crtc->GetId()) !=
+          0) {
+        return -EINVAL;
+      }
+    }
+  }
+
+  if (args.composition) {
+    for (auto &plane : unused_planes) {
+      if (plane->Get()->AtomicDisablePlane(*pset) != 0) {
+        return -EINVAL;
+      }
+    }
+  }
+
+  uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
+  if (args.test_only)
+    flags |= DRM_MODE_ATOMIC_TEST_ONLY;
+
+  int err = drmModeAtomicCommit(drm->GetFd(), pset.get(), flags, drm);
+  if (err != 0) {
+    if (!args.test_only)
+      ALOGE("Failed to commit pset ret=%d\n", err);
+    return err;
+  }
+
+  if (!args.test_only) {
+    if (args.display_mode) {
+      /* TODO(nobody): we still need this for synthetic vsync, remove after
+       * vsync reworked */
+      connector->SetActiveMode(*args.display_mode);
+    }
+
+    active_frame_state_ = std::move(new_frame_state);
+
+    if (crtc->GetOutFencePtrProperty()) {
+      args.out_fence = UniqueFd((int)out_fence);
+    }
+  }
+
+  return 0;
+}
+
+auto DrmAtomicStateManager::ExecuteAtomicCommit(AtomicCommitArgs &args) -> int {
+  int err = CommitFrame(args);
+
+  if (!args.test_only) {
+    if (err != 0) {
+      ALOGE("Composite failed for pipeline %s",
+            pipe_->connector->Get()->GetName().c_str());
+      // 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{};
+      cl_args.composition = std::make_shared<DrmKmsPlan>();
+      if (CommitFrame(cl_args) != 0) {
+        ALOGE("Failed to clean-up active composition for pipeline %s",
+              pipe_->connector->Get()->GetName().c_str());
+      }
+      return err;
+    }
+  }
+
+  return err;
+}  // namespace android
+
+auto DrmAtomicStateManager::ActivateDisplayUsingDPMS() -> int {
+  return drmModeConnectorSetProperty(pipe_->device->GetFd(),
+                                     pipe_->connector->Get()->GetId(),
+                                     pipe_->connector->Get()
+                                         ->GetDpmsProperty()
+                                         .id(),
+                                     DRM_MODE_DPMS_ON);
+}
+
+}  // namespace android
diff --git a/compositor/DrmDisplayCompositor.h b/drm/DrmAtomicStateManager.h
similarity index 71%
rename from compositor/DrmDisplayCompositor.h
rename to drm/DrmAtomicStateManager.h
index 9679520..08a1c13 100644
--- a/compositor/DrmDisplayCompositor.h
+++ b/drm/DrmAtomicStateManager.h
@@ -14,11 +14,9 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_DRM_DISPLAY_COMPOSITOR_H_
-#define ANDROID_DRM_DISPLAY_COMPOSITOR_H_
+#ifndef ANDROID_DRM_ATOMIC_STATE_MANAGER_H_
+#define ANDROID_DRM_ATOMIC_STATE_MANAGER_H_
 
-#include <hardware/hardware.h>
-#include <hardware/hwcomposer.h>
 #include <pthread.h>
 
 #include <functional>
@@ -27,7 +25,8 @@
 #include <sstream>
 #include <tuple>
 
-#include "DrmDisplayComposition.h"
+#include "compositor/DrmKmsPlan.h"
+#include "drm/DrmPlane.h"
 #include "drm/ResourceManager.h"
 #include "drm/VSyncWorker.h"
 #include "drmhwcomposer.h"
@@ -39,29 +38,24 @@
   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;
+  std::shared_ptr<DrmKmsPlan> composition;
 
   /* out */
   UniqueFd out_fence;
 
   /* helpers */
   auto HasInputs() -> bool {
-    return display_mode || active || composition || clear_active_composition;
+    return display_mode || active || composition;
   }
 };
 
-class DrmDisplayCompositor {
+class DrmAtomicStateManager {
  public:
-  DrmDisplayCompositor() = default;
-  ~DrmDisplayCompositor() = default;
-  auto Init(ResourceManager *resource_manager, int display) -> int;
+  explicit DrmAtomicStateManager(DrmDisplayPipeline *pipe) : pipe_(pipe){};
+  DrmAtomicStateManager(const DrmAtomicStateManager &) = delete;
+  ~DrmAtomicStateManager() = default;
 
   auto ExecuteAtomicCommit(AtomicCommitArgs &args) -> int;
-
-  DrmDisplayCompositor(const DrmDisplayCompositor &) = delete;
-
   auto ActivateDisplayUsingDPMS() -> int;
 
  private:
@@ -69,7 +63,7 @@
 
   struct KmsState {
     /* Required to cleanup unused planes */
-    std::vector<DrmPlane *> used_planes;
+    std::vector<std::shared_ptr<BindingOwner<DrmPlane>>> used_planes;
     /* We have to hold a reference to framebuffer while displaying it ,
      * otherwise picture will blink */
     std::vector<std::shared_ptr<DrmFbIdHandle>> used_framebuffers;
@@ -88,9 +82,7 @@
     };
   }
 
-  ResourceManager *resource_manager_ = nullptr;
-  bool initialized_{};
-  int display_ = -1;
+  DrmDisplayPipeline *const pipe_;
 };
 }  // namespace android
 
diff --git a/drm/DrmConnector.cpp b/drm/DrmConnector.cpp
index 7cbec95..4737316 100644
--- a/drm/DrmConnector.cpp
+++ b/drm/DrmConnector.cpp
@@ -29,10 +29,12 @@
 #include "utils/log.h"
 
 #ifndef DRM_MODE_CONNECTOR_SPI
+// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
 #define DRM_MODE_CONNECTOR_SPI 19
 #endif
 
 #ifndef DRM_MODE_CONNECTOR_USB
+// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
 #define DRM_MODE_CONNECTOR_USB 20
 #endif
 
@@ -40,62 +42,60 @@
 
 constexpr size_t kTypesCount = 21;
 
-DrmConnector::DrmConnector(DrmDevice *drm, drmModeConnectorPtr c,
-                           DrmEncoder *current_encoder,
-                           std::vector<DrmEncoder *> &possible_encoders)
-    : drm_(drm),
-      id_(c->connector_id),
-      encoder_(current_encoder),
-      display_(-1),
-      type_(c->connector_type),
-      type_id_(c->connector_type_id),
-      state_(c->connection),
-      mm_width_(c->mmWidth),
-      mm_height_(c->mmHeight),
-      possible_encoders_(possible_encoders) {
+static bool GetOptionalConnectorProperty(const DrmDevice &dev,
+                                         const DrmConnector &connector,
+                                         const char *prop_name,
+                                         DrmProperty *property) {
+  return dev.GetProperty(connector.GetId(), DRM_MODE_OBJECT_CONNECTOR,
+                         prop_name, property) == 0;
 }
 
-int DrmConnector::Init() {
-  int ret = drm_->GetConnectorProperty(*this, "DPMS", &dpms_property_);
-  if (ret) {
-    ALOGE("Could not get DPMS property\n");
-    return ret;
+static bool GetConnectorProperty(const DrmDevice &dev,
+                                 const DrmConnector &connector,
+                                 const char *prop_name, DrmProperty *property) {
+  if (!GetOptionalConnectorProperty(dev, connector, prop_name, property)) {
+    ALOGE("Could not get %s property\n", prop_name);
+    return false;
   }
-  ret = drm_->GetConnectorProperty(*this, "CRTC_ID", &crtc_id_property_);
-  if (ret) {
-    ALOGE("Could not get CRTC_ID property\n");
-    return ret;
+  return true;
+}
+
+auto DrmConnector::CreateInstance(DrmDevice &dev, uint32_t connector_id,
+                                  uint32_t index)
+    -> std::unique_ptr<DrmConnector> {
+  auto conn = MakeDrmModeConnectorUnique(dev.GetFd(), connector_id);
+  if (!conn) {
+    ALOGE("Failed to get connector %d", connector_id);
+    return {};
   }
-  UpdateEdidProperty();
-  if (writeback()) {
-    ret = drm_->GetConnectorProperty(*this, "WRITEBACK_PIXEL_FORMATS",
-                                     &writeback_pixel_formats_);
-    if (ret) {
-      ALOGE("Could not get WRITEBACK_PIXEL_FORMATS connector_id = %d\n", id_);
-      return ret;
-    }
-    ret = drm_->GetConnectorProperty(*this, "WRITEBACK_FB_ID",
-                                     &writeback_fb_id_);
-    if (ret) {
-      ALOGE("Could not get WRITEBACK_FB_ID connector_id = %d\n", id_);
-      return ret;
-    }
-    ret = drm_->GetConnectorProperty(*this, "WRITEBACK_OUT_FENCE_PTR",
-                                     &writeback_out_fence_);
-    if (ret) {
-      ALOGE("Could not get WRITEBACK_OUT_FENCE_PTR connector_id = %d\n", id_);
-      return ret;
-    }
+
+  auto c = std::unique_ptr<DrmConnector>(
+      new DrmConnector(std::move(conn), &dev, index));
+
+  if (!GetConnectorProperty(dev, *c, "DPMS", &c->dpms_property_) ||
+      !GetConnectorProperty(dev, *c, "CRTC_ID", &c->crtc_id_property_)) {
+    return {};
   }
-  return 0;
+
+  c->UpdateEdidProperty();
+
+  if (c->IsWriteback() &&
+      (!GetConnectorProperty(dev, *c, "WRITEBACK_PIXEL_FORMATS",
+                             &c->writeback_pixel_formats_) ||
+       !GetConnectorProperty(dev, *c, "WRITEBACK_FB_ID",
+                             &c->writeback_fb_id_) ||
+       !GetConnectorProperty(dev, *c, "WRITEBACK_OUT_FENCE_PTR",
+                             &c->writeback_out_fence_))) {
+    return {};
+  }
+
+  return c;
 }
 
 int DrmConnector::UpdateEdidProperty() {
-  int ret = drm_->GetConnectorProperty(*this, "EDID", &edid_property_);
-  if (ret) {
-    ALOGW("Could not get EDID property\n");
-  }
-  return ret;
+  return GetOptionalConnectorProperty(*drm_, *this, "EDID", &edid_property_)
+             ? 0
+             : -EINVAL;
 }
 
 auto DrmConnector::GetEdidBlob() -> DrmModePropertyBlobUnique {
@@ -105,145 +105,87 @@
     return {};
   }
 
-  std::tie(ret, blob_id) = edid_property().value();
+  std::tie(ret, blob_id) = GetEdidProperty().value();
   if (ret != 0) {
     return {};
   }
 
-  return MakeDrmModePropertyBlobUnique(drm_->fd(), blob_id);
+  return MakeDrmModePropertyBlobUnique(drm_->GetFd(), blob_id);
 }
 
-uint32_t DrmConnector::id() const {
-  return id_;
+bool DrmConnector::IsInternal() const {
+  auto type = connector_->connector_type;
+  return type == DRM_MODE_CONNECTOR_LVDS || type == DRM_MODE_CONNECTOR_eDP ||
+         type == DRM_MODE_CONNECTOR_DSI || type == DRM_MODE_CONNECTOR_VIRTUAL ||
+         type == DRM_MODE_CONNECTOR_DPI || type == DRM_MODE_CONNECTOR_SPI;
 }
 
-int DrmConnector::display() const {
-  return display_;
+bool DrmConnector::IsExternal() const {
+  auto type = connector_->connector_type;
+  return type == DRM_MODE_CONNECTOR_HDMIA ||
+         type == DRM_MODE_CONNECTOR_DisplayPort ||
+         type == DRM_MODE_CONNECTOR_DVID || type == DRM_MODE_CONNECTOR_DVII ||
+         type == DRM_MODE_CONNECTOR_VGA || type == DRM_MODE_CONNECTOR_USB;
 }
 
-void DrmConnector::set_display(int display) {
-  display_ = display;
-}
-
-bool DrmConnector::internal() const {
-  return type_ == DRM_MODE_CONNECTOR_LVDS || type_ == DRM_MODE_CONNECTOR_eDP ||
-         type_ == DRM_MODE_CONNECTOR_DSI ||
-         type_ == DRM_MODE_CONNECTOR_VIRTUAL ||
-         type_ == DRM_MODE_CONNECTOR_DPI || type_ == DRM_MODE_CONNECTOR_SPI;
-}
-
-bool DrmConnector::external() const {
-  return type_ == DRM_MODE_CONNECTOR_HDMIA ||
-         type_ == DRM_MODE_CONNECTOR_DisplayPort ||
-         type_ == DRM_MODE_CONNECTOR_DVID || type_ == DRM_MODE_CONNECTOR_DVII ||
-         type_ == DRM_MODE_CONNECTOR_VGA || type_ == DRM_MODE_CONNECTOR_USB;
-}
-
-bool DrmConnector::writeback() const {
+bool DrmConnector::IsWriteback() const {
 #ifdef DRM_MODE_CONNECTOR_WRITEBACK
-  return type_ == DRM_MODE_CONNECTOR_WRITEBACK;
+  return connector_->connector_type == DRM_MODE_CONNECTOR_WRITEBACK;
 #else
   return false;
 #endif
 }
 
-bool DrmConnector::valid_type() const {
-  return internal() || external() || writeback();
+bool DrmConnector::IsValid() const {
+  return IsInternal() || IsExternal() || IsWriteback();
 }
 
-std::string DrmConnector::name() const {
+std::string DrmConnector::GetName() const {
   constexpr std::array<const char *, kTypesCount> kNames =
       {"None",      "VGA",  "DVI-I",     "DVI-D",   "DVI-A", "Composite",
        "SVIDEO",    "LVDS", "Component", "DIN",     "DP",    "HDMI-A",
        "HDMI-B",    "TV",   "eDP",       "Virtual", "DSI",   "DPI",
        "Writeback", "SPI",  "USB"};
 
-  if (type_ < kTypesCount) {
+  if (connector_->connector_type < kTypesCount) {
     std::ostringstream name_buf;
-    name_buf << kNames[type_] << "-" << type_id_;
+    name_buf << kNames[connector_->connector_type] << "-"
+             << connector_->connector_type_id;
     return name_buf.str();
   }
 
-  ALOGE("Unknown type in connector %d, could not make his name", id_);
+  ALOGE("Unknown type in connector %d, could not make his name", GetId());
   return "None";
 }
 
 int DrmConnector::UpdateModes() {
-  drmModeConnectorPtr c = drmModeGetConnector(drm_->fd(), id_);
-  if (!c) {
-    ALOGE("Failed to get connector %d", id_);
+  auto conn = MakeDrmModeConnectorUnique(drm_->GetFd(), GetId());
+  if (!conn) {
+    ALOGE("Failed to get connector %d", GetId());
     return -ENODEV;
   }
-
-  state_ = c->connection;
+  connector_ = std::move(conn);
 
   modes_.clear();
-  for (int i = 0; i < c->count_modes; ++i) {
+  for (int i = 0; i < connector_->count_modes; ++i) {
     bool exists = false;
     for (const DrmMode &mode : modes_) {
-      if (mode == c->modes[i]) {
+      if (mode == connector_->modes[i]) {
         exists = true;
         break;
       }
     }
 
     if (!exists) {
-      modes_.emplace_back(DrmMode(&c->modes[i]));
+      modes_.emplace_back(DrmMode(&connector_->modes[i]));
     }
   }
 
   return 0;
 }
 
-const DrmMode &DrmConnector::active_mode() const {
-  return active_mode_;
-}
-
-void DrmConnector::set_active_mode(const DrmMode &mode) {
+void DrmConnector::SetActiveMode(DrmMode &mode) {
   active_mode_ = mode;
 }
 
-const DrmProperty &DrmConnector::dpms_property() const {
-  return dpms_property_;
-}
-
-const DrmProperty &DrmConnector::crtc_id_property() const {
-  return crtc_id_property_;
-}
-
-const DrmProperty &DrmConnector::edid_property() const {
-  return edid_property_;
-}
-
-const DrmProperty &DrmConnector::writeback_pixel_formats() const {
-  return writeback_pixel_formats_;
-}
-
-const DrmProperty &DrmConnector::writeback_fb_id() const {
-  return writeback_fb_id_;
-}
-
-const DrmProperty &DrmConnector::writeback_out_fence() const {
-  return writeback_out_fence_;
-}
-
-DrmEncoder *DrmConnector::encoder() const {
-  return encoder_;
-}
-
-void DrmConnector::set_encoder(DrmEncoder *encoder) {
-  encoder_ = encoder;
-}
-
-drmModeConnection DrmConnector::state() const {
-  return state_;
-}
-
-uint32_t DrmConnector::mm_width() const {
-  return mm_width_;
-}
-
-uint32_t DrmConnector::mm_height() const {
-  return mm_height_;
-}
 }  // namespace android
diff --git a/drm/DrmConnector.h b/drm/DrmConnector.h
index 2bcb543..629b3cc 100644
--- a/drm/DrmConnector.h
+++ b/drm/DrmConnector.h
@@ -32,69 +32,97 @@
 
 class DrmDevice;
 
-class DrmConnector {
+class DrmConnector : public PipelineBindable<DrmConnector> {
  public:
-  DrmConnector(DrmDevice *drm, drmModeConnectorPtr c,
-               DrmEncoder *current_encoder,
-               std::vector<DrmEncoder *> &possible_encoders);
+  static auto CreateInstance(DrmDevice &dev, uint32_t connector_id,
+                             uint32_t index) -> std::unique_ptr<DrmConnector>;
+
   DrmConnector(const DrmProperty &) = delete;
   DrmConnector &operator=(const DrmProperty &) = delete;
 
-  int Init();
   int UpdateEdidProperty();
   auto GetEdidBlob() -> DrmModePropertyBlobUnique;
 
-  uint32_t id() const;
+  auto GetDev() const -> DrmDevice & {
+    return *drm_;
+  }
 
-  int display() const;
-  void set_display(int display);
+  auto GetId() const {
+    return connector_->connector_id;
+  }
 
-  bool internal() const;
-  bool external() const;
-  bool writeback() const;
-  bool valid_type() const;
+  auto GetIndexInResArray() const {
+    return index_in_res_array_;
+  }
 
-  std::string name() const;
+  auto GetCurrentEncoderId() const {
+    return connector_->encoder_id;
+  }
+
+  auto SupportsEncoder(DrmEncoder &enc) const {
+    for (int i = 0; i < connector_->count_encoders; i++) {
+      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
+      if (connector_->encoders[i] == enc.GetId()) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  bool IsInternal() const;
+  bool IsExternal() const;
+  bool IsWriteback() const;
+  bool IsValid() const;
+
+  std::string GetName() const;
 
   int UpdateModes();
 
-  const std::vector<DrmMode> &modes() const {
+  auto &GetModes() const {
     return modes_;
   }
-  const DrmMode &active_mode() const;
-  void set_active_mode(const DrmMode &mode);
 
-  const DrmProperty &dpms_property() const;
-  const DrmProperty &crtc_id_property() const;
-  const DrmProperty &edid_property() const;
-  const DrmProperty &writeback_pixel_formats() const;
-  const DrmProperty &writeback_fb_id() const;
-  const DrmProperty &writeback_out_fence() const;
-
-  const std::vector<DrmEncoder *> &possible_encoders() const {
-    return possible_encoders_;
+  auto &GetActiveMode() const {
+    return active_mode_;
   }
-  DrmEncoder *encoder() const;
-  void set_encoder(DrmEncoder *encoder);
 
-  drmModeConnection state() const;
+  void SetActiveMode(DrmMode &mode);
 
-  uint32_t mm_width() const;
-  uint32_t mm_height() const;
+  auto &GetDpmsProperty() const {
+    return dpms_property_;
+  }
+
+  auto &GetCrtcIdProperty() const {
+    return crtc_id_property_;
+  }
+
+  auto &GetEdidProperty() const {
+    return edid_property_;
+  }
+
+  auto IsConnected() const {
+    return connector_->connection == DRM_MODE_CONNECTED;
+  }
+
+  auto GetMmWidth() const {
+    return connector_->mmWidth;
+  }
+
+  auto GetMmHeight() const {
+    return connector_->mmHeight;
+  };
 
  private:
-  DrmDevice *drm_;
+  DrmConnector(DrmModeConnectorUnique connector, DrmDevice *drm, uint32_t index)
+      : connector_(std::move(connector)),
+        drm_(drm),
+        index_in_res_array_(index){};
 
-  uint32_t id_;
-  DrmEncoder *encoder_;
-  int display_;
+  DrmModeConnectorUnique connector_;
+  DrmDevice *const drm_;
 
-  uint32_t type_;
-  uint32_t type_id_;
-  drmModeConnection state_;
-
-  uint32_t mm_width_;
-  uint32_t mm_height_;
+  const uint32_t index_in_res_array_;
 
   DrmMode active_mode_;
   std::vector<DrmMode> modes_;
@@ -105,8 +133,6 @@
   DrmProperty writeback_pixel_formats_;
   DrmProperty writeback_fb_id_;
   DrmProperty writeback_out_fence_;
-
-  std::vector<DrmEncoder *> possible_encoders_;
 };
 }  // namespace android
 
diff --git a/drm/DrmCrtc.cpp b/drm/DrmCrtc.cpp
index 08a1922..b54f14b 100644
--- a/drm/DrmCrtc.cpp
+++ b/drm/DrmCrtc.cpp
@@ -27,60 +27,41 @@
 
 namespace android {
 
-DrmCrtc::DrmCrtc(DrmDevice *drm, drmModeCrtcPtr c, unsigned pipe)
-    : drm_(drm), id_(c->crtc_id), pipe_(pipe), display_(-1), mode_(&c->mode) {
+static int GetCrtcProperty(const DrmDevice &dev, const DrmCrtc &crtc,
+                           const char *prop_name, DrmProperty *property) {
+  return dev.GetProperty(crtc.GetId(), DRM_MODE_OBJECT_CRTC, prop_name,
+                         property);
 }
 
-int DrmCrtc::Init() {
-  int ret = drm_->GetCrtcProperty(*this, "ACTIVE", &active_property_);
+auto DrmCrtc::CreateInstance(DrmDevice &dev, uint32_t crtc_id, uint32_t index)
+    -> std::unique_ptr<DrmCrtc> {
+  auto crtc = MakeDrmModeCrtcUnique(dev.GetFd(), crtc_id);
+  if (!crtc) {
+    ALOGE("Failed to get CRTC %d", crtc_id);
+    return {};
+  }
+
+  auto c = std::unique_ptr<DrmCrtc>(new DrmCrtc(std::move(crtc), index));
+
+  int ret = GetCrtcProperty(dev, *c, "ACTIVE", &c->active_property_);
   if (ret != 0) {
     ALOGE("Failed to get ACTIVE property");
-    return ret;
+    return {};
   }
 
-  ret = drm_->GetCrtcProperty(*this, "MODE_ID", &mode_property_);
+  ret = GetCrtcProperty(dev, *c, "MODE_ID", &c->mode_property_);
   if (ret != 0) {
     ALOGE("Failed to get MODE_ID property");
-    return ret;
+    return {};
   }
 
-  ret = drm_->GetCrtcProperty(*this, "OUT_FENCE_PTR", &out_fence_ptr_property_);
+  ret = GetCrtcProperty(dev, *c, "OUT_FENCE_PTR", &c->out_fence_ptr_property_);
   if (ret != 0) {
     ALOGE("Failed to get OUT_FENCE_PTR property");
-    return ret;
+    return {};
   }
-  return 0;
+
+  return c;
 }
 
-uint32_t DrmCrtc::id() const {
-  return id_;
-}
-
-unsigned DrmCrtc::pipe() const {
-  return pipe_;
-}
-
-int DrmCrtc::display() const {
-  return display_;
-}
-
-void DrmCrtc::set_display(int display) {
-  display_ = display;
-}
-
-bool DrmCrtc::can_bind(int display) const {
-  return display_ == -1 || display_ == display;
-}
-
-const DrmProperty &DrmCrtc::active_property() const {
-  return active_property_;
-}
-
-const DrmProperty &DrmCrtc::mode_property() const {
-  return mode_property_;
-}
-
-const DrmProperty &DrmCrtc::out_fence_ptr_property() const {
-  return out_fence_ptr_property_;
-}
 }  // namespace android
diff --git a/drm/DrmCrtc.h b/drm/DrmCrtc.h
index 85e067a..ebf0a97 100644
--- a/drm/DrmCrtc.h
+++ b/drm/DrmCrtc.h
@@ -21,41 +21,51 @@
 
 #include <cstdint>
 
+#include "DrmDisplayPipeline.h"
 #include "DrmMode.h"
 #include "DrmProperty.h"
+#include "DrmUnique.h"
 
 namespace android {
 
 class DrmDevice;
 
-class DrmCrtc {
+class DrmCrtc : public PipelineBindable<DrmCrtc> {
  public:
-  DrmCrtc(DrmDevice *drm, drmModeCrtcPtr c, unsigned pipe);
+  static auto CreateInstance(DrmDevice &dev, uint32_t crtc_id, uint32_t index)
+      -> std::unique_ptr<DrmCrtc>;
+
+  DrmCrtc() = delete;
   DrmCrtc(const DrmCrtc &) = delete;
   DrmCrtc &operator=(const DrmCrtc &) = delete;
 
-  int Init();
+  auto GetId() const {
+    return crtc_->crtc_id;
+  }
 
-  uint32_t id() const;
-  unsigned pipe() const;
+  auto GetIndexInResArray() const {
+    return index_in_res_array_;
+  }
 
-  int display() const;
-  void set_display(int display);
+  auto &GetActiveProperty() const {
+    return active_property_;
+  }
 
-  bool can_bind(int display) const;
+  auto &GetModeProperty() const {
+    return mode_property_;
+  }
 
-  const DrmProperty &active_property() const;
-  const DrmProperty &mode_property() const;
-  const DrmProperty &out_fence_ptr_property() const;
+  auto &GetOutFencePtrProperty() const {
+    return out_fence_ptr_property_;
+  }
 
  private:
-  DrmDevice *drm_;
+  DrmCrtc(DrmModeCrtcUnique crtc, uint32_t index)
+      : crtc_(std::move(crtc)), index_in_res_array_(index){};
 
-  uint32_t id_;
-  unsigned pipe_;
-  int display_;
+  DrmModeCrtcUnique crtc_;
 
-  DrmMode mode_;
+  const uint32_t index_in_res_array_;
 
   DrmProperty active_property_;
   DrmProperty mode_property_;
diff --git a/drm/DrmDevice.cpp b/drm/DrmDevice.cpp
index 8245b78..fd4589e 100644
--- a/drm/DrmDevice.cpp
+++ b/drm/DrmDevice.cpp
@@ -22,148 +22,66 @@
 #include <xf86drm.h>
 #include <xf86drmMode.h>
 
-#include <algorithm>
-#include <array>
-#include <cerrno>
 #include <cinttypes>
 #include <cstdint>
-#include <sstream>
 #include <string>
 
+#include "drm/DrmAtomicStateManager.h"
 #include "drm/DrmPlane.h"
 #include "utils/log.h"
 #include "utils/properties.h"
 
-static void trim_left(std::string *str) {
-  str->erase(std::begin(*str),
-             std::find_if(std::begin(*str), std::end(*str),
-                          [](int ch) { return std::isspace(ch) == 0; }));
-}
-
-static void trim_right(std::string *str) {
-  str->erase(std::find_if(std::rbegin(*str), std::rend(*str),
-                          [](int ch) { return std::isspace(ch) == 0; })
-                 .base(),
-             std::end(*str));
-}
-
-static void trim(std::string *str) {
-  trim_left(str);
-  trim_right(str);
-}
-
 namespace android {
 
-static std::vector<std::string> read_primary_display_order_prop() {
-  std::array<char, PROPERTY_VALUE_MAX> display_order_buf{};
-  property_get("vendor.hwc.drm.primary_display_order", display_order_buf.data(),
-               "...");
-
-  std::vector<std::string> display_order;
-  std::istringstream str(display_order_buf.data());
-  for (std::string conn_name; std::getline(str, conn_name, ',');) {
-    trim(&conn_name);
-    display_order.push_back(std::move(conn_name));
-  }
-  return display_order;
-}
-
-static std::vector<DrmConnector *> make_primary_display_candidates(
-    const std::vector<std::unique_ptr<DrmConnector>> &connectors) {
-  std::vector<DrmConnector *> primary_candidates;
-  std::transform(std::begin(connectors), std::end(connectors),
-                 std::back_inserter(primary_candidates),
-                 [](const std::unique_ptr<DrmConnector> &conn) {
-                   return conn.get();
-                 });
-  primary_candidates.erase(std::remove_if(std::begin(primary_candidates),
-                                          std::end(primary_candidates),
-                                          [](const DrmConnector *conn) {
-                                            return conn->state() !=
-                                                   DRM_MODE_CONNECTED;
-                                          }),
-                           std::end(primary_candidates));
-
-  std::vector<std::string> display_order = read_primary_display_order_prop();
-  bool use_other = display_order.back() == "...";
-
-  // putting connectors from primary_display_order first
-  auto curr_connector = std::begin(primary_candidates);
-  for (const std::string &display_name : display_order) {
-    auto it = std::find_if(std::begin(primary_candidates),
-                           std::end(primary_candidates),
-                           [&display_name](const DrmConnector *conn) {
-                             return conn->name() == display_name;
-                           });
-    if (it != std::end(primary_candidates)) {
-      std::iter_swap(it, curr_connector);
-      ++curr_connector;
-    }
-  }
-
-  if (use_other) {
-    // then putting internal connectors second, everything else afterwards
-    std::partition(curr_connector, std::end(primary_candidates),
-                   [](const DrmConnector *conn) { return conn->internal(); });
-  } else {
-    primary_candidates.erase(curr_connector, std::end(primary_candidates));
-  }
-
-  return primary_candidates;
-}
-
 DrmDevice::DrmDevice() {
-  self.reset(this);
-  mDrmFbImporter = std::make_unique<DrmFbImporter>(self);
+  drm_fb_importer_ = std::make_unique<DrmFbImporter>(*this);
 }
 
-// NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme
-std::tuple<int, int> DrmDevice::Init(const char *path, int num_displays) {
+auto DrmDevice::Init(const char *path) -> int {
   /* TODO: Use drmOpenControl here instead */
   fd_ = UniqueFd(open(path, O_RDWR | O_CLOEXEC));
-  if (fd() < 0) {
+  if (!fd_) {
     // NOLINTNEXTLINE(concurrency-mt-unsafe): Fixme
     ALOGE("Failed to open dri %s: %s", path, strerror(errno));
-    return std::make_tuple(-ENODEV, 0);
+    return -ENODEV;
   }
 
-  int ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
-  if (ret) {
+  int ret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+  if (ret != 0) {
     ALOGE("Failed to set universal plane cap %d", ret);
-    return std::make_tuple(ret, 0);
+    return ret;
   }
 
-  ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_ATOMIC, 1);
-  if (ret) {
+  ret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_ATOMIC, 1);
+  if (ret != 0) {
     ALOGE("Failed to set atomic cap %d", ret);
-    return std::make_tuple(ret, 0);
+    return ret;
   }
 
 #ifdef DRM_CLIENT_CAP_WRITEBACK_CONNECTORS
-  ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
-  if (ret) {
+  ret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
+  if (ret != 0) {
     ALOGI("Failed to set writeback cap %d", ret);
-    ret = 0;
   }
 #endif
 
   uint64_t cap_value = 0;
-  if (drmGetCap(fd(), DRM_CAP_ADDFB2_MODIFIERS, &cap_value)) {
+  if (drmGetCap(GetFd(), DRM_CAP_ADDFB2_MODIFIERS, &cap_value) != 0) {
     ALOGW("drmGetCap failed. Fallback to no modifier support.");
     cap_value = 0;
   }
   HasAddFb2ModifiersSupport_ = cap_value != 0;
 
-  drmSetMaster(fd());
-  if (!drmIsMaster(fd())) {
+  drmSetMaster(GetFd());
+  if (drmIsMaster(GetFd()) == 0) {
     ALOGE("DRM/KMS master access required");
-    return std::make_tuple(-EACCES, 0);
+    return -EACCES;
   }
 
-  auto res = MakeDrmModeResUnique(fd());
+  auto res = MakeDrmModeResUnique(GetFd());
   if (!res) {
     ALOGE("Failed to get DrmDevice resources");
-    return std::make_tuple(-ENODEV, 0);
+    return -ENODEV;
   }
 
   min_resolution_ = std::pair<uint32_t, uint32_t>(res->min_width,
@@ -171,264 +89,64 @@
   max_resolution_ = std::pair<uint32_t, uint32_t>(res->max_width,
                                                   res->max_height);
 
-  // Assumes that the primary display will always be in the first
-  // drm_device opened.
-  bool found_primary = num_displays != 0;
-
-  for (int i = 0; !ret && i < res->count_crtcs; ++i) {
-    auto c = MakeDrmModeCrtcUnique(fd(), res->crtcs[i]);
-    if (!c) {
-      ALOGE("Failed to get crtc %d", res->crtcs[i]);
-      ret = -ENODEV;
-      break;
+  for (int i = 0; i < res->count_crtcs; ++i) {
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
+    auto crtc = DrmCrtc::CreateInstance(*this, res->crtcs[i], i);
+    if (crtc) {
+      crtcs_.emplace_back(std::move(crtc));
     }
-
-    std::unique_ptr<DrmCrtc> crtc(new DrmCrtc(this, c.get(), i));
-
-    ret = crtc->Init();
-    if (ret) {
-      ALOGE("Failed to initialize crtc %d", res->crtcs[i]);
-      break;
-    }
-    crtcs_.emplace_back(std::move(crtc));
   }
 
-  std::vector<uint32_t> possible_clones;
-  for (int i = 0; !ret && i < res->count_encoders; ++i) {
-    auto e = MakeDrmModeEncoderUnique(fd(), res->encoders[i]);
-    if (!e) {
-      ALOGE("Failed to get encoder %d", res->encoders[i]);
-      ret = -ENODEV;
-      break;
+  for (int i = 0; i < res->count_encoders; ++i) {
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
+    auto enc = DrmEncoder::CreateInstance(*this, res->encoders[i], i);
+    if (enc) {
+      encoders_.emplace_back(std::move(enc));
     }
-
-    std::vector<DrmCrtc *> possible_crtcs;
-    DrmCrtc *current_crtc = nullptr;
-    for (auto &crtc : crtcs_) {
-      if ((1 << crtc->pipe()) & e->possible_crtcs)
-        possible_crtcs.push_back(crtc.get());
-
-      if (crtc->id() == e->crtc_id)
-        current_crtc = crtc.get();
-    }
-
-    std::unique_ptr<DrmEncoder> enc(
-        new DrmEncoder(e.get(), current_crtc, possible_crtcs));
-    possible_clones.push_back(e->possible_clones);
-
-    encoders_.emplace_back(std::move(enc));
   }
 
-  for (unsigned int i = 0; i < encoders_.size(); i++) {
-    for (unsigned int j = 0; j < encoders_.size(); j++)
-      if (possible_clones[i] & (1 << j))
-        encoders_[i]->AddPossibleClone(encoders_[j].get());
-  }
+  for (int i = 0; i < res->count_connectors; ++i) {
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
+    auto conn = DrmConnector::CreateInstance(*this, res->connectors[i], i);
 
-  for (int i = 0; !ret && i < res->count_connectors; ++i) {
-    auto c = MakeDrmModeConnectorUnique(fd(), res->connectors[i]);
-    if (!c) {
-      ALOGE("Failed to get connector %d", res->connectors[i]);
-      ret = -ENODEV;
-      break;
+    if (!conn) {
+      continue;
     }
 
-    std::vector<DrmEncoder *> possible_encoders;
-    DrmEncoder *current_encoder = nullptr;
-    for (int j = 0; j < c->count_encoders; ++j) {
-      for (auto &encoder : encoders_) {
-        if (encoder->id() == c->encoders[j])
-          possible_encoders.push_back(encoder.get());
-        if (encoder->id() == c->encoder_id)
-          current_encoder = encoder.get();
-      }
-    }
-
-    std::unique_ptr<DrmConnector> conn(
-        new DrmConnector(this, c.get(), current_encoder, possible_encoders));
-
-    ret = conn->Init();
-    if (ret) {
-      ALOGE("Init connector %d failed", res->connectors[i]);
-      break;
-    }
-
-    if (conn->writeback())
+    if (conn->IsWriteback()) {
       writeback_connectors_.emplace_back(std::move(conn));
-    else
+    } else {
       connectors_.emplace_back(std::move(conn));
-  }
-
-  // Primary display priority:
-  // 1) vendor.hwc.drm.primary_display_order property
-  // 2) internal connectors
-  // 3) anything else
-  std::vector<DrmConnector *>
-      primary_candidates = make_primary_display_candidates(connectors_);
-  if (!primary_candidates.empty() && !found_primary) {
-    DrmConnector &conn = **std::begin(primary_candidates);
-    conn.set_display(num_displays);
-    displays_[num_displays] = num_displays;
-    ++num_displays;
-    found_primary = true;
-  } else {
-    ALOGE(
-        "Failed to find primary display from "
-        "\"vendor.hwc.drm.primary_display_order\" property");
-  }
-
-  // If no priority display were found then pick first available as primary and
-  // for the others assign consecutive display_numbers.
-  for (auto &conn : connectors_) {
-    if (conn->external() || conn->internal()) {
-      if (!found_primary) {
-        conn->set_display(num_displays);
-        displays_[num_displays] = num_displays;
-        found_primary = true;
-        ++num_displays;
-      } else if (conn->display() < 0) {
-        conn->set_display(num_displays);
-        displays_[num_displays] = num_displays;
-        ++num_displays;
-      }
     }
   }
 
-  // Catch-all for the above loops
-  if (ret)
-    return std::make_tuple(ret, 0);
-
-  auto plane_res = MakeDrmModePlaneResUnique(fd());
+  auto plane_res = MakeDrmModePlaneResUnique(GetFd());
   if (!plane_res) {
     ALOGE("Failed to get plane resources");
-    return std::make_tuple(-ENOENT, 0);
+    return -ENOENT;
   }
 
   for (uint32_t i = 0; i < plane_res->count_planes; ++i) {
-    auto p = MakeDrmModePlaneUnique(fd(), plane_res->planes[i]);
-    if (!p) {
-      ALOGE("Failed to get plane %d", plane_res->planes[i]);
-      ret = -ENODEV;
-      break;
-    }
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
+    auto plane = DrmPlane::CreateInstance(*this, plane_res->planes[i]);
 
-    std::unique_ptr<DrmPlane> plane(new DrmPlane(this, p.get()));
-
-    ret = plane->Init();
-    if (ret) {
-      ALOGE("Init plane %d failed", plane_res->planes[i]);
-      break;
-    }
-
-    planes_.emplace_back(std::move(plane));
-  }
-  if (ret)
-    return std::make_tuple(ret, 0);
-
-  for (auto &conn : connectors_) {
-    ret = CreateDisplayPipe(conn.get());
-    if (ret) {
-      ALOGE("Failed CreateDisplayPipe %d with %d", conn->id(), ret);
-      return std::make_tuple(ret, 0);
-    }
-  }
-  return std::make_tuple(ret, displays_.size());
-}
-
-bool DrmDevice::HandlesDisplay(int display) const {
-  return displays_.find(display) != displays_.end();
-}
-
-DrmConnector *DrmDevice::GetConnectorForDisplay(int display) const {
-  for (const auto &conn : connectors_) {
-    if (conn->display() == display)
-      return conn.get();
-  }
-  return nullptr;
-}
-
-DrmCrtc *DrmDevice::GetCrtcForDisplay(int display) const {
-  for (const auto &crtc : crtcs_) {
-    if (crtc->display() == display)
-      return crtc.get();
-  }
-  return nullptr;
-}
-
-const std::vector<std::unique_ptr<DrmCrtc>> &DrmDevice::crtcs() const {
-  return crtcs_;
-}
-
-uint32_t DrmDevice::next_mode_id() {
-  return ++mode_id_;
-}
-
-int DrmDevice::TryEncoderForDisplay(int display, DrmEncoder *enc) {
-  /* First try to use the currently-bound crtc */
-  DrmCrtc *crtc = enc->crtc();
-  if (crtc && crtc->can_bind(display)) {
-    crtc->set_display(display);
-    enc->set_crtc(crtc);
-    return 0;
-  }
-
-  /* Try to find a possible crtc which will work */
-  for (DrmCrtc *crtc : enc->possible_crtcs()) {
-    /* We've already tried this earlier */
-    if (crtc == enc->crtc())
-      continue;
-
-    if (crtc->can_bind(display)) {
-      crtc->set_display(display);
-      enc->set_crtc(crtc);
-      return 0;
+    if (plane) {
+      planes_.emplace_back(std::move(plane));
     }
   }
 
-  /* We can't use the encoder, but nothing went wrong, try another one */
-  return -EAGAIN;
-}
-
-int DrmDevice::CreateDisplayPipe(DrmConnector *connector) {
-  int display = connector->display();
-  /* Try to use current setup first */
-  if (connector->encoder()) {
-    int ret = TryEncoderForDisplay(display, connector->encoder());
-    if (!ret) {
-      return 0;
-    }
-
-    if (ret != -EAGAIN) {
-      ALOGE("Could not set mode %d/%d", display, ret);
-      return ret;
-    }
-  }
-
-  for (DrmEncoder *enc : connector->possible_encoders()) {
-    int ret = TryEncoderForDisplay(display, enc);
-    if (!ret) {
-      connector->set_encoder(enc);
-      return 0;
-    }
-
-    if (ret != -EAGAIN) {
-      ALOGE("Could not set mode %d/%d", display, ret);
-      return ret;
-    }
-  }
-  ALOGE("Could not find a suitable encoder/crtc for display %d",
-        connector->display());
-  return -ENODEV;
+  return 0;
 }
 
 auto DrmDevice::RegisterUserPropertyBlob(void *data, size_t length) const
     -> DrmModeUserPropertyBlobUnique {
   struct drm_mode_create_blob create_blob {};
   create_blob.length = length;
+  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
   create_blob.data = (__u64)data;
 
-  int ret = drmIoctl(fd(), DRM_IOCTL_MODE_CREATEPROPBLOB, &create_blob);
-  if (ret) {
+  int ret = drmIoctl(GetFd(), DRM_IOCTL_MODE_CREATEPROPBLOB, &create_blob);
+  if (ret != 0) {
     ALOGE("Failed to create mode property blob %d", ret);
     return {};
   }
@@ -437,7 +155,8 @@
       new uint32_t(create_blob.blob_id), [this](const uint32_t *it) {
         struct drm_mode_destroy_blob destroy_blob {};
         destroy_blob.blob_id = (__u32)*it;
-        int err = drmIoctl(fd(), DRM_IOCTL_MODE_DESTROYPROPBLOB, &destroy_blob);
+        int err = drmIoctl(GetFd(), DRM_IOCTL_MODE_DESTROYPROPBLOB,
+                           &destroy_blob);
         if (err != 0) {
           ALOGE("Failed to destroy mode property blob %" PRIu32 "/%d", *it,
                 err);
@@ -451,16 +170,18 @@
                            const char *prop_name, DrmProperty *property) const {
   drmModeObjectPropertiesPtr props = nullptr;
 
-  props = drmModeObjectGetProperties(fd(), obj_id, obj_type);
-  if (!props) {
+  props = drmModeObjectGetProperties(GetFd(), obj_id, obj_type);
+  if (props == nullptr) {
     ALOGE("Failed to get properties for %d/%x", obj_id, obj_type);
     return -ENODEV;
   }
 
   bool found = false;
   for (int i = 0; !found && (size_t)i < props->count_props; ++i) {
-    drmModePropertyPtr p = drmModeGetProperty(fd(), props->props[i]);
-    if (!strcmp(p->name, prop_name)) {
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
+    drmModePropertyPtr p = drmModeGetProperty(GetFd(), props->props[i]);
+    if (strcmp(p->name, prop_name) == 0) {
+      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
       property->Init(obj_id, p, props->prop_values[i]);
       found = true;
     }
@@ -471,22 +192,10 @@
   return found ? 0 : -ENOENT;
 }
 
-int DrmDevice::GetCrtcProperty(const DrmCrtc &crtc, const char *prop_name,
-                               DrmProperty *property) const {
-  return GetProperty(crtc.id(), DRM_MODE_OBJECT_CRTC, prop_name, property);
-}
-
-int DrmDevice::GetConnectorProperty(const DrmConnector &connector,
-                                    const char *prop_name,
-                                    DrmProperty *property) const {
-  return GetProperty(connector.id(), DRM_MODE_OBJECT_CONNECTOR, prop_name,
-                     property);
-}
-
 std::string DrmDevice::GetName() const {
-  auto *ver = drmGetVersion(fd());
-  if (!ver) {
-    ALOGW("Failed to get drm version for fd=%d", fd());
+  auto *ver = drmGetVersion(GetFd());
+  if (ver == nullptr) {
+    ALOGW("Failed to get drm version for fd=%d", GetFd());
     return "generic";
   }
 
@@ -512,4 +221,22 @@
   return is_kms;
 }
 
+auto DrmDevice::GetConnectors()
+    -> const std::vector<std::unique_ptr<DrmConnector>> & {
+  return connectors_;
+}
+
+auto DrmDevice::GetPlanes() -> const std::vector<std::unique_ptr<DrmPlane>> & {
+  return planes_;
+}
+
+auto DrmDevice::GetCrtcs() -> const std::vector<std::unique_ptr<DrmCrtc>> & {
+  return crtcs_;
+}
+
+auto DrmDevice::GetEncoders()
+    -> const std::vector<std::unique_ptr<DrmEncoder>> & {
+  return encoders_;
+}
+
 }  // namespace android
diff --git a/drm/DrmDevice.h b/drm/DrmDevice.h
index 9983d61..f2530ee 100644
--- a/drm/DrmDevice.h
+++ b/drm/DrmDevice.h
@@ -37,66 +37,65 @@
   DrmDevice();
   ~DrmDevice() = default;
 
-  std::tuple<int, int> Init(const char *path, int num_displays);
+  auto Init(const char *path) -> int;
 
-  int fd() const {
+  auto GetFd() const {
     return fd_.Get();
   }
 
-  const std::vector<std::unique_ptr<DrmConnector>> &connectors() const {
-    return connectors_;
-  }
+  auto GetConnectors() -> 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>> &;
 
-  const std::vector<std::unique_ptr<DrmPlane>> &planes() const {
-    return planes_;
-  }
-
-  std::pair<uint32_t, uint32_t> min_resolution() const {
+  auto GetMinResolution() const {
     return min_resolution_;
   }
 
-  std::pair<uint32_t, uint32_t> max_resolution() const {
+  auto GetMaxResolution() const {
     return max_resolution_;
   }
 
-  DrmConnector *GetConnectorForDisplay(int display) const;
-  DrmCrtc *GetCrtcForDisplay(int display) const;
-
-  int GetCrtcProperty(const DrmCrtc &crtc, const char *prop_name,
-                      DrmProperty *property) const;
-  int GetConnectorProperty(const DrmConnector &connector, const char *prop_name,
-                           DrmProperty *property) const;
-
   std::string GetName() const;
 
-  const std::vector<std::unique_ptr<DrmCrtc>> &crtcs() const;
-  uint32_t next_mode_id();
-
   auto RegisterUserPropertyBlob(void *data, size_t length) const
       -> DrmModeUserPropertyBlobUnique;
 
-  bool HandlesDisplay(int display) const;
-
-  bool HasAddFb2ModifiersSupport() const {
+  auto HasAddFb2ModifiersSupport() const {
     return HasAddFb2ModifiersSupport_;
   }
 
-  DrmFbImporter &GetDrmFbImporter() {
-    return *mDrmFbImporter;
+  auto &GetDrmFbImporter() {
+    return *drm_fb_importer_;
   }
 
   static auto IsKMSDev(const char *path) -> bool;
 
+  auto FindCrtcById(uint32_t id) const -> DrmCrtc * {
+    for (const auto &crtc : crtcs_) {
+      if (crtc->GetId() == id) {
+        return crtc.get();
+      }
+    };
+
+    return nullptr;
+  }
+
+  auto FindEncoderById(uint32_t id) const -> DrmEncoder * {
+    for (const auto &enc : encoders_) {
+      if (enc->GetId() == id) {
+        return enc.get();
+      }
+    };
+
+    return nullptr;
+  }
+
   int GetProperty(uint32_t obj_id, uint32_t obj_type, const char *prop_name,
                   DrmProperty *property) const;
 
  private:
-  int TryEncoderForDisplay(int display, DrmEncoder *enc);
-
-  int CreateDisplayPipe(DrmConnector *connector);
-
   UniqueFd fd_;
-  uint32_t mode_id_ = 0;
 
   std::vector<std::unique_ptr<DrmConnector>> connectors_;
   std::vector<std::unique_ptr<DrmConnector>> writeback_connectors_;
@@ -106,13 +105,10 @@
 
   std::pair<uint32_t, uint32_t> min_resolution_;
   std::pair<uint32_t, uint32_t> max_resolution_;
-  std::map<int, int> displays_;
 
   bool HasAddFb2ModifiersSupport_{};
 
-  std::shared_ptr<DrmDevice> self;
-
-  std::unique_ptr<DrmFbImporter> mDrmFbImporter;
+  std::unique_ptr<DrmFbImporter> drm_fb_importer_;
 };
 }  // namespace android
 
diff --git a/drm/DrmDisplayPipeline.cpp b/drm/DrmDisplayPipeline.cpp
new file mode 100644
index 0000000..f993d28
--- /dev/null
+++ b/drm/DrmDisplayPipeline.cpp
@@ -0,0 +1,192 @@
+/*
+ * 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 "DrmAtomicStateManager.h"
+#include "DrmConnector.h"
+#include "DrmCrtc.h"
+#include "DrmDevice.h"
+#include "DrmEncoder.h"
+#include "DrmPlane.h"
+#include "utils/log.h"
+#include "utils/properties.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));
+
+  owner_object_ = owner_object;
+  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 {};
+  }
+
+  pipe->atomic_state_manager = std::make_unique<DrmAtomicStateManager>(
+      pipe.get());
+
+  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 {};
+}
+
+static bool ReadUseOverlayProperty() {
+  char use_overlay_planes_prop[PROPERTY_VALUE_MAX];
+  property_get("vendor.hwc.drm.use_overlay_planes", use_overlay_planes_prop,
+               "1");
+  constexpr int kStrtolBase = 10;
+  return strtol(use_overlay_planes_prop, nullptr, kStrtolBase) != 0;
+}
+
+auto DrmDisplayPipeline::GetUsablePlanes()
+    -> std::vector<std::shared_ptr<BindingOwner<DrmPlane>>> {
+  std::vector<std::shared_ptr<BindingOwner<DrmPlane>>> planes;
+  planes.emplace_back(primary_plane);
+
+  static bool use_overlay_planes = ReadUseOverlayProperty();
+
+  if (use_overlay_planes) {
+    for (const auto &plane : device->GetPlanes()) {
+      if (plane->IsCrtcSupported(*crtc->Get())) {
+        if (plane->GetType() == DRM_PLANE_TYPE_OVERLAY) {
+          auto op = plane->BindPipeline(this, true);
+          if (op) {
+            planes.emplace_back(op);
+          }
+        }
+      }
+    }
+  }
+
+  return planes;
+}
+
+}  // namespace android
diff --git a/drm/DrmDisplayPipeline.h b/drm/DrmDisplayPipeline.h
new file mode 100644
index 0000000..7ec619e
--- /dev/null
+++ b/drm/DrmDisplayPipeline.h
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_DRMDISPLAYPIPELINE_H_
+#define ANDROID_DRMDISPLAYPIPELINE_H_
+
+#include <memory>
+#include <vector>
+
+namespace android {
+
+class DrmConnector;
+class DrmDevice;
+class DrmPlane;
+class DrmCrtc;
+class DrmEncoder;
+class DrmAtomicStateManager;
+
+struct DrmDisplayPipeline;
+
+template <class O>
+class BindingOwner;
+
+template <class O>
+class PipelineBindable {
+  friend class BindingOwner<O>;
+
+ public:
+  auto *GetPipeline() {
+    return bound_pipeline_;
+  }
+
+  auto BindPipeline(DrmDisplayPipeline *pipeline,
+                    bool return_object_if_bound = false)
+      -> std::shared_ptr<BindingOwner<O>>;
+
+ private:
+  DrmDisplayPipeline *bound_pipeline_;
+  std::weak_ptr<BindingOwner<O>> owner_object_;
+};
+
+template <class B>
+class BindingOwner {
+ public:
+  explicit BindingOwner(B *pb) : bindable_(pb){};
+  ~BindingOwner() {
+    bindable_->bound_pipeline_ = nullptr;
+  }
+
+  B *Get() {
+    return bindable_;
+  }
+
+ private:
+  B *const bindable_;
+};
+
+struct DrmDisplayPipeline {
+  static auto CreatePipeline(DrmConnector &connector)
+      -> std::unique_ptr<DrmDisplayPipeline>;
+
+  auto GetUsablePlanes()
+      -> std::vector<std::shared_ptr<BindingOwner<DrmPlane>>>;
+
+  DrmDevice *device;
+
+  std::shared_ptr<BindingOwner<DrmConnector>> connector;
+  std::shared_ptr<BindingOwner<DrmEncoder>> encoder;
+  std::shared_ptr<BindingOwner<DrmCrtc>> crtc;
+  std::shared_ptr<BindingOwner<DrmPlane>> primary_plane;
+
+  std::unique_ptr<DrmAtomicStateManager> atomic_state_manager;
+};
+
+}  // namespace android
+
+#endif
diff --git a/drm/DrmEncoder.cpp b/drm/DrmEncoder.cpp
index ad05f88..eed5b5f 100644
--- a/drm/DrmEncoder.cpp
+++ b/drm/DrmEncoder.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "hwc-drm-encoder"
+
 #include "DrmEncoder.h"
 
 #include <xf86drmMode.h>
@@ -21,43 +23,19 @@
 #include <cstdint>
 
 #include "DrmDevice.h"
+#include "utils/log.h"
 
 namespace android {
 
-DrmEncoder::DrmEncoder(drmModeEncoderPtr e, DrmCrtc *current_crtc,
-                       std::vector<DrmCrtc *> possible_crtcs)
-    : id_(e->encoder_id),
-      crtc_(current_crtc),
-      display_(-1),
-      possible_crtcs_(std::move(possible_crtcs)) {
+auto DrmEncoder::CreateInstance(DrmDevice &dev, uint32_t encoder_id,
+                                uint32_t index) -> std::unique_ptr<DrmEncoder> {
+  auto e = MakeDrmModeEncoderUnique(dev.GetFd(), encoder_id);
+  if (!e) {
+    ALOGE("Failed to get encoder %d", encoder_id);
+    return {};
+  }
+
+  return std::unique_ptr<DrmEncoder>(new DrmEncoder(std::move(e), index));
 }
 
-uint32_t DrmEncoder::id() const {
-  return id_;
-}
-
-DrmCrtc *DrmEncoder::crtc() const {
-  return crtc_;
-}
-
-bool DrmEncoder::CanClone(DrmEncoder *encoder) {
-  return possible_clones_.find(encoder) != possible_clones_.end();
-}
-
-void DrmEncoder::AddPossibleClone(DrmEncoder *possible_clone) {
-  possible_clones_.insert(possible_clone);
-}
-
-void DrmEncoder::set_crtc(DrmCrtc *crtc) {
-  crtc_ = crtc;
-  display_ = crtc->display();
-}
-
-int DrmEncoder::display() const {
-  return display_;
-}
-
-bool DrmEncoder::can_bind(int display) const {
-  return display_ == -1 || display_ == display;
-}
 }  // namespace android
diff --git a/drm/DrmEncoder.h b/drm/DrmEncoder.h
index b130b7d..39a695c 100644
--- a/drm/DrmEncoder.h
+++ b/drm/DrmEncoder.h
@@ -24,36 +24,45 @@
 #include <vector>
 
 #include "DrmCrtc.h"
+#include "DrmDisplayPipeline.h"
 
 namespace android {
 
-class DrmEncoder {
+class DrmEncoder : public PipelineBindable<DrmEncoder> {
  public:
-  DrmEncoder(drmModeEncoderPtr e, DrmCrtc *current_crtc,
-             std::vector<DrmCrtc *> possible_crtcs);
+  static auto CreateInstance(DrmDevice &dev, uint32_t encoder_id,
+                             uint32_t index) -> std::unique_ptr<DrmEncoder>;
+
   DrmEncoder(const DrmEncoder &) = delete;
   DrmEncoder &operator=(const DrmEncoder &) = delete;
 
-  uint32_t id() const;
+  auto GetId() const {
+    return enc_->encoder_id;
+  };
 
-  DrmCrtc *crtc() const;
-  void set_crtc(DrmCrtc *crtc);
-  bool can_bind(int display) const;
-  int display() const;
-
-  const std::vector<DrmCrtc *> &possible_crtcs() const {
-    return possible_crtcs_;
+  auto GetIndexInResArray() const {
+    return index_in_res_array_;
   }
-  bool CanClone(DrmEncoder *encoder);
-  void AddPossibleClone(DrmEncoder *possible_clone);
+
+  auto CanClone(DrmEncoder &encoder) {
+    return (enc_->possible_clones & (1 << encoder.GetIndexInResArray())) != 0;
+  }
+
+  auto SupportsCrtc(DrmCrtc &crtc) {
+    return (enc_->possible_crtcs & (1 << crtc.GetIndexInResArray())) != 0;
+  }
+
+  auto GetCurrentCrtcId() {
+    return enc_->crtc_id;
+  }
 
  private:
-  uint32_t id_;
-  DrmCrtc *crtc_;
-  int display_;
+  DrmEncoder(DrmModeEncoderUnique enc, uint32_t index)
+      : enc_(std::move(enc)), index_in_res_array_(index){};
 
-  std::vector<DrmCrtc *> possible_crtcs_;
-  std::set<DrmEncoder *> possible_clones_;
+  DrmModeEncoderUnique enc_;
+
+  const uint32_t index_in_res_array_;
 };
 }  // namespace android
 
diff --git a/drm/DrmFbImporter.cpp b/drm/DrmFbImporter.cpp
index 6f2abe8..eeab076 100644
--- a/drm/DrmFbImporter.cpp
+++ b/drm/DrmFbImporter.cpp
@@ -32,7 +32,7 @@
 namespace android {
 
 auto DrmFbIdHandle::CreateInstance(hwc_drm_bo_t *bo, GemHandle first_gem_handle,
-                                   const std::shared_ptr<DrmDevice> &drm)
+                                   DrmDevice &drm)
     -> std::shared_ptr<DrmFbIdHandle> {
   // NOLINTNEXTLINE(cppcoreguidelines-owning-memory): priv. constructor usage
   std::shared_ptr<DrmFbIdHandle> local(new DrmFbIdHandle(drm));
@@ -44,7 +44,7 @@
   for (size_t i = 1; i < local->gem_handles_.size(); i++) {
     if (bo->prime_fds[i] > 0) {
       if (bo->prime_fds[i] != bo->prime_fds[0]) {
-        err = drmPrimeFDToHandle(drm->fd(), bo->prime_fds[i],
+        err = drmPrimeFDToHandle(drm.GetFd(), bo->prime_fds[i],
                                  &local->gem_handles_.at(i));
         if (err != 0) {
           ALOGE("failed to import prime fd %d errno=%d", bo->prime_fds[i],
@@ -59,7 +59,7 @@
   bool has_modifiers = bo->modifiers[0] != DRM_FORMAT_MOD_NONE &&
                        bo->modifiers[0] != DRM_FORMAT_MOD_INVALID;
 
-  if (!drm->HasAddFb2ModifiersSupport() && has_modifiers) {
+  if (!drm.HasAddFb2ModifiersSupport() && has_modifiers) {
     ALOGE("No ADDFB2 with modifier support. Can't import modifier %" PRIu64,
           bo->modifiers[0]);
     local.reset();
@@ -68,11 +68,11 @@
 
   /* Create framebuffer object */
   if (!has_modifiers) {
-    err = drmModeAddFB2(drm->fd(), bo->width, bo->height, bo->format,
+    err = drmModeAddFB2(drm.GetFd(), bo->width, bo->height, bo->format,
                         &local->gem_handles_[0], &bo->pitches[0],
                         &bo->offsets[0], &local->fb_id_, 0);
   } else {
-    err = drmModeAddFB2WithModifiers(drm->fd(), bo->width, bo->height,
+    err = drmModeAddFB2WithModifiers(drm.GetFd(), bo->width, bo->height,
                                      bo->format, &local->gem_handles_[0],
                                      &bo->pitches[0], &bo->offsets[0],
                                      &bo->modifiers[0], &local->fb_id_,
@@ -88,7 +88,7 @@
 
 DrmFbIdHandle::~DrmFbIdHandle() {
   /* Destroy framebuffer object */
-  if (drmModeRmFB(drm_->fd(), fb_id_) != 0) {
+  if (drmModeRmFB(drm_->GetFd(), fb_id_) != 0) {
     ALOGE("Failed to rm fb");
   }
 
@@ -109,7 +109,7 @@
       continue;
     }
     gem_close.handle = gem_handles_[i];
-    int32_t err = drmIoctl(drm_->fd(), DRM_IOCTL_GEM_CLOSE, &gem_close);
+    int32_t err = drmIoctl(drm_->GetFd(), DRM_IOCTL_GEM_CLOSE, &gem_close);
     if (err != 0) {
       ALOGE("Failed to close gem handle %d, errno: %d", gem_handles_[i], errno);
     }
@@ -120,7 +120,8 @@
     -> std::shared_ptr<DrmFbIdHandle> {
   /* Lookup DrmFbIdHandle in cache first. First handle serves as a cache key. */
   GemHandle first_handle = 0;
-  int32_t err = drmPrimeFDToHandle(drm_->fd(), bo->prime_fds[0], &first_handle);
+  int32_t err = drmPrimeFDToHandle(drm_->GetFd(), bo->prime_fds[0],
+                                   &first_handle);
 
   if (err != 0) {
     ALOGE("Failed to import prime fd %d ret=%d", bo->prime_fds[0], err);
@@ -143,7 +144,7 @@
   }
 
   /* No DrmFbIdHandle found in cache, create framebuffer object */
-  auto fb_id_handle = DrmFbIdHandle::CreateInstance(bo, first_handle, drm_);
+  auto fb_id_handle = DrmFbIdHandle::CreateInstance(bo, first_handle, *drm_);
   if (fb_id_handle) {
     drm_fb_id_handle_cache_[first_handle] = fb_id_handle;
   }
diff --git a/drm/DrmFbImporter.h b/drm/DrmFbImporter.h
index efeb457..7f17bbe 100644
--- a/drm/DrmFbImporter.h
+++ b/drm/DrmFbImporter.h
@@ -37,8 +37,7 @@
 class DrmFbIdHandle {
  public:
   static auto CreateInstance(hwc_drm_bo_t *bo, GemHandle first_gem_handle,
-                             const std::shared_ptr<DrmDevice> &drm)
-      -> std::shared_ptr<DrmFbIdHandle>;
+                             DrmDevice &drm) -> std::shared_ptr<DrmFbIdHandle>;
 
   ~DrmFbIdHandle();
   DrmFbIdHandle(DrmFbIdHandle &&) = delete;
@@ -51,10 +50,9 @@
   }
 
  private:
-  explicit DrmFbIdHandle(std::shared_ptr<DrmDevice> drm)
-      : drm_(std::move(drm)){};
+  explicit DrmFbIdHandle(DrmDevice &drm) : drm_(&drm){};
 
-  const std::shared_ptr<DrmDevice> drm_;
+  DrmDevice *const drm_;
 
   uint32_t fb_id_{};
   std::array<GemHandle, kHwcDrmBoMaxPlanes> gem_handles_{};
@@ -62,8 +60,7 @@
 
 class DrmFbImporter {
  public:
-  explicit DrmFbImporter(std::shared_ptr<DrmDevice> drm)
-      : drm_(std::move(drm)){};
+  explicit DrmFbImporter(DrmDevice &drm) : drm_(&drm){};
   ~DrmFbImporter() = default;
   DrmFbImporter(const DrmFbImporter &) = delete;
   DrmFbImporter(DrmFbImporter &&) = delete;
@@ -84,7 +81,7 @@
     }
   }
 
-  const std::shared_ptr<DrmDevice> drm_;
+  DrmDevice *const drm_;
 
   std::map<GemHandle, std::weak_ptr<DrmFbIdHandle>> drm_fb_id_handle_cache_;
 };
diff --git a/drm/DrmPlane.cpp b/drm/DrmPlane.cpp
index 25a4902..28f48f3 100644
--- a/drm/DrmPlane.cpp
+++ b/drm/DrmPlane.cpp
@@ -29,15 +29,28 @@
 
 namespace android {
 
-DrmPlane::DrmPlane(DrmDevice *drm, drmModePlanePtr p)
-    : drm_(drm),
-      id_(p->plane_id),
-      possible_crtc_mask_(p->possible_crtcs),
-      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
-      formats_(p->formats, p->formats + p->count_formats) {
+auto DrmPlane::CreateInstance(DrmDevice &dev, uint32_t plane_id)
+    -> std::unique_ptr<DrmPlane> {
+  auto p = MakeDrmModePlaneUnique(dev.GetFd(), plane_id);
+  if (!p) {
+    ALOGE("Failed to get plane %d", plane_id);
+    return {};
+  }
+
+  auto plane = std::unique_ptr<DrmPlane>(new DrmPlane(dev, std::move(p)));
+
+  if (plane->Init() != 0) {
+    ALOGE("Failed to init plane %d", plane_id);
+    return {};
+  }
+
+  return plane;
 }
 
 int DrmPlane::Init() {
+  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
+  formats_ = {plane_->formats, plane_->formats + plane_->count_formats};
+
   DrmProperty p;
 
   if (!GetPlaneProperty("type", p)) {
@@ -134,38 +147,38 @@
   return 0;
 }
 
-bool DrmPlane::GetCrtcSupported(const DrmCrtc &crtc) const {
-  return ((1 << crtc.pipe()) & possible_crtc_mask_) != 0;
+bool DrmPlane::IsCrtcSupported(const DrmCrtc &crtc) const {
+  return ((1 << crtc.GetIndexInResArray()) & plane_->possible_crtcs) != 0;
 }
 
 bool DrmPlane::IsValidForLayer(DrmHwcLayer *layer) {
   if (!rotation_property_) {
     if (layer->transform != DrmHwcTransform::kIdentity) {
-      ALOGV("No rotation property on plane %d", id_);
+      ALOGV("No rotation property on plane %d", GetId());
       return false;
     }
   } else {
     if (transform_enum_map_.count(layer->transform) == 0) {
-      ALOGV("Transform is not supported on plane %d", id_);
+      ALOGV("Transform is not supported on plane %d", GetId());
       return false;
     }
   }
 
   if (alpha_property_.id() == 0 && layer->alpha != UINT16_MAX) {
-    ALOGV("Alpha is not supported on plane %d", id_);
+    ALOGV("Alpha is not supported on plane %d", GetId());
     return false;
   }
 
   if (blending_enum_map_.count(layer->blending) == 0 &&
       layer->blending != DrmHwcBlending::kNone &&
       layer->blending != DrmHwcBlending::kPreMult) {
-    ALOGV("Blending is not supported on plane %d", id_);
+    ALOGV("Blending is not supported on plane %d", GetId());
     return false;
   }
 
   uint32_t format = layer->buffer_info.format;
   if (!IsFormatSupported(format)) {
-    ALOGV("Plane %d does not supports %c%c%c%c format", id_, format,
+    ALOGV("Plane %d does not supports %c%c%c%c format", GetId(), format,
           format >> 8, format >> 16, format >> 24);
     return false;
   }
@@ -173,10 +186,6 @@
   return true;
 }
 
-uint32_t DrmPlane::GetType() const {
-  return type_;
-}
-
 bool DrmPlane::IsFormatSupported(uint32_t format) const {
   return std::find(std::begin(formats_), std::end(formats_), format) !=
          std::end(formats_);
@@ -290,20 +299,17 @@
   return 0;
 }
 
-const DrmProperty &DrmPlane::GetZPosProperty() const {
-  return zpos_property_;
-}
-
 auto DrmPlane::GetPlaneProperty(const char *prop_name, DrmProperty &property,
                                 Presence presence) -> bool {
-  int err = drm_->GetProperty(id_, DRM_MODE_OBJECT_PLANE, prop_name, &property);
+  int err = drm_->GetProperty(GetId(), DRM_MODE_OBJECT_PLANE, prop_name,
+                              &property);
   if (err != 0) {
     if (presence == Presence::kMandatory) {
       ALOGE("Could not get mandatory property \"%s\" from plane %d", prop_name,
-            id_);
+            GetId());
     } else {
       ALOGV("Could not get optional property \"%s\" from plane %d", prop_name,
-            id_);
+            GetId());
     }
     return false;
   }
diff --git a/drm/DrmPlane.h b/drm/DrmPlane.h
index e1ee920..65f458f 100644
--- a/drm/DrmPlane.h
+++ b/drm/DrmPlane.h
@@ -31,18 +31,20 @@
 class DrmDevice;
 struct DrmHwcLayer;
 
-class DrmPlane {
+class DrmPlane : public PipelineBindable<DrmPlane> {
  public:
-  DrmPlane(DrmDevice *drm, drmModePlanePtr p);
   DrmPlane(const DrmPlane &) = delete;
   DrmPlane &operator=(const DrmPlane &) = delete;
 
-  int Init();
+  static auto CreateInstance(DrmDevice &dev, uint32_t plane_id)
+      -> std::unique_ptr<DrmPlane>;
 
-  bool GetCrtcSupported(const DrmCrtc &crtc) const;
+  bool IsCrtcSupported(const DrmCrtc &crtc) const;
   bool IsValidForLayer(DrmHwcLayer *layer);
 
-  uint32_t GetType() const;
+  auto GetType() const {
+    return type_;
+  }
 
   bool IsFormatSupported(uint32_t format) const;
   bool HasNonRgbFormat() const;
@@ -50,19 +52,26 @@
   auto AtomicSetState(drmModeAtomicReq &pset, DrmHwcLayer &layer, uint32_t zpos,
                       uint32_t crtc_id) -> int;
   auto AtomicDisablePlane(drmModeAtomicReq &pset) -> int;
-  const DrmProperty &GetZPosProperty() const;
+  auto &GetZPosProperty() const {
+    return zpos_property_;
+  }
+
+  auto GetId() const {
+    return plane_->plane_id;
+  }
 
  private:
-  DrmDevice *drm_;
-  uint32_t id_;
+  DrmPlane(DrmDevice &dev, DrmModePlaneUnique plane)
+      : drm_(&dev), plane_(std::move(plane)){};
+  DrmDevice *const drm_;
+  DrmModePlaneUnique plane_;
 
   enum class Presence { kOptional, kMandatory };
 
+  auto Init() -> int;
   auto GetPlaneProperty(const char *prop_name, DrmProperty &property,
                         Presence presence = Presence::kMandatory) -> bool;
 
-  uint32_t possible_crtc_mask_;
-
   uint32_t type_{};
 
   std::vector<uint32_t> formats_;
diff --git a/drm/ResourceManager.cpp b/drm/ResourceManager.cpp
index 46f77e4..c8235e9 100644
--- a/drm/ResourceManager.cpp
+++ b/drm/ResourceManager.cpp
@@ -21,35 +21,47 @@
 #include <fcntl.h>
 #include <sys/stat.h>
 
+#include <ctime>
 #include <sstream>
 
 #include "bufferinfo/BufferInfoGetter.h"
+#include "drm/DrmAtomicStateManager.h"
 #include "drm/DrmDevice.h"
+#include "drm/DrmDisplayPipeline.h"
 #include "drm/DrmPlane.h"
 #include "utils/log.h"
 #include "utils/properties.h"
 
 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;
 
@@ -57,49 +69,116 @@
       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;
 }
 
-DrmDevice *ResourceManager::GetDrmDevice(int display) {
-  for (auto &drm : drms_) {
-    if (drm->HandlesDisplay(display))
-      return drm.get();
+auto ResourceManager::GetTimeMonotonicNs() -> int64_t {
+  struct timespec ts {};
+  clock_gettime(CLOCK_MONOTONIC, &ts);
+  constexpr int64_t kNsInSec = 1000000000LL;
+  return int64_t(ts.tv_sec) * kNsInSec + int64_t(ts.tv_nsec);
+}
+
+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
diff --git a/drm/ResourceManager.h b/drm/ResourceManager.h
index f54d682..88ba878 100644
--- a/drm/ResourceManager.h
+++ b/drm/ResourceManager.h
@@ -20,42 +20,50 @@
 #include <cstring>
 
 #include "DrmDevice.h"
+#include "DrmDisplayPipeline.h"
 #include "DrmFbImporter.h"
 #include "UEventListener.h"
 
 namespace android {
 
+class PipelineToFrontendBindingInterface {
+ public:
+  virtual ~PipelineToFrontendBindingInterface() = default;
+  virtual bool BindDisplay(DrmDisplayPipeline *);
+  virtual bool UnbindDisplay(DrmDisplayPipeline *);
+  virtual void FinalizeDisplayBinding();
+};
+
 class ResourceManager {
  public:
-  ResourceManager();
+  explicit ResourceManager(
+      PipelineToFrontendBindingInterface *p2f_bind_interface);
   ResourceManager(const ResourceManager &) = delete;
   ResourceManager &operator=(const ResourceManager &) = delete;
+  ResourceManager(const ResourceManager &&) = delete;
+  ResourceManager &&operator=(const ResourceManager &&) = delete;
   ~ResourceManager();
 
-  int Init();
-  DrmDevice *GetDrmDevice(int display);
-  const std::vector<std::unique_ptr<DrmDevice>> &GetDrmDevices() const {
-    return drms_;
-  }
-  int GetDisplayCount() const {
-    return num_displays_;
-  }
+  void Init();
+
+  void DeInit();
+
   bool ForcedScalingWithGpu() const {
     return scale_with_gpu_;
   }
 
-  UEventListener *GetUEventListener() {
-    return &uevent_listener_;
-  }
-
   auto &GetMainLock() {
     return main_lock_;
   }
 
- private:
-  int AddDrmDevice(std::string const &path);
+  static auto GetTimeMonotonicNs() -> int64_t;
 
-  int num_displays_;
+ private:
+  auto AddDrmDevice(std::string const &path) -> int;
+  auto GetOrderedConnectors() -> std::vector<DrmConnector *>;
+  void UpdateFrontendDisplays();
+  void DetachAllFrontendDisplays();
+
   std::vector<std::unique_ptr<DrmDevice>> drms_;
 
   bool scale_with_gpu_{};
@@ -63,6 +71,13 @@
   UEventListener uevent_listener_;
 
   std::mutex main_lock_;
+
+  std::map<DrmConnector *, std::unique_ptr<DrmDisplayPipeline>>
+      attached_pipelines_;
+
+  PipelineToFrontendBindingInterface *const frontend_interface_;
+
+  bool initialized_{};
 };
 }  // namespace android
 
diff --git a/drm/UEventListener.cpp b/drm/UEventListener.cpp
index 44c503d..b56b8e1 100644
--- a/drm/UEventListener.cpp
+++ b/drm/UEventListener.cpp
@@ -18,82 +18,43 @@
 
 #include "UEventListener.h"
 
-#include <linux/netlink.h>
-#include <sys/socket.h>
-#include <xf86drm.h>
-
-#include <cassert>
 #include <cerrno>
-#include <cstring>
 
 #include "utils/log.h"
 
-/* Originally defined in system/core/libsystem/include/system/graphics.h */
-#define HAL_PRIORITY_URGENT_DISPLAY (-8)
+/* Originally defined in system/core/libsystem/include/system/graphics.h as
+ * #define HAL_PRIORITY_URGENT_DISPLAY (-8)*/
+constexpr int kHalPriorityUrgentDisplay = -8;
 
 namespace android {
 
 UEventListener::UEventListener()
-    : Worker("uevent-listener", HAL_PRIORITY_URGENT_DISPLAY){};
+    : Worker("uevent-listener", kHalPriorityUrgentDisplay){};
 
 int UEventListener::Init() {
-  uevent_fd_ = UniqueFd(
-      socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT));
-  if (!uevent_fd_) {
-    // NOLINTNEXTLINE(concurrency-mt-unsafe): Fixme
-    ALOGE("Failed to open uevent socket: %s", strerror(errno));
-    return -errno;
-  }
-
-  struct sockaddr_nl addr {};
-  addr.nl_family = AF_NETLINK;
-  addr.nl_pid = 0;
-  addr.nl_groups = 0xFFFFFFFF;
-
-  int ret = bind(uevent_fd_.Get(), (struct sockaddr *)&addr, sizeof(addr));
-  if (ret) {
-    // NOLINTNEXTLINE(concurrency-mt-unsafe): Fixme
-    ALOGE("Failed to bind uevent socket: %s", strerror(errno));
-    return -errno;
+  uevent_ = UEvent::CreateInstance();
+  if (!uevent_) {
+    return -ENODEV;
   }
 
   return InitWorker();
 }
 
 void UEventListener::Routine() {
-  char buffer[1024];
-  ssize_t ret = 0;
-
   while (true) {
-    ret = read(uevent_fd_.Get(), &buffer, sizeof(buffer));
-    if (ret == 0)
-      return;
+    auto uevent_str = uevent_->ReadNext();
 
-    if (ret < 0) {
-      ALOGE("Got error reading uevent %zd", ret);
-      return;
-    }
-
-    if (!hotplug_handler_)
+    if (!hotplug_handler_ || !uevent_str)
       continue;
 
-    bool drm_event = false;
-    bool hotplug_event = false;
-    for (uint32_t i = 0; i < ret;) {
-      char *event = buffer + i;
-      if (strcmp(event, "DEVTYPE=drm_minor") != 0)
-        drm_event = true;
-      else if (strcmp(event, "HOTPLUG=1") != 0)
-        hotplug_event = true;
+    bool drm_event = uevent_str->find("DEVTYPE=drm_minor") != std::string::npos;
+    bool hotplug_event = uevent_str->find("HOTPLUG=1") != std::string::npos;
 
-      i += strlen(event) + 1;
-    }
-
-    if (drm_event && hotplug_event && hotplug_handler_) {
-      constexpr useconds_t delay_after_uevent_us = 200000;
+    if (drm_event && hotplug_event) {
+      constexpr useconds_t kDelayAfterUeventUs = 200000;
       /* We need some delay to ensure DrmConnector::UpdateModes() will query
        * correct modes list, otherwise at least RPI4 board may report 0 modes */
-      usleep(delay_after_uevent_us);
+      usleep(kDelayAfterUeventUs);
       hotplug_handler_();
     }
   }
diff --git a/drm/UEventListener.h b/drm/UEventListener.h
index 0724443..c8b8582 100644
--- a/drm/UEventListener.h
+++ b/drm/UEventListener.h
@@ -19,7 +19,7 @@
 
 #include <functional>
 
-#include "utils/UniqueFd.h"
+#include "utils/UEvent.h"
 #include "utils/Worker.h"
 
 namespace android {
@@ -39,7 +39,7 @@
   void Routine() override;
 
  private:
-  UniqueFd uevent_fd_;
+  std::unique_ptr<UEvent> uevent_;
 
   std::function<void()> hotplug_handler_;
 };
diff --git a/drm/VSyncWorker.cpp b/drm/VSyncWorker.cpp
index 8d1cb99..8cb4d08 100644
--- a/drm/VSyncWorker.cpp
+++ b/drm/VSyncWorker.cpp
@@ -29,19 +29,12 @@
 
 namespace android {
 
-VSyncWorker::VSyncWorker()
-    : Worker("vsync", HAL_PRIORITY_URGENT_DISPLAY),
-      drm_(nullptr),
-      display_(-1),
-      enabled_(false),
-      last_timestamp_(-1) {
-}
+VSyncWorker::VSyncWorker() : Worker("vsync", HAL_PRIORITY_URGENT_DISPLAY){};
 
-auto VSyncWorker::Init(DrmDevice *drm, int display,
+auto VSyncWorker::Init(DrmDisplayPipeline *pipe,
                        std::function<void(uint64_t /*timestamp*/)> callback)
     -> int {
-  drm_ = drm;
-  display_ = display;
+  pipe_ = pipe;
   callback_ = std::move(callback);
 
   return InitWorker();
@@ -86,12 +79,10 @@
     return ret;
 
   float refresh = 60.0F;  // Default to 60Hz refresh rate
-  DrmConnector *conn = drm_->GetConnectorForDisplay(display_);
-  if (conn && conn->active_mode().v_refresh() != 0.0F)
-    refresh = conn->active_mode().v_refresh();
-  else
-    ALOGW("Vsync worker active with conn=%p refresh=%f\n", conn,
-          conn ? conn->active_mode().v_refresh() : 0.0F);
+  if (pipe_ != nullptr &&
+      pipe_->connector->Get()->GetActiveMode().v_refresh() != 0.0F) {
+    refresh = pipe_->connector->Get()->GetActiveMode().v_refresh();
+  }
 
   int64_t phased_timestamp = GetPhasedVSync(kOneSecondNs /
                                                 static_cast<int>(refresh),
@@ -121,27 +112,26 @@
     }
   }
 
-  int display = display_;
+  auto *pipe = pipe_;
   Unlock();
 
-  DrmCrtc *crtc = drm_->GetCrtcForDisplay(display);
-  if (!crtc) {
-    ALOGE("Failed to get crtc for display");
-    return;
-  }
-  uint32_t high_crtc = (crtc->pipe() << DRM_VBLANK_HIGH_CRTC_SHIFT);
-
-  drmVBlank vblank;
-  memset(&vblank, 0, sizeof(vblank));
-  vblank.request.type = (drmVBlankSeqType)(DRM_VBLANK_RELATIVE |
-                                           (high_crtc &
-                                            DRM_VBLANK_HIGH_CRTC_MASK));
-  vblank.request.sequence = 1;
-
+  ret = -EAGAIN;
   int64_t timestamp = 0;
-  ret = drmWaitVBlank(drm_->fd(), &vblank);
-  if (ret == -EINTR)
-    return;
+  drmVBlank vblank{};
+
+  if (pipe != nullptr) {
+    uint32_t high_crtc = (pipe->crtc->Get()->GetIndexInResArray()
+                          << DRM_VBLANK_HIGH_CRTC_SHIFT);
+
+    vblank.request.type = (drmVBlankSeqType)(DRM_VBLANK_RELATIVE |
+                                             (high_crtc &
+                                              DRM_VBLANK_HIGH_CRTC_MASK));
+    vblank.request.sequence = 1;
+
+    ret = drmWaitVBlank(pipe->device->GetFd(), &vblank);
+    if (ret == -EINTR)
+      return;
+  }
 
   if (ret) {
     ret = SyntheticWaitVBlank(&timestamp);
diff --git a/drm/VSyncWorker.h b/drm/VSyncWorker.h
index 67aaa16..1e6d39f 100644
--- a/drm/VSyncWorker.h
+++ b/drm/VSyncWorker.h
@@ -36,7 +36,7 @@
   VSyncWorker();
   ~VSyncWorker() override = default;
 
-  auto Init(DrmDevice *drm, int display,
+  auto Init(DrmDisplayPipeline *pipe,
             std::function<void(uint64_t /*timestamp*/)> callback) -> int;
 
   void VSyncControl(bool enabled);
@@ -48,13 +48,11 @@
   int64_t GetPhasedVSync(int64_t frame_ns, int64_t current) const;
   int SyntheticWaitVBlank(int64_t *timestamp);
 
-  DrmDevice *drm_;
-
   std::function<void(uint64_t /*timestamp*/)> callback_;
 
-  int display_;
-  std::atomic_bool enabled_;
-  int64_t last_timestamp_;
+  DrmDisplayPipeline *pipe_ = nullptr;
+  std::atomic_bool enabled_ = false;
+  int64_t last_timestamp_ = -1;
 };
 }  // namespace android
 
diff --git a/hwc2_device/DrmHwcTwo.cpp b/hwc2_device/DrmHwcTwo.cpp
index a2a093f..e689419 100644
--- a/hwc2_device/DrmHwcTwo.cpp
+++ b/hwc2_device/DrmHwcTwo.cpp
@@ -18,61 +18,117 @@
 
 #include "DrmHwcTwo.h"
 
+#include <cinttypes>
+
 #include "backend/Backend.h"
 #include "utils/log.h"
 
 namespace android {
 
-DrmHwcTwo::DrmHwcTwo() = default;
+DrmHwcTwo::DrmHwcTwo() : resource_manager_(this){};
 
-HWC2::Error DrmHwcTwo::CreateDisplay(hwc2_display_t displ,
-                                     HWC2::DisplayType type) {
-  DrmDevice *drm = resource_manager_.GetDrmDevice(static_cast<int>(displ));
-  if (!drm) {
-    ALOGE("Failed to get a valid drmresource");
-    return HWC2::Error::NoResources;
+/* Must be called after every display attach/detach cycle */
+void DrmHwcTwo::FinalizeDisplayBinding() {
+  if (displays_.count(kPrimaryDisplay) == 0) {
+    /* Primary display MUST always exist */
+    ALOGI("No pipelines available. Creating null-display for headless mode");
+    displays_[kPrimaryDisplay] = std::make_unique<
+        HwcDisplay>(kPrimaryDisplay, HWC2::DisplayType::Physical, this);
+    /* Initializes null-display */
+    displays_[kPrimaryDisplay]->SetPipeline(nullptr);
   }
-  displays_.emplace(std::piecewise_construct, std::forward_as_tuple(displ),
-                    std::forward_as_tuple(&resource_manager_, drm, displ, type,
-                                          this));
 
-  DrmCrtc *crtc = drm->GetCrtcForDisplay(static_cast<int>(displ));
-  if (!crtc) {
-    ALOGE("Failed to get crtc for display %d", static_cast<int>(displ));
-    return HWC2::Error::BadDisplay;
+  if (displays_[kPrimaryDisplay]->IsInHeadlessMode() &&
+      !display_handles_.empty()) {
+    /* Reattach first secondary display to take place of the primary */
+    auto *pipe = display_handles_.begin()->first;
+    ALOGI("Primary display was disconnected, reattaching '%s' as new primary",
+          pipe->connector->Get()->GetName().c_str());
+    UnbindDisplay(pipe);
+    BindDisplay(pipe);
   }
-  auto display_planes = std::vector<DrmPlane *>();
-  for (const auto &plane : drm->planes()) {
-    if (plane->GetCrtcSupported(*crtc))
-      display_planes.push_back(plane.get());
+
+  // Finally, send hotplug events to the client
+  for (auto &dhe : deferred_hotplug_events_) {
+    SendHotplugEventToClient(dhe.first, dhe.second);
   }
-  displays_.at(displ).Init(&display_planes);
-  return HWC2::Error::None;
+  deferred_hotplug_events_.clear();
+
+  /* Wait 0.2s before removing the displays to flush pending HWC2 transactions
+   */
+  auto &mutex = GetResMan().GetMainLock();
+  mutex.unlock();
+  const int kTimeForSFToDisposeDisplayUs = 200000;
+  usleep(kTimeForSFToDisposeDisplayUs);
+  mutex.lock();
+  std::vector<std::unique_ptr<HwcDisplay>> for_disposal;
+  for (auto handle : displays_for_removal_list_) {
+    for_disposal.emplace_back(
+        std::unique_ptr<HwcDisplay>(displays_[handle].release()));
+    displays_.erase(handle);
+  }
+  /* Destroy HwcDisplays while unlocked to avoid vsyncworker deadlocks */
+  mutex.unlock();
+  for_disposal.clear();
+  mutex.lock();
 }
 
-HWC2::Error DrmHwcTwo::Init() {
-  int rv = resource_manager_.Init();
-  if (rv) {
-    ALOGE("Can't initialize the resource manager %d", rv);
-    return HWC2::Error::NoResources;
+bool DrmHwcTwo::BindDisplay(DrmDisplayPipeline *pipeline) {
+  if (display_handles_.count(pipeline) != 0) {
+    ALOGE("%s, pipeline is already used by another display, FIXME!!!: %p",
+          __func__, pipeline);
+    return false;
   }
 
-  HWC2::Error ret = HWC2::Error::None;
-  for (int i = 0; i < resource_manager_.GetDisplayCount(); i++) {
-    ret = CreateDisplay(i, HWC2::DisplayType::Physical);
-    if (ret != HWC2::Error::None) {
-      ALOGE("Failed to create display %d with error %d", i, ret);
-      return ret;
-    }
+  uint32_t disp_handle = kPrimaryDisplay;
+
+  if (displays_.count(kPrimaryDisplay) != 0 &&
+      !displays_[kPrimaryDisplay]->IsInHeadlessMode()) {
+    disp_handle = ++last_display_handle_;
   }
 
-  resource_manager_.GetUEventListener()->RegisterHotplugHandler([this] {
-    const std::lock_guard<std::mutex> lock(GetResMan().GetMainLock());
+  if (displays_.count(disp_handle) == 0) {
+    auto disp = std::make_unique<HwcDisplay>(disp_handle,
+                                             HWC2::DisplayType::Physical, this);
+    displays_[disp_handle] = std::move(disp);
+  }
 
-    HandleHotplugUEvent();
-  });
+  ALOGI("Attaching pipeline '%s' to the display #%d%s",
+        pipeline->connector->Get()->GetName().c_str(), (int)disp_handle,
+        disp_handle == kPrimaryDisplay ? " (Primary)" : "");
 
-  return ret;
+  displays_[disp_handle]->SetPipeline(pipeline);
+  display_handles_[pipeline] = disp_handle;
+
+  return true;
+}
+
+bool DrmHwcTwo::UnbindDisplay(DrmDisplayPipeline *pipeline) {
+  if (display_handles_.count(pipeline) == 0) {
+    ALOGE("%s, can't find the display, pipeline: %p", __func__, pipeline);
+    return false;
+  }
+  auto handle = display_handles_[pipeline];
+  display_handles_.erase(pipeline);
+
+  ALOGI("Detaching the pipeline '%s' from the display #%i%s",
+        pipeline->connector->Get()->GetName().c_str(), (int)handle,
+        handle == kPrimaryDisplay ? " (Primary)" : "");
+
+  if (displays_.count(handle) == 0) {
+    ALOGE("%s, can't find the display, handle: %" PRIu64, __func__, handle);
+    return false;
+  }
+  displays_[handle]->SetPipeline(nullptr);
+
+  /* We must defer display disposal and removal, since it may still have pending
+   * HWC_API calls scheduled and waiting until ueventlistener thread releases
+   * main lock, otherwise transaction may fail and SF may crash
+   */
+  if (handle != kPrimaryDisplay) {
+    displays_for_removal_list_.emplace_back(handle);
+  }
+  return true;
 }
 
 HWC2::Error DrmHwcTwo::CreateVirtualDisplay(uint32_t /*width*/,
@@ -99,8 +155,8 @@
 
   output << "-- drm_hwcomposer --\n\n";
 
-  for (std::pair<const hwc2_display_t, HwcDisplay> &dp : displays_)
-    output << dp.second.Dump();
+  for (auto &disp : displays_)
+    output << disp.second->Dump();
 
   mDumpString = output.str();
   *outSize = static_cast<uint32_t>(mDumpString.size());
@@ -117,9 +173,13 @@
   switch (static_cast<HWC2::Callback>(descriptor)) {
     case HWC2::Callback::Hotplug: {
       hotplug_callback_ = std::make_pair(HWC2_PFN_HOTPLUG(function), data);
-      const auto &drm_devices = resource_manager_.GetDrmDevices();
-      for (const auto &device : drm_devices)
-        HandleInitialHotplugState(device.get());
+      if (function != nullptr) {
+        resource_manager_.Init();
+      } else {
+        resource_manager_.DeInit();
+        /* Headless display may still be here, remove it */
+        displays_.erase(kPrimaryDisplay);
+      }
       break;
     }
     case HWC2::Callback::Refresh: {
@@ -135,6 +195,11 @@
       vsync_2_4_callback_ = std::make_pair(HWC2_PFN_VSYNC_2_4(function), data);
       break;
     }
+    case HWC2::Callback::VsyncPeriodTimingChanged: {
+      period_timing_changed_callback_ = std::
+          make_pair(HWC2_PFN_VSYNC_PERIOD_TIMING_CHANGED(function), data);
+      break;
+    }
 #endif
     default:
       break;
@@ -142,7 +207,8 @@
   return HWC2::Error::None;
 }
 
-void DrmHwcTwo::HandleDisplayHotplug(hwc2_display_t displayid, int state) {
+void DrmHwcTwo::SendHotplugEventToClient(hwc2_display_t displayid,
+                                         bool connected) {
   auto &mutex = GetResMan().GetMainLock();
   if (mutex.try_lock()) {
     ALOGE("FIXME!!!: Main mutex must be locked in %s", __func__);
@@ -157,52 +223,44 @@
      */
     mutex.unlock();
     hc.first(hc.second, displayid,
-             state == DRM_MODE_CONNECTED ? HWC2_CONNECTION_CONNECTED
-                                         : HWC2_CONNECTION_DISCONNECTED);
+             connected == DRM_MODE_CONNECTED ? HWC2_CONNECTION_CONNECTED
+                                             : HWC2_CONNECTION_DISCONNECTED);
     mutex.lock();
   }
 }
 
-void DrmHwcTwo::HandleInitialHotplugState(DrmDevice *drmDevice) {
-  for (const auto &conn : drmDevice->connectors()) {
-    int display_id = conn->display();
-    auto &display = displays_.at(display_id);
-
-    if (conn->state() != DRM_MODE_CONNECTED && !display.IsInHeadlessMode())
-      continue;
-    HandleDisplayHotplug(conn->display(), display.IsInHeadlessMode()
-                                              ? DRM_MODE_CONNECTED
-                                              : conn->state());
+void DrmHwcTwo::SendVsyncEventToClient(
+    hwc2_display_t displayid, int64_t timestamp,
+    [[maybe_unused]] uint32_t vsync_period) const {
+  /* vsync callback */
+#if PLATFORM_SDK_VERSION > 29
+  if (vsync_2_4_callback_.first != nullptr &&
+      vsync_2_4_callback_.second != nullptr) {
+    vsync_2_4_callback_.first(vsync_2_4_callback_.second, displayid, timestamp,
+                              vsync_period);
+  } else
+#endif
+      if (vsync_callback_.first != nullptr &&
+          vsync_callback_.second != nullptr) {
+    vsync_callback_.first(vsync_callback_.second, displayid, timestamp);
   }
 }
 
-void DrmHwcTwo::HandleHotplugUEvent() {
-  for (const auto &drm : resource_manager_.GetDrmDevices()) {
-    for (const auto &conn : drm->connectors()) {
-      drmModeConnection old_state = conn->state();
-      drmModeConnection cur_state = conn->UpdateModes()
-                                        ? DRM_MODE_UNKNOWNCONNECTION
-                                        : conn->state();
-
-      if (cur_state == old_state)
-        continue;
-
-      ALOGI("%s event for connector %u on display %d",
-            cur_state == DRM_MODE_CONNECTED ? "Plug" : "Unplug", conn->id(),
-            conn->display());
-
-      int display_id = conn->display();
-      auto &display = displays_.at(display_id);
-      display.ChosePreferredConfig();
-      if (cur_state != DRM_MODE_CONNECTED) {
-        display.ClearDisplay();
-      }
-
-      HandleDisplayHotplug(display_id, display.IsInHeadlessMode()
-                                           ? DRM_MODE_CONNECTED
-                                           : cur_state);
-    }
+void DrmHwcTwo::SendVsyncPeriodTimingChangedEventToClient(
+    [[maybe_unused]] hwc2_display_t displayid,
+    [[maybe_unused]] int64_t timestamp) const {
+#if PLATFORM_SDK_VERSION > 29
+  hwc_vsync_period_change_timeline_t timeline = {
+      .newVsyncAppliedTimeNanos = timestamp,
+      .refreshRequired = false,
+      .refreshTimeNanos = 0,
+  };
+  if (period_timing_changed_callback_.first != nullptr &&
+      period_timing_changed_callback_.second != nullptr) {
+    period_timing_changed_callback_
+        .first(period_timing_changed_callback_.second, displayid, &timeline);
   }
+#endif
 }
 
 }  // namespace android
diff --git a/hwc2_device/DrmHwcTwo.h b/hwc2_device/DrmHwcTwo.h
index f38ba05..2b8a74f 100644
--- a/hwc2_device/DrmHwcTwo.h
+++ b/hwc2_device/DrmHwcTwo.h
@@ -24,27 +24,20 @@
 
 namespace android {
 
-class DrmHwcTwo {
+class DrmHwcTwo : public PipelineToFrontendBindingInterface {
  public:
   DrmHwcTwo();
-
-  HWC2::Error Init();
+  ~DrmHwcTwo() override = default;
 
   std::pair<HWC2_PFN_HOTPLUG, hwc2_callback_data_t> hotplug_callback_{};
   std::pair<HWC2_PFN_VSYNC, hwc2_callback_data_t> vsync_callback_{};
 #if PLATFORM_SDK_VERSION > 29
   std::pair<HWC2_PFN_VSYNC_2_4, hwc2_callback_data_t> vsync_2_4_callback_{};
+  std::pair<HWC2_PFN_VSYNC_PERIOD_TIMING_CHANGED, hwc2_callback_data_t>
+      period_timing_changed_callback_{};
 #endif
   std::pair<HWC2_PFN_REFRESH, hwc2_callback_data_t> refresh_callback_{};
 
-  static HwcDisplay *GetDisplay(DrmHwcTwo *hwc, hwc2_display_t display_handle) {
-    auto it = hwc->displays_.find(display_handle);
-    if (it == hwc->displays_.end())
-      return nullptr;
-
-    return &it->second;
-  }
-
   // Device functions
   HWC2::Error CreateVirtualDisplay(uint32_t width, uint32_t height,
                                    int32_t *format, hwc2_display_t *display);
@@ -53,22 +46,44 @@
   uint32_t GetMaxVirtualDisplayCount();
   HWC2::Error RegisterCallback(int32_t descriptor, hwc2_callback_data_t data,
                                hwc2_function_pointer_t function);
-  HWC2::Error CreateDisplay(hwc2_display_t displ, HWC2::DisplayType type);
+
+  auto GetDisplay(hwc2_display_t display_handle) {
+    return displays_.count(display_handle) != 0
+               ? displays_[display_handle].get()
+               : nullptr;
+  }
 
   auto &GetResMan() {
     return resource_manager_;
   }
 
- private:
-  void HandleDisplayHotplug(hwc2_display_t displayid, int state);
-  void HandleInitialHotplugState(DrmDevice *drmDevice);
+  void ScheduleHotplugEvent(hwc2_display_t displayid, bool connected) {
+    deferred_hotplug_events_[displayid] = connected;
+  }
 
-  void HandleHotplugUEvent();
+  // PipelineToFrontendBindingInterface
+  bool BindDisplay(DrmDisplayPipeline *pipeline) override;
+  bool UnbindDisplay(DrmDisplayPipeline *pipeline) override;
+  void FinalizeDisplayBinding() override;
+
+  void SendVsyncEventToClient(hwc2_display_t displayid, int64_t timestamp,
+                              uint32_t vsync_period) const;
+  void SendVsyncPeriodTimingChangedEventToClient(hwc2_display_t displayid,
+                                                 int64_t timestamp) const;
+
+ private:
+  void SendHotplugEventToClient(hwc2_display_t displayid, bool connected);
 
   ResourceManager resource_manager_;
-  std::map<hwc2_display_t, HwcDisplay> displays_;
+  std::map<hwc2_display_t, std::unique_ptr<HwcDisplay>> displays_;
+  std::map<DrmDisplayPipeline *, hwc2_display_t> display_handles_;
 
   std::string mDumpString;
+
+  std::map<hwc2_display_t, bool> deferred_hotplug_events_;
+  std::vector<hwc2_display_t> displays_for_removal_list_;
+
+  uint32_t last_display_handle_ = kPrimaryDisplay;
 };
 }  // namespace android
 
diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp
index 8936136..cedac19 100644
--- a/hwc2_device/HwcDisplay.cpp
+++ b/hwc2_device/HwcDisplay.cpp
@@ -20,6 +20,7 @@
 #include "HwcDisplay.h"
 
 #include "DrmHwcTwo.h"
+#include "backend/Backend.h"
 #include "backend/BackendManager.h"
 #include "bufferinfo/BufferInfoGetter.h"
 #include "utils/log.h"
@@ -68,8 +69,12 @@
                              " VSync remains";
   }
 
+  std::string connector_name = IsInHeadlessMode()
+                                   ? "NULL-DISPLAY"
+                                   : GetPipe().connector->Get()->GetName();
+
   std::stringstream ss;
-  ss << "- Display on: " << connector_->name() << "\n"
+  ss << "- Display on: " << connector_name << "\n"
      << "  Flattening state: " << flattening_state_str << "\n"
      << "Statistics since system boot:\n"
      << DumpDelta(total_stats_) << "\n\n"
@@ -80,12 +85,9 @@
   return ss.str();
 }
 
-HwcDisplay::HwcDisplay(ResourceManager *resource_manager, DrmDevice *drm,
-                       hwc2_display_t handle, HWC2::DisplayType type,
+HwcDisplay::HwcDisplay(hwc2_display_t handle, HWC2::DisplayType type,
                        DrmHwcTwo *hwc2)
     : hwc2_(hwc2),
-      resource_manager_(resource_manager),
-      drm_(drm),
       handle_(handle),
       type_(type),
       color_transform_hint_(HAL_COLOR_TRANSFORM_IDENTITY) {
@@ -97,108 +99,72 @@
   // clang-format on
 }
 
-void HwcDisplay::ClearDisplay() {
-  if (IsInHeadlessMode()) {
-    ALOGE("%s: Headless mode, should never reach here: ", __func__);
-    return;
-  }
+HwcDisplay::~HwcDisplay() = default;
 
-  AtomicCommitArgs a_args = {.clear_active_composition = true};
-  compositor_.ExecuteAtomicCommit(a_args);
+void HwcDisplay::SetPipeline(DrmDisplayPipeline *pipeline) {
+  pipeline_ = pipeline;
+
+  if (pipeline != nullptr) {
+    ChosePreferredConfig();
+    Init();
+
+    hwc2_->ScheduleHotplugEvent(handle_, /*connected = */ true);
+  } else {
+    backend_.reset();
+    vsync_worker_.Init(nullptr, [](int64_t) {});
+    SetClientTarget(nullptr, -1, 0, {});
+    if (handle_ != kPrimaryDisplay) {
+      hwc2_->ScheduleHotplugEvent(handle_, /*connected = */ false);
+    }
+  }
 }
 
-HWC2::Error HwcDisplay::Init(std::vector<DrmPlane *> *planes) {
-  int display = static_cast<int>(handle_);
-  int ret = compositor_.Init(resource_manager_, display);
-  if (ret) {
-    ALOGE("Failed display compositor init for display %d (%d)", display, ret);
-    return HWC2::Error::NoResources;
-  }
-
-  // Split up the given display planes into primary and overlay to properly
-  // interface with the composition
-  char use_overlay_planes_prop[PROPERTY_VALUE_MAX];
-  property_get("vendor.hwc.drm.use_overlay_planes", use_overlay_planes_prop,
-               "1");
-  bool use_overlay_planes = strtol(use_overlay_planes_prop, nullptr, 10);
-  for (auto &plane : *planes) {
-    if (plane->GetType() == DRM_PLANE_TYPE_PRIMARY)
-      primary_planes_.push_back(plane);
-    else if (use_overlay_planes && (plane)->GetType() == DRM_PLANE_TYPE_OVERLAY)
-      overlay_planes_.push_back(plane);
-  }
-
-  crtc_ = drm_->GetCrtcForDisplay(display);
-  if (!crtc_) {
-    ALOGE("Failed to get crtc for display %d", display);
-    return HWC2::Error::BadDisplay;
-  }
-
-  connector_ = drm_->GetConnectorForDisplay(display);
-  if (!connector_) {
-    ALOGE("Failed to get connector for display %d", display);
-    return HWC2::Error::BadDisplay;
-  }
-
-  ret = vsync_worker_.Init(drm_, display, [this](int64_t timestamp) {
+HWC2::Error HwcDisplay::Init() {
+  int ret = vsync_worker_.Init(pipeline_, [this](int64_t timestamp) {
     const std::lock_guard<std::mutex> lock(hwc2_->GetResMan().GetMainLock());
-    /* vsync callback */
-#if PLATFORM_SDK_VERSION > 29
-    if (hwc2_->vsync_2_4_callback_.first != nullptr &&
-        hwc2_->vsync_2_4_callback_.second != nullptr) {
-      hwc2_vsync_period_t period_ns{};
+    if (vsync_event_en_) {
+      uint32_t period_ns{};
       GetDisplayVsyncPeriod(&period_ns);
-      hwc2_->vsync_2_4_callback_.first(hwc2_->vsync_2_4_callback_.second,
-                                       handle_, timestamp, period_ns);
-    } else
-#endif
-        if (hwc2_->vsync_callback_.first != nullptr &&
-            hwc2_->vsync_callback_.second != nullptr) {
-      hwc2_->vsync_callback_.first(hwc2_->vsync_callback_.second, handle_,
-                                   timestamp);
+      hwc2_->SendVsyncEventToClient(handle_, timestamp, period_ns);
+    }
+    if (vsync_flattening_en_) {
+      ProcessFlatenningVsyncInternal();
+    }
+    if (vsync_tracking_en_) {
+      last_vsync_ts_ = timestamp;
+    }
+    if (!vsync_event_en_ && !vsync_flattening_en_ && !vsync_tracking_en_) {
+      vsync_worker_.VSyncControl(false);
     }
   });
-  if (ret) {
-    ALOGE("Failed to create event worker for d=%d %d\n", display, ret);
+  if (ret && ret != -EALREADY) {
+    ALOGE("Failed to create event worker for d=%d %d\n", int(handle_), ret);
     return HWC2::Error::BadDisplay;
   }
 
-  ret = flattening_vsync_worker_
-            .Init(drm_, display, [this](int64_t /*timestamp*/) {
-              const std::lock_guard<std::mutex> lock(
-                  hwc2_->GetResMan().GetMainLock());
-              /* Frontend flattening */
-              if (flattenning_state_ >
-                      ClientFlattenningState::ClientRefreshRequested &&
-                  --flattenning_state_ ==
-                      ClientFlattenningState::ClientRefreshRequested &&
-                  hwc2_->refresh_callback_.first != nullptr &&
-                  hwc2_->refresh_callback_.second != nullptr) {
-                hwc2_->refresh_callback_.first(hwc2_->refresh_callback_.second,
-                                               handle_);
-                flattening_vsync_worker_.VSyncControl(false);
-              }
-            });
-  if (ret) {
-    ALOGE("Failed to create event worker for d=%d %d\n", display, ret);
-    return HWC2::Error::BadDisplay;
-  }
-
-  ret = BackendManager::GetInstance().SetBackendForDisplay(this);
-  if (ret) {
-    ALOGE("Failed to set backend for d=%d %d\n", display, ret);
-    return HWC2::Error::BadDisplay;
+  if (!IsInHeadlessMode()) {
+    ret = BackendManager::GetInstance().SetBackendForDisplay(this);
+    if (ret) {
+      ALOGE("Failed to set backend for d=%d %d\n", int(handle_), ret);
+      return HWC2::Error::BadDisplay;
+    }
   }
 
   client_layer_.SetLayerBlendMode(HWC2_BLEND_MODE_PREMULTIPLIED);
 
-  return ChosePreferredConfig();
+  return HWC2::Error::None;
 }
 
 HWC2::Error HwcDisplay::ChosePreferredConfig() {
-  HWC2::Error err = configs_.Update(*connector_);
-  if (!IsInHeadlessMode() && err != HWC2::Error::None)
+  HWC2::Error err{};
+  if (!IsInHeadlessMode()) {
+    err = configs_.Update(*pipeline_->connector->Get());
+  } else {
+    configs_.FillHeadless();
+  }
+  if (!IsInHeadlessMode() && err != HWC2::Error::None) {
     return HWC2::Error::BadDisplay;
+  }
 
   return SetActiveConfig(configs_.preferred_config_id);
 }
@@ -217,18 +183,19 @@
 }
 
 HWC2::Error HwcDisplay::DestroyLayer(hwc2_layer_t layer) {
-  if (!get_layer(layer))
+  if (!get_layer(layer)) {
     return HWC2::Error::BadLayer;
+  }
 
   layers_.erase(layer);
   return HWC2::Error::None;
 }
 
 HWC2::Error HwcDisplay::GetActiveConfig(hwc2_config_t *config) const {
-  if (configs_.hwc_configs.count(configs_.active_config_id) == 0)
+  if (configs_.hwc_configs.count(staged_mode_config_id_) == 0)
     return HWC2::Error::BadConfig;
 
-  *config = configs_.active_config_id;
+  *config = staged_mode_config_id_;
   return HWC2::Error::None;
 }
 
@@ -258,12 +225,13 @@
 HWC2::Error HwcDisplay::GetClientTargetSupport(uint32_t width, uint32_t height,
                                                int32_t /*format*/,
                                                int32_t dataspace) {
-  std::pair<uint32_t, uint32_t> min = drm_->min_resolution();
-  std::pair<uint32_t, uint32_t> max = drm_->max_resolution();
   if (IsInHeadlessMode()) {
     return HWC2::Error::None;
   }
 
+  std::pair<uint32_t, uint32_t> min = pipeline_->device->GetMinResolution();
+  std::pair<uint32_t, uint32_t> max = pipeline_->device->GetMaxResolution();
+
   if (width < min.first || height < min.second)
     return HWC2::Error::Unsupported;
 
@@ -293,7 +261,7 @@
   int conf = static_cast<int>(config);
 
   if (configs_.hwc_configs.count(conf) == 0) {
-    ALOGE("Could not find active mode for %d", conf);
+    ALOGE("Could not find mode #%d", conf);
     return HWC2::Error::BadConfig;
   }
 
@@ -330,7 +298,7 @@
     case HWC2::Attribute::ConfigGroup:
       /* Dispite ConfigGroup is a part of HWC2.4 API, framework
        * able to request it even if service @2.1 is used */
-      *value = hwc_config.group_id;
+      *value = int(hwc_config.group_id);
       break;
 #endif
     default:
@@ -363,7 +331,11 @@
 
 HWC2::Error HwcDisplay::GetDisplayName(uint32_t *size, char *name) {
   std::ostringstream stream;
-  stream << "display-" << connector_->id();
+  if (IsInHeadlessMode()) {
+    stream << "null-display";
+  } else {
+    stream << "display-" << GetPipe().connector->Get()->GetId();
+  }
   std::string string = stream.str();
   size_t length = string.length();
   if (!name) {
@@ -441,6 +413,26 @@
     return HWC2::Error::None;
   }
 
+  int PrevModeVsyncPeriodNs = static_cast<int>(
+      1E9 / GetPipe().connector->Get()->GetActiveMode().v_refresh());
+
+  auto mode_update_commited_ = false;
+  if (staged_mode_ &&
+      staged_mode_change_time_ <= ResourceManager::GetTimeMonotonicNs()) {
+    client_layer_.SetLayerDisplayFrame(
+        (hwc_rect_t){.left = 0,
+                     .top = 0,
+                     .right = static_cast<int>(staged_mode_->h_display()),
+                     .bottom = static_cast<int>(staged_mode_->v_display())});
+
+    configs_.active_config_id = staged_mode_config_id_;
+
+    a_args.display_mode = *staged_mode_;
+    if (!a_args.test_only) {
+      mode_update_commited_ = true;
+    }
+  }
+
   // order the layers by z-order
   bool use_client_layer = false;
   uint32_t client_z_order = UINT32_MAX;
@@ -471,7 +463,7 @@
   for (std::pair<const uint32_t, HwcLayer *> &l : z_map) {
     DrmHwcLayer layer;
     l.second->PopulateDrmLayer(&layer);
-    int ret = layer.ImportBuffer(drm_);
+    int ret = layer.ImportBuffer(GetPipe().device);
     if (ret) {
       ALOGE("Failed to import layer, ret=%d", ret);
       return HWC2::Error::NoResources;
@@ -479,29 +471,21 @@
     composition_layers.emplace_back(std::move(layer));
   }
 
-  auto composition = std::make_shared<DrmDisplayComposition>(crtc_);
-
-  // TODO(nobody): Don't always assume geometry changed
-  int ret = composition->SetLayers(composition_layers.data(),
-                                   composition_layers.size());
-  if (ret) {
-    ALOGE("Failed to set layers in the composition ret=%d", ret);
-    return HWC2::Error::BadLayer;
-  }
-
-  std::vector<DrmPlane *> primary_planes(primary_planes_);
-  std::vector<DrmPlane *> overlay_planes(overlay_planes_);
-  ret = composition->Plan(&primary_planes, &overlay_planes);
-  if (ret) {
-    ALOGV("Failed to plan the composition ret=%d", ret);
+  /* Store plan to ensure shared planes won't be stolen by other display
+   * in between of ValidateDisplay() and PresentDisplay() calls
+   */
+  current_plan_ = DrmKmsPlan::CreateDrmKmsPlan(GetPipe(),
+                                               std::move(composition_layers));
+  if (!current_plan_) {
+    if (!a_args.test_only) {
+      ALOGE("Failed to create DrmKmsPlan");
+    }
     return HWC2::Error::BadConfig;
   }
 
-  a_args.composition = composition;
-  if (staged_mode) {
-    a_args.display_mode = *staged_mode;
-  }
-  ret = compositor_.ExecuteAtomicCommit(a_args);
+  a_args.composition = current_plan_;
+
+  int ret = GetPipe().atomic_state_manager->ExecuteAtomicCommit(a_args);
 
   if (ret) {
     if (!a_args.test_only)
@@ -509,8 +493,13 @@
     return HWC2::Error::BadParameter;
   }
 
-  if (!a_args.test_only) {
-    staged_mode.reset();
+  if (mode_update_commited_) {
+    staged_mode_.reset();
+    vsync_tracking_en_ = false;
+    if (last_vsync_ts_ != 0) {
+      hwc2_->SendVsyncPeriodTimingChangedEventToClient(
+          handle_, last_vsync_ts_ + PrevModeVsyncPeriodNs);
+    }
   }
 
   return HWC2::Error::None;
@@ -548,30 +537,24 @@
   return HWC2::Error::None;
 }
 
-HWC2::Error HwcDisplay::SetActiveConfig(hwc2_config_t config) {
-  int conf = static_cast<int>(config);
-
-  if (configs_.hwc_configs.count(conf) == 0) {
-    ALOGE("Could not find active mode for %d", conf);
+HWC2::Error HwcDisplay::SetActiveConfigInternal(uint32_t config,
+                                                int64_t change_time) {
+  if (configs_.hwc_configs.count(config) == 0) {
+    ALOGE("Could not find active mode for %u", config);
     return HWC2::Error::BadConfig;
   }
 
-  auto &mode = configs_.hwc_configs[conf].mode;
-
-  staged_mode = mode;
-
-  configs_.active_config_id = conf;
-
-  // Setup the client layer's dimensions
-  hwc_rect_t display_frame = {.left = 0,
-                              .top = 0,
-                              .right = static_cast<int>(mode.h_display()),
-                              .bottom = static_cast<int>(mode.v_display())};
-  client_layer_.SetLayerDisplayFrame(display_frame);
+  staged_mode_ = configs_.hwc_configs[config].mode;
+  staged_mode_change_time_ = change_time;
+  staged_mode_config_id_ = config;
 
   return HWC2::Error::None;
 }
 
+HWC2::Error HwcDisplay::SetActiveConfig(hwc2_config_t config) {
+  return SetActiveConfigInternal(config, ResourceManager::GetTimeMonotonicNs());
+}
+
 /* Find API details at:
  * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r3:hardware/libhardware/include/hardware/hwcomposer2.h;l=1861
  */
@@ -656,7 +639,7 @@
        * true, as the next composition frame will implicitly activate
        * the display
        */
-      return compositor_.ActivateDisplayUsingDPMS() == 0
+      return GetPipe().atomic_state_manager->ActivateDisplayUsingDPMS() == 0
                  ? HWC2::Error::None
                  : HWC2::Error::BadParameter;
       break;
@@ -668,7 +651,7 @@
       return HWC2::Error::BadParameter;
   };
 
-  int err = compositor_.ExecuteAtomicCommit(a_args);
+  int err = GetPipe().atomic_state_manager->ExecuteAtomicCommit(a_args);
   if (err) {
     ALOGE("Failed to apply the dpms composition err=%d", err);
     return HWC2::Error::BadParameter;
@@ -677,7 +660,10 @@
 }
 
 HWC2::Error HwcDisplay::SetVsyncEnabled(int32_t enabled) {
-  vsync_worker_.VSyncControl(HWC2_VSYNC_ENABLE == enabled);
+  vsync_event_en_ = HWC2_VSYNC_ENABLE == enabled;
+  if (vsync_event_en_) {
+    vsync_worker_.VSyncControl(true);
+  }
   return HWC2::Error::None;
 }
 
@@ -706,11 +692,25 @@
   return ordered_layers;
 }
 
+HWC2::Error HwcDisplay::GetDisplayVsyncPeriod(
+    uint32_t *outVsyncPeriod /* ns */) {
+  return GetDisplayAttribute(configs_.active_config_id,
+                             HWC2_ATTRIBUTE_VSYNC_PERIOD,
+                             (int32_t *)(outVsyncPeriod));
+}
+
 #if PLATFORM_SDK_VERSION > 29
 HWC2::Error HwcDisplay::GetDisplayConnectionType(uint32_t *outType) {
-  if (connector_->internal())
+  if (IsInHeadlessMode()) {
     *outType = static_cast<uint32_t>(HWC2::DisplayConnectionType::Internal);
-  else if (connector_->external())
+    return HWC2::Error::None;
+  }
+  /* Primary display should be always internal,
+   * otherwise SF will be unhappy and will crash
+   */
+  if (GetPipe().connector->Get()->IsInternal() || handle_ == kPrimaryDisplay)
+    *outType = static_cast<uint32_t>(HWC2::DisplayConnectionType::Internal);
+  else if (GetPipe().connector->Get()->IsExternal())
     *outType = static_cast<uint32_t>(HWC2::DisplayConnectionType::External);
   else
     return HWC2::Error::BadConfig;
@@ -718,22 +718,38 @@
   return HWC2::Error::None;
 }
 
-HWC2::Error HwcDisplay::GetDisplayVsyncPeriod(
-    hwc2_vsync_period_t *outVsyncPeriod /* ns */) {
-  return GetDisplayAttribute(configs_.active_config_id,
-                             HWC2_ATTRIBUTE_VSYNC_PERIOD,
-                             (int32_t *)(outVsyncPeriod));
-}
-
 HWC2::Error HwcDisplay::SetActiveConfigWithConstraints(
-    hwc2_config_t /*config*/,
+    hwc2_config_t config,
     hwc_vsync_period_change_constraints_t *vsyncPeriodChangeConstraints,
     hwc_vsync_period_change_timeline_t *outTimeline) {
   if (vsyncPeriodChangeConstraints == nullptr || outTimeline == nullptr) {
     return HWC2::Error::BadParameter;
   }
 
-  return HWC2::Error::BadConfig;
+  uint32_t current_vsync_period{};
+  GetDisplayVsyncPeriod(&current_vsync_period);
+
+  if (vsyncPeriodChangeConstraints->seamlessRequired) {
+    return HWC2::Error::SeamlessNotAllowed;
+  }
+
+  outTimeline->refreshTimeNanos = vsyncPeriodChangeConstraints
+                                      ->desiredTimeNanos -
+                                  current_vsync_period;
+  auto ret = SetActiveConfigInternal(config, outTimeline->refreshTimeNanos);
+  if (ret != HWC2::Error::None) {
+    return ret;
+  }
+
+  outTimeline->refreshRequired = true;
+  outTimeline->newVsyncAppliedTimeNanos = vsyncPeriodChangeConstraints
+                                              ->desiredTimeNanos;
+
+  last_vsync_ts_ = 0;
+  vsync_tracking_en_ = true;
+  vsync_worker_.VSyncControl(true);
+
+  return HWC2::Error::None;
 }
 
 HWC2::Error HwcDisplay::SetAutoLowLatencyMode(bool /*on*/) {
@@ -765,11 +781,18 @@
 HWC2::Error HwcDisplay::GetDisplayIdentificationData(uint8_t *outPort,
                                                      uint32_t *outDataSize,
                                                      uint8_t *outData) {
-  auto blob = connector_->GetEdidBlob();
+  if (IsInHeadlessMode()) {
+    return HWC2::Error::None;
+  }
+  auto blob = GetPipe().connector->Get()->GetEdidBlob();
+
+  *outPort = handle_ - 1;
 
   if (!blob) {
-    ALOGE("Failed to get edid property value.");
-    return HWC2::Error::Unsupported;
+    if (outData == nullptr) {
+      *outDataSize = 0;
+    }
+    return HWC2::Error::None;
   }
 
   if (outData) {
@@ -778,7 +801,6 @@
   } else {
     *outDataSize = blob->length;
   }
-  *outPort = connector_->id();
 
   return HWC2::Error::None;
 }
@@ -851,4 +873,37 @@
   backend_ = std::move(backend);
 }
 
+/* returns true if composition should be sent to client */
+bool HwcDisplay::ProcessClientFlatteningState(bool skip) {
+  int flattenning_state = flattenning_state_;
+  if (flattenning_state == ClientFlattenningState::Disabled) {
+    return false;
+  }
+
+  if (skip) {
+    flattenning_state_ = ClientFlattenningState::NotRequired;
+    return false;
+  }
+
+  if (flattenning_state == ClientFlattenningState::ClientRefreshRequested) {
+    flattenning_state_ = ClientFlattenningState::Flattened;
+    return true;
+  }
+
+  vsync_flattening_en_ = true;
+  vsync_worker_.VSyncControl(true);
+  flattenning_state_ = ClientFlattenningState::VsyncCountdownMax;
+  return false;
+}
+
+void HwcDisplay::ProcessFlatenningVsyncInternal() {
+  if (flattenning_state_ > ClientFlattenningState::ClientRefreshRequested &&
+      --flattenning_state_ == ClientFlattenningState::ClientRefreshRequested &&
+      hwc2_->refresh_callback_.first != nullptr &&
+      hwc2_->refresh_callback_.second != nullptr) {
+    hwc2_->refresh_callback_.first(hwc2_->refresh_callback_.second, handle_);
+    vsync_flattening_en_ = false;
+  }
+}
+
 }  // namespace android
diff --git a/hwc2_device/HwcDisplay.h b/hwc2_device/HwcDisplay.h
index c3e0f6e..98d8e9b 100644
--- a/hwc2_device/HwcDisplay.h
+++ b/hwc2_device/HwcDisplay.h
@@ -22,7 +22,7 @@
 #include <optional>
 
 #include "HwcDisplayConfigs.h"
-#include "compositor/DrmDisplayCompositor.h"
+#include "drm/DrmAtomicStateManager.h"
 #include "drm/ResourceManager.h"
 #include "drm/VSyncWorker.h"
 #include "drmhwcomposer.h"
@@ -33,12 +33,16 @@
 class Backend;
 class DrmHwcTwo;
 
+inline constexpr uint32_t kPrimaryDisplay = 0;
+
 class HwcDisplay {
  public:
-  HwcDisplay(ResourceManager *resource_manager, DrmDevice *drm,
-             hwc2_display_t handle, HWC2::DisplayType type, DrmHwcTwo *hwc2);
+  HwcDisplay(hwc2_display_t handle, HWC2::DisplayType type, DrmHwcTwo *hwc2);
   HwcDisplay(const HwcDisplay &) = delete;
-  HWC2::Error Init(std::vector<DrmPlane *> *planes);
+  ~HwcDisplay();
+
+  /* SetPipeline should be carefully used only by DrmHwcTwo hotplug handlers */
+  void SetPipeline(DrmDisplayPipeline *pipeline);
 
   HWC2::Error CreateComposition(AtomicCommitArgs &a_args);
   std::vector<HwcLayer *> GetOrderLayersByZPos();
@@ -81,7 +85,6 @@
 #endif
 #if PLATFORM_SDK_VERSION > 29
   HWC2::Error GetDisplayConnectionType(uint32_t *outType);
-  HWC2::Error GetDisplayVsyncPeriod(hwc2_vsync_period_t *outVsyncPeriod);
 
   HWC2::Error SetActiveConfigWithConstraints(
       hwc2_config_t config,
@@ -94,6 +97,7 @@
 
   HWC2::Error SetContentType(int32_t contentType);
 #endif
+  HWC2::Error GetDisplayVsyncPeriod(uint32_t *outVsyncPeriod);
 
   HWC2::Error GetDozeSupport(int32_t *support);
   HWC2::Error GetHdrCapabilities(uint32_t *num_types, int32_t *types,
@@ -142,32 +146,16 @@
   const Backend *backend() const;
   void set_backend(std::unique_ptr<Backend> backend);
 
-  const std::vector<DrmPlane *> &primary_planes() const {
-    return primary_planes_;
-  }
-
-  const std::vector<DrmPlane *> &overlay_planes() const {
-    return overlay_planes_;
+  auto GetHwc2() {
+    return hwc2_;
   }
 
   std::map<hwc2_layer_t, HwcLayer> &layers() {
     return layers_;
   }
 
-  const DrmDisplayCompositor &compositor() const {
-    return compositor_;
-  }
-
-  const DrmDevice *drm() const {
-    return drm_;
-  }
-
-  const DrmConnector *connector() const {
-    return connector_;
-  }
-
-  ResourceManager *resource_manager() const {
-    return resource_manager_;
+  auto &GetPipe() {
+    return *pipeline_;
   }
 
   android_color_transform_t &color_transform_hint() {
@@ -179,26 +167,8 @@
   }
 
   /* returns true if composition should be sent to client */
-  bool ProcessClientFlatteningState(bool skip) {
-    int flattenning_state = flattenning_state_;
-    if (flattenning_state == ClientFlattenningState::Disabled) {
-      return false;
-    }
-
-    if (skip) {
-      flattenning_state_ = ClientFlattenningState::NotRequired;
-      return false;
-    }
-
-    if (flattenning_state == ClientFlattenningState::ClientRefreshRequested) {
-      flattenning_state_ = ClientFlattenningState::Flattened;
-      return true;
-    }
-
-    flattening_vsync_worker_.VSyncControl(true);
-    flattenning_state_ = ClientFlattenningState::VsyncCountdownMax;
-    return false;
-  }
+  bool ProcessClientFlatteningState(bool skip);
+  void ProcessFlatenningVsyncInternal();
 
   /* Headless mode required to keep SurfaceFlinger alive when all display are
    * disconnected, Without headless mode Android will continuously crash.
@@ -207,7 +177,7 @@
    * https://source.android.com/devices/graphics/hotplug#handling-common-scenarios
    */
   bool IsInHeadlessMode() {
-    return handle_ == 0 && connector_->state() != DRM_MODE_CONNECTED;
+    return !pipeline_;
   }
 
  private:
@@ -220,41 +190,48 @@
   };
 
   std::atomic_int flattenning_state_{ClientFlattenningState::NotRequired};
-  VSyncWorker flattening_vsync_worker_;
 
   constexpr static size_t MATRIX_SIZE = 16;
 
   HwcDisplayConfigs configs_;
 
-  DrmHwcTwo *hwc2_;
+  DrmHwcTwo *const hwc2_;
 
-  std::optional<DrmMode> staged_mode;
+  std::optional<DrmMode> staged_mode_;
+  int64_t staged_mode_change_time_{};
+  uint32_t staged_mode_config_id_{};
 
-  ResourceManager *resource_manager_;
-  DrmDevice *drm_;
-  DrmDisplayCompositor compositor_;
-
-  std::vector<DrmPlane *> primary_planes_;
-  std::vector<DrmPlane *> overlay_planes_;
+  DrmDisplayPipeline *pipeline_{};
 
   std::unique_ptr<Backend> backend_;
 
   VSyncWorker vsync_worker_;
-  DrmConnector *connector_ = nullptr;
-  DrmCrtc *crtc_ = nullptr;
-  hwc2_display_t handle_;
+  bool vsync_event_en_{};
+  bool vsync_flattening_en_{};
+  bool vsync_tracking_en_{};
+  int64_t last_vsync_ts_{};
+
+  const hwc2_display_t handle_;
   HWC2::DisplayType type_;
-  uint32_t layer_idx_ = 0;
+
+  uint32_t layer_idx_{};
+
   std::map<hwc2_layer_t, HwcLayer> layers_;
   HwcLayer client_layer_;
   int32_t color_mode_{};
   std::array<float, MATRIX_SIZE> color_transform_matrix_{};
   android_color_transform_t color_transform_hint_;
 
+  std::shared_ptr<DrmKmsPlan> current_plan_;
+
   uint32_t frame_no_ = 0;
   Stats total_stats_;
   Stats prev_stats_;
   std::string DumpDelta(HwcDisplay::Stats delta);
+
+  HWC2::Error Init();
+
+  HWC2::Error SetActiveConfigInternal(uint32_t config, int64_t change_time);
 };
 
 }  // namespace android
diff --git a/hwc2_device/HwcDisplayConfigs.cpp b/hwc2_device/HwcDisplayConfigs.cpp
index 16f1ed0..6a3ed5a 100644
--- a/hwc2_device/HwcDisplayConfigs.cpp
+++ b/hwc2_device/HwcDisplayConfigs.cpp
@@ -31,10 +31,10 @@
 
 namespace android {
 
-// NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme
-HWC2::Error HwcDisplayConfigs::Update(DrmConnector &connector) {
-  /* In case UpdateModes will fail we will still have one mode for headless
-   * mode*/
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+uint32_t HwcDisplayConfigs::last_config_id = 1;
+
+void HwcDisplayConfigs::FillHeadless() {
   hwc_configs.clear();
 
   last_config_id++;
@@ -53,7 +53,13 @@
 
   mm_width = kHeadlessModeDisplayWidthMm;
   mm_height = kHeadlessModeDisplayHeightMm;
+}
 
+// NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme
+HWC2::Error HwcDisplayConfigs::Update(DrmConnector &connector) {
+  /* In case UpdateModes will fail we will still have one mode for headless
+   * mode*/
+  FillHeadless();
   /* Read real configs */
   int ret = connector.UpdateModes();
   if (ret != 0) {
@@ -61,25 +67,25 @@
     return HWC2::Error::BadDisplay;
   }
 
-  if (connector.modes().empty()) {
+  if (connector.GetModes().empty()) {
     ALOGE("No modes reported by KMS");
     return HWC2::Error::BadDisplay;
   }
 
   hwc_configs.clear();
-  mm_width = connector.mm_width();
-  mm_height = connector.mm_height();
+  mm_width = connector.GetMmWidth();
+  mm_height = connector.GetMmHeight();
 
   preferred_config_id = 0;
-  int preferred_config_group_id = 0;
+  uint32_t preferred_config_group_id = 0;
 
-  int first_config_id = last_config_id;
-  int last_group_id = 1;
+  uint32_t first_config_id = last_config_id;
+  uint32_t last_group_id = 1;
 
   /* Group modes */
-  for (const auto &mode : connector.modes()) {
+  for (const auto &mode : connector.GetModes()) {
     /* Find group for the new mode or create new group */
-    int group_found = 0;
+    uint32_t group_found = 0;
     for (auto &hwc_config : hwc_configs) {
       if (mode.h_display() == hwc_config.second.mode.h_display() &&
           mode.v_display() == hwc_config.second.mode.v_display()) {
@@ -122,7 +128,7 @@
     preferred_config_group_id = 1;
   }
 
-  for (int group = 1; group < last_group_id; group++) {
+  for (uint32_t group = 1; group < last_group_id; group++) {
     bool has_interlaced = false;
     bool has_progressive = false;
     for (auto &hwc_config : hwc_configs) {
@@ -173,8 +179,8 @@
    * otherwise android.graphics.cts.SetFrameRateTest CTS will fail
    */
   constexpr float kMinFpsDelta = 1.0;  // FPS
-  for (int m1 = first_config_id; m1 < last_config_id; m1++) {
-    for (int m2 = first_config_id; m2 < last_config_id; m2++) {
+  for (uint32_t m1 = first_config_id; m1 < last_config_id; m1++) {
+    for (uint32_t m2 = first_config_id; m2 < last_config_id; m2++) {
       if (m1 != m2 && hwc_configs[m1].group_id == hwc_configs[m2].group_id &&
           !hwc_configs[m1].disabled && !hwc_configs[m2].disabled &&
           fabsf(hwc_configs[m1].mode.v_refresh() -
@@ -190,8 +196,6 @@
     }
   }
 
-  /* Set active mode to be valid mode */
-  active_config_id = preferred_config_id;
   return HWC2::Error::None;
 }
 
diff --git a/hwc2_device/HwcDisplayConfigs.h b/hwc2_device/HwcDisplayConfigs.h
index 5bcf696..7c173d6 100644
--- a/hwc2_device/HwcDisplayConfigs.h
+++ b/hwc2_device/HwcDisplayConfigs.h
@@ -28,8 +28,8 @@
 class DrmConnector;
 
 struct HwcDisplayConfig {
-  int id{};
-  int group_id{};
+  uint32_t id{};
+  uint32_t group_id{};
   DrmMode mode;
   bool disabled{};
 
@@ -40,13 +40,15 @@
 
 struct HwcDisplayConfigs {
   HWC2::Error Update(DrmConnector &conn);
+  void FillHeadless();
 
-  std::map<int /*config_id*/, struct HwcDisplayConfig> hwc_configs;
+  std::map<uint32_t /*config_id*/, struct HwcDisplayConfig> hwc_configs;
 
-  int active_config_id = 0;
-  int preferred_config_id = 0;
+  uint32_t active_config_id = 0;
+  uint32_t preferred_config_id = 0;
 
-  int last_config_id = 1;
+  // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+  static uint32_t last_config_id;
 
   uint32_t mm_width = 0;
   uint32_t mm_height = 0;
diff --git a/hwc2_device/hwc2_device.cpp b/hwc2_device/hwc2_device.cpp
index 6d258e8..a6dedb4 100644
--- a/hwc2_device/hwc2_device.cpp
+++ b/hwc2_device/hwc2_device.cpp
@@ -74,8 +74,8 @@
         GetFuncName(__PRETTY_FUNCTION__).c_str());
   DrmHwcTwo *hwc = ToDrmHwcTwo(dev);
   const std::lock_guard<std::mutex> lock(hwc->GetResMan().GetMainLock());
-  HwcDisplay *display = DrmHwcTwo::GetDisplay(hwc, display_handle);
-  if (!display)
+  auto *display = hwc->GetDisplay(display_handle);
+  if (display == nullptr)
     return static_cast<int32_t>(HWC2::Error::BadDisplay);
 
   return static_cast<int32_t>((display->*func)(std::forward<Args>(args)...));
@@ -88,8 +88,8 @@
         layer_handle, GetFuncName(__PRETTY_FUNCTION__).c_str());
   DrmHwcTwo *hwc = ToDrmHwcTwo(dev);
   const std::lock_guard<std::mutex> lock(hwc->GetResMan().GetMainLock());
-  HwcDisplay *display = DrmHwcTwo::GetDisplay(hwc, display_handle);
-  if (!display)
+  auto *display = hwc->GetDisplay(display_handle);
+  if (display == nullptr)
     return static_cast<int32_t>(HWC2::Error::BadDisplay);
 
   HwcLayer *layer = display->get_layer(layer_handle);
@@ -390,12 +390,6 @@
   ctx->getCapabilities = HookDevGetCapabilities;
   ctx->getFunction = HookDevGetFunction;
 
-  HWC2::Error err = ctx->drmhwctwo.Init();
-  if (err != HWC2::Error::None) {
-    ALOGE("Failed to initialize DrmHwcTwo err=%d\n", err);
-    return -EINVAL;
-  }
-
   *dev = &ctx.release()->common;
 
   return 0;
diff --git a/tests/Android.bp b/tests/Android.bp
index 20b87a8..b25342e 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -42,3 +42,17 @@
         "external/drm_hwcomposer/include",
     ],
 }
+
+// Tool for listening and dumping uevents
+cc_test {
+    name: "hwc-drm-uevent-print",
+
+    srcs: ["uevent_print.cpp"],
+
+    vendor: true,
+    header_libs: ["libhardware_headers"],
+    shared_libs: ["liblog"],
+    include_dirs: [
+        "external/drm_hwcomposer",
+    ],
+}
diff --git a/tests/uevent_print.cpp b/tests/uevent_print.cpp
new file mode 100644
index 0000000..6ffbbfb
--- /dev/null
+++ b/tests/uevent_print.cpp
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include <iostream>
+
+#include "utils/UEvent.h"
+
+int main() {
+  auto uevent = android::UEvent::CreateInstance();
+  if (!uevent) {
+    std::cout << "Can't initialize UEvent class" << std::endl;
+    return -ENODEV;
+  }
+
+  int number = 0;
+  for (;;) {
+    auto msg = uevent->ReadNext();
+    if (!msg) {
+      continue;
+    }
+
+    std::cout << "New event #" << number++ << std::endl
+              << *msg << std::endl
+              << std::endl;
+  }
+}
diff --git a/utils/UEvent.h b/utils/UEvent.h
new file mode 100644
index 0000000..17b3cab
--- /dev/null
+++ b/utils/UEvent.h
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <linux/netlink.h>
+#include <sys/socket.h>
+
+#include <cerrno>
+#include <memory>
+#include <optional>
+#include <string>
+
+#include "UniqueFd.h"
+#include "log.h"
+
+namespace android {
+
+class UEvent {
+ public:
+  static auto CreateInstance() -> std::unique_ptr<UEvent> {
+    auto fd = UniqueFd(
+        socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT));
+
+    if (!fd) {
+      ALOGE("Failed to open uevent socket: errno=%i", errno);
+      return {};
+    }
+
+    struct sockaddr_nl addr {};
+    addr.nl_family = AF_NETLINK;
+    addr.nl_pid = 0;
+    addr.nl_groups = UINT32_MAX;
+
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
+    int ret = bind(fd.Get(), (struct sockaddr *)&addr, sizeof(addr));
+    if (ret != 0) {
+      ALOGE("Failed to bind uevent socket: errno=%i", errno);
+      return {};
+    }
+
+    return std::unique_ptr<UEvent>(new UEvent(fd));
+  }
+
+  auto ReadNext() -> std::optional<std::string> {
+    constexpr int kUEventBufferSize = 1024;
+    char buffer[kUEventBufferSize];
+    ssize_t ret = 0;
+    ret = read(fd_.Get(), &buffer, sizeof(buffer));
+    if (ret == 0)
+      return {};
+
+    if (ret < 0) {
+      ALOGE("Got error reading uevent %zd", ret);
+      return {};
+    }
+
+    for (int i = 0; i < ret - 1; i++) {
+      if (buffer[i] == '\0') {
+        buffer[i] = '\n';
+      }
+    }
+
+    return std::string(buffer);
+  }
+
+ private:
+  explicit UEvent(UniqueFd &fd) : fd_(std::move(fd)){};
+  UniqueFd fd_;
+};
+
+}  // namespace android