drm_hwcomposer: Add resource manager support

Add a resource manager class that is responsible for detecting all
kms devices and allocates unique display numbers for every detected
display.

This is controlled by the value of hwc.drm.device property, if it ends
with a %, it will try to open minor devices until an error is detected.
E.g: /dev/dri/card%

Additionally, this will be used for finding an available writeback
connector that will be used for the flattening of the currently
displayed scene.

Signed-off-by: Alexandru Gheorghe <alexandru-cosmin.gheorghe@arm.com>
diff --git a/Android.mk b/Android.mk
index b8576a5..65c0f3a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -56,6 +56,7 @@
 
 LOCAL_SRC_FILES := \
 	autolock.cpp \
+	resourcemanager.cpp \
 	drmdevice.cpp \
 	drmconnector.cpp \
 	drmcrtc.cpp \
diff --git a/drmdevice.cpp b/drmdevice.cpp
index 1eae32b..138bb3d 100644
--- a/drmdevice.cpp
+++ b/drmdevice.cpp
@@ -42,33 +42,30 @@
   event_listener_.Exit();
 }
 
-int DrmDevice::Init() {
-  char path[PROPERTY_VALUE_MAX];
-  property_get("hwc.drm.device", path, "/dev/dri/card0");
-
+std::tuple<int, int> DrmDevice::Init(const char *path, int num_displays) {
   /* TODO: Use drmOpenControl here instead */
   fd_.Set(open(path, O_RDWR));
   if (fd() < 0) {
     ALOGE("Failed to open dri- %s", strerror(-errno));
-    return -ENODEV;
+    return std::make_tuple(-ENODEV, 0);
   }
 
   int ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
   if (ret) {
     ALOGE("Failed to set universal plane cap %d", ret);
-    return ret;
+    return std::make_tuple(ret, 0);
   }
 
   ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_ATOMIC, 1);
   if (ret) {
     ALOGE("Failed to set atomic cap %d", ret);
-    return ret;
+    return std::make_tuple(ret, 0);
   }
 
   drmModeResPtr res = drmModeGetResources(fd());
   if (!res) {
     ALOGE("Failed to get DrmDevice resources");
-    return -ENODEV;
+    return std::make_tuple(-ENODEV, 0);
   }
 
   min_resolution_ =
@@ -76,8 +73,9 @@
   max_resolution_ =
       std::pair<uint32_t, uint32_t>(res->max_width, res->max_height);
 
-  bool found_primary = false;
-  int display_num = 1;
+  // 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) {
     drmModeCrtcPtr c = drmModeGetCrtc(fd(), res->crtcs[i]);
@@ -160,19 +158,28 @@
   // First look for primary amongst internal connectors
   for (auto &conn : connectors_) {
     if (conn->internal() && !found_primary) {
-      conn->set_display(0);
+      conn->set_display(num_displays);
+      displays_[num_displays] = num_displays;
+      ++num_displays;
       found_primary = true;
-    } else {
-      conn->set_display(display_num);
-      ++display_num;
+      break;
     }
   }
 
-  // Then look for primary amongst external connectors
+  // Then pick first available as primary and for the others assign
+  // consecutive display_numbers.
   for (auto &conn : connectors_) {
-    if (conn->external() && !found_primary) {
-      conn->set_display(0);
-      found_primary = true;
+    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;
+      }
     }
   }
 
@@ -181,12 +188,12 @@
 
   // Catch-all for the above loops
   if (ret)
-    return ret;
+    return std::make_tuple(ret, 0);
 
   drmModePlaneResPtr plane_res = drmModeGetPlaneResources(fd());
   if (!plane_res) {
     ALOGE("Failed to get plane resources");
-    return -ENOENT;
+    return std::make_tuple(-ENOENT, 0);
   }
 
   for (uint32_t i = 0; i < plane_res->count_planes; ++i) {
@@ -211,22 +218,26 @@
   }
   drmModeFreePlaneResources(plane_res);
   if (ret)
