drm_hwcomposer: Use hw planes + drm atomic interface

Replace the basic, single overlay, modeset/flip implementation with
a new Compositor instantiation for drm. The new compositor uses the
drm atomic interface to composite layers to multiple hardware planes.

This change also introduces an importer argument in
Compositor::CreateComposition. By specifying the importer, Compositor
instances are responsible for cleaning up buffer objects submitted
via Composition::AddLayer.

Change-Id: Ic292829cd93475d754294b00428de132301092b2
Signed-off-by: Sean Paul <seanpaul@chromium.org>
diff --git a/drmcompositor.cpp b/drmcompositor.cpp
new file mode 100644
index 0000000..ffca4df
--- /dev/null
+++ b/drmcompositor.cpp
@@ -0,0 +1,270 @@
+/*
+ * 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-compositor"
+
+#include "drmcompositor.h"
+#include "drmcrtc.h"
+#include "drmplane.h"
+#include "drmresources.h"
+
+#include <pthread.h>
+#include <stdlib.h>
+
+#include <cutils/log.h>
+#include <sync/sync.h>
+
+namespace android {
+
+DrmCompositor::DrmCompositor(DrmResources *drm)
+    : drm_(drm),
+      worker_(this),
+      active_composition_(NULL),
+      frame_no_(0),
+      initialized_(false) {
+}
+
+DrmCompositor::~DrmCompositor() {
+  if (initialized_)
+    pthread_mutex_destroy(&lock_);
+}
+
+int DrmCompositor::Init() {
+  int ret = pthread_mutex_init(&lock_, NULL);
+  if (ret) {
+    ALOGE("Failed to initialize drm compositor lock %d\n", ret);
+    return ret;
+  }
+  ret = worker_.Init();
+  if (ret) {
+    pthread_mutex_destroy(&lock_);
+    ALOGE("Failed to initialize compositor worker %d\n", ret);
+    return ret;
+  }
+
+  initialized_ = true;
+  return 0;
+}
+
+Composition *DrmCompositor::CreateComposition(Importer *importer) {
+  DrmComposition *composition = new DrmComposition(drm_, importer, frame_no_++);
+  if (!composition) {
+    ALOGE("Failed to allocate drm composition");
+    return NULL;
+  }
+  int ret = composition->Init();
+  if (ret) {
+    ALOGE("Failed to initialize drm composition %d", ret);
+    delete composition;
+    return NULL;
+  }
+  return composition;
+}
+
+int DrmCompositor::QueueComposition(Composition *composition) {
+  int ret = pthread_mutex_lock(&lock_);
+  if (ret) {
+    ALOGE("Failed to acquire compositor lock %d", ret);
+    return ret;
+  }
+
+  composite_queue_.push((DrmComposition *)composition);
+
+  ret = pthread_mutex_unlock(&lock_);
+  if (ret) {
+    ALOGE("Failed to release compositor lock %d", ret);
+    return ret;
+  }
+
+  worker_.Signal();
+  return 0;
+}
+
+int DrmCompositor::PerformModeset(DrmCompositionLayerMap_t::iterator begin,
+                                  DrmCompositionLayerMap_t::iterator end) {
+  DrmCompositionLayer *layer = NULL;
+  for (DrmCompositionLayerMap_t::iterator iter = begin; iter != end; ++iter) {
+    if (iter->second.layer.compositionType == HWC_FRAMEBUFFER_TARGET) {
+      layer = &iter->second;
+      break;
+    }
+  }
+  int display = begin->first;
+  if (!layer) {
+    ALOGE("Could not find target framebuffer for display %d", display);
+    return -ENOENT;
+  }
+
+  drmModeModeInfo m;
+  DrmConnector *connector = drm_->GetConnectorForDisplay(display);
+  connector->active_mode().ToModeModeInfo(&m);
+
+  uint32_t connectors = connector->id();
+  int ret = drmModeSetCrtc(drm_->fd(), layer->crtc->id(), layer->bo.fb_id, 0, 0,
+                           &connectors, 1, &m);
+  if (ret)
+    ALOGE("Failed set crtc for disp %d/%d", display, ret);
+  else
+    layer->crtc->set_requires_modeset(false);
+
+  return ret;
+}
+
+int DrmCompositor::CompositeDisplay(DrmCompositionLayerMap_t::iterator begin,
+                                    DrmCompositionLayerMap_t::iterator end) {
+  int ret = 0;
+  // Wait for all acquire fences to signal
+  for (DrmCompositionLayerMap_t::iterator iter = begin; iter != end; ++iter) {
+    hwc_layer_1_t *layer = &iter->second.layer;
+
+    if (layer->acquireFenceFd < 0)
+      continue;
+
+    ret = sync_wait(layer->acquireFenceFd, -1);
+    if (ret) {
+      ALOGE("Failed to wait for acquire %d/%d", layer->acquireFenceFd, ret);
+      return ret;
+    }
+    close(layer->acquireFenceFd);
+    layer->acquireFenceFd = -1;
+  }
+
+  DrmCrtc *crtc = begin->second.crtc;
+  if (crtc->requires_modeset()) {
+    ret = PerformModeset(begin, end);
+    if (ret)
+      ALOGE("Failed modeset on display %d", begin->first);
+    return ret;
+  }
+
+  drmModePropertySetPtr pset = drmModePropertySetAlloc();
+  if (!pset) {
+    ALOGE("Failed to allocate property set");
+    return -ENOMEM;
+  }
+
+  for (DrmCompositionLayerMap_t::iterator iter = begin; iter != end; ++iter) {
+    DrmCompositionLayer_t *comp = &iter->second;
+    hwc_layer_1_t *layer = &comp->layer;
+    DrmPlane *plane = comp->plane;
+
+    ret =
+        drmModePropertySetAdd(pset, plane->id(), plane->crtc_property().id(),
+                              crtc->id()) ||
+        drmModePropertySetAdd(pset, plane->id(), plane->fb_property().id(),
+                              comp->bo.fb_id) ||
+        drmModePropertySetAdd(pset, plane->id(), plane->crtc_x_property().id(),
+                              layer->displayFrame.left) ||
+        drmModePropertySetAdd(pset, plane->id(), plane->crtc_y_property().id(),
+                              layer->displayFrame.top) ||
+        drmModePropertySetAdd(
+            pset, plane->id(), plane->crtc_w_property().id(),
+            layer->displayFrame.right - layer->displayFrame.left) ||
+        drmModePropertySetAdd(
+            pset, plane->id(), plane->crtc_h_property().id(),
+            layer->displayFrame.bottom - layer->displayFrame.top) ||
+        drmModePropertySetAdd(pset, plane->id(), plane->src_x_property().id(),
+                              layer->sourceCropf.left) ||
+        drmModePropertySetAdd(pset, plane->id(), plane->src_y_property().id(),
+                              layer->sourceCropf.top) ||
+        drmModePropertySetAdd(
+            pset, plane->id(), plane->src_w_property().id(),
+            (int)(layer->sourceCropf.right - layer->sourceCropf.left) << 16) ||
+        drmModePropertySetAdd(
+            pset, plane->id(), plane->src_h_property().id(),
+            (int)(layer->sourceCropf.bottom - layer->sourceCropf.top) << 16);
+    if (ret) {
+      ALOGE("Failed to add plane %d to set", plane->id());
+      break;
+    }
+  }
+
+  if (!ret) {
+    ret = drmModePropertySetCommit(drm_->fd(), 0, drm_, pset);
+    if (ret)
+      ALOGE("Failed to commit pset ret=%d\n", ret);
+  }
+  if (pset)
+    drmModePropertySetFree(pset);
+
+  return ret;
+}
+
+int DrmCompositor::Composite() {
+  int ret = pthread_mutex_lock(&lock_);
+  if (ret) {
+    ALOGE("Failed to acquire compositor lock %d", ret);
+    return ret;
+  }
+  if (composite_queue_.empty()) {
+    ret = pthread_mutex_unlock(&lock_);
+    if (ret)
+      ALOGE("Failed to release compositor lock %d", ret);
+    return ret;
+  }
+
+  DrmComposition *composition = composite_queue_.front();
+  composite_queue_.pop();
+
+  ret = pthread_mutex_unlock(&lock_);
+  if (ret) {
+    ALOGE("Failed to release compositor lock %d", ret);
+    return ret;
+  }
+
+  DrmCompositionLayerMap_t *map = composition->GetCompositionMap();
+  for (DrmResources::ConnectorIter iter = drm_->begin_connectors();
+       iter != drm_->end_connectors(); ++iter) {
+    int display = (*iter)->display();
+    std::pair<DrmCompositionLayerMap_t::iterator,
+              DrmCompositionLayerMap_t::iterator> layer_iters =
+        map->equal_range(display);
+
+    if (layer_iters.first != layer_iters.second) {
+      ret = CompositeDisplay(layer_iters.first, layer_iters.second);
+      if (ret) {
+        ALOGE("Composite failed for display %d:", display);
+        break;
+      }
+    }
+  }
+
+  if (active_composition_) {
+    active_composition_->FinishComposition();
+    delete active_composition_;
+  }
+  active_composition_ = composition;
+  return ret;
+}
+
+bool DrmCompositor::HaveQueuedComposites() const {
+  int ret = pthread_mutex_lock(&lock_);
+  if (ret) {
+    ALOGE("Failed to acquire compositor lock %d", ret);
+    return false;
+  }
+
+  bool empty_ret = !composite_queue_.empty();
+
+  ret = pthread_mutex_unlock(&lock_);
+  if (ret) {
+    ALOGE("Failed to release compositor lock %d", ret);
+    return false;
+  }
+
+  return empty_ret;
+}
+}