OSDN Git Service

Add iahwc backend inside Hwcomposer.
authorHarish Krupo <harish.krupo.kps@intel.com>
Tue, 13 Feb 2018 05:43:09 +0000 (11:13 +0530)
committerKalyan Kondapally <kalyan.kondapally@intel.com>
Sun, 18 Feb 2018 18:43:26 +0000 (10:43 -0800)
Signed-off-by: Harish Krupo <harish.krupo.kps@intel.com>
os/linux/weston/build_script.sh [new file with mode: 0755]
os/linux/weston/patches/0001-Enable-IAHWC-backend-in-weston.patch [new file with mode: 0644]
os/linux/weston/plugin/compositor-iahwc.c [new file with mode: 0644]
os/linux/weston/plugin/compositor-iahwc.h [new file with mode: 0644]

diff --git a/os/linux/weston/build_script.sh b/os/linux/weston/build_script.sh
new file mode 100755 (executable)
index 0000000..ac10de8
--- /dev/null
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+# This script assumes that it is run from the root
+# of the weston directory and also that hwcomposer
+# is cloned inside the root of weston's dir as iahwc
+
+function build_iahwc() {
+
+    # Build IAHWC
+    pushd . > /dev/null
+    cd iahwc
+    git clean -xfd
+    ./autogen.sh $AUTOGEN_CMDLINE --enable-gbm --enable-linux-frontend && \
+        make -j$PARALLEL install
+    popd
+}
+
+function build_weston() {
+    # Build weston
+    git clean -xfd
+    ./autogen.sh $AUTOGEN_CMDLINE && \
+        make -j$PARALLEL && \
+        sudo make install
+}
+
+BKR=71c4f70e08faad6002ec8fe8cd1c7930bee8373b
+AUTOGEN_CMDLINE=""
+PATCHES="iahwc/os/linux/weston/patches/*"
+APPLY_PATCHES=0;
+BUILD_IAHWC=0
+BUILD_WESTON=0
+PARALLEL=9
+
+if [ ! -z ${WLD+x} ]; then
+    echo "WLD found: $WLD"
+    AUTOGEN_CMDLINE="--prefix=$WLD"
+fi
+
+for i in $@; do
+    case $i in
+        --apply-patches)
+            APPLY_PATCHES=1;
+            ;;
+        --build)
+            BUILD_IAHWC=1;
+            BUILD_WESTON=1;
+            ;;
+        --build-iahwc)
+            BUILD_IAHWC=1;
+            ;;
+        --build-weston)
+            BUILD_WESTON=1;
+            ;;
+        -j=* | --parallel=*)
+            PARALLEL="${i//*=}";
+            ;;
+    esac
+done
+
+# checkout to weston best known revision and apply patches
+if [ $APPLY_PATCHES -eq 1 ]; then
+    git checkout $BKR
+    git apply --check $PATCHES && git am $PATCHES
+fi
+
+[ $BUILD_IAHWC -eq 1 ] && build_iahwc;
+[ $BUILD_WESTON -eq 1 ] && build_weston;
diff --git a/os/linux/weston/patches/0001-Enable-IAHWC-backend-in-weston.patch b/os/linux/weston/patches/0001-Enable-IAHWC-backend-in-weston.patch
new file mode 100644 (file)
index 0000000..24f6a11
--- /dev/null
@@ -0,0 +1,242 @@
+From 57294f63ee9d1408ac86df720c56db58d7c952b6 Mon Sep 17 00:00:00 2001
+From: Harish Krupo <harish.krupo.kps@intel.com>
+Date: Tue, 13 Feb 2018 07:56:15 +0530
+Subject: [PATCH 1/2] Enable IAHWC backend in weston
+
+Signed-off-by: Harish Krupo <harish.krupo.kps@intel.com>
+---
+ Makefile.am            | 28 ++++++++++++++++
+ compositor/main.c      | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++
+ configure.ac           | 11 +++++++
+ libweston/compositor.c |  1 +
+ libweston/compositor.h |  1 +
+ 5 files changed, 129 insertions(+)
+
+diff --git a/Makefile.am b/Makefile.am
+index 9d99c694..40eac766 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -84,6 +84,7 @@ libweston_@LIBWESTON_MAJOR@_la_SOURCES =                     \
+       libweston/compositor.c                          \
+       libweston/compositor.h                          \
+       libweston/compositor-drm.h                      \
++      iahwc/os/linux/weston/plugin/compositor-iahwc.h                 \
+       libweston/compositor-fbdev.h                    \
+       libweston/compositor-headless.h                 \
+       libweston/compositor-rdp.h                      \
+@@ -294,6 +295,7 @@ libwestoninclude_HEADERS =                 \
+       libweston/version.h                     \
+       libweston/compositor.h                  \
+       libweston/compositor-drm.h              \
++      iahwc/os/linux/weston/plugin/compositor-iahwc.h         \
+       libweston/compositor-fbdev.h            \
+       libweston/compositor-headless.h         \
+       libweston/compositor-rdp.h              \
+@@ -400,6 +402,32 @@ drm_backend_la_CFLAGS += $(LIBVA_CFLAGS)
+ endif
+ endif
+
++if ENABLE_IAHWC_COMPOSITOR
++libweston_module_LTLIBRARIES += iahwc-backend.la
++iahwc_backend_la_LDFLAGS = -module -avoid-version
++iahwc_backend_la_LIBADD =                             \
++      libsession-helper.la                    \
++      libweston-@LIBWESTON_MAJOR@.la          \
++      $(COMPOSITOR_LIBS)                      \
++      $(DRM_COMPOSITOR_LIBS)                  \
++      $(INPUT_BACKEND_LIBS)                   \
++      libshared.la                            \
++      $(CLOCK_GETTIME_LIBS)
++iahwc_backend_la_CFLAGS =                             \
++      $(COMPOSITOR_CFLAGS)                    \
++      $(EGL_CFLAGS)                           \
++      $(IAHWC_COMPOSITOR_CFLAGS)              \
++      $(AM_CFLAGS)
++iahwc_backend_la_SOURCES =                    \
++      iahwc/os/linux/weston/plugin/compositor-iahwc.c         \
++      iahwc/os/linux/weston/plugin/compositor-iahwc.h         \
++      $(INPUT_BACKEND_SOURCES)                \
++      shared/helpers.h                        \
++      shared/timespec-util.h                  \
++      libweston/libbacklight.c                \
++      libweston/libbacklight.h
++endif
++
+ if ENABLE_WAYLAND_COMPOSITOR
+ libweston_module_LTLIBRARIES += wayland-backend.la
+ wayland_backend_la_LDFLAGS = -module -avoid-version
+diff --git a/compositor/main.c b/compositor/main.c
+index 9e4451e5..569f8ac8 100644
+--- a/compositor/main.c
++++ b/compositor/main.c
+@@ -58,6 +58,7 @@
+ #include "weston.h"
+
+ #include "compositor-drm.h"
++#include "iahwc/os/linux/weston/plugin/compositor-iahwc.h"
+ #include "compositor-headless.h"
+ #include "compositor-rdp.h"
+ #include "compositor-fbdev.h"
+@@ -535,6 +536,9 @@ usage(int error_code)
+ #if defined(BUILD_DRM_COMPOSITOR)
+                       "\t\t\t\tdrm-backend.so\n"
+ #endif
++#if defined(BUILD_IAHWC_COMPOSITOR)
++          "\t\t\t\tiahwc-backend.so\n"
++#endif
+ #if defined(BUILD_FBDEV_COMPOSITOR)
+                       "\t\t\t\tfbdev-backend.so\n"
+ #endif
+@@ -1714,6 +1718,88 @@ load_wayland_backend(struct weston_compositor *c,
+       return 0;
+ }
+
++static void
++iahwc_backend_output_configure(struct wl_listener *listener, void *data)
++{
++      struct weston_output *output = data;
++      struct weston_config *wc = wet_get_config(output->compositor);
++      struct weston_config_section *section;
++      const struct weston_iahwc_output_api *api = weston_iahwc_output_get_api(output->compositor);
++
++      char *gbm_format = NULL;
++      char *seat = NULL;
++
++      if (!api) {
++              weston_log("Cannot use weston_drm_output_api.\n");
++              return;
++      }
++
++      section = weston_config_get_section(wc, "output", "name", output->name);
++      //XXX/TODO: preferred mode set by iahwc.
++
++      if (api->set_mode(output, WESTON_DRM_BACKEND_OUTPUT_CURRENT, NULL) < 0) {
++              weston_log("Cannot configure an output using weston_drm_output_api.\n");
++              return;
++      }
++
++      wet_output_set_scale(output, section, 1, 0);
++      wet_output_set_transform(output, section, WL_OUTPUT_TRANSFORM_NORMAL, UINT32_MAX);
++
++      weston_config_section_get_string(section,
++                                       "gbm-format", &gbm_format, NULL);
++
++      api->set_gbm_format(output, gbm_format);
++      free(gbm_format);
++
++      weston_config_section_get_string(section, "seat", &seat, "");
++
++      api->set_seat(output, seat);
++      free(seat);
++
++      weston_output_enable(output);
++}
++
++static int
++load_iahwc_backend(struct weston_compositor *c,
++               int *argc, char **argv, struct weston_config *wc)
++{
++      struct weston_iahwc_backend_config config = {{ 0, }};
++      struct weston_config_section *section;
++      struct wet_compositor *wet = to_wet_compositor(c);
++      int ret = 0;
++
++      wet->drm_use_current_mode = false;
++
++      const struct weston_option options[] = {
++              { WESTON_OPTION_STRING, "seat", 0, &config.seat_id },
++              { WESTON_OPTION_INTEGER, "tty", 0, &config.tty },
++              { WESTON_OPTION_BOOLEAN, "current-mode", 0, &wet->drm_use_current_mode },
++              { WESTON_OPTION_BOOLEAN, "use-pixman", 0, &config.use_pixman },
++      };
++
++      parse_options(options, ARRAY_LENGTH(options), argc, argv);
++
++      section = weston_config_get_section(wc, "core", NULL, NULL);
++      weston_config_section_get_string(section,
++                                       "gbm-format", &config.gbm_format,
++                                       NULL);
++      weston_config_section_get_uint(section, "pageflip-timeout",
++                                     &config.pageflip_timeout, 0);
++
++      config.base.struct_version = WESTON_IAHWC_BACKEND_CONFIG_VERSION;
++      config.base.struct_size = sizeof(struct weston_iahwc_backend_config);
++      config.configure_device = configure_input_device;
++
++      ret = weston_compositor_load_backend(c, WESTON_BACKEND_IAHWC,
++                                           &config.base);
++
++      wet_set_pending_output_handler(c, iahwc_backend_output_configure);
++
++      free(config.gbm_format);
++      free(config.seat_id);
++
++      return ret;
++}
+
+ static int
+ load_backend(struct weston_compositor *compositor, const char *backend,
+@@ -1727,6 +1813,8 @@ load_backend(struct weston_compositor *compositor, const char *backend,
+               return load_fbdev_backend(compositor, argc, argv, config);
+       else if (strstr(backend, "drm-backend.so"))
+               return load_drm_backend(compositor, argc, argv, config);
++      else if (strstr(backend, "iahwc-backend.so"))
++              return load_iahwc_backend(compositor, argc, argv, config);
+       else if (strstr(backend, "x11-backend.so"))
+               return load_x11_backend(compositor, argc, argv, config);
+       else if (strstr(backend, "wayland-backend.so"))
+diff --git a/configure.ac b/configure.ac
+index 5f31bbcb..f5e466bb 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -206,6 +206,16 @@ if test x$enable_drm_compositor = xyes; then
+                   [AC_MSG_WARN([gbm does not support dmabuf import, will omit that capability])])
+ fi
+
++AC_ARG_ENABLE(iahwc-compositor, [  --enable-iahwc-compositor],,
++enable_iahwc_compositor=yes)
++AM_CONDITIONAL(ENABLE_IAHWC_COMPOSITOR, test x$enable_iahwc_compositor = xyes)
++if test x$enable_iahwc_compositor = xyes; then
++AC_DEFINE([BUILD_IAHWC_COMPOSITOR], [1], [Build the IAHWC compositor])
++PKG_CHECK_MODULES(IAHWC_COMPOSITOR, [iahwc libudev >= 136 libdrm >= 2.4.30 gbm])
++PKG_CHECK_MODULES(IAHWC_COMPOSITOR_GBM, [gbm >= 10.2],
++[AC_DEFINE([HAVE_GBM_FD_IMPORT], 1, [gbm supports dmabuf import])],
++[AC_MSG_WARN([gbm does not support dmabuf import, will omit that capability])])
++fi
+
+ PKG_CHECK_MODULES(LIBINPUT_BACKEND, [libinput >= 0.8.0])
+ PKG_CHECK_MODULES(COMPOSITOR, [$COMPOSITOR_MODULES])
+@@ -708,6 +718,7 @@ AC_MSG_RESULT([
+       systemd notify support          ${enable_systemd_notify}
+
+       DRM Compositor                  ${enable_drm_compositor}
++      IAHWC Compositor                ${enable_iahwc_compositor}
+       X11 Compositor                  ${enable_x11_compositor}
+       Wayland Compositor              ${enable_wayland_compositor}
+       Headless Compositor             ${enable_headless_compositor}
+diff --git a/libweston/compositor.c b/libweston/compositor.c
+index 71a9b38c..35f71cb6 100644
+--- a/libweston/compositor.c
++++ b/libweston/compositor.c
+@@ -5626,6 +5626,7 @@ weston_compositor_get_user_data(struct weston_compositor *compositor)
+
+ static const char * const backend_map[] = {
+       [WESTON_BACKEND_DRM] =          "drm-backend.so",
++      [WESTON_BACKEND_IAHWC] =        "iahwc-backend.so",
+       [WESTON_BACKEND_FBDEV] =        "fbdev-backend.so",
+       [WESTON_BACKEND_HEADLESS] =     "headless-backend.so",
+       [WESTON_BACKEND_RDP] =          "rdp-backend.so",
+diff --git a/libweston/compositor.h b/libweston/compositor.h
+index 8b2d2b06..ad640189 100644
+--- a/libweston/compositor.h
++++ b/libweston/compositor.h
+@@ -1671,6 +1671,7 @@ weston_compositor_create(struct wl_display *display, void *user_data);
+
+ enum weston_compositor_backend {
+       WESTON_BACKEND_DRM,
++      WESTON_BACKEND_IAHWC,
+       WESTON_BACKEND_FBDEV,
+       WESTON_BACKEND_HEADLESS,
+       WESTON_BACKEND_RDP,
+--
+2.16.1
diff --git a/os/linux/weston/plugin/compositor-iahwc.c b/os/linux/weston/plugin/compositor-iahwc.c
new file mode 100644 (file)
index 0000000..ba19f78
--- /dev/null
@@ -0,0 +1,2203 @@
+/*
+// Copyright (c) 2016 Intel Corporation
+//
+// 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 "config.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <linux/vt.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <drm_fourcc.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include <gbm.h>
+#include <libudev.h>
+
+#include <libsync.h>
+
+#include <iahwc.h>
+
+#include "compositor-iahwc.h"
+#include "compositor.h"
+#include "gl-renderer.h"
+#include "launcher-util.h"
+#include "libbacklight.h"
+#include "libinput-seat.h"
+#include "linux-dmabuf-unstable-v1-server-protocol.h"
+#include "linux-dmabuf.h"
+#include "pixel-formats.h"
+#include "pixman-renderer.h"
+#include "presentation-time-server-protocol.h"
+#include "shared/helpers.h"
+#include "shared/timespec-util.h"
+#include "vaapi-recorder.h"
+#include "weston-egl-ext.h"
+
+#ifndef GBM_BO_USE_CURSOR
+#define GBM_BO_USE_CURSOR GBM_BO_USE_CURSOR_64X64
+#endif
+
+struct iahwc_backend {
+  struct weston_backend base;
+  struct weston_compositor *compositor;
+
+  iahwc_module_t *iahwc_module;
+  iahwc_device_t *iahwc_device;
+
+  struct udev *udev;
+  struct wl_event_source *iahwc_source;
+
+  struct udev_monitor *udev_monitor;
+  struct wl_event_source *udev_iahwc_source;
+
+  struct {
+    int id;
+    int fd;
+    char *filename;
+  } iahwc;
+
+  struct gbm_device *gbm;
+  struct wl_listener session_listener;
+  uint32_t gbm_format;
+
+  IAHWC_PFN_GET_NUM_DISPLAYS iahwc_get_num_displays;
+  IAHWC_PFN_REGISTER_CALLBACK iahwc_register_callback;
+  IAHWC_PFN_DISPLAY_GET_INFO iahwc_get_display_info;
+  IAHWC_PFN_DISPLAY_GET_NAME iahwc_get_display_name;
+  IAHWC_PFN_DISPLAY_GET_CONFIGS iahwc_get_display_configs;
+  IAHWC_PFN_DISPLAY_SET_GAMMA iahwc_set_display_gamma;
+  IAHWC_PFN_DISPLAY_SET_CONFIG iahwc_set_display_config;
+  IAHWC_PFN_DISPLAY_GET_CONFIG iahwc_get_display_config;
+  IAHWC_PFN_DISPLAY_CLEAR_ALL_LAYERS iahwc_display_clear_all_layers;
+  IAHWC_PFN_PRESENT_DISPLAY iahwc_present_display;
+  IAHWC_PFN_CREATE_LAYER iahwc_create_layer;
+  IAHWC_PFN_LAYER_SET_BO iahwc_layer_set_bo;
+  IAHWC_PFN_LAYER_SET_RAW_PIXEL_DATA iahwc_layer_set_raw_pixel_data;
+  IAHWC_PFN_LAYER_SET_SOURCE_CROP iahwc_layer_set_source_crop;
+  IAHWC_PFN_LAYER_SET_DISPLAY_FRAME iahwc_layer_set_display_frame;
+  IAHWC_PFN_LAYER_SET_SURFACE_DAMAGE iahwc_layer_set_surface_damage;
+  IAHWC_PFN_LAYER_SET_ACQUIRE_FENCE iahwc_layer_set_acquire_fence;
+  IAHWC_PFN_LAYER_SET_USAGE iahwc_layer_set_usage;
+
+  /* we need these parameters in order to not fail iahwcModeAddFB2()
+   * due to out of bounds dimensions, and then mistakenly set
+   * sprites_are_broken:
+   */
+  int min_width, max_width;
+  int min_height, max_height;
+  int no_addfb2;
+
+  struct wl_list plane_list;
+  int sprites_are_broken;
+  int sprites_hidden;
+
+  void *repaint_data;
+
+  int cursors_are_broken;
+
+  bool universal_planes;
+
+  int use_pixman;
+
+  struct udev_input input;
+
+  int32_t cursor_width;
+  int32_t cursor_height;
+
+  uint32_t pageflip_timeout;
+};
+
+struct iahwc_mode {
+  struct weston_mode base;
+  uint32_t config_id;
+};
+
+enum iahwc_fb_type {
+  BUFFER_INVALID = 0, /**< never used */
+  BUFFER_CLIENT,      /**< directly sourced from client */
+  BUFFER_PIXMAN_DUMB, /**< internal Pixman rendering */
+  BUFFER_GBM_SURFACE, /**< internal EGL rendering */
+  BUFFER_CURSOR,      /**< internal cursor buffer */
+};
+
+struct iahwc_fb {
+  enum iahwc_fb_type type;
+
+  int refcnt;
+
+  uint32_t fb_id, stride, handle, size;
+  const struct pixel_format_info *format;
+  int width, height;
+  int fd;
+  struct weston_buffer_reference buffer_ref;
+
+  /* Used by gbm fbs */
+  struct gbm_bo *bo;
+  struct gbm_surface *gbm_surface;
+
+  /* Used by dumb fbs */
+  void *map;
+};
+
+struct iahwc_edid {
+  char eisa_id[13];
+  char monitor_name[13];
+  char pnp_id[5];
+  char serial_number[13];
+};
+
+/**
+ * Pending state holds one or more iahwc_output_state structures, collected from
+ * performing repaint. This pending state is transient, and only lives between
+ * beginning a repaint group and flushing the results: after flush, each
+ * output state will complete and be retired separately.
+ */
+struct iahwc_pending_state {
+  struct iahwc_backend *backend;
+};
+
+struct iahwc_output {
+  struct weston_output base;
+  drmModeConnector *connector;
+
+  uint32_t crtc_id; /* object ID to pass to IAHWC functions */
+  int pipe;         /* index of CRTC in resource array / bitmasks */
+  uint32_t connector_id;
+  drmModeCrtcPtr original_crtc;
+  struct iahwc_edid edid;
+
+  enum dpms_enum dpms;
+  struct backlight *backlight;
+
+  bool state_invalid;
+
+  int vblank_pending;
+  int page_flip_pending;
+  int destroy_pending;
+  int disable_pending;
+
+  int64_t primary_layer_id;
+  int64_t cursor_layer_id;
+  int64_t overlay_layer_id;
+
+  struct gbm_bo *bo;
+
+  struct gbm_bo *gbm_cursor_bo[2];
+  struct weston_plane cursor_plane;
+  struct weston_plane d_plane;
+  struct weston_view *cursor_view;
+  struct wl_shm *prev_cursor_mem;
+  int current_cursor;
+
+  struct gbm_surface *gbm_surface;
+  uint32_t gbm_format;
+  struct gbm_bo *overlay_bo;
+
+  /* Plane for a fullscreen direct scanout view */
+  struct weston_plane scanout_plane;
+
+  /* The last framebuffer submitted to the kernel for this CRTC. */
+  struct iahwc_fb *fb_current;
+  /* The previously-submitted framebuffer, where the hardware has not
+   * yet acknowledged display of fb_current. */
+  struct iahwc_fb *fb_last;
+  /* Framebuffer we are going to submit to the kernel when the current
+   * repaint is flushed. */
+  struct iahwc_fb *fb_pending;
+
+  struct iahwc_fb *dumb[2];
+  pixman_image_t *image[2];
+  int current_image;
+  pixman_region32_t previous_damage;
+
+  struct vaapi_recorder *recorder;
+  struct wl_listener recorder_frame_listener;
+
+  struct wl_event_source *pageflip_timer;
+  int frame_commited;
+};
+
+static struct gl_renderer_interface *gl_renderer;
+
+static const char default_seat[] = "seat0";
+
+static inline struct iahwc_output *to_iahwc_output(struct weston_output *base) {
+  return container_of(base, struct iahwc_output, base);
+}
+
+static inline struct iahwc_backend *to_iahwc_backend(
+    struct weston_compositor *base) {
+  return container_of(base->backend, struct iahwc_backend, base);
+}
+
+static void iahwc_fb_destroy(struct iahwc_fb *fb) {
+  if (fb->fb_id != 0)
+
+    drmModeRmFB(fb->fd, fb->fb_id);
+  weston_buffer_reference(&fb->buffer_ref, NULL);
+  free(fb);
+}
+
+static void iahwc_fb_destroy_dumb(struct iahwc_fb *fb) {
+  struct drm_mode_destroy_dumb destroy_arg;
+
+  assert(fb->type == BUFFER_PIXMAN_DUMB);
+
+  if (fb->map && fb->size > 0)
+    munmap(fb->map, fb->size);
+
+  memset(&destroy_arg, 0, sizeof(destroy_arg));
+  destroy_arg.handle = fb->handle;
+  drmIoctl(fb->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
+
+  iahwc_fb_destroy(fb);
+}
+
+static void iahwc_fb_destroy_gbm(struct gbm_bo *bo, void *data) {
+  struct iahwc_fb *fb = data;
+
+  assert(fb->type == BUFFER_GBM_SURFACE || fb->type == BUFFER_CLIENT ||
+         fb->type == BUFFER_CURSOR);
+  iahwc_fb_destroy(fb);
+}
+
+static struct iahwc_fb *iahwc_fb_create_dumb(struct iahwc_backend *b, int width,
+                                             int height, uint32_t format) {
+  struct iahwc_fb *fb;
+  int ret;
+
+  struct drm_mode_create_dumb create_arg;
+  struct drm_mode_destroy_dumb destroy_arg;
+  struct drm_mode_map_dumb map_arg;
+
+  fb = zalloc(sizeof *fb);
+  if (!fb)
+    return NULL;
+
+  fb->refcnt = 1;
+
+  fb->format = pixel_format_get_info(format);
+  if (!fb->format) {
+    weston_log("failed to look up format 0x%lx\n", (unsigned long)format);
+    goto err_fb;
+  }
+
+  if (!fb->format->depth || !fb->format->bpp) {
+    weston_log("format 0x%lx is not compatible with dumb buffers\n",
+               (unsigned long)format);
+    goto err_fb;
+  }
+
+  memset(&create_arg, 0, sizeof create_arg);
+  create_arg.bpp = fb->format->bpp;
+  create_arg.width = width;
+  create_arg.height = height;
+
+  ret = drmIoctl(b->iahwc.fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg);
+  if (ret)
+    goto err_fb;
+
+  fb->type = BUFFER_PIXMAN_DUMB;
+  fb->handle = create_arg.handle;
+  fb->stride = create_arg.pitch;
+  fb->size = create_arg.size;
+  fb->width = width;
+  fb->height = height;
+  fb->fd = b->iahwc.fd;
+
+  ret = -1;
+
+  if (!b->no_addfb2) {
+    uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
+
+    handles[0] = fb->handle;
+    pitches[0] = fb->stride;
+    offsets[0] = 0;
+
+    ret = drmModeAddFB2(b->iahwc.fd, width, height, fb->format->format, handles,
+                        pitches, offsets, &fb->fb_id, 0);
+    if (ret) {
+      weston_log("addfb2 failed: %m\n");
+      b->no_addfb2 = 1;
+    }
+  }
+
+  if (ret) {
+    ret = drmModeAddFB(b->iahwc.fd, width, height, fb->format->depth,
+                       fb->format->bpp, fb->stride, fb->handle, &fb->fb_id);
+  }
+
+  if (ret)
+    goto err_bo;
+
+  memset(&map_arg, 0, sizeof map_arg);
+  map_arg.handle = fb->handle;
+  ret = drmIoctl(fb->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_arg);
+  if (ret)
+    goto err_add_fb;
+
+  fb->map =
+      mmap(NULL, fb->size, PROT_WRITE, MAP_SHARED, b->iahwc.fd, map_arg.offset);
+  if (fb->map == MAP_FAILED)
+    goto err_add_fb;
+
+  return fb;
+
+err_add_fb:
+  drmModeRmFB(b->iahwc.fd, fb->fb_id);
+err_bo:
+  memset(&destroy_arg, 0, sizeof(destroy_arg));
+  destroy_arg.handle = create_arg.handle;
+  drmIoctl(b->iahwc.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
+err_fb:
+  free(fb);
+  return NULL;
+}
+
+static struct iahwc_fb *iahwc_fb_ref(struct iahwc_fb *fb) {
+  fb->refcnt++;
+  return fb;
+}
+
+static struct iahwc_fb *iahwc_fb_get_from_bo(struct gbm_bo *bo,
+                                             struct iahwc_backend *backend,
+                                             uint32_t format,
+                                             enum iahwc_fb_type type) {
+  struct iahwc_fb *fb = gbm_bo_get_user_data(bo);
+
+  if (fb) {
+    assert(fb->type == type);
+    return iahwc_fb_ref(fb);
+  }
+
+  fb = zalloc(sizeof *fb);
+  if (fb == NULL)
+    return NULL;
+
+  fb->type = type;
+  fb->refcnt = 1;
+  fb->bo = bo;
+
+  fb->width = gbm_bo_get_width(bo);
+  fb->height = gbm_bo_get_height(bo);
+  fb->stride = gbm_bo_get_stride(bo);
+  fb->handle = gbm_bo_get_handle(bo).u32;
+  fb->format = pixel_format_get_info(format);
+  fb->size = fb->stride * fb->height;
+  fb->fd = backend->iahwc.fd;
+
+  if (!fb->format) {
+    weston_log("couldn't look up format 0x%lx\n", (unsigned long)format);
+    goto err_free;
+  }
+
+  gbm_bo_set_user_data(bo, fb, iahwc_fb_destroy_gbm);
+
+  return fb;
+
+err_free:
+  free(fb);
+  return NULL;
+}
+
+static void iahwc_fb_unref(struct iahwc_fb *fb) {
+  if (!fb)
+    return;
+
+  assert(fb->refcnt > 0);
+  if (--fb->refcnt > 0)
+    return;
+
+  switch (fb->type) {
+    case BUFFER_PIXMAN_DUMB:
+      iahwc_fb_destroy_dumb(fb);
+      break;
+    case BUFFER_CURSOR:
+    case BUFFER_CLIENT:
+      gbm_bo_destroy(fb->bo);
+      break;
+    case BUFFER_GBM_SURFACE:
+      gbm_surface_release_buffer(fb->gbm_surface, fb->bo);
+      break;
+    default:
+      assert(NULL);
+      break;
+  }
+}
+
+/**
+ * Allocate a new iahwc_pending_state
+ *
+ * Allocate a new, empty, 'pending state' structure to be used across a
+ * repaint cycle or similar.
+ *
+ * @param backend IAHWC backend
+ * @returns Newly-allocated pending state structure
+ */
+static struct iahwc_pending_state *iahwc_pending_state_alloc(
+    struct iahwc_backend *backend) {
+  struct iahwc_pending_state *ret;
+
+  ret = calloc(1, sizeof(*ret));
+  if (!ret)
+    return NULL;
+
+  ret->backend = backend;
+
+  return ret;
+}
+
+/**
+ * Free a iahwc_pending_state structure
+ *
+ * Frees a pending_state structure.
+ *
+ * @param pending_state Pending state structure to free
+ */
+static void iahwc_pending_state_free(
+    struct iahwc_pending_state *pending_state) {
+  if (!pending_state)
+    return;
+
+  free(pending_state);
+}
+
+static struct iahwc_fb *iahwc_output_render_gl(struct iahwc_output *output,
+                                               pixman_region32_t *damage) {
+  struct iahwc_backend *b = to_iahwc_backend(output->base.compositor);
+  struct gbm_bo *bo;
+  struct iahwc_fb *ret;
+
+  output->base.compositor->renderer->repaint_output(&output->base, damage);
+
+  bo = gbm_surface_lock_front_buffer(output->gbm_surface);
+  if (!bo) {
+    weston_log("failed to lock front buffer: %m\n");
+    return NULL;
+  }
+
+  b->iahwc_layer_set_bo(b->iahwc_device, 0, output->primary_layer_id, bo);
+
+  // XXX/TODO: need to get the acquire fence and pass it instead of -1
+  b->iahwc_layer_set_acquire_fence(b->iahwc_device, 0, output->primary_layer_id,
+                                   -1);
+
+  ret = iahwc_fb_get_from_bo(bo, b, output->gbm_format, BUFFER_GBM_SURFACE);
+
+  if (!ret) {
+    weston_log("failed to get iahwc_fb for bo\n");
+    gbm_surface_release_buffer(output->gbm_surface, bo);
+    return NULL;
+  }
+
+  if (output->bo)
+    gbm_surface_release_buffer(output->gbm_surface, output->bo);
+
+  output->bo = bo;
+
+  ret->gbm_surface = output->gbm_surface;
+
+  return ret;
+}
+
+static void iahwc_output_render(struct iahwc_output *output,
+                                pixman_region32_t *damage) {
+  struct weston_compositor *c = output->base.compositor;
+  struct iahwc_fb *fb;
+
+  /* If we already have a client buffer promoted to scanout, then we don't
+   * want to render. */
+  if (output->fb_pending)
+    return;
+
+  fb = iahwc_output_render_gl(output, damage);
+
+  if (!fb)
+    return;
+  output->fb_pending = fb;
+
+  pixman_region32_subtract(&c->primary_plane.damage, &c->primary_plane.damage,
+                           damage);
+}
+
+static void iahwc_output_set_gamma(struct weston_output *output_base,
+                                   uint16_t size, uint16_t *r, uint16_t *g,
+                                   uint16_t *b) {
+  int rc;
+  struct iahwc_output *output = to_iahwc_output(output_base);
+  struct iahwc_backend *backend = to_iahwc_backend(output->base.compositor);
+
+  float rs = *r, gs = *g, bs = *b;
+  rc = backend->iahwc_set_display_gamma(backend->iahwc_device, 0, rs, gs, bs);
+  if (rc)
+    weston_log("set gamma failed: %m\n");
+}
+
+static int iahwc_output_repaint(struct weston_output *output_base,
+                                pixman_region32_t *damage, void *repaint_data) {
+  struct iahwc_output *output = to_iahwc_output(output_base);
+  struct iahwc_backend *backend = to_iahwc_backend(output->base.compositor);
+  int release_fence, ret;
+
+  if (output->disable_pending || output->destroy_pending)
+    return -1;
+
+  /* assert(!output->fb_last); */
+
+  /* If disable_planes is set then assign_planes() wasn't
+   * called for this render, so we could still have a stale
+   * cursor plane set up.
+   */
+  if (output->base.disable_planes) {
+    output->cursor_view = NULL;
+    output->cursor_plane.x = INT32_MIN;
+    output->cursor_plane.y = INT32_MIN;
+  }
+
+  iahwc_output_render(output, damage);
+  if (!output->fb_pending)
+    return -1;
+
+  backend->iahwc_present_display(backend->iahwc_device, 0, &release_fence);
+  output->frame_commited = 1;
+
+  weston_log("release fence is %d\n", release_fence);
+  if (release_fence > 0) {
+    ret = sync_wait(release_fence, -1);
+    if (ret < 0)
+      weston_log("failed to wait on fence %d: %s\n", release_fence,
+                 strerror(errno));
+  }
+
+  close(release_fence);
+
+  if (output->prev_cursor_mem)
+    wl_shm_buffer_end_access(output->prev_cursor_mem);
+
+  output->fb_last = output->fb_current;
+  output->fb_current = output->fb_pending;
+  output->fb_pending = NULL;
+
+  uint32_t refresh = output_base->current_mode->refresh;
+
+  return 0;
+
+  /* if (output->fb_pending) { */
+  /*   iahwc_fb_unref(output->fb_pending); */
+  /*   output->fb_pending = NULL; */
+  /* } */
+
+  /* return -1; */
+}
+
+static void iahwc_output_start_repaint_loop(struct weston_output *output_base) {
+  struct iahwc_output *output = to_iahwc_output(output_base);
+  struct iahwc_backend *b = to_iahwc_backend(output_base->compositor);
+
+  uint32_t fb_id;
+
+  if (output->disable_pending || output->destroy_pending)
+    return;
+
+  if (!output->fb_current) {
+    /* We can't page flip if there's no mode set */
+    goto finish_frame;
+  }
+
+  /* Immediate query didn't provide valid timestamp.
+   * Use pageflip fallback.
+   */
+  fb_id = output->fb_current->fb_id;
+
+  output->fb_last = iahwc_fb_ref(output->fb_current);
+
+finish_frame:
+  /* if we cannot page-flip, immediately finish frame */
+  /* weston_compositor_read_presentation_clock(output->base.compositor, &ts); */
+  /* weston_output_finish_frame(&output->base, &ts, 0); */
+  weston_output_finish_frame(output_base, NULL,
+                             WP_PRESENTATION_FEEDBACK_INVALID);
+
+  return;
+}
+
+static void iahwc_output_destroy(struct weston_output *base);
+
+/**
+ * Begin a new repaint cycle
+ *
+ * Called by the core compositor at the beginning of a repaint cycle.
+ */
+static void *iahwc_repaint_begin(struct weston_compositor *compositor) {
+  struct iahwc_backend *b = to_iahwc_backend(compositor);
+  struct iahwc_pending_state *ret;
+
+  ret = iahwc_pending_state_alloc(b);
+  b->repaint_data = ret;
+
+  return ret;
+}
+
+/**
+ * Flush a repaint set
+ *
+ * Called by the core compositor when a repaint cycle has been completed
+ * and should be flushed.
+ */
+static void iahwc_repaint_flush(struct weston_compositor *compositor,
+                                void *repaint_data) {
+  struct iahwc_backend *b = to_iahwc_backend(compositor);
+  struct iahwc_pending_state *pending_state = repaint_data;
+
+  iahwc_pending_state_free(pending_state);
+  b->repaint_data = NULL;
+}
+
+/**
+ * Cancel a repaint set
+ *
+ * Called by the core compositor when a repaint has finished, so the data
+ * held across the repaint cycle should be discarded.
+ */
+static void iahwc_repaint_cancel(struct weston_compositor *compositor,
+                                 void *repaint_data) {
+  struct iahwc_backend *b = to_iahwc_backend(compositor);
+  struct iahwc_pending_state *pending_state = repaint_data;
+
+  iahwc_pending_state_free(pending_state);
+  b->repaint_data = NULL;
+}
+
+/**
+ * Find the closest-matching mode for a given target
+ *
+ * Given a target mode, find the most suitable mode amongst the output's
+ * current mode list to use, preferring the current mode if possible, to
+ * avoid an expensive mode switch.
+ *
+ * @param output IAHWC output
+ * @param target_mode Mode to attempt to match
+ * @returns Pointer to a mode from the output's mode list
+ */
+static struct iahwc_mode *choose_mode(struct iahwc_output *output,
+                                      struct weston_mode *target_mode) {
+  struct iahwc_mode *tmp_mode = NULL, *mode;
+
+  if (output->base.current_mode->width == target_mode->width &&
+      output->base.current_mode->height == target_mode->height &&
+      (output->base.current_mode->refresh == target_mode->refresh ||
+       target_mode->refresh == 0))
+    return (struct iahwc_mode *)output->base.current_mode;
+
+  wl_list_for_each(mode, &output->base.mode_list, base.link) {
+    if (mode->base.width == target_mode->width &&
+        mode->base.height == target_mode->height) {
+      if (mode->base.refresh == target_mode->refresh ||
+          target_mode->refresh == 0) {
+        return mode;
+      } else if (!tmp_mode)
+        tmp_mode = mode;
+    }
+  }
+
+  return tmp_mode;
+}
+
+static int iahwc_output_init_egl(struct iahwc_output *output,
+                                 struct iahwc_backend *b);
+static void iahwc_output_fini_egl(struct iahwc_output *output);
+static int iahwc_output_init_pixman(struct iahwc_output *output,
+                                    struct iahwc_backend *b);
+static void iahwc_output_fini_pixman(struct iahwc_output *output);
+
+static int iahwc_output_switch_mode(struct weston_output *output_base,
+                                    struct weston_mode *mode) {
+  struct iahwc_output *output;
+  struct iahwc_mode *iahwc_mode;
+  struct iahwc_backend *b;
+
+  if (output_base == NULL) {
+    weston_log("output is NULL.\n");
+    return -1;
+  }
+
+  if (mode == NULL) {
+    weston_log("mode is NULL.\n");
+    return -1;
+  }
+
+  b = to_iahwc_backend(output_base->compositor);
+  output = to_iahwc_output(output_base);
+  iahwc_mode = choose_mode(output, mode);
+
+  if (!iahwc_mode) {
+    weston_log("%s, invalid resolution:%dx%d\n", __func__, mode->width,
+               mode->height);
+    return -1;
+  }
+
+  if (&iahwc_mode->base == output->base.current_mode)
+    return 0;
+
+  b->iahwc_set_display_config(b->iahwc_device, 0, iahwc_mode->config_id);
+
+  output->base.current_mode->flags = 0;
+
+  output->base.current_mode = &iahwc_mode->base;
+  output->base.current_mode->flags =
+      WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+
+  /* XXX: This drops our current buffer too early, before we've started
+   *      displaying it. Ideally this should be much more atomic and
+   *      integrated with a full repaint cycle, rather than doing a
+   *      sledgehammer modeswitch first, and only later showing new
+   *      content.
+   */
+  iahwc_fb_unref(output->fb_current);
+  assert(!output->fb_last);
+  assert(!output->fb_pending);
+  output->fb_last = output->fb_current = NULL;
+
+  if (b->use_pixman) {
+    iahwc_output_fini_pixman(output);
+    if (iahwc_output_init_pixman(output, b) < 0) {
+      weston_log(
+          "failed to init output pixman state with "
+          "new mode\n");
+      return -1;
+    }
+  } else {
+    iahwc_output_fini_egl(output);
+    if (iahwc_output_init_egl(output, b) < 0) {
+      weston_log(
+          "failed to init output egl state with "
+          "new mode");
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+static struct gbm_device *create_gbm_device(int fd) {
+  struct gbm_device *gbm;
+
+  gl_renderer = weston_load_module("gl-renderer.so", "gl_renderer_interface");
+  if (!gl_renderer)
+    return NULL;
+
+  /* GBM will load a dri driver, but even though they need symbols from
+   * libglapi, in some version of Mesa they are not linked to it. Since
+   * only the gl-renderer module links to it, the call above won't make
+   * these symbols globally available, and loading the DRI driver fails.
+   * Workaround this by dlopen()'ing libglapi with RTLD_GLOBAL. */
+  dlopen("libglapi.so.0", RTLD_LAZY | RTLD_GLOBAL);
+
+  gbm = gbm_create_device(fd);
+
+  return gbm;
+}
+
+/* When initializing EGL, if the preferred buffer format isn't available
+ * we may be able to substitute an ARGB format for an XRGB one.
+ *
+ * This returns 0 if substitution isn't possible, but 0 might be a
+ * legitimate format for other EGL platforms, so the caller is
+ * responsible for checking for 0 before calling gl_renderer->create().
+ *
+ * This works around https://bugs.freedesktop.org/show_bug.cgi?id=89689
+ * but it's entirely possible we'll see this again on other implementations.
+ */
+static int fallback_format_for(uint32_t format) {
+  switch (format) {
+    case GBM_FORMAT_XRGB8888:
+      return GBM_FORMAT_ARGB8888;
+    case GBM_FORMAT_XRGB2101010:
+      return GBM_FORMAT_ARGB2101010;
+    default:
+      return 0;
+  }
+}
+
+static int iahwc_backend_create_gl_renderer(struct iahwc_backend *b) {
+  EGLint format[3] = {
+      b->gbm_format,
+      fallback_format_for(b->gbm_format),
+      0,
+  };
+  int n_formats = 2;
+
+  if (format[1])
+    n_formats = 3;
+  if (gl_renderer->display_create(
+          b->compositor, EGL_PLATFORM_GBM_KHR, (void *)b->gbm, NULL,
+          gl_renderer->opaque_attribs, format, n_formats) < 0) {
+    return -1;
+  }
+
+  return 0;
+}
+
+static int init_egl(struct iahwc_backend *b) {
+  b->gbm = create_gbm_device(b->iahwc.fd);
+
+  if (!b->gbm)
+    return -1;
+
+  if (iahwc_backend_create_gl_renderer(b) < 0) {
+    gbm_device_destroy(b->gbm);
+    return -1;
+  }
+
+  return 0;
+}
+
+static struct weston_plane *iahwc_output_prepare_cursor_view(
+    struct iahwc_output *output, struct weston_view *ev) {
+  struct iahwc_backend *b = to_iahwc_backend(output->base.compositor);
+  struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
+  struct wl_shm_buffer *shmbuf;
+  float x, y;
+  struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
+  uint32_t *buf;
+  int32_t stride;
+  uint32_t rstride;
+  uint8_t *s;
+  int i;
+  struct gbm_bo *bo;
+
+  if (output->cursor_view)
+    return NULL;
+
+  /* Don't import buffers which span multiple outputs. */
+  if (ev->output_mask != (1u << output->base.id))
+    return NULL;
+
+  /* We use GBM to import SHM buffers. */
+  if (b->gbm == NULL)
+    return NULL;
+
+  if (ev->surface->buffer_ref.buffer == NULL)
+    return NULL;
+  shmbuf = wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource);
+  if (!shmbuf)
+    return NULL;
+  if (wl_shm_buffer_get_format(shmbuf) != WL_SHM_FORMAT_ARGB8888)
+    return NULL;
+
+  if (output->base.transform != WL_OUTPUT_TRANSFORM_NORMAL)
+    return NULL;
+  if (ev->transform.enabled &&
+      (ev->transform.matrix.type > WESTON_MATRIX_TRANSFORM_TRANSLATE))
+    return NULL;
+  if (viewport->buffer.scale != output->base.current_scale)
+    return NULL;
+  if (ev->geometry.scissor_enabled)
+    return NULL;
+
+  if (ev->surface->width > b->cursor_width ||
+      ev->surface->height > b->cursor_height)
+    return NULL;
+
+  output->cursor_view = ev;
+  weston_view_to_global_float(ev, 0, 0, &x, &y);
+  output->cursor_plane.x = x;
+  output->cursor_plane.y = y;
+
+  if (output->cursor_layer_id == -1) {
+    b->iahwc_create_layer(b->iahwc_device, 0, &output->cursor_layer_id);
+    b->iahwc_layer_set_usage(b->iahwc_device, 0, output->cursor_layer_id,
+                             IAHWC_LAYER_USAGE_CURSOR);
+  }
+
+  int32_t surfwidth = ev->surface->width;
+  int32_t surfheight = ev->surface->height;
+
+  stride = wl_shm_buffer_get_stride(buffer->shm_buffer);
+  s = wl_shm_buffer_get_data(buffer->shm_buffer);
+
+  wl_shm_buffer_begin_access(buffer->shm_buffer);
+  output->prev_cursor_mem = buffer->shm_buffer;
+
+  struct iahwc_raw_pixel_data dbo;
+  dbo.width = surfwidth;
+  dbo.height = surfheight;
+  dbo.stride = stride;
+  dbo.format = DRM_FORMAT_ARGB8888;
+  dbo.buffer = s;
+
+  b->iahwc_layer_set_raw_pixel_data(b->iahwc_device, 0, output->cursor_layer_id,
+                                    dbo);
+
+  iahwc_rect_t source_crop = {0, 0, ev->surface->width, ev->surface->height};
+
+  iahwc_rect_t display_frame = {x, y, ev->surface->width, ev->surface->height};
+
+  iahwc_region_t damage_region;
+  damage_region.numRects = 1;
+  damage_region.rects = &source_crop;
+
+  b->iahwc_layer_set_source_crop(b->iahwc_device, 0, output->cursor_layer_id,
+                                 source_crop);
+  b->iahwc_layer_set_display_frame(b->iahwc_device, 0, output->cursor_layer_id,
+                                   display_frame);
+  b->iahwc_layer_set_surface_damage(b->iahwc_device, 0, output->cursor_layer_id,
+                                    damage_region);
+
+  output->gbm_cursor_bo[0] = bo;
+
+  return &output->cursor_plane;
+}
+
+static struct weston_plane *iahwc_output_prepare_overlay_view(
+    struct iahwc_output *output, struct weston_view *ev) {
+  struct weston_compositor *ec = output->base.compositor;
+  struct iahwc_backend *b = to_iahwc_backend(ec);
+  struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
+  struct wl_resource *buffer_resource;
+  struct weston_plane *p = &output->d_plane;
+  struct linux_dmabuf_buffer *dmabuf;
+  int found = 0;
+  struct gbm_bo *bo;
+  pixman_region32_t dest_rect, src_rect;
+  pixman_box32_t *box, tbox;
+  uint32_t format;
+  wl_fixed_t sx1, sy1, sx2, sy2;
+  int32_t src_x, src_y;
+  uint32_t src_w, src_h;
+  uint32_t dest_x, dest_y;
+  uint32_t dest_w, dest_h;
+
+  if (output->overlay_layer_id != -1)
+    return NULL;
+
+  /* Don't import buffers which span multiple outputs. */
+  if (ev->output_mask != (1u << output->base.id))
+    return NULL;
+
+  /* We can only import GBM buffers. */
+  if (b->gbm == NULL)
+    return NULL;
+
+  if (ev->surface->buffer_ref.buffer == NULL)
+    return NULL;
+  buffer_resource = ev->surface->buffer_ref.buffer->resource;
+  if (wl_shm_buffer_get(buffer_resource))
+    return NULL;
+  if (viewport->buffer.transform != output->base.transform)
+    return NULL;
+  if (viewport->buffer.scale != output->base.current_scale)
+    return NULL;
+  if (ev->alpha != 1.0f)
+    return NULL;
+
+  if ((dmabuf = linux_dmabuf_buffer_get(buffer_resource))) {
+#ifdef HAVE_GBM_FD_IMPORT
+    /* XXX: TODO:
+     *
+     * Use AddFB2 directly, do not go via GBM.
+     * Add support for multiplanar formats.
+     * Both require refactoring in the IAHWC-backend to
+     * support a mix of gbm_bos and iahwcfbs.
+     */
+    struct gbm_import_fd_data gbm_dmabuf = {
+        .fd = dmabuf->attributes.fd[0],
+        .width = dmabuf->attributes.width,
+        .height = dmabuf->attributes.height,
+        .stride = dmabuf->attributes.stride[0],
+        .format = dmabuf->attributes.format};
+
+    /* XXX: TODO:
+     *
+     * Currently the buffer is rejected if any dmabuf attribute
+     * flag is set.  This keeps us from passing an inverted /
+     * interlaced / bottom-first buffer (or any other type that may
+     * be added in the future) through to an overlay.  Ultimately,
+     * these types of buffers should be handled through buffer
+     * transforms and not as spot-checks requiring specific
+     * knowledge. */
+    if (dmabuf->attributes.n_planes != 1 || dmabuf->attributes.offset[0] != 0 ||
+        dmabuf->attributes.flags)
+      return NULL;
+
+    bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_FD, &gbm_dmabuf,
+                       GBM_BO_USE_SCANOUT);
+#else
+    return NULL;
+#endif
+  } else {
+    bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER, buffer_resource,
+                       GBM_BO_USE_SCANOUT);
+  }
+
+  if (!bo)
+    return NULL;
+
+  box = pixman_region32_extents(&ev->transform.boundingbox);
+  p->x = box->x1;
+  p->y = box->y1;
+
+  /*
+   * Calculate the source & dest rects properly based on actual
+   * position (note the caller has called weston_view_update_transform()
+   * for us already).
+   */
+  pixman_region32_init(&dest_rect);
+  pixman_region32_intersect(&dest_rect, &ev->transform.boundingbox,
+                            &output->base.region);
+  pixman_region32_translate(&dest_rect, -output->base.x, -output->base.y);
+  box = pixman_region32_extents(&dest_rect);
+  tbox = weston_transformed_rect(output->base.width, output->base.height,
+                                 output->base.transform,
+                                 output->base.current_scale, *box);
+  dest_x = tbox.x1;
+  dest_y = tbox.y1;
+  dest_w = tbox.x2 - tbox.x1;
+  dest_h = tbox.y2 - tbox.y1;
+  pixman_region32_fini(&dest_rect);
+
+  pixman_region32_init(&src_rect);
+  pixman_region32_intersect(&src_rect, &ev->transform.boundingbox,
+                            &output->base.region);
+  box = pixman_region32_extents(&src_rect);
+
+  weston_view_from_global_fixed(ev, wl_fixed_from_int(box->x1),
+                                wl_fixed_from_int(box->y1), &sx1, &sy1);
+  weston_view_from_global_fixed(ev, wl_fixed_from_int(box->x2),
+                                wl_fixed_from_int(box->y2), &sx2, &sy2);
+
+  if (sx1 < 0)
+    sx1 = 0;
+  if (sy1 < 0)
+    sy1 = 0;
+  if (sx2 > wl_fixed_from_int(ev->surface->width))
+    sx2 = wl_fixed_from_int(ev->surface->width);
+  if (sy2 > wl_fixed_from_int(ev->surface->height))
+    sy2 = wl_fixed_from_int(ev->surface->height);
+
+  tbox.x1 = sx1;
+  tbox.y1 = sy1;
+  tbox.x2 = sx2;
+  tbox.y2 = sy2;
+
+  tbox = weston_transformed_rect(wl_fixed_from_int(ev->surface->width),
+                                 wl_fixed_from_int(ev->surface->height),
+                                 viewport->buffer.transform,
+                                 viewport->buffer.scale, tbox);
+
+  src_x = tbox.x1 << 8;
+  src_y = tbox.y1 << 8;
+  src_w = (tbox.x2 - tbox.x1) << 8;
+  src_h = (tbox.y2 - tbox.y1) << 8;
+  pixman_region32_fini(&src_rect);
+
+  b->iahwc_create_layer(b->iahwc_device, 0, &output->overlay_layer_id);
+  b->iahwc_layer_set_usage(b->iahwc_device, 0, output->overlay_layer_id,
+                           IAHWC_LAYER_USAGE_CURSOR);
+  b->iahwc_layer_set_bo(b->iahwc_device, 0, output->overlay_layer_id, bo);
+  iahwc_rect_t source_crop = {src_x, src_y, src_w, src_h};
+
+  iahwc_rect_t display_frame = {dest_x, dest_y, dest_w, dest_h};
+
+  iahwc_region_t damage_region;
+  damage_region.numRects = 1;
+  damage_region.rects = &source_crop;
+
+  b->iahwc_layer_set_source_crop(b->iahwc_device, 0, output->overlay_layer_id,
+                                 source_crop);
+  b->iahwc_layer_set_display_frame(b->iahwc_device, 0, output->overlay_layer_id,
+                                   display_frame);
+  b->iahwc_layer_set_surface_damage(b->iahwc_device, 0,
+                                    output->overlay_layer_id, damage_region);
+
+  output->overlay_bo = bo;
+  return p;
+}
+
+/**
+ * Add a mode to output's mode list
+ *
+ * Copy the supplied IAHWC mode into a Weston mode structure, and add it to the
+ * output's mode list.
+ *
+ * @param output IAHWC output to add mode to
+ * @param info IAHWC mode structure to add
+ * @returns Newly-allocated Weston/IAHWC mode structure
+ */
+static struct iahwc_mode *iahwc_output_add_mode(struct iahwc_backend *b,
+                                                struct iahwc_output *output,
+                                                int config_id) {
+  struct iahwc_mode *mode;
+
+  mode = malloc(sizeof *mode);
+  if (mode == NULL)
+    return NULL;
+
+  mode->base.flags = 0;
+  b->iahwc_get_display_info(b->iahwc_device, 0, config_id, IAHWC_CONFIG_WIDTH,
+                            &mode->base.width);
+  b->iahwc_get_display_info(b->iahwc_device, 0, config_id, IAHWC_CONFIG_HEIGHT,
+                            &mode->base.height);
+  b->iahwc_get_display_info(b->iahwc_device, 0, config_id,
+                            IAHWC_CONFIG_REFRESHRATE, &mode->base.refresh);
+
+  mode->config_id = config_id;
+
+  // XXX/TODO: Get current mode and set preffered mode flag.
+  /* if (info->type & IAHWC_MODE_TYPE_PREFERRED) */
+  /*   mode->base.flags |= WL_OUTPUT_MODE_PREFERRED; */
+
+  wl_list_insert(output->base.mode_list.prev, &mode->base.link);
+
+  return mode;
+}
+
+static int iahwc_subpixel_to_wayland(int iahwc_value) {
+  switch (iahwc_value) {
+    default:
+    case DRM_MODE_SUBPIXEL_UNKNOWN:
+      return WL_OUTPUT_SUBPIXEL_UNKNOWN;
+    case DRM_MODE_SUBPIXEL_NONE:
+      return WL_OUTPUT_SUBPIXEL_NONE;
+    case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
+      return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB;
+    case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
+      return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR;
+    case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
+      return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB;
+    case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
+      return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR;
+  }
+}
+
+/* returns a value between 0-255 range, where higher is brighter */
+static uint32_t iahwc_get_backlight(struct iahwc_output *output) {
+  long brightness, max_brightness, norm;
+
+  brightness = backlight_get_brightness(output->backlight);
+  max_brightness = backlight_get_max_brightness(output->backlight);
+
+  /* convert it on a scale of 0 to 255 */
+  norm = (brightness * 255) / (max_brightness);
+
+  return (uint32_t)norm;
+}
+
+/* values accepted are between 0-255 range */
+static void iahwc_set_backlight(struct weston_output *output_base,
+                                uint32_t value) {
+  struct iahwc_output *output = to_iahwc_output(output_base);
+  long max_brightness, new_brightness;
+
+  if (!output->backlight)
+    return;
+
+  if (value > 255)
+    return;
+
+  max_brightness = backlight_get_max_brightness(output->backlight);
+
+  /* get denormalized value */
+  new_brightness = (value * max_brightness) / 255;
+
+  backlight_set_brightness(output->backlight, new_brightness);
+}
+
+static void iahwc_output_fini_cursor_egl(struct iahwc_output *output) {
+  unsigned int i;
+
+  for (i = 0; i < ARRAY_LENGTH(output->gbm_cursor_bo); i++) {
+    gbm_bo_destroy(output->gbm_cursor_bo[i]);
+    output->gbm_cursor_bo[i] = NULL;
+  }
+}
+
+static int iahwc_output_init_cursor_egl(struct iahwc_output *output,
+                                        struct iahwc_backend *b) {
+  unsigned int i;
+
+  for (i = 0; i < ARRAY_LENGTH(output->gbm_cursor_bo); i++) {
+    struct gbm_bo *bo;
+
+    bo = gbm_bo_create(b->gbm, b->cursor_width, b->cursor_height,
+                       GBM_FORMAT_ARGB8888,
+                       GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+    /* GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE); */
+    if (!bo) {
+      goto err;
+    }
+
+    output->gbm_cursor_bo[i] = bo;
+    if (!output->gbm_cursor_bo[i]) {
+      gbm_bo_destroy(bo);
+      goto err;
+    }
+  }
+
+  return 0;
+
+err:
+  weston_log("cursor buffers unavailable, using gl cursors\n");
+  b->cursors_are_broken = 1;
+  iahwc_output_fini_cursor_egl(output);
+  return -1;
+}
+
+/* Init output state that depends on gl or gbm */
+static int iahwc_output_init_egl(struct iahwc_output *output,
+                                 struct iahwc_backend *b) {
+  EGLint format[2] = {
+      output->gbm_format,
+      fallback_format_for(output->gbm_format),
+  };
+  int n_formats = 1;
+
+  output->gbm_surface =
+      gbm_surface_create(b->gbm, output->base.current_mode->width,
+                         output->base.current_mode->height, format[0],
+                         GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+  if (!output->gbm_surface) {
+    weston_log("failed to create gbm surface\n");
+    return -1;
+  }
+
+  if (format[1])
+    n_formats = 2;
+  if (gl_renderer->output_window_create(
+          &output->base, (EGLNativeWindowType)output->gbm_surface,
+          output->gbm_surface, gl_renderer->opaque_attribs, format,
+          n_formats) < 0) {
+    weston_log("failed to create gl renderer output state\n");
+    gbm_surface_destroy(output->gbm_surface);
+    return -1;
+  }
+
+  // iahwc_output_init_cursor_egl(output, b);
+
+  return 0;
+}
+
+static void iahwc_output_fini_egl(struct iahwc_output *output) {
+  gl_renderer->output_destroy(&output->base);
+  gbm_surface_destroy(output->gbm_surface);
+}
+
+static int iahwc_output_init_pixman(struct iahwc_output *output,
+                                    struct iahwc_backend *b) {
+  int w = output->base.current_mode->width;
+  int h = output->base.current_mode->height;
+  uint32_t format = output->gbm_format;
+  uint32_t pixman_format;
+  unsigned int i;
+
+  switch (format) {
+    case GBM_FORMAT_XRGB8888:
+      pixman_format = PIXMAN_x8r8g8b8;
+      break;
+    case GBM_FORMAT_RGB565:
+      pixman_format = PIXMAN_r5g6b5;
+      break;
+    default:
+      weston_log("Unsupported pixman format 0x%x\n", format);
+      return -1;
+  }
+
+  /* FIXME error checking */
+  for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) {
+    output->dumb[i] = iahwc_fb_create_dumb(b, w, h, format);
+    if (!output->dumb[i])
+      goto err;
+
+    output->image[i] = pixman_image_create_bits(
+        pixman_format, w, h, output->dumb[i]->map, output->dumb[i]->stride);
+    if (!output->image[i])
+      goto err;
+  }
+
+  if (pixman_renderer_output_create(&output->base) < 0)
+    goto err;
+
+  pixman_region32_init_rect(&output->previous_damage, output->base.x,
+                            output->base.y, output->base.width,
+                            output->base.height);
+
+  return 0;
+
+err:
+  for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) {
+    if (output->dumb[i])
+      iahwc_fb_unref(output->dumb[i]);
+    if (output->image[i])
+      pixman_image_unref(output->image[i]);
+
+    output->dumb[i] = NULL;
+    output->image[i] = NULL;
+  }
+
+  return -1;
+}
+
+static void iahwc_assign_planes(struct weston_output *output_base,
+                                void *repaint_data) {
+  struct iahwc_backend *b = to_iahwc_backend(output_base->compositor);
+  struct iahwc_output *output = to_iahwc_output(output_base);
+  struct weston_view *ev, *next;
+  pixman_region32_t overlap, surface_overlap;
+  struct weston_plane *primary, *next_plane;
+
+  pixman_region32_init(&overlap);
+  primary = &output_base->compositor->primary_plane;
+
+  output->cursor_view = NULL;
+  output->cursor_plane.x = INT32_MIN;
+  output->cursor_plane.y = INT32_MIN;
+
+  b->iahwc_display_clear_all_layers(b->iahwc_device, 0);
+  output->primary_layer_id = -1;
+  output->cursor_layer_id = -1;
+  output->overlay_layer_id = -1;
+  if (output->overlay_bo) {
+    gbm_bo_destroy(output->overlay_bo);
+    output->overlay_bo = NULL;
+  }
+
+  wl_list_for_each_safe(ev, next, &output_base->compositor->view_list, link) {
+    struct weston_surface *es = ev->surface;
+    pixman_box32_t *box = pixman_region32_extents(&ev->transform.boundingbox);
+    /* Test whether this buffer can ever go into a plane:
+     * non-shm, or small enough to be a cursor.
+     *
+     * Also, keep a reference when using the pixman renderer.
+     * That makes it possible to do a seamless switch to the GL
+     * renderer and since the pixman renderer keeps a reference
+     * to the buffer anyway, there is no side effects.
+     */
+    if (b->use_pixman ||
+        (es->buffer_ref.buffer &&
+         (!wl_shm_buffer_get(es->buffer_ref.buffer->resource) ||
+          (ev->surface->width <= b->cursor_width &&
+           ev->surface->height <= b->cursor_height))))
+      es->keep_buffer = true;
+    else
+      es->keep_buffer = false;
+
+    pixman_region32_init(&surface_overlap);
+    pixman_region32_intersect(&surface_overlap, &overlap,
+                              &ev->transform.boundingbox);
+
+    next_plane = NULL;
+    /* if (pixman_region32_not_empty(&surface_overlap)) */
+    /*         next_plane = primary; */
+    /* if (next_plane == NULL) */
+    /*         next_plane = iahwc_output_prepare_cursor_view(output, ev); */
+    /* next_plane = iahwc_output_prepare_overlay_view(output, ev); */
+    if (next_plane == NULL)
+      next_plane = primary;
+
+    weston_view_move_to_plane(ev, next_plane);
+
+    if (next_plane == primary) {
+      if (output->primary_layer_id == -1) {
+        b->iahwc_create_layer(b->iahwc_device, 0, &output->primary_layer_id);
+
+        iahwc_rect_t viewport = {0, 0, output_base->current_mode->width,
+                                 output_base->current_mode->height};
+
+        iahwc_region_t damage_region;
+        damage_region.numRects = 1;
+        damage_region.rects = &viewport;
+
+        b->iahwc_layer_set_source_crop(b->iahwc_device, 0,
+                                       output->primary_layer_id, viewport);
+        b->iahwc_layer_set_display_frame(b->iahwc_device, 0,
+                                         output->primary_layer_id, viewport);
+        b->iahwc_layer_set_surface_damage(
+            b->iahwc_device, 0, output->primary_layer_id, damage_region);
+      }
+      pixman_region32_union(&overlap, &overlap, &ev->transform.boundingbox);
+    }
+
+    if (next_plane == primary || next_plane == &output->cursor_plane) {
+      /* cursor plane involves a copy */
+      ev->psf_flags = 0;
+    } else {
+      /* All other planes are a direct scanout of a
+       * single client buffer.
+       */
+      ev->psf_flags = WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY;
+    }
+
+    pixman_region32_fini(&surface_overlap);
+  }
+  pixman_region32_fini(&overlap);
+}
+
+static void iahwc_output_fini_pixman(struct iahwc_output *output) {
+  unsigned int i;
+
+  pixman_renderer_output_destroy(&output->base);
+  pixman_region32_fini(&output->previous_damage);
+
+  for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) {
+    pixman_image_unref(output->image[i]);
+    iahwc_fb_unref(output->dumb[i]);
+    output->dumb[i] = NULL;
+    output->image[i] = NULL;
+  }
+}
+
+static void setup_output_seat_constraint(struct iahwc_backend *b,
+                                         struct weston_output *output,
+                                         const char *s) {
+  if (strcmp(s, "") != 0) {
+    struct weston_pointer *pointer;
+    struct udev_seat *seat;
+
+    seat = udev_seat_get_named(&b->input, s);
+    if (!seat)
+      return;
+
+    seat->base.output = output;
+
+    pointer = weston_seat_get_pointer(&seat->base);
+    if (pointer)
+      weston_pointer_clamp(pointer, &pointer->x, &pointer->y);
+  }
+}
+
+static int parse_gbm_format(const char *s, uint32_t default_value,
+                            uint32_t *gbm_format) {
+  int ret = 0;
+
+  if (s == NULL)
+    *gbm_format = default_value;
+  else if (strcmp(s, "xrgb8888") == 0)
+    *gbm_format = GBM_FORMAT_XRGB8888;
+  else if (strcmp(s, "rgb565") == 0)
+    *gbm_format = GBM_FORMAT_RGB565;
+  else if (strcmp(s, "xrgb2101010") == 0)
+    *gbm_format = GBM_FORMAT_XRGB2101010;
+  else {
+    weston_log("fatal: unrecognized pixel format: %s\n", s);
+    ret = -1;
+  }
+
+  return ret;
+}
+
+/**
+ * Choose suitable mode for an output
+ *
+ * Find the most suitable mode to use for initial setup (or reconfiguration on
+ * hotplug etc) for a IAHWC output.
+ *
+ * @param output IAHWC output to choose mode for
+ * @param kind Strategy and preference to use when choosing mode
+ * @param width Desired width for this output
+ * @param height Desired height for this output
+ * @param current_mode Mode currently being displayed on this output
+ * @param modeline Manually-entered mode (may be NULL)
+ * @returns A mode from the output's mode list, or NULL if none available
+ */
+static struct iahwc_mode *iahwc_output_choose_initial_mode(
+    struct iahwc_backend *backend, struct iahwc_output *output,
+    enum weston_iahwc_backend_output_mode mode, const char *modeline) {
+  struct iahwc_mode *iahwc_mode;
+  uint32_t active_config;
+
+  backend->iahwc_get_display_config(backend->iahwc_device, 0, &active_config);
+
+  wl_list_for_each_reverse(iahwc_mode, &output->base.mode_list, base.link) {
+    if (iahwc_mode->config_id == active_config)
+      return iahwc_mode;
+  }
+
+  weston_log("no available modes for %s\n", output->base.name);
+  return NULL;
+}
+
+// XXX/TODO: rewrite this to suit iahwc
+static int iahwc_output_set_mode(struct weston_output *base,
+                                 enum weston_iahwc_backend_output_mode mode,
+                                 const char *modeline) {
+  struct iahwc_output *output = to_iahwc_output(base);
+  struct iahwc_backend *b = to_iahwc_backend(base->compositor);
+
+  struct iahwc_mode *current;
+
+  current = iahwc_output_choose_initial_mode(b, output, mode, modeline);
+  if (!current)
+    return -1;
+
+  output->base.current_mode = &current->base;
+  output->base.current_mode->flags |= WL_OUTPUT_MODE_CURRENT;
+
+  /* Set native_ fields, so weston_output_mode_switch_to_native() works */
+  output->base.native_mode = output->base.current_mode;
+  output->base.native_scale = output->base.current_scale;
+
+  return 0;
+}
+
+static void iahwc_output_set_gbm_format(struct weston_output *base,
+                                        const char *gbm_format) {
+  struct iahwc_output *output = to_iahwc_output(base);
+  struct iahwc_backend *b = to_iahwc_backend(base->compositor);
+
+  if (parse_gbm_format(gbm_format, b->gbm_format, &output->gbm_format) == -1)
+    output->gbm_format = b->gbm_format;
+}
+
+static void iahwc_output_set_seat(struct weston_output *base,
+                                  const char *seat) {
+  struct iahwc_output *output = to_iahwc_output(base);
+  struct iahwc_backend *b = to_iahwc_backend(base->compositor);
+
+  setup_output_seat_constraint(b, &output->base, seat ? seat : "");
+}
+
+static int finish_frame_handler(void *data) {
+  struct iahwc_output *output = data;
+  struct timespec ts;
+
+  weston_compositor_read_presentation_clock(output->base.compositor, &ts);
+  weston_output_finish_frame(&output->base, &ts, 0);
+
+  return 1;
+}
+
+static int iahwc_output_enable(struct weston_output *base) {
+  struct iahwc_output *output = to_iahwc_output(base);
+  struct iahwc_backend *b = to_iahwc_backend(base->compositor);
+  struct weston_mode *m;
+  struct wl_event_loop *loop;
+
+  if (b->use_pixman) {
+    if (iahwc_output_init_pixman(output, b) < 0) {
+      weston_log("Failed to init output pixman state\n");
+      goto err;
+    }
+  } else if (iahwc_output_init_egl(output, b) < 0) {
+    weston_log("Failed to init output gl state\n");
+    goto err;
+  }
+
+  if (output->backlight) {
+    weston_log("Initialized backlight, device %s\n", output->backlight->path);
+    output->base.set_backlight = iahwc_set_backlight;
+    output->base.backlight_current = iahwc_get_backlight(output);
+  } else {
+    weston_log("Failed to initialize backlight\n");
+  }
+
+  // XXX/TODO: fill them  with the correct functions
+  output->base.start_repaint_loop = iahwc_output_start_repaint_loop;
+  output->base.repaint = iahwc_output_repaint;
+  // XXX/TODO: minimal plane assignment for now
+  output->base.assign_planes = iahwc_assign_planes;
+
+  // XXX/TODO: No dpms for now.
+  output->base.set_dpms = NULL;
+  output->base.switch_mode = iahwc_output_switch_mode;
+
+  output->base.set_gamma = iahwc_output_set_gamma;
+
+  weston_plane_init(&output->cursor_plane, b->compositor, INT32_MIN, INT32_MIN);
+  weston_plane_init(&output->d_plane, b->compositor, INT32_MIN, INT32_MIN);
+  weston_plane_init(&output->scanout_plane, b->compositor, 0, 0);
+
+  weston_compositor_stack_plane(b->compositor, &output->cursor_plane, NULL);
+  weston_compositor_stack_plane(b->compositor, &output->scanout_plane,
+                                &b->compositor->primary_plane);
+
+  loop = wl_display_get_event_loop(base->compositor->wl_display);
+  output->pageflip_timer =
+      wl_event_loop_add_timer(loop, finish_frame_handler, output);
+
+  output->frame_commited = 0;
+
+  weston_log("Output %s, (connector %d, crtc %d)\n", output->base.name,
+             output->connector_id, output->crtc_id);
+  wl_list_for_each(m, &output->base.mode_list, link) weston_log_continue(
+      STAMP_SPACE "mode %dx%d@%.1d\n", m->width, m->height, m->refresh);
+
+  output->state_invalid = true;
+
+  return 0;
+
+err:
+  return -1;
+}
+
+static void iahwc_output_deinit(struct weston_output *base) {
+  struct iahwc_output *output = to_iahwc_output(base);
+  struct iahwc_backend *b = to_iahwc_backend(base->compositor);
+
+  /* output->fb_last and output->fb_pending must not be set here;
+   * destroy_pending/disable_pending exist to guarantee exactly this. */
+  assert(!output->fb_last);
+  assert(!output->fb_pending);
+  iahwc_fb_unref(output->fb_current);
+  output->fb_current = NULL;
+
+  if (b->use_pixman)
+    iahwc_output_fini_pixman(output);
+  else
+    iahwc_output_fini_egl(output);
+
+  weston_plane_release(&output->scanout_plane);
+  weston_plane_release(&output->cursor_plane);
+}
+
+static void iahwc_output_destroy(struct weston_output *base) {
+  struct iahwc_output *output = to_iahwc_output(base);
+  struct iahwc_mode *iahwc_mode, *next;
+
+  wl_list_for_each_safe(iahwc_mode, next, &output->base.mode_list, base.link) {
+    wl_list_remove(&iahwc_mode->base.link);
+    free(iahwc_mode);
+  }
+
+  weston_output_release(&output->base);
+
+  if (output->backlight)
+    backlight_destroy(output->backlight);
+
+  free(output);
+}
+
+static int iahwc_output_disable(struct weston_output *base) {
+  struct iahwc_output *output = to_iahwc_output(base);
+
+  if (output->page_flip_pending) {
+    output->disable_pending = 1;
+    return -1;
+  }
+
+  if (output->base.enabled)
+    iahwc_output_deinit(&output->base);
+
+  output->disable_pending = 0;
+
+  weston_log("Disabling output %s\n", output->base.name);
+
+  return 0;
+}
+
+static int vsync_callback(iahwc_callback_data_t data, iahwc_display_t display,
+                          int64_t timestamp) {
+  struct iahwc_output *output = data;
+  struct timespec ts;
+  ts.tv_nsec = timestamp;
+  ts.tv_sec = timestamp / (1000 * 1000 * 1000);
+
+  if (output->pageflip_timer && output->frame_commited)
+    wl_event_source_timer_update(output->pageflip_timer, 1);
+
+  output->frame_commited = 0;
+
+  return 1;
+}
+
+/**
+ * Create a Weston output structure
+ *
+ * Given a IAHWC connector, create a matching iahwc_output structure and add it
+ * to Weston's output list. It also takes ownership of the connector, which
+ * is released when output is destroyed.
+ *
+ * @param b Weston backend structure
+ * @param resources IAHWC resources for this device
+ * @param connector IAHWC connector to use for this new output
+ * @param iahwc_device udev device pointer
+ * @returns 0 on success, or -1 on failure
+ */
+// XXX?TODO: Rename this function
+static int create_output_for_connector(struct iahwc_backend *b) {
+  struct iahwc_output *output;
+  struct iahwc_mode *iahwc_mode;
+  char *name;
+  const char *make = "unknown";
+  const char *model = "unknown";
+  const char *serial_number = "unknown";
+  int i, num_displays;
+  uint32_t size, num_configs;
+  uint32_t *configs;
+  int ret;
+
+  output = zalloc(sizeof *output);
+  if (output == NULL)
+    goto err;
+
+  b->iahwc_get_num_displays(b->iahwc_device, &num_displays);
+
+  if (num_displays < 1) {
+    weston_log("Unable to find any connected displays");
+    goto err;
+  }
+  /* output->backlight = backlight_init(iahwc_device, */
+  /*                              connector->connector_type); */
+
+  // XXX/TODO: support more than one display
+
+  b->iahwc_get_display_name(b->iahwc_device, 0, &size, NULL);
+  weston_log("Size of name is %d\n", size);
+  name = (char *)calloc(size + 1, sizeof(char));
+  b->iahwc_get_display_name(b->iahwc_device, 0, &size, name);
+  name[size] = '\0';
+
+  weston_log("Name of the display is %s\n", name);
+
+  weston_output_init(&output->base, b->compositor, name);
+  free(name);
+
+  // XXX/TODO: Fill these with appropriate functions.
+  output->base.enable = iahwc_output_enable;
+  output->base.destroy = iahwc_output_destroy;
+  output->base.disable = iahwc_output_disable;
+
+  output->destroy_pending = 0;
+  output->disable_pending = 0;
+
+  output->base.make = (char *)make;
+  output->base.model = (char *)model;
+  output->base.serial_number = (char *)serial_number;
+  output->base.subpixel = iahwc_subpixel_to_wayland(DRM_MODE_SUBPIXEL_UNKNOWN);
+
+  output->base.connection_internal = true;
+
+  output->cursor_layer_id = -1;
+  output->primary_layer_id = -1;
+
+  b->iahwc_get_display_configs(b->iahwc_device, 0, &num_configs, NULL);
+  configs = (uint32_t *)calloc(num_configs, sizeof(uint32_t));
+  b->iahwc_get_display_configs(b->iahwc_device, 0, &num_configs, configs);
+
+  b->iahwc_get_display_info(b->iahwc_device, 0, configs[0], IAHWC_CONFIG_DPIX,
+                            &output->base.mm_width);
+  b->iahwc_get_display_info(b->iahwc_device, 0, configs[0], IAHWC_CONFIG_DPIY,
+                            &output->base.mm_height);
+
+  for (i = 0; i < num_configs; i++) {
+    iahwc_mode = iahwc_output_add_mode(b, output, configs[i]);
+    if (!iahwc_mode) {
+      iahwc_output_destroy(&output->base);
+      return -1;
+    }
+  }
+
+  ret =
+      b->iahwc_register_callback(b->iahwc_device, IAHWC_CALLBACK_VSYNC, 0,
+                                 output, (iahwc_function_ptr_t)vsync_callback);
+
+  if (ret != IAHWC_ERROR_NONE) {
+    weston_log("unable to register callback\n");
+  }
+
+  weston_compositor_add_pending_output(&output->base, b->compositor);
+
+  return 0;
+
+err:
+
+  return -1;
+}
+
+static int create_outputs(struct iahwc_backend *b) {
+  create_output_for_connector(b);
+
+  if (wl_list_empty(&b->compositor->output_list) &&
+      wl_list_empty(&b->compositor->pending_output_list))
+    weston_log("No currently active connector found.\n");
+
+  return 0;
+}
+
+static void iahwc_restore(struct weston_compositor *ec) {
+  weston_launcher_restore(ec->launcher);
+}
+
+static void iahwc_destroy(struct weston_compositor *ec) {
+  struct iahwc_backend *b = to_iahwc_backend(ec);
+
+  udev_input_destroy(&b->input);
+
+  wl_event_source_remove(b->udev_iahwc_source);
+  wl_event_source_remove(b->iahwc_source);
+
+  weston_compositor_shutdown(ec);
+
+  if (b->gbm)
+    gbm_device_destroy(b->gbm);
+
+  udev_unref(b->udev);
+
+  weston_launcher_destroy(ec->launcher);
+
+  b->iahwc_device->close(b->iahwc_device);
+
+  free(b);
+}
+
+static void session_notify(struct wl_listener *listener, void *data) {
+  struct weston_compositor *compositor = data;
+  struct iahwc_backend *b = to_iahwc_backend(compositor);
+  struct iahwc_output *output;
+
+  if (compositor->session_active) {
+    weston_log("activating session\n");
+    weston_compositor_wake(compositor);
+    weston_compositor_damage_all(compositor);
+
+    wl_list_for_each(output, &compositor->output_list, base.link)
+        output->state_invalid = true;
+
+    udev_input_enable(&b->input);
+  } else {
+    weston_log("deactivating session\n");
+    udev_input_disable(&b->input);
+
+    weston_compositor_offscreen(compositor);
+
+    /* If we have a repaint scheduled (either from a
+     * pending pageflip or the idle handler), make sure we
+     * cancel that so we don't try to pageflip when we're
+     * vt switched away.  The OFFSCREEN state will prevent
+     * further attempts at repainting.  When we switch
+     * back, we schedule a repaint, which will process
+     * pending frame callbacks. */
+  }
+}
+
+static void planes_binding(struct weston_keyboard *keyboard, uint32_t time,
+                           uint32_t key, void *data) {
+  struct iahwc_backend *b = data;
+
+  switch (key) {
+    case KEY_C:
+      b->cursors_are_broken ^= 1;
+      break;
+    case KEY_V:
+      b->sprites_are_broken ^= 1;
+      break;
+    case KEY_O:
+      b->sprites_hidden ^= 1;
+      break;
+    default:
+      break;
+  }
+}
+
+static void switch_to_gl_renderer(struct iahwc_backend *b) {
+  struct iahwc_output *output;
+  bool dmabuf_support_inited;
+
+  if (!b->use_pixman)
+    return;
+
+  dmabuf_support_inited = !!b->compositor->renderer->import_dmabuf;
+
+  weston_log("Switching to GL renderer\n");
+
+  b->gbm = create_gbm_device(b->iahwc.fd);
+  if (!b->gbm) {
+    weston_log(
+        "Failed to create gbm device. "
+        "Aborting renderer switch\n");
+    return;
+  }
+
+  wl_list_for_each(output, &b->compositor->output_list, base.link)
+      pixman_renderer_output_destroy(&output->base);
+
+  b->compositor->renderer->destroy(b->compositor);
+
+  if (iahwc_backend_create_gl_renderer(b) < 0) {
+    gbm_device_destroy(b->gbm);
+    weston_log("Failed to create GL renderer. Quitting.\n");
+    /* FIXME: we need a function to shutdown cleanly */
+    assert(0);
+  }
+
+  wl_list_for_each(output, &b->compositor->output_list, base.link)
+      iahwc_output_init_egl(output, b);
+
+  b->use_pixman = 0;
+
+  if (!dmabuf_support_inited && b->compositor->renderer->import_dmabuf) {
+    if (linux_dmabuf_setup(b->compositor) < 0)
+      weston_log(
+          "Error: initializing dmabuf "
+          "support failed.\n");
+  }
+}
+
+static void renderer_switch_binding(struct weston_keyboard *keyboard,
+                                    uint32_t time, uint32_t key, void *data) {
+  struct iahwc_backend *b = to_iahwc_backend(keyboard->seat->compositor);
+
+  switch_to_gl_renderer(b);
+}
+
+static const struct weston_iahwc_output_api api = {
+    iahwc_output_set_mode,
+    iahwc_output_set_gbm_format,
+    iahwc_output_set_seat,
+};
+
+static struct iahwc_backend *iahwc_backend_create(
+    struct weston_compositor *compositor,
+    struct weston_iahwc_backend_config *config) {
+  struct iahwc_backend *b;
+  void *iahwc_dl_handle, *gl_handle;
+  iahwc_module_t *iahwc_module;
+  iahwc_device_t *iahwc_device;
+
+  /* const char* device = "/dev/dri/renderD128"; */
+  const char *device = "/dev/dri/card0";
+  const char *seat_id = default_seat;
+
+  weston_log("initializing iahwc backend\n");
+
+  b = zalloc(sizeof *b);
+  if (b == NULL)
+    return NULL;
+
+  b->compositor = compositor;
+  compositor->backend = &b->base;
+
+  // XXX/TODO: use pixman
+  b->use_pixman = 0;
+
+  // Work around when libhwcomposer is not linked to lib{EGL,GLESv2}
+  gl_handle = dlopen("libEGL.so", RTLD_NOW | RTLD_GLOBAL);
+  if (!gl_handle)
+    weston_log("Unable to open libEGL.so: %s\n", dlerror());
+
+  gl_handle = dlopen("libGLESv2.so", RTLD_NOW | RTLD_GLOBAL);
+  if (!gl_handle) {
+    weston_log("Unable to open libGLESv2.so: %s\n", dlerror());
+    weston_log("Unable to open libhwcomposer prerequisites aborting...\n");
+    abort();
+  }
+
+  // XXX/TODO: Initialize hwc
+  iahwc_dl_handle = dlopen("libhwcomposer.so", RTLD_NOW);
+  if (!iahwc_dl_handle) {
+    weston_log("Unable to open libhwcomposer.so: %s\n", dlerror());
+    weston_log("aborting...\n");
+    abort();
+  }
+
+  iahwc_module = (iahwc_module_t *)dlsym(iahwc_dl_handle, IAHWC_MODULE_STR);
+  iahwc_module->open(iahwc_module, &iahwc_device);
+
+  b->iahwc_module = iahwc_module;
+  b->iahwc_device = iahwc_device;
+
+  // XXX/TODO: Get all required function poinsters. add them to iahwc_backend
+  b->iahwc_get_num_displays =
+      (IAHWC_PFN_GET_NUM_DISPLAYS)iahwc_device->getFunctionPtr(
+          iahwc_device, IAHWC_FUNC_GET_NUM_DISPLAYS);
+  b->iahwc_create_layer = (IAHWC_PFN_CREATE_LAYER)iahwc_device->getFunctionPtr(
+      iahwc_device, IAHWC_FUNC_CREATE_LAYER);
+  b->iahwc_get_display_info =
+      (IAHWC_PFN_DISPLAY_GET_INFO)iahwc_device->getFunctionPtr(
+          iahwc_device, IAHWC_FUNC_DISPLAY_GET_INFO);
+  b->iahwc_get_display_configs =
+      (IAHWC_PFN_DISPLAY_GET_CONFIGS)iahwc_device->getFunctionPtr(
+          iahwc_device, IAHWC_FUNC_DISPLAY_GET_CONFIGS);
+  b->iahwc_get_display_name =
+      (IAHWC_PFN_DISPLAY_GET_NAME)iahwc_device->getFunctionPtr(
+          iahwc_device, IAHWC_FUNC_DISPLAY_GET_NAME);
+  b->iahwc_set_display_gamma =
+      (IAHWC_PFN_DISPLAY_SET_GAMMA)iahwc_device->getFunctionPtr(
+          iahwc_device, IAHWC_FUNC_DISPLAY_SET_GAMMA);
+  b->iahwc_set_display_config =
+      (IAHWC_PFN_DISPLAY_SET_CONFIG)iahwc_device->getFunctionPtr(
+          iahwc_device, IAHWC_FUNC_DISPLAY_SET_CONFIG);
+  b->iahwc_get_display_config =
+      (IAHWC_PFN_DISPLAY_GET_CONFIG)iahwc_device->getFunctionPtr(
+          iahwc_device, IAHWC_FUNC_DISPLAY_GET_CONFIG);
+  b->iahwc_display_clear_all_layers =
+      (IAHWC_PFN_DISPLAY_CLEAR_ALL_LAYERS)iahwc_device->getFunctionPtr(
+          iahwc_device, IAHWC_FUNC_DISPLAY_CLEAR_ALL_LAYERS);
+  b->iahwc_present_display =
+      (IAHWC_PFN_PRESENT_DISPLAY)iahwc_device->getFunctionPtr(
+          iahwc_device, IAHWC_FUNC_PRESENT_DISPLAY);
+  b->iahwc_layer_set_bo = (IAHWC_PFN_LAYER_SET_BO)iahwc_device->getFunctionPtr(
+      iahwc_device, IAHWC_FUNC_LAYER_SET_BO);
+  b->iahwc_layer_set_raw_pixel_data =
+      (IAHWC_PFN_LAYER_SET_RAW_PIXEL_DATA)iahwc_device->getFunctionPtr(
+          iahwc_device, IAHWC_FUNC_LAYER_SET_RAW_PIXEL_DATA);
+  b->iahwc_layer_set_acquire_fence =
+      (IAHWC_PFN_LAYER_SET_ACQUIRE_FENCE)iahwc_device->getFunctionPtr(
+          iahwc_device, IAHWC_FUNC_LAYER_SET_ACQUIRE_FENCE);
+  b->iahwc_layer_set_source_crop =
+      (IAHWC_PFN_LAYER_SET_SOURCE_CROP)iahwc_device->getFunctionPtr(
+          iahwc_device, IAHWC_FUNC_LAYER_SET_SOURCE_CROP);
+  b->iahwc_layer_set_display_frame =
+      (IAHWC_PFN_LAYER_SET_DISPLAY_FRAME)iahwc_device->getFunctionPtr(
+          iahwc_device, IAHWC_FUNC_LAYER_SET_DISPLAY_FRAME);
+  b->iahwc_layer_set_surface_damage =
+      (IAHWC_PFN_LAYER_SET_SURFACE_DAMAGE)iahwc_device->getFunctionPtr(
+          iahwc_device, IAHWC_FUNC_LAYER_SET_SURFACE_DAMAGE);
+  b->iahwc_layer_set_usage =
+      (IAHWC_PFN_LAYER_SET_USAGE)iahwc_device->getFunctionPtr(
+          iahwc_device, IAHWC_FUNC_LAYER_SET_USAGE);
+  b->iahwc_register_callback =
+      (IAHWC_PFN_REGISTER_CALLBACK)iahwc_device->getFunctionPtr(
+          iahwc_device, IAHWC_FUNC_REGISTER_CALLBACK);
+
+  if (parse_gbm_format(config->gbm_format, GBM_FORMAT_XRGB8888,
+                       &b->gbm_format) < 0)
+    goto err_compositor;
+
+  // Check if we run drm-backend using weston-launch
+  compositor->launcher =
+      weston_launcher_connect(compositor, config->tty, seat_id, true);
+  if (compositor->launcher == NULL) {
+    weston_log(
+        "fatal: drm backend should be run "
+        "using weston-launch binary or as root\n");
+    goto err_compositor;
+  }
+
+  b->iahwc.fd = weston_launcher_open(b->compositor->launcher, device, O_RDWR);
+  if (b->iahwc.fd < 0) {
+    weston_log("unable to open gpu file\n");
+    goto err_compositor;
+  }
+
+  b->udev = udev_new();
+  if (b->udev == NULL) {
+    weston_log("failed to initialize udev context\n");
+    goto err_compositor;
+  }
+
+  if (config->seat_id)
+    seat_id = config->seat_id;
+
+  if (create_outputs(b) < 0) {
+    weston_log("failed to create output");
+    goto err_compositor;
+  }
+
+  // session_notification XXX?TODO: make necessary changes
+  b->session_listener.notify = session_notify;
+  wl_signal_add(&compositor->session_signal, &b->session_listener);
+
+  // XXX/TODO: currently using egl, add pixman support?
+  if (init_egl(b) < 0) {
+    weston_log("failed to initialize egl\n");
+    goto err_compositor;
+  }
+
+  b->cursor_width = 256;
+  b->cursor_height = 256;
+
+  b->base.destroy = iahwc_destroy;
+  b->base.restore = iahwc_restore;
+  b->base.repaint_begin = iahwc_repaint_begin;
+  b->base.repaint_flush = iahwc_repaint_flush;
+  b->base.repaint_cancel = iahwc_repaint_cancel;
+
+  wl_list_init(&b->plane_list);
+
+  // XXX/TODO: No sprites for now
+  // XXX/TODO: Add api in hwc to get plane info.
+  // create_sprites(b);
+
+  if (udev_input_init(&b->input, compositor, b->udev, seat_id,
+                      config->configure_device) < 0) {
+    weston_log("failed to create input devices\n");
+    goto err_compositor;
+  }
+
+  // XXX/TODO: setup hotplugging support from IAHWC
+
+  weston_setup_vt_switch_bindings(compositor);
+
+  // XXX/TODO: just import not sure we need it
+  weston_compositor_add_debug_binding(compositor, KEY_O, planes_binding, b);
+  weston_compositor_add_debug_binding(compositor, KEY_C, planes_binding, b);
+  weston_compositor_add_debug_binding(compositor, KEY_V, planes_binding, b);
+  /* weston_compositor_add_debug_binding(compositor, KEY_Q, */
+  /*                                     recorder_binding, b); */
+  weston_compositor_add_debug_binding(compositor, KEY_W,
+                                      renderer_switch_binding, b);
+
+  // XXX/TODO: This should be there.
+  if (compositor->renderer->import_dmabuf) {
+    if (linux_dmabuf_setup(compositor) < 0)
+      weston_log(
+          "Error: initializing dmabuf "
+          "support failed.\n");
+  }
+
+  int ret = weston_plugin_api_register(compositor, WESTON_IAHWC_OUTPUT_API_NAME,
+                                       &api, sizeof(api));
+
+  if (ret)
+    goto err_compositor;
+
+  return b;
+
+err_compositor:
+  weston_compositor_shutdown(compositor);
+  free(b);
+  return NULL;
+}
+
+static void config_init_to_defaults(
+    struct weston_iahwc_backend_config *config) {
+}
+
+WL_EXPORT int weston_backend_init(struct weston_compositor *compositor,
+                                  struct weston_backend_config *config_base) {
+  struct iahwc_backend *b;
+  struct weston_iahwc_backend_config config = {{
+      0,
+  }};
+
+  if (config_base == NULL ||
+      config_base->struct_version != WESTON_IAHWC_BACKEND_CONFIG_VERSION ||
+      config_base->struct_size > sizeof(struct weston_iahwc_backend_config)) {
+    weston_log("iahwc backend config structure is invalid\n");
+    return -1;
+  }
+
+  config_init_to_defaults(&config);
+  memcpy(&config, config_base, config_base->struct_size);
+
+  b = iahwc_backend_create(compositor, &config);
+  if (b == NULL)
+    return -1;
+
+  return 0;
+}
diff --git a/os/linux/weston/plugin/compositor-iahwc.h b/os/linux/weston/plugin/compositor-iahwc.h
new file mode 100644 (file)
index 0000000..6f86b74
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+// Copyright (c) 2016 Intel Corporation
+//
+// 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 WESTON_COMPOSITOR_IAHWC_H
+#define WESTON_COMPOSITOR_IAHWC_H
+
+#include "compositor.h"
+#include "plugin-registry.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define WESTON_IAHWC_BACKEND_CONFIG_VERSION 1
+
+struct libinput_device;
+
+enum weston_iahwc_backend_output_mode {
+  /** The output is disabled */
+  WESTON_IAHWC_BACKEND_OUTPUT_OFF,
+  /** The output will use the current active mode */
+  WESTON_IAHWC_BACKEND_OUTPUT_CURRENT,
+  /** The output will use the preferred mode. A modeline can be provided
+   * by setting weston_backend_output_config::modeline in the form of
+   * "WIDTHxHEIGHT" or in the form of an explicit modeline calculated
+   * using e.g. the cvt tool. If a valid modeline is supplied it will be
+   * used, if invalid or NULL the preferred available mode will be used. */
+  WESTON_IAHWC_BACKEND_OUTPUT_PREFERRED,
+};
+
+#define WESTON_IAHWC_OUTPUT_API_NAME "weston_iahwc_output_api_v1"
+
+struct weston_iahwc_output_api {
+  /** The mode to be used by the output. Refer to the documentation
+   *  of WESTON_IAHWC_BACKEND_OUTPUT_PREFERRED for details.
+   *
+   * Returns 0 on success, -1 on failure.
+   */
+  int (*set_mode)(struct weston_output *output,
+                  enum weston_iahwc_backend_output_mode mode,
+                  const char *modeline);
+
+  /** The pixel format to be used by the output. Valid values are:
+   * - NULL - The format set at backend creation time will be used;
+   * - "xrgb8888";
+   * - "rgb565"
+   * - "xrgb2101010"
+   */
+  void (*set_gbm_format)(struct weston_output *output, const char *gbm_format);
+
+  /** The seat to be used by the output. Set to NULL to use the
+   *  default seat.
+   */
+  void (*set_seat)(struct weston_output *output, const char *seat);
+};
+
+static inline const struct weston_iahwc_output_api *weston_iahwc_output_get_api(
+    struct weston_compositor *compositor) {
+  const void *api;
+  api = weston_plugin_api_get(compositor, WESTON_IAHWC_OUTPUT_API_NAME,
+                              sizeof(struct weston_iahwc_output_api));
+
+  return (const struct weston_iahwc_output_api *)api;
+}
+
+/** The backend configuration struct.
+ *
+ * weston_iahwc_backend_config contains the configuration used by a IAHWC
+ * backend.
+ */
+struct weston_iahwc_backend_config {
+  struct weston_backend_config base;
+
+  /** The tty to be used. Set to 0 to use the current tty. */
+  int tty;
+
+  /** Whether to use the pixman renderer instead of the OpenGL ES renderer. */
+  bool use_pixman;
+
+  /** The seat to be used for input and output.
+   *
+   * If NULL the default "seat0" will be used.  The backend will
+   * take ownership of the seat_id pointer and will free it on
+   * backend destruction.
+   */
+  char *seat_id;
+
+  /** The pixel format of the framebuffer to be used.
+   *
+   * Valid values are:
+   * - NULL - The default format ("xrgb8888") will be used;
+   * - "xrgb8888";
+   * - "rgb565"
+   * - "xrgb2101010"
+   * The backend will take ownership of the format pointer and will free
+   * it on backend destruction.
+   */
+  char *gbm_format;
+
+  /** Callback used to configure input devices.
+   *
+   * This function will be called by the backend when a new input device
+   * needs to be configured.
+   * If NULL the device will use the default configuration.
+   */
+  void (*configure_device)(struct weston_compositor *compositor,
+                           struct libinput_device *device);
+
+  /** Maximum duration for a pageflip event to arrive, after which the
+   * compositor will consider the IAHWC driver crashed and will try to exit
+   * cleanly.
+   *
+   * It is exprimed in milliseconds, 0 means disabled. */
+  uint32_t pageflip_timeout;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WESTON_COMPOSITOR_IAHWC_H */