-    return ret;
+    return std::make_tuple(ret, 0);
 
   ret = event_listener_.Init();
   if (ret) {
     ALOGE("Can't initialize event listener %d", ret);
-    return 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 ret;
+      return std::make_tuple(ret, 0);
     }
   }
-  return 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 {
diff --git a/drmdevice.h b/drmdevice.h
index 5a82f90..e086067 100644
--- a/drmdevice.h
+++ b/drmdevice.h
@@ -22,8 +22,10 @@
 #include "drmencoder.h"
 #include "drmeventlistener.h"
 #include "drmplane.h"
+#include "platform.h"
 
 #include <stdint.h>
+#include <tuple>
 
 namespace android {
 
@@ -32,7 +34,7 @@
   DrmDevice();
   ~DrmDevice();
 
-  int Init();
+  std::tuple<int, int> Init(const char *path, int num_displays);
 
   int fd() const {
     return fd_.get();
@@ -71,6 +73,7 @@
 
   int CreatePropertyBlob(void *data, size_t length, uint32_t *blob_id);
   int DestroyPropertyBlob(uint32_t blob_id);
+  bool HandlesDisplay(int display) const;
 
  private:
   int TryEncoderForDisplay(int display, DrmEncoder *enc);
@@ -90,6 +93,7 @@
 
   std::pair<uint32_t, uint32_t> min_resolution_;
   std::pair<uint32_t, uint32_t> max_resolution_;
+  std::map<int, int> displays_;
 };
 }
 
diff --git a/drmhwctwo.cpp b/drmhwctwo.cpp
index 2dcf088..56ba07b 100644
--- a/drmhwctwo.cpp
+++ b/drmhwctwo.cpp
@@ -58,24 +58,26 @@
 }
 
 HWC2::Error DrmHwcTwo::Init() {
-  int ret = drm_.Init();
+  int ret = resource_manager_.Init();
   if (ret) {
-    ALOGE("Can't initialize drm object %d", ret);
+    ALOGE("Can't initialize the resource manager %d", ret);
     return HWC2::Error::NoResources;
   }
 
-  importer_.reset(Importer::CreateInstance(&drm_));
-  if (!importer_) {
-    ALOGE("Failed to create importer instance");
+  DrmDevice *drm = resource_manager_.GetDrmDevice(HWC_DISPLAY_PRIMARY);
+  std::shared_ptr<Importer> importer =
+      resource_manager_.GetImporter(HWC_DISPLAY_PRIMARY);
+  if (!drm || !importer) {
+    ALOGE("Failed to get a valid drmresource and importer");
     return HWC2::Error::NoResources;
   }
 
   displays_.emplace(std::piecewise_construct,
                     std::forward_as_tuple(HWC_DISPLAY_PRIMARY),
-                    std::forward_as_tuple(&drm_, importer_, HWC_DISPLAY_PRIMARY,
+                    std::forward_as_tuple(drm, importer, HWC_DISPLAY_PRIMARY,
                                           HWC2::DisplayType::Physical));
 
-  DrmCrtc *crtc = drm_.GetCrtcForDisplay(static_cast<int>(HWC_DISPLAY_PRIMARY));
+  DrmCrtc *crtc = drm->GetCrtcForDisplay(static_cast<int>(HWC_DISPLAY_PRIMARY));
   if (!crtc) {
     ALOGE("Failed to get crtc for display %d",
           static_cast<int>(HWC_DISPLAY_PRIMARY));
@@ -83,7 +85,7 @@
   }
 
   std::vector<DrmPlane *> display_planes;
-  for (auto &plane : drm_.planes()) {
+  for (auto &plane : drm->planes()) {
     if (plane->GetCrtcSupported(*crtc))
       display_planes.push_back(plane.get());
   }
diff --git a/drmhwctwo.h b/drmhwctwo.h
index 33c688c..b65d37d 100644
--- a/drmhwctwo.h
+++ b/drmhwctwo.h
@@ -16,8 +16,8 @@
 
 #include "drmdisplaycompositor.h"
 #include "drmhwcomposer.h"
-#include "drmdevice.h"
 #include "platform.h"
+#include "resourcemanager.h"
 #include "vsyncworker.h"
 
 #include <hardware/hwcomposer2.h>
@@ -261,8 +261,7 @@
   HWC2::Error RegisterCallback(int32_t descriptor, hwc2_callback_data_t data,
                                hwc2_function_pointer_t function);
 
-  DrmDevice drm_;
-  std::shared_ptr<Importer> importer_;  // Shared with HwcDisplay
+  ResourceManager resource_manager_;
   std::map<hwc2_display_t, HwcDisplay> displays_;
   std::map<HWC2::Callback, HwcCallback> callbacks_;
 };
diff --git a/resourcemanager.cpp b/resourcemanager.cpp
new file mode 100644
index 0000000..80a67b7
--- /dev/null
+++ b/resourcemanager.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2018 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-resource-manager"
+
+#include "resourcemanager.h"
+
+#include <cutils/properties.h>
+#include <log/log.h>
+#include <sstream>
+#include <string>
+
+namespace android {
+
+ResourceManager::ResourceManager() : num_displays_(0), gralloc_(NULL) {
+}
+
+int ResourceManager::Init() {
+  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("hwc.drm.device", path_pattern, "/dev/dri/card0");
+  int ret = 0;
+  if (path_pattern[path_len - 1] != '%') {
+    ret = AddDrmDevice(std::string(path_pattern));
+  } else {
+    path_pattern[path_len - 1] = '\0';
+    for (int idx = 0; !ret; ++idx) {
+      std::ostringstream path;
+      path << path_pattern << idx;
+      ret = AddDrmDevice(path.str());
+    }
+  }
+
+  if (!num_displays_) {
+    ALOGE("Failed to initialize any displays");
+    return ret ? -EINVAL : ret;
+  }
+
+  return hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
+                       (const hw_module_t **)&gralloc_);
+}
+
+int ResourceManager::AddDrmDevice(std::string path) {
+  std::unique_ptr<DrmDevice> drm = std::make_unique<DrmDevice>();
+  int displays_added, ret;
+  std::tie(ret, displays_added) = drm->Init(path.c_str(), num_displays_);
+  if (ret)
+    return ret;
+  std::shared_ptr<Importer> importer;
+  importer.reset(Importer::CreateInstance(drm.get()));
+  if (!importer) {
+    ALOGE("Failed to create importer instance");
+    return -ENODEV;
+  }
+  importers_.push_back(importer);
+  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();
+  }
+  return NULL;
+}
+
+std::shared_ptr<Importer> ResourceManager::GetImporter(int display) {
+  for (unsigned int i = 0; i < drms_.size(); i++) {
+    if (drms_[i]->HandlesDisplay(display))
+      return importers_[i];
+  }
+  return NULL;
+}
+
+const gralloc_module_t *ResourceManager::gralloc() {
+  return gralloc_;
+}
+}
diff --git a/resourcemanager.h b/resourcemanager.h
new file mode 100644
index 0000000..4b51608
--- /dev/null
+++ b/resourcemanager.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 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 RESOURCEMANAGER_H
+#define RESOURCEMANAGER_H
+
+#include "drmdevice.h"
+#include "platform.h"
+
+#include <string.h>
+
+namespace android {
+
+class ResourceManager {
+ public:
+  ResourceManager();
+  ResourceManager(const ResourceManager &) = delete;
+  ResourceManager &operator=(const ResourceManager &) = delete;
+  int Init();
+  DrmDevice *GetDrmDevice(int display);
+  std::shared_ptr<Importer> GetImporter(int display);
+  const gralloc_module_t *gralloc();
+
+ private:
+  int AddDrmDevice(std::string path);
+
+  int num_displays_;
+  std::vector<std::unique_ptr<DrmDevice>> drms_;
+  std::vector<std::shared_ptr<Importer>> importers_;
+  const gralloc_module_t *gralloc_;
+};
+}
+
+#endif  // RESOURCEMANAGER_H