drm_hwcomposer: Use DUMB buffer for modeset

Now, as the LayerProperties struct accepts BufferInfo and BufferInfo can
carry the RAII-wrapped dmabuf FD, it has become elementary to use dumb
buffer for a modeset.

There are two benefits compared to using the gralloc:

1. We aim to make the DRM composer backend Android-agnostic.
2. Not every gralloc may support mapping the HWFB buffer.

Change-Id: I661c88be276de8f068d3af1e44da2740b1bec60d
Signed-off-by: Roman Stratiienko <r.stratiienko@gmail.com>
diff --git a/drm/DrmDevice.cpp b/drm/DrmDevice.cpp
index 4534104..787d77c 100644
--- a/drm/DrmDevice.cpp
+++ b/drm/DrmDevice.cpp
@@ -18,6 +18,7 @@
 
 #include "DrmDevice.h"
 
+#include <sys/mman.h>
 #include <xf86drm.h>
 #include <xf86drmMode.h>
 
@@ -260,4 +261,93 @@
   return encoders_;
 }
 
+class DumbBufferFd : public PrimeFdsSharedBase {
+ public:
+  SharedFd fd;
+};
+
+// NOLINTBEGIN(cppcoreguidelines-avoid-goto)
+auto DrmDevice::CreateBufferForModeset(uint32_t width, uint32_t height)
+    -> std::optional<BufferInfo> {
+  constexpr uint32_t kDumbBufferFormat = DRM_FORMAT_XRGB8888;
+  constexpr uint32_t kDumbBufferBpp = 32;
+
+  std::optional<BufferInfo> result;
+  void *ptr = MAP_FAILED;
+  struct drm_mode_create_dumb create = {
+      .height = height,
+      .width = width,
+      .bpp = kDumbBufferBpp,
+      .flags = 0,
+  };
+
+  int ret = drmIoctl(*fd_, DRM_IOCTL_MODE_CREATE_DUMB, &create);
+  if (ret != 0) {
+    ALOGE("Failed to DRM_IOCTL_MODE_CREATE_DUMB %d", errno);
+    return {};
+  }
+
+  struct drm_mode_map_dumb map = {
+      .handle = create.handle,
+  };
+
+  auto dumb_buffer_fd = std::make_shared<DumbBufferFd>();
+
+  BufferInfo buffer_info = {
+      .width = width,
+      .height = height,
+
+      .format = kDumbBufferFormat,
+      .pitches = {create.pitch},
+      .prime_fds = {-1, -1, -1, -1},
+      .modifiers = {DRM_FORMAT_MOD_NONE},
+
+      .color_space = BufferColorSpace::kUndefined,
+      .sample_range = BufferSampleRange::kUndefined,
+      .blend_mode = BufferBlendMode::kNone,
+
+      .fds_shared = dumb_buffer_fd,
+  };
+
+  ret = drmIoctl(*fd_, DRM_IOCTL_MODE_MAP_DUMB, &map);
+  if (ret != 0) {
+    ALOGE("Failed to DRM_IOCTL_MODE_MAP_DUMB %d", errno);
+    goto done;
+  }
+
+  ptr = mmap(nullptr, create.size, PROT_READ | PROT_WRITE, MAP_SHARED, *fd_,
+             (off_t)map.offset);
+  if (ptr == MAP_FAILED) {
+    ALOGE("Failed to mmap dumb buffer %d", errno);
+    goto done;
+  }
+
+  memset(ptr, 0, create.size);
+
+  if (munmap(ptr, create.size) != 0) {
+    ALOGE("Failed to unmap dumb buffer: %d", errno);
+  }
+
+  ret = drmPrimeHandleToFD(*fd_, create.handle, 0, &buffer_info.prime_fds[0]);
+  if (ret != 0) {
+    ALOGE("Failed to export dumb buffer as FD: %d", errno);
+    goto done;
+  }
+
+  dumb_buffer_fd->fd = MakeSharedFd(buffer_info.prime_fds[0]);
+
+  result = buffer_info;
+
+done:
+  if (create.handle > 0) {
+    struct drm_mode_destroy_dumb destroy = {
+        .handle = create.handle,
+    };
+    drmIoctl(*fd_, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
+  }
+
+  return result;
+}
+// NOLINTEND(cppcoreguidelines-avoid-goto)
+
 }  // namespace android