Introduce tdm-backend
authorChangyeon Lee <cyeon.lee@samsung.com>
Wed, 28 Jul 2021 09:38:24 +0000 (18:38 +0900)
committerChangyeon Lee <cyeon.lee@samsung.com>
Wed, 25 Aug 2021 07:48:28 +0000 (16:48 +0900)
13 files changed:
compositor/main.c
include/libweston/libweston.h
libweston/backend-tdm/meson.build [new file with mode: 0644]
libweston/backend-tdm/tdm-internal.h [new file with mode: 0644]
libweston/backend-tdm/tdm.c [new file with mode: 0644]
libweston/compositor.c
libweston/meson.build
libweston/renderer-gl/gl-renderer-internal.h
libweston/renderer-gl/gl-renderer.c
libweston/renderer-gl/meson.build
meson_options.txt
packaging/weston.spec
shared/os-compatibility.c

index 8eb8a470e243e2687818c993630677c9c988df05..035ee32333950945a7bc0222bf21dccf71f13127 100644 (file)
@@ -642,6 +642,9 @@ usage(int error_code)
 #endif
 #if defined(BUILD_X11_COMPOSITOR)
                        "\t\t\t\tx11-backend.so\n"
+#endif
+#if defined(BUILD_TDM_COMPOSITOR)
+                       "\t\t\t\ttdm-backend.so\n"
 #endif
                "  --shell=MODULE\tShell module, defaults to desktop-shell.so\n"
                "  -S, --socket=NAME\tName of socket to listen on\n"
@@ -735,6 +738,15 @@ usage(int error_code)
                "  --no-input\t\tDont create input devices\n\n");
 #endif
 
+#if defined(BUILD_TDM_COMPOSITOR)
+       fprintf(out,
+               "Options for tdm-backend.so:\n\n"
+               "  --seat=SEAT\t\tThe seat that weston should run on, instead of the seat defined in XDG_SEAT\n"
+               "  --tty=TTY\t\tThe tty to use\n"
+               "  --use-pixman\t\tUse the pixman (CPU) renderer\n"
+               "  --continue-without-input\tAllow the compositor to start without input devices\n\n");
+#endif
+
        exit(error_code);
 }
 
@@ -2980,6 +2992,60 @@ load_wayland_backend(struct weston_compositor *c,
        return 0;
 }
 
+static int
+load_tdm_backend(struct weston_compositor *c,
+                int *argc, char **argv, struct weston_config *wc)
+{
+       struct weston_drm_backend_config config = {{ 0, }};
+       struct weston_config_section *section;
+       struct wet_compositor *wet = to_wet_compositor(c);
+       int ret = 0;
+
+       section = weston_config_get_section(wc, "core", NULL, NULL);
+       weston_config_section_get_bool(section, "use-pixman", &config.use_pixman,
+                                      false);
+
+       const struct weston_option options[] = {
+               { WESTON_OPTION_STRING, "seat", 0, &config.seat_id },
+               { WESTON_OPTION_INTEGER, "tty", 0, &config.tty },
+               { WESTON_OPTION_STRING, "drm-device", 0, &config.specific_device },
+               { 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);
+       weston_config_section_get_bool(section, "pixman-shadow",
+                                      &config.use_pixman_shadow, true);
+
+       config.base.struct_version = WESTON_DRM_BACKEND_CONFIG_VERSION;
+       config.base.struct_size = sizeof(struct weston_drm_backend_config);
+       config.configure_device = configure_input_device;
+
+       wet->heads_changed_listener.notify = drm_heads_changed;
+       weston_compositor_add_heads_changed_listener(c,
+                                               &wet->heads_changed_listener);
+
+       ret = weston_compositor_load_backend(c, WESTON_BACKEND_TDM,
+                                            &config.base);
+
+       /* remoting */
+       load_remoting(c, wc);
+
+       /* pipewire */
+       load_pipewire(c, wc);
+
+       free(config.seat_id);
+
+       return ret;
+}
+
 
 static int
 load_backend(struct weston_compositor *compositor, const char *backend,
@@ -2997,6 +3063,8 @@ load_backend(struct weston_compositor *compositor, const char *backend,
                return load_x11_backend(compositor, argc, argv, config);
        else if (strstr(backend, "wayland-backend.so"))
                return load_wayland_backend(compositor, argc, argv, config);
+       else if (strstr(backend, "tdm-backend.so"))
+               return load_tdm_backend(compositor, argc, argv, config);
 
        weston_log("Error: unknown backend \"%s\"\n", backend);
        return -1;
index dc54b170df09cdc85be2d23221440426f3c6c44a..d191669288a26a6e4036630b4da5857ba12bbc0b 100644 (file)
@@ -1783,6 +1783,7 @@ enum weston_compositor_backend {
        WESTON_BACKEND_RDP,
        WESTON_BACKEND_WAYLAND,
        WESTON_BACKEND_X11,
+       WESTON_BACKEND_TDM,
 };
 
 int
diff --git a/libweston/backend-tdm/meson.build b/libweston/backend-tdm/meson.build
new file mode 100644 (file)
index 0000000..bec333d
--- /dev/null
@@ -0,0 +1,35 @@
+if not get_option('backend-tdm')
+       subdir_done()
+endif
+
+config_h.set('BUILD_TDM_COMPOSITOR', '1')
+
+srcs_tdm = [
+       'tdm.c',
+       linux_dmabuf_unstable_v1_protocol_c,
+       linux_dmabuf_unstable_v1_server_protocol_h,
+       presentation_time_server_protocol_h,
+]
+
+deps_tdm = [
+       dep_libdl,
+       dep_libweston_private,
+       dep_session_helper,
+       dep_libinput_backend,
+       dependency('libtbm'),
+       dependency('libtdm'),
+       dependency('libudev', version: '>= 136'),
+]
+
+plugin_tdm = shared_library(
+       'tdm-backend',
+       srcs_tdm,
+       include_directories: common_inc,
+       dependencies: deps_tdm,
+       name_prefix: '',
+       install: true,
+       install_dir: dir_module_libweston
+)
+env_modmap += 'tdm-backend.so=@0@;'.format(plugin_tdm.full_path())
+
+install_headers(backend_drm_h, subdir: dir_include_libweston_install)
diff --git a/libweston/backend-tdm/tdm-internal.h b/libweston/backend-tdm/tdm-internal.h
new file mode 100644 (file)
index 0000000..6fb8818
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2011 Intel Corporation
+ * Copyright © 2017, 2018 Collabora, Ltd.
+ * Copyright © 2017, 2018 General Electric Company
+ * Copyright (c) 2018 DisplayLink (UK) Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/input.h>
+#include <linux/vt.h>
+#include <assert.h>
+#include <sys/mman.h>
+#include <time.h>
+
+#include <libudev.h>
+
+#include <libweston/libweston.h>
+#include <libweston/backend-drm.h>
+#include <libweston/weston-log.h>
+#include <tdm.h>
+#include <tbm_bufmgr.h>
+#include <tbm_surface_internal.h>
+#include <tbm_dummy_display.h>
+#include "shared/helpers.h"
+#include "libinput-seat.h"
+#include "backend.h"
+#include "libweston-internal.h"
+
+#define tdm_debug(b, ...) \
+       weston_log_scope_printf((b)->debug, __VA_ARGS__)
+
+/**
+ * Possible values for the WTDM_PLANE_TYPE property.
+ */
+enum wtdm_backend_plane_type {
+       WTDM_PLANE_TYPE_PRIMARY = 0,
+       WTDM_PLANE_TYPE_OVERLAY,
+};
+
+struct tdm_backend {
+       struct weston_backend base;
+       struct weston_compositor *compositor;
+
+       struct udev *udev;
+       struct wl_event_source *tdm_source;
+
+       struct wl_listener session_listener;
+       uint32_t gbm_format;
+
+       struct udev_input input;
+
+       bool use_pixman;
+       bool use_pixman_shadow;
+
+       uint32_t pageflip_timeout;
+
+       struct weston_log_scope *debug;
+
+       tbm_bufmgr tbufmgr;
+       tbm_dummy_display *tbm_display;
+       tdm_display *tdisplay;
+
+       int tdm_fd;
+
+       struct wl_list plane_list;
+};
+
+struct tdm_backend_mode {
+       struct weston_mode base;
+
+       struct wl_list head_link;
+       const tdm_output_mode *tmode;
+};
+
+struct tdm_backend_plane {
+       struct weston_plane base;
+       struct tdm_backend *backend;
+       enum wtdm_backend_plane_type type;
+       struct wl_list link;
+};
+
+struct tdm_backend_hwc {
+       struct tdm_backend *backend;
+
+       tdm_hwc            *thwc;
+       tbm_surface_queue_h target_buffer_queue;
+
+       tbm_surface_h       commit_fb;
+       tbm_surface_h       display_fb;
+};
+
+struct tdm_backend_head {
+       struct weston_head base;
+       struct tdm_backend *backend;
+
+       int index;
+       unsigned int pipe;
+
+       tdm_output *toutput;
+       tdm_output_type toutput_type;
+       struct tdm_backend_hwc *hwc;
+
+       struct wl_list mode_list;
+
+       tdm_output_dpms dpms;
+       tdm_output_conn_status conn_status;
+};
+
+struct tdm_backend_output {
+       struct weston_output base;
+       struct tdm_backend *backend;
+
+       tbm_surface_h pending_fb;
+       tbm_surface_h current_fb;
+
+       tbm_surface_h pixman_tsurfaces[2];
+       pixman_image_t *image[2];
+       int current_image;
+       pixman_region32_t previous_damage;
+};
+
+struct tdm_pending_state {
+       struct tdm_backend *backend;
+       struct wl_list output_list;
+};
+
+static inline struct tdm_backend_head *
+to_tdm_head(struct weston_head *base)
+{
+       return container_of(base, struct tdm_backend_head, base);
+}
+
+static inline struct tdm_backend_output *
+to_tdm_output(struct weston_output *base)
+{
+       return container_of(base, struct tdm_backend_output, base);
+}
+
+static inline struct tdm_backend *
+to_tdm_backend(struct weston_compositor *base)
+{
+       return container_of(base->backend, struct tdm_backend, base);
+}
+
+static inline struct tdm_backend_mode *
+to_tdm_mode(struct weston_mode *base)
+{
+       return container_of(base, struct tdm_backend_mode, base);
+}
diff --git a/libweston/backend-tdm/tdm.c b/libweston/backend-tdm/tdm.c
new file mode 100644 (file)
index 0000000..3140ca3
--- /dev/null
@@ -0,0 +1,1280 @@
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2011 Intel Corporation
+ * Copyright © 2017, 2018 Collabora, Ltd.
+ * Copyright © 2017, 2018 General Electric Company
+ * Copyright (c) 2018 DisplayLink (UK) Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/input.h>
+#include <linux/vt.h>
+#include <assert.h>
+#include <sys/mman.h>
+#include <time.h>
+
+#include <libudev.h>
+
+#include <libweston/weston-log.h>
+#include "tdm-internal.h"
+#include "launcher-util.h"
+#include "renderer-gl/gl-renderer.h"
+#include "pixman-renderer.h"
+#include "presentation-time-server-protocol.h"
+#include "linux-dmabuf.h"
+
+struct gl_renderer_interface *gl_renderer;
+
+static const char default_seat[] = "seat0";
+
+static struct tdm_backend_head *
+tdm_backend_output_get_primary_head(struct tdm_backend_output *output)
+{
+       struct weston_head *base;
+       struct tdm_backend_head *head, *primary_head = NULL;
+
+       wl_list_for_each(base, &output->base.head_list, output_link) {
+               head = to_tdm_head(base);
+               if (head->pipe == 0) {
+                       primary_head = head;
+                       break;
+               }
+       }
+
+       return primary_head;
+}
+
+static void
+tdm_backend_hwc_destroy(struct tdm_backend_hwc *hwc)
+{
+       if (!hwc) return;
+
+       free(hwc);
+}
+
+static struct tdm_backend_hwc *
+tdm_backend_hwc_create(struct tdm_backend_head *head)
+{
+       struct tdm_backend_hwc *hwc;
+       tdm_hwc *thwc;
+       tdm_error terror;
+
+       hwc = zalloc(sizeof *hwc);
+       if (!hwc)
+               return NULL;
+
+       thwc = tdm_output_get_hwc(head->toutput, &terror);
+       if (terror != TDM_ERROR_NONE) goto err_alloc;
+
+       hwc->thwc = thwc;
+
+       hwc->target_buffer_queue = tdm_hwc_get_client_target_buffer_queue(thwc, &terror);
+       if (terror != TDM_ERROR_NONE) goto err_alloc;
+
+       return hwc;
+
+err_alloc:
+       free(hwc);
+
+       return NULL;
+}
+
+static int
+tdm_backend_hwc_target_acquire_buffer(struct tdm_backend_hwc *hwc, tbm_surface_h *tsurface)
+{
+       tbm_surface_queue_error_e tsq_err;
+
+       if (!tbm_surface_queue_can_acquire(hwc->target_buffer_queue, 1))
+               return 0;
+
+       tsq_err = tbm_surface_queue_acquire(hwc->target_buffer_queue, tsurface);
+       if (tsq_err != TBM_SURFACE_QUEUE_ERROR_NONE) {
+               weston_log("failed to tbm_surface_queue_acquire\n");
+               return tsq_err;
+       }
+
+       return 0;
+}
+
+static int
+tdm_backend_hwc_target_release_buffer(struct tdm_backend_hwc *hwc, tbm_surface_h tsurface)
+{
+       tbm_surface_queue_error_e tsq_err;
+
+       tsq_err = tbm_surface_queue_release(hwc->target_buffer_queue, tsurface);
+       if (tsq_err != TBM_SURFACE_QUEUE_ERROR_NONE) {
+               weston_log("failed to tbm_surface_queue_release\n");
+               return tsq_err;
+       }
+
+       return 0;
+}
+
+static void
+tdm_backend_hwc_commit_handler(tdm_hwc *thwc, unsigned int sequence,
+                                                               unsigned int tv_sec, unsigned int tv_usec,
+                                                               void *user_data)
+{
+       struct tdm_backend_output *output = (struct tdm_backend_output *)user_data;
+       struct tdm_backend_head *head = tdm_backend_output_get_primary_head(output);
+       struct tdm_backend *b = output->backend;;
+       struct tdm_backend_hwc *hwc = head->hwc;
+       struct timespec ts;
+       uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
+                        WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
+                        WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
+
+       if (output->current_fb) {
+               tbm_surface_internal_unref(output->current_fb);
+
+               if (!b->use_pixman)
+                       tdm_backend_hwc_target_release_buffer(hwc, output->current_fb);
+       }
+
+       output->current_fb = output->pending_fb;
+       output->pending_fb = NULL;
+
+       ts.tv_sec = tv_sec;
+       ts.tv_nsec = tv_usec * 1000;
+
+       weston_output_finish_frame(&output->base, &ts, flags);
+}
+
+static tbm_surface_h
+tdm_backend_output_render_gl(struct tdm_backend_output *output,
+                  pixman_region32_t *damage)
+{
+       struct weston_compositor *ec = output->base.compositor;
+       struct tdm_backend_head *head = tdm_backend_output_get_primary_head(output);
+       tbm_surface_h tsurface;
+
+       ec->renderer->repaint_output(&output->base, damage);
+
+       if (tdm_backend_hwc_target_acquire_buffer(head->hwc, &tsurface) < 0)
+               return NULL;
+
+       tbm_surface_internal_ref(tsurface);
+
+       return tsurface;
+}
+
+static tbm_surface_h
+tdm_backend_output_render_pixman(struct tdm_backend_output *output,
+                        pixman_region32_t *damage)
+{
+       struct weston_compositor *ec = output->base.compositor;
+
+       output->current_image ^= 1;
+
+       pixman_renderer_output_set_buffer(&output->base,
+                                         output->image[output->current_image]);
+       pixman_renderer_output_set_hw_extra_damage(&output->base,
+                                                  &output->previous_damage);
+
+       ec->renderer->repaint_output(&output->base, damage);
+
+       pixman_region32_copy(&output->previous_damage, damage);
+
+       tbm_surface_internal_ref(output->pixman_tsurfaces[output->current_image]);
+
+       return output->pixman_tsurfaces[output->current_image];
+}
+
+static void
+tdm_backend_output_render(struct tdm_backend_output *output, pixman_region32_t *damage)
+{
+       struct weston_compositor *c = output->base.compositor;
+       struct tdm_backend *b = to_tdm_backend(c);
+       tbm_surface_h fb;
+
+       if (b->use_pixman) {
+               fb = tdm_backend_output_render_pixman(output, damage);
+       } else {
+               fb = tdm_backend_output_render_gl(output, damage);
+       }
+
+       if (!fb) return;
+
+       if (output->pending_fb)
+               tbm_surface_internal_unref(output->pending_fb);
+
+       output->pending_fb = fb;
+
+       pixman_region32_subtract(&c->primary_plane.damage,
+                                &c->primary_plane.damage, damage);
+}
+
+static int
+tdm_backend_output_repaint(struct weston_output *output_base,
+                  pixman_region32_t *damage,
+                  void *repaint_data)
+{
+       struct tdm_backend_output *output = to_tdm_output(output_base);
+
+       tdm_backend_output_render(output, damage);
+
+       return 0;
+}
+
+static int
+tdm_backend_output_start_repaint_loop(struct weston_output *output_base)
+{
+       weston_output_finish_frame(output_base, NULL,
+                                  WP_PRESENTATION_FEEDBACK_INVALID);
+       return 0;
+}
+
+/**
+ * Begin a new repaint cycle
+ */
+static void *
+tdm_repaint_begin(struct weston_compositor *compositor)
+{
+       struct tdm_backend *b = to_tdm_backend(compositor);
+
+       if (weston_log_scope_is_enabled(b->debug)) {
+               char *dbg = weston_compositor_print_scene_graph(compositor);
+               tdm_debug(b, "[repaint] Beginning repaint\n");
+               tdm_debug(b, "%s", dbg);
+               free(dbg);
+       }
+
+       return NULL;
+}
+
+/**
+ * Flush a repaint set
+ */
+static int
+tdm_repaint_flush(struct weston_compositor *compositor, void *repaint_data)
+{
+       struct weston_output *output_base = NULL;
+       uint32_t num_types;
+       tdm_region tdamage;
+
+       wl_list_for_each(output_base, &compositor->output_list, link) {
+               struct tdm_backend_output *output = to_tdm_output(output_base);
+               struct tdm_backend_head *head = tdm_backend_output_get_primary_head(output);
+
+               if (!output->pending_fb)
+                       continue;
+
+               memset(&tdamage, 0, sizeof(tdamage));
+
+               tdm_hwc_set_client_target_buffer(head->hwc->thwc, output->pending_fb, tdamage);
+               tdm_hwc_validate(head->hwc->thwc, NULL, 0, &num_types);
+               tdm_hwc_accept_validation(head->hwc->thwc);
+               tdm_hwc_commit(head->hwc->thwc, 0, tdm_backend_hwc_commit_handler, output);
+       }
+
+       return 0;
+}
+
+/**
+ * Cancel a repaint set
+ */
+static void
+tdm_repaint_cancel(struct weston_compositor *compositor, void *repaint_data)
+{
+       struct tdm_backend *b = to_tdm_backend(compositor);
+       struct weston_output *output_base = NULL;
+
+       wl_list_for_each(output_base, &compositor->output_list, link) {
+               struct tdm_backend_output *output = to_tdm_output(output_base);
+               struct tdm_backend_head *head = tdm_backend_output_get_primary_head(output);
+
+               if (!output->pending_fb)
+                       continue;
+
+               if (!b->use_pixman) {
+                       tdm_backend_hwc_target_release_buffer(head->hwc, output->pending_fb);
+               }
+
+               tbm_surface_internal_unref(output->pending_fb);
+               output->pending_fb = NULL;
+       }
+}
+
+static int
+tdm_backend_output_switch_mode(struct weston_output *output_base, struct weston_mode *mode)
+{
+       return 0;
+}
+
+static struct tdm_backend_plane *
+tdm_backend_plane_create(struct tdm_backend *b, struct tdm_backend_output *output,
+                enum wtdm_backend_plane_type type)
+{
+       struct tdm_backend_plane *plane;
+
+       plane = zalloc(sizeof(*plane));
+       if (!plane) {
+               weston_log("%s: out of memory\n", __func__);
+               return NULL;
+       }
+
+       plane->backend = b;
+       plane->type = type;
+
+       weston_plane_init(&plane->base, b->compositor, 0, 0);
+       wl_list_insert(&b->plane_list, &plane->link);
+
+       return plane;
+}
+
+/**
+ * Destroy one TDM plane
+ *
+ * Destroy a TDM plane, removing it from screen and releasing its retained
+ * buffers in the process. The counterpart to tdm_backend_plane_create.
+ *
+ * @param plane Plane to deallocate (will be freed)
+ */
+static void
+tdm_backend_plane_destroy(struct tdm_backend_plane *plane)
+{
+       weston_plane_release(&plane->base);
+       wl_list_remove(&plane->link);
+       free(plane);
+}
+
+/**
+ * Initialise sprites (overlay planes)
+ *
+ * Walk the list of provided TDM planes, and add overlay planes.
+ *
+ * Call destroy_sprites to free these planes.
+ *
+ * @param b TDM compositor backend
+ */
+static void
+create_sprites(struct tdm_backend *b)
+{
+       struct tdm_backend_plane *primary_plane, *overlay_plane;
+
+       primary_plane = tdm_backend_plane_create(b, NULL, WTDM_PLANE_TYPE_PRIMARY);
+       if (!primary_plane) {
+               weston_log("failed to tdm_backend_plane_create\n");
+               return;
+       }
+
+       overlay_plane = tdm_backend_plane_create(b, NULL, WTDM_PLANE_TYPE_OVERLAY);
+       if (!overlay_plane) {
+               weston_log("failed to tdm_backend_plane_create\n");
+               tdm_backend_plane_destroy(primary_plane);
+               return;
+       }
+
+       weston_compositor_stack_plane(b->compositor,
+                                     &primary_plane->base,
+                                     &b->compositor->primary_plane);
+}
+
+/**
+ * Clean up sprites (overlay planes)
+ *
+ * The counterpart to create_sprites.
+ *
+ * @param b TDM compositor backend
+ */
+static void
+destroy_sprites(struct tdm_backend *b)
+{
+       struct tdm_backend_plane *plane, *next;
+
+       wl_list_for_each_safe(plane, next, &b->plane_list, link)
+               tdm_backend_plane_destroy(plane);
+}
+
+/**
+ * Power output on or off
+ *
+ * The DPMS/power level of an output is used to switch it on or off. This
+ * is TDM's hook for doing so, which can called either as part of repaint,
+ * or independently of the repaint loop.
+ *
+ * If we are called as part of repaint, we simply set the relevant bit in
+ * state and return.
+ *
+ * This function is never called on a virtual output.
+ */
+static void
+tdm_backend_output_set_dpms(struct weston_output *output_base, enum dpms_enum level)
+{
+       tdm_error terror;
+       struct tdm_backend_output *output = to_tdm_output(output_base);
+       struct tdm_backend_head *head = tdm_backend_output_get_primary_head(output);
+
+       terror = tdm_output_set_dpms(head->toutput, level);
+       if (terror != TDM_ERROR_NONE)
+               weston_log("fail to tdm_output_set_dpms\n");
+}
+
+static int
+tdm_backend_output_init_egl(struct tdm_backend_output *output, struct tdm_backend *b)
+{
+       struct tdm_backend_head *head;
+       struct tdm_backend_hwc *hwc;
+       tbm_format format;
+
+       head = tdm_backend_output_get_primary_head(output);
+       if (!head) {
+               weston_log("fail to get primary head\n");
+               return -1;
+       }
+
+       hwc = head->hwc;
+       if (!hwc) {
+               weston_log("fail to get hwc\n");
+               return -1;
+       }
+
+       if (!hwc->target_buffer_queue) {
+               weston_log("fail to get target buffer queue\n");
+               return -1;
+       }
+
+       format = tbm_surface_queue_get_format(hwc->target_buffer_queue);
+
+       if (gl_renderer->output_window_create(&output->base,
+                                             (EGLNativeWindowType)hwc->target_buffer_queue,
+                                             hwc->target_buffer_queue,
+                                             &format,
+                                             0) < 0) {
+               weston_log("failed to create gl renderer output state\n");
+               return -1;
+       }
+
+       return 1;
+}
+
+static void
+tdm_backend_output_fini_egl(struct tdm_backend_output *output)
+{
+}
+
+static int
+tdm_backend_output_init_pixman(struct tdm_backend_output *output, struct tdm_backend *b)
+{
+       int w = output->base.current_mode->width;
+       int h = output->base.current_mode->height;
+       /* only support xrgb8888 */
+       uint32_t pixman_format = PIXMAN_x8r8g8b8;
+       unsigned int i;
+       uint32_t flags = 0;
+
+       /* FIXME error checking */
+       for (i = 0; i < ARRAY_LENGTH(output->pixman_tsurfaces); i++) {
+               tbm_surface_info_s info;
+
+               output->pixman_tsurfaces[i] = tbm_surface_create(w, h, TBM_FORMAT_XRGB8888);
+               if (!output->pixman_tsurfaces[i])
+                       goto err;
+
+               tbm_surface_get_info(output->pixman_tsurfaces[i], &info);
+
+               output->image[i] =
+                       pixman_image_create_bits(pixman_format, w, h,
+                                                (uint32_t *)info.planes[0].ptr,
+                                                info.planes[0].stride);
+               if (!output->image[i])
+                       goto err;
+       }
+
+       if (b->use_pixman_shadow)
+               flags |= PIXMAN_RENDERER_OUTPUT_USE_SHADOW;
+
+       if (pixman_renderer_output_create(&output->base, flags) < 0)
+               goto err;
+
+       weston_log("TDM: output %s %s shadow framebuffer.\n", output->base.name,
+                  b->use_pixman_shadow ? "uses" : "does not use");
+
+       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->pixman_tsurfaces); i++) {
+               if (output->pixman_tsurfaces[i])
+                       tbm_surface_internal_unref(output->pixman_tsurfaces[i]);
+               if (output->image[i])
+                       pixman_image_unref(output->image[i]);
+
+               output->pixman_tsurfaces[i] = NULL;
+               output->image[i] = NULL;
+       }
+
+       return -1;
+}
+
+static void
+tdm_backend_output_fini_pixman(struct tdm_backend_output *output)
+{
+       unsigned int i;
+
+       pixman_renderer_output_destroy(&output->base);
+       pixman_region32_fini(&output->previous_damage);
+
+       for (i = 0; i < ARRAY_LENGTH(output->pixman_tsurfaces); i++) {
+               pixman_image_unref(output->image[i]);
+               tbm_surface_internal_unref(output->pixman_tsurfaces[i]);
+               output->pixman_tsurfaces[i] = NULL;
+               output->image[i] = NULL;
+       }
+}
+
+static void
+setup_output_seat_constraint(struct tdm_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
+tdm_backend_output_attach_head(struct weston_output *output_base,
+                      struct weston_head *head_base)
+{
+       if (!output_base->enabled)
+               return 0;
+
+       weston_output_schedule_repaint(output_base);
+
+       return 0;
+}
+
+static void
+tdm_backend_output_detach_head(struct weston_output *output_base,
+                      struct weston_head *head_base)
+{
+       if (!output_base->enabled)
+               return;
+
+       weston_output_schedule_repaint(output_base);
+}
+
+static void
+tdm_backend_output_set_seat(struct weston_output *base,
+                   const char *seat)
+{
+       struct tdm_backend_output *output = to_tdm_output(base);
+       struct tdm_backend *b = to_tdm_backend(base->compositor);
+
+       setup_output_seat_constraint(b, &output->base,
+                                    seat ? seat : "");
+}
+
+static int
+tdm_backend_output_enable(struct weston_output *base)
+{
+       struct tdm_backend_output *output = to_tdm_output(base);
+       struct tdm_backend *b = to_tdm_backend(base->compositor);
+       struct tdm_backend_head *head = tdm_backend_output_get_primary_head(output);
+
+       head->hwc = tdm_backend_hwc_create(head);
+       if (!head->hwc) goto err;
+
+       if (b->use_pixman) {
+               if (tdm_backend_output_init_pixman(output, b) < 0) {
+                       weston_log("Failed to init output pixman state\n");
+                       goto err;
+               }
+       } else if (tdm_backend_output_init_egl(output, b) < 0) {
+               weston_log("Failed to init output gl state\n");
+               goto err;
+       }
+
+       output->base.start_repaint_loop = tdm_backend_output_start_repaint_loop;
+       output->base.repaint = tdm_backend_output_repaint;
+       output->base.assign_planes = NULL;// todo
+       output->base.set_dpms = tdm_backend_output_set_dpms;
+       output->base.switch_mode = tdm_backend_output_switch_mode;
+       output->base.set_gamma = NULL;
+
+       weston_log("Output %s (crtc %d) video modes:\n",
+                  output->base.name, /*output->crtc_id*/ 0);
+
+       return 0;
+
+err:
+       return -1;
+}
+
+static void
+tdm_backend_output_deinit(struct weston_output *base)
+{
+       struct tdm_backend_output *output = to_tdm_output(base);
+       struct tdm_backend *b = to_tdm_backend(base->compositor);
+
+       if (b->use_pixman)
+               tdm_backend_output_fini_pixman(output);
+       else
+               tdm_backend_output_fini_egl(output);
+}
+
+static void
+tdm_backend_output_destroy(struct weston_output *base)
+{
+       struct tdm_backend_output *output = to_tdm_output(base);
+
+       if (output->base.enabled)
+               tdm_backend_output_deinit(&output->base);
+
+       weston_output_release(&output->base);
+
+       free(output);
+}
+
+static int
+tdm_backend_output_disable(struct weston_output *base)
+{
+       struct tdm_backend_output *output = to_tdm_output(base);
+
+       weston_log("Disabling output %s\n", output->base.name);
+
+       if (output->base.enabled)
+               tdm_backend_output_deinit(&output->base);
+
+       return 0;
+}
+
+static char *
+_output_type_to_str(tdm_output_type output_type)
+{
+       if (output_type == TDM_OUTPUT_TYPE_Unknown) return "Unknown";
+       else if (output_type == TDM_OUTPUT_TYPE_VGA) return "VGA";
+       else if (output_type == TDM_OUTPUT_TYPE_DVII) return "DVII";
+       else if (output_type == TDM_OUTPUT_TYPE_DVID) return "DVID";
+       else if (output_type == TDM_OUTPUT_TYPE_DVIA) return "DVIA";
+       else if (output_type == TDM_OUTPUT_TYPE_SVIDEO) return "SVIDEO";
+       else if (output_type == TDM_OUTPUT_TYPE_LVDS) return "LVDS";
+       else if (output_type == TDM_OUTPUT_TYPE_Component) return "Component";
+       else if (output_type == TDM_OUTPUT_TYPE_9PinDIN) return "9PinDIN";
+       else if (output_type == TDM_OUTPUT_TYPE_DisplayPort) return "DisplayPort";
+       else if (output_type == TDM_OUTPUT_TYPE_HDMIA) return "HDMIA";
+       else if (output_type == TDM_OUTPUT_TYPE_HDMIB) return "HDMIB";
+       else if (output_type == TDM_OUTPUT_TYPE_TV) return "TV";
+       else if (output_type == TDM_OUTPUT_TYPE_eDP) return "eDP";
+       else if (output_type == TDM_OUTPUT_TYPE_DSI) return "DSI";
+       else return "Unknown";
+}
+
+static int
+tdm_backend_head_update(struct tdm_backend_head *head)
+{
+       tdm_error terror;
+       tdm_output_conn_status status;
+       int i;
+
+       terror = tdm_output_get_conn_status(head->toutput, &status);
+       if (terror != TDM_ERROR_NONE) {
+               weston_log("fail to tdm_backend_output_get_conn_status\n");
+               return terror;
+       }
+
+       if ((head->conn_status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) &&
+               (status == TDM_OUTPUT_CONN_STATUS_CONNECTED)) {
+               const tdm_output_mode *tmodes = NULL;
+               int num_tmodes = 0;
+               const char *maker, *model;
+               unsigned int phy_w, phy_h;
+
+               terror = tdm_output_get_model_info(head->toutput, &maker, &model, NULL);
+               if (terror != TDM_ERROR_NONE) {
+                       weston_log("fail to get model info\n");
+                       return terror;
+               }
+
+               terror = tdm_output_get_physical_size(head->toutput, &phy_w, &phy_h);
+               if (terror != TDM_ERROR_NONE) {
+                       weston_log("fail to get physical size\n");
+                       return terror;
+               }
+
+               weston_head_set_monitor_strings(&head->base, maker, model, NULL);
+               weston_head_set_physical_size(&head->base, phy_w, phy_h);
+               weston_head_set_connection_status(&head->base, status == TDM_OUTPUT_CONN_STATUS_CONNECTED);
+
+               terror = tdm_output_get_available_modes(head->toutput, &tmodes, &num_tmodes);
+               if (terror != TDM_ERROR_NONE || num_tmodes == 0) {
+                       weston_log("fail to get tmodes\n");
+                       return terror;
+               }
+
+               for (i = 0; i < num_tmodes; i++) {
+                       struct tdm_backend_mode *mode;
+
+                       mode = zalloc(sizeof *mode);
+                       if (!mode) {
+                               weston_log("fail to alloc tmode\n");
+                               return -1;
+                       }
+
+                       mode->tmode = &tmodes[i];
+                       mode->base.flags = 0;
+                       mode->base.width = tmodes[i].hdisplay;
+                       mode->base.height = tmodes[i].vdisplay;;
+                       mode->base.refresh = tmodes[i].clock;
+
+                       wl_list_insert(&head->mode_list, &mode->head_link);
+               }
+       }
+
+       head->conn_status = status;
+
+       return 0;
+}
+
+static void
+_tdm_backend_head_cb_toutput_change(tdm_output *toutput, tdm_output_change_type type,
+                                                       tdm_value value, void *user_data)
+{
+       struct tdm_backend_head *head = NULL;
+       tdm_output_dpms tdpms;
+       tdm_output_conn_status status;
+
+       head = (struct tdm_backend_head *)user_data;
+
+       switch (type) {
+               case TDM_OUTPUT_CHANGE_CONNECTION:
+                       status = (tdm_output_conn_status)value.u32;
+                       head->conn_status = status;
+                       if ((status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) ||
+                               (status == TDM_OUTPUT_CONN_STATUS_CONNECTED))
+                               tdm_backend_head_update(head);
+                       break;
+               case TDM_OUTPUT_CHANGE_DPMS:
+                       tdpms = (tdm_output_dpms)value.u32;
+                       head->dpms = tdpms;
+               default:
+                       break;
+       }
+}
+
+/**
+ * Create a Weston head for a connector
+ *
+ * Given a TDM connector, create a matching tdm_backend_head structure and add it
+ * to Weston's head list.
+ *
+ * @param backend Weston backend structure
+ * @param index tdm_output index
+ * @returns The new head, or NULL on failure.
+ */
+static struct tdm_backend_head *
+tdm_backend_head_create(struct tdm_backend *backend, int index)
+{
+       struct tdm_backend_head *head;
+       tdm_output *toutput = NULL;
+       tdm_output_type output_type;
+       tdm_error terror;
+       const char *name;
+
+       head = zalloc(sizeof *head);
+       if (!head)
+               return NULL;
+
+       head->index = index;
+
+       toutput = tdm_display_get_output(backend->tdisplay, index, NULL);
+       if (!toutput) goto err_alloc;
+
+       terror = tdm_output_get_output_type(toutput, &output_type);
+       if (terror != TDM_ERROR_NONE) goto err_alloc;
+
+       terror = tdm_output_get_pipe(toutput, &head->pipe);
+       if (terror != TDM_ERROR_NONE) goto err_alloc;
+
+       terror = tdm_output_add_change_handler(toutput, _tdm_backend_head_cb_toutput_change, head);
+       if (terror != TDM_ERROR_NONE) {
+               weston_log("failed to tdm_backend_output_add_change_handler\n");
+               goto err_alloc;
+       }
+
+       head->index = index;
+       head->toutput = toutput;
+       head->toutput_type = output_type;
+       head->toutput = toutput;
+       head->backend = backend;
+       wl_list_init(&head->mode_list);
+
+       name = _output_type_to_str(output_type);
+
+       if ((output_type == TDM_OUTPUT_TYPE_LVDS) ||
+               (output_type == TDM_OUTPUT_TYPE_eDP) ||
+               (output_type == TDM_OUTPUT_TYPE_DSI))
+               weston_head_set_internal(&head->base);
+
+       weston_head_init(&head->base, name);
+       head->backend = backend;
+
+       tdm_backend_head_update(head);
+       weston_compositor_add_head(backend->compositor, &head->base);
+
+       return head;
+
+err_alloc:
+       free(head);
+
+       return NULL;
+}
+
+static void
+tdm_backend_head_destroy(struct tdm_backend_head *head)
+{
+       weston_head_release(&head->base);
+
+       if (head->hwc)
+               tdm_backend_hwc_destroy(head->hwc);
+
+       tdm_output_remove_change_handler(head->toutput, _tdm_backend_head_cb_toutput_change, head);
+
+       free(head);
+}
+
+/**
+ * Create a Weston output structure
+ *
+ * Create an "empty" tdm_output. This is the implementation of
+ * weston_backend::create_output.
+ *
+ * Creating an output is usually followed by tdm_backend_output_attach_head()
+ * and tdm_backend_output_enable() to make use of it.
+ *
+ * @param compositor The compositor instance.
+ * @param name Name for the new output.
+ * @returns The output, or NULL on failure.
+ */
+static struct weston_output *
+tdm_backend_output_create(struct weston_compositor *compositor, const char *name)
+{
+       struct tdm_backend *b = to_tdm_backend(compositor);
+       struct tdm_backend_output *output;
+
+       output = zalloc(sizeof *output);
+       if (output == NULL)
+               return NULL;
+
+       output->backend = b;
+
+       weston_output_init(&output->base, compositor, name);
+
+       output->base.enable = tdm_backend_output_enable;
+       output->base.destroy = tdm_backend_output_destroy;
+       output->base.disable = tdm_backend_output_disable;
+       output->base.attach_head = tdm_backend_output_attach_head;
+       output->base.detach_head = tdm_backend_output_detach_head;
+
+       weston_compositor_add_pending_output(&output->base, b->compositor);
+
+       return &output->base;
+}
+
+static int
+tdm_backend_create_heads(struct tdm_backend *b)
+{
+       struct tdm_backend_head *head;
+       tdm_error terror;
+       int num_outputs;
+       int i;
+
+       terror = tdm_display_get_output_count(b->tdisplay, &num_outputs);
+       if ((terror != TDM_ERROR_NONE) || (num_outputs < 1)) {
+               weston_log("fail to get tdm_display_get_output_count\n");
+               return -1;
+       }
+
+       for (i = 0; i < num_outputs; i++) {
+               head = tdm_backend_head_create(b, i);
+               if (!head)
+                       weston_log("TDM: failed to create head index:%d.\n", i);
+       }
+
+       return 0;
+}
+
+static void
+tdm_destroy(struct weston_compositor *ec)
+{
+       struct tdm_backend *b = to_tdm_backend(ec);
+       struct weston_head *base, *next;
+
+       udev_input_destroy(&b->input);
+
+       wl_event_source_remove(b->tdm_source);
+
+       destroy_sprites(b);
+
+       weston_compositor_log_scope_destroy(b->debug);
+       b->debug = NULL;
+       weston_compositor_shutdown(ec);
+
+       wl_list_for_each_safe(base, next, &ec->head_list, compositor_link)
+               tdm_backend_head_destroy(to_tdm_head(base));
+
+       udev_unref(b->udev);
+
+       weston_launcher_destroy(ec->launcher);
+
+       tbm_dummy_display_destroy(b->tbm_display);
+       tbm_bufmgr_deinit(b->tbufmgr);
+       tdm_display_deinit(b->tdisplay);
+
+       close(b->tdm_fd);
+       free(b);
+}
+
+static void
+session_notify(struct wl_listener *listener, void *data)
+{
+       struct weston_compositor *compositor = data;
+       struct tdm_backend *b = to_tdm_backend(compositor);
+
+       if (compositor->session_active) {
+               weston_log("activating session\n");
+               weston_compositor_wake(compositor);
+               weston_compositor_damage_all(compositor);
+               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. */
+
+               // unset all plane?
+       }
+}
+
+static int
+init_pixman(struct tdm_backend *b)
+{
+       return pixman_renderer_init(b->compositor);
+}
+
+static int
+tdm_backend_create_gl_renderer(struct tdm_backend *b)
+{
+       if (gl_renderer->display_create(b->compositor,
+                                                                       0,
+                                                                       b->tbm_display,
+                                                                       EGL_WINDOW_BIT,
+                                                                       NULL,
+                                                                       0) < 0)
+               return -1;
+
+       return 0;
+}
+
+static int
+init_egl(struct tdm_backend *b)
+{
+       gl_renderer = weston_load_module("gl-renderer.so",
+                                        "gl_renderer_interface");
+       if (!gl_renderer)
+               return -1;
+
+       b->tbm_display = tbm_dummy_display_create();
+       if (!b->tbm_display) {
+               weston_log("Failed to tbm_dummy_display_create\n");
+               return -1;
+       }
+
+       if (tdm_backend_create_gl_renderer(b) < 0) {
+               tbm_dummy_display_destroy(b->tbm_display);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+on_tdm_input(int fd, uint32_t mask, void *data)
+{
+       struct tdm_backend *b = data;
+       tdm_error terror;
+
+       terror = tdm_display_handle_events(b->tdisplay);
+       if (terror != TDM_ERROR_NONE)
+               weston_log("tdm_display_handle_events failed\n");
+
+       return 1;
+}
+
+static int
+tdm_backend_output_set_mode(struct weston_output *base,
+                   enum weston_drm_backend_output_mode mode,
+                   const char *modeline)
+{
+       struct tdm_backend_output *output = to_tdm_output(base);
+       struct tdm_backend_head *head = tdm_backend_output_get_primary_head(output);
+       struct tdm_backend_mode *backend_mode;
+       tdm_error terror;
+
+       if (!head) return -1;
+
+       /* temporary use only preferred */
+       wl_list_for_each(backend_mode, &head->mode_list, head_link) {
+               if (backend_mode->tmode->type & TDM_OUTPUT_MODE_TYPE_PREFERRED) {
+                       output->base.current_mode = &backend_mode->base;
+                       output->base.current_mode->flags |= WL_OUTPUT_MODE_CURRENT;
+
+                       output->base.native_mode = output->base.current_mode;
+                       output->base.native_scale = output->base.current_scale;
+
+                       wl_list_insert(output->base.mode_list.prev, &backend_mode->base.link);
+                       break;
+               }
+
+       }
+
+       terror = tdm_output_set_mode(head->toutput, backend_mode->tmode);
+       if (terror != TDM_ERROR_NONE) {
+               weston_log("fail to set tmode.\n");
+               return terror;
+       }
+
+       return 0;
+}
+
+static void
+tdm_backend_output_set_gbm_format(struct weston_output *base,
+                         const char *gbm_format)
+{
+       /* not support */
+}
+
+static const struct weston_drm_output_api api = {
+       tdm_backend_output_set_mode,
+       tdm_backend_output_set_gbm_format,
+       tdm_backend_output_set_seat,
+};
+
+static struct tdm_backend *
+tdm_backend_create(struct weston_compositor *compositor,
+                  struct weston_drm_backend_config *config)
+{
+       struct tdm_backend *b;
+       struct wl_event_loop *loop;
+       const char *seat_id = default_seat;
+       const char *session_seat;
+       int ret;
+       tdm_error terror;
+       int tdm_fd;
+
+       session_seat = getenv("XDG_SEAT");
+       if (session_seat)
+               seat_id = session_seat;
+
+       if (config->seat_id)
+               seat_id = config->seat_id;
+
+       weston_log("initializing tdm backend\n");
+
+       b = zalloc(sizeof *b);
+       if (b == NULL)
+               return NULL;
+
+       b->compositor = compositor;
+       b->use_pixman = config->use_pixman;
+       b->pageflip_timeout = config->pageflip_timeout;
+       b->use_pixman_shadow = config->use_pixman_shadow;
+
+       b->debug = weston_compositor_add_log_scope(compositor->weston_log_ctx, "tdm-backend",
+                                                  "Debug messages from TDM backend\n",
+                                                  NULL, NULL, NULL);
+
+       compositor->backend = &b->base;
+
+       /* Check if we run tdm-backend using weston-launch */
+       compositor->launcher = weston_launcher_connect(compositor, config->tty,
+                                                      seat_id, true);
+       if (compositor->launcher == NULL) {
+               weston_log("fatal: tdm backend should be run using "
+                          "weston-launch binary, or your system should "
+                          "provide the logind D-Bus API.\n");
+               goto err_compositor;
+       }
+
+       b->udev = udev_new();
+       if (b->udev == NULL) {
+               weston_log("failed to initialize udev context\n");
+               goto err_launcher;
+       }
+
+       b->session_listener.notify = session_notify;
+       wl_signal_add(&compositor->session_signal, &b->session_listener);
+
+       b->tbufmgr = tbm_bufmgr_server_init();
+       if (!b->tbufmgr) {
+               weston_log("failed to initialize tbm_bufmgr\n");
+               goto err_udev;
+       }
+
+       b->tdisplay = tdm_display_init(&terror);
+       if (!b->tdisplay) {
+               weston_log("failed to initialize tdm_display\n");
+               goto err_tbm;
+       }
+
+       if (b->use_pixman) {
+               if (init_pixman(b) < 0) {
+                       weston_log("failed to initialize pixman renderer\n");
+                       goto err_tdm;
+               }
+       }
+       else {
+               if (init_egl(b) < 0) {
+                       weston_log("failed to initialize egl\n");
+                       goto err_tdm;
+               }
+       }
+
+       if (tdm_backend_create_heads(b) < 0) {
+               weston_log("Failed to create heads for\n");
+               goto err_tdm;
+       }
+
+       b->base.destroy = tdm_destroy;
+       b->base.repaint_begin = tdm_repaint_begin;
+       b->base.repaint_flush = tdm_repaint_flush;
+       b->base.repaint_cancel = tdm_repaint_cancel;
+       b->base.create_output = tdm_backend_output_create;
+       b->base.device_changed = NULL;
+       b->base.can_scanout_dmabuf = NULL;
+
+       weston_setup_vt_switch_bindings(compositor);
+
+       wl_list_init(&b->plane_list);
+       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_tdm;
+       }
+
+       tdm_display_get_fd(b->tdisplay, &tdm_fd);
+       b->tdm_fd = dup(tdm_fd);
+
+       loop = wl_display_get_event_loop(compositor->wl_display);
+       b->tdm_source =
+               wl_event_loop_add_fd(loop, b->tdm_fd,
+                                    WL_EVENT_READABLE, on_tdm_input, b);
+
+       if (compositor->renderer->import_dmabuf) {
+               if (linux_dmabuf_setup(compositor) < 0)
+                       weston_log("Error: initializing dmabuf "
+                                  "support failed.\n");
+       }
+
+       ret = weston_plugin_api_register(compositor, WESTON_DRM_OUTPUT_API_NAME,
+                                        &api, sizeof(api));
+
+       if (ret < 0) {
+               weston_log("Failed to register output API.\n");
+               goto err_tdm_source;
+       }
+
+       return b;
+
+err_tdm_source:
+       wl_event_source_remove(b->tdm_source);
+       udev_input_destroy(&b->input);
+err_tdm:
+       tdm_display_deinit(b->tdisplay);
+err_tbm:
+       tbm_bufmgr_deinit(b->tbufmgr);
+err_udev:
+       udev_unref(b->udev);
+err_launcher:
+       weston_launcher_destroy(compositor->launcher);
+err_compositor:
+       weston_compositor_shutdown(compositor);
+       free(b);
+       return NULL;
+}
+
+static void
+config_init_to_defaults(struct weston_drm_backend_config *config)
+{
+       config->use_pixman_shadow = true;
+}
+
+WL_EXPORT int
+weston_backend_init(struct weston_compositor *compositor,
+                   struct weston_backend_config *config_base)
+{
+       struct tdm_backend *b;
+       struct weston_drm_backend_config config = {{ 0, }};
+
+       if (config_base == NULL ||
+           config_base->struct_version != WESTON_DRM_BACKEND_CONFIG_VERSION ||
+           config_base->struct_size > sizeof(struct weston_drm_backend_config)) {
+               weston_log("tdm backend config structure is invalid\n");
+               return -1;
+       }
+
+       config_init_to_defaults(&config);
+       memcpy(&config, config_base, config_base->struct_size);
+
+       b = tdm_backend_create(compositor, &config);
+       if (b == NULL)
+               return -1;
+
+       return 0;
+}
index dbc64927948e0883c9ad4732da56f0158ec7ca1a..1e7c2b94d75341a6f4f6f8ddf2a4d00cc5658bbf 100644 (file)
@@ -7757,6 +7757,7 @@ static const char * const backend_map[] = {
        [WESTON_BACKEND_RDP] =          "rdp-backend.so",
        [WESTON_BACKEND_WAYLAND] =      "wayland-backend.so",
        [WESTON_BACKEND_X11] =          "x11-backend.so",
+       [WESTON_BACKEND_TDM] =          "tdm-backend.so",
 };
 
 /** Load a backend into a weston_compositor
index 08d23ecd5b9f63c37679101d5f7111c003edfd81..d989b287e2bf7f759df7bce0784eb44cb929ce13 100644 (file)
@@ -240,3 +240,4 @@ subdir('backend-headless')
 subdir('backend-rdp')
 subdir('backend-wayland')
 subdir('backend-x11')
+subdir('backend-tdm')
index 00f617a90e7a78aa94361177870060966ac23875..5f8bac10c0e526f9d550b5bc1f16dd523d33ecd9 100644 (file)
@@ -115,6 +115,8 @@ struct gl_renderer {
 
        bool has_wait_sync;
        PFNEGLWAITSYNCKHRPROC wait_sync;
+
+       void *wl_tbm_server;
 };
 
 static inline struct gl_renderer *
index 6c4355076aa2f13614b9f4c818e0ec9819bad330..967827e63e962ea81181e807733b9f03a772915b 100644 (file)
 #include "shared/timespec-util.h"
 #include "shared/weston-egl-ext.h"
 
+#ifdef HAVE_TBM
+#include <tbm_bufmgr.h>
+#include <wayland-tbm-server.h>
+#endif
+
 #define GR_GL_VERSION(major, minor) \
        (((uint32_t)(major) << 16) | (uint32_t)(minor))
 
@@ -2430,6 +2435,83 @@ dmabuf_is_opaque(struct linux_dmabuf_buffer *dmabuf)
        return pixel_format_is_opaque(info);
 }
 
+#ifdef HAVE_TBM
+static bool
+tbm_surface_is_opaque(tbm_surface_h tsurface)
+{
+       const struct pixel_format_info *info;
+
+       /* format of tbm surface use fourcc */
+       info = pixel_format_get_info(tbm_surface_get_format(tsurface) &
+                                    ~DRM_FORMAT_BIG_ENDIAN);
+       if (!info)
+               return false;
+
+       return pixel_format_is_opaque(info);
+}
+
+static void
+gl_renderer_attach_tbm_surface(struct weston_surface *surface,
+                         struct weston_buffer *buffer,
+                         tbm_surface_h tsurface)
+{
+       struct gl_renderer *gr = get_renderer(surface->compositor);
+       struct gl_surface_state *gs = get_surface_state(surface);
+       tbm_format format;
+       int num_planes = 1;
+       int i;
+
+       buffer->width = tbm_surface_get_width(tsurface);
+       buffer->height = tbm_surface_get_height(tsurface);
+       /* how to know it? */
+       buffer->y_inverted = true;
+
+       for (i = 0; i < gs->num_images; i++)
+               egl_image_unref(gs->images[i]);
+       gs->num_images = 0;
+
+       gs->target = GL_TEXTURE_2D;
+       gs->pitch = buffer->width;
+       gs->height = buffer->height;
+       gs->buffer_type = BUFFER_TYPE_EGL;
+       gs->y_inverted = buffer->y_inverted;
+       surface->is_opaque = tbm_surface_is_opaque(tsurface);
+
+       format = tbm_surface_get_format(tsurface);
+       switch(format) {
+               case TBM_FORMAT_ARGB8888:
+               case TBM_FORMAT_XRGB8888:
+                       gs->shader = &gr->texture_shader_rgba;
+                       break;
+               default:
+                       gs->shader = &gr->texture_shader_egl_external;
+                       break;
+       }
+
+#ifndef EGL_NATIVE_SURFACE_TIZEN
+#define EGL_NATIVE_SURFACE_TIZEN 0x32A1
+#endif
+
+       ensure_textures(gs, num_planes);
+       for (i = 0; i < num_planes; i++) {
+               gs->images[i] = egl_image_create(gr,
+                                                EGL_NATIVE_SURFACE_TIZEN,
+                                                (void *)tsurface,
+                                                NULL);
+               if (!gs->images[i]) {
+                       weston_log("failed to create img for plane %d\n", i);
+                       continue;
+               }
+               gs->num_images++;
+
+               glActiveTexture(GL_TEXTURE0 + i);
+               glBindTexture(gs->target, gs->textures[i]);
+               gr->image_target_texture_2d(gs->target,
+                                           gs->images[i]->image);
+       }
+}
+#endif
+
 static void
 gl_renderer_attach_dmabuf(struct weston_surface *surface,
                          struct weston_buffer *buffer,
@@ -2520,6 +2602,9 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
        struct linux_dmabuf_buffer *dmabuf;
        EGLint format;
        int i;
+#ifdef HAVE_TBM
+       tbm_surface_h tsurface;
+#endif
 
        weston_buffer_reference(&gs->buffer_ref, buffer);
        weston_buffer_release_reference(&gs->buffer_release_ref,
@@ -2544,6 +2629,10 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
 
        if (shm_buffer)
                gl_renderer_attach_shm(es, buffer, shm_buffer);
+#ifdef HAVE_TBM
+       else if ((tsurface = wayland_tbm_server_get_surface(gr->wl_tbm_server, buffer->resource)))
+               gl_renderer_attach_tbm_surface(es, buffer, tsurface);
+#endif
        else if (gr->has_bind_display &&
                 gr->query_buffer(gr->egl_display, (void *)buffer->resource,
                                  EGL_TEXTURE_FORMAT, &format))
@@ -3584,6 +3673,24 @@ gl_renderer_display_create(struct weston_compositor *ec,
                goto fail_with_error;
        }
 
+#ifdef HAVE_TBM
+       tbm_bufmgr bufmgr;
+
+       gr->wl_tbm_server = wayland_tbm_server_init(ec->wl_display, NULL, -1, -1);
+       if (!gr->wl_tbm_server) {
+               weston_log("Fail to init wayland_tbm_server\n");
+               goto fail_with_error;
+       }
+
+       bufmgr = wayland_tbm_server_get_bufmgr(gr->wl_tbm_server);
+       if (!bufmgr) {
+               weston_log("Fail to get bufmgr\n");
+               wayland_tbm_server_deinit(gr->wl_tbm_server);
+               goto fail_with_error;
+       }
+
+       tbm_bufmgr_bind_native_display(bufmgr, (void *)ec->wl_display);
+#endif
        return 0;
 
 fail_with_error:
index 374e65ba51563f9e0c5dc1c0446f3e3bdd020c8a..2d0d39388ebb794509b6ea5ac7ec60d1eba7c9bf 100644 (file)
@@ -27,6 +27,18 @@ foreach name : [ 'egl', 'glesv2' ]
        deps_renderer_gl += d
 endforeach
 
+if get_option('backend-tdm')
+       foreach name : [ 'libtbm', 'wayland-tbm-server' ]
+               d = dependency(name, required: false)
+               if not d.found()
+                       error('backend-tdm of gl-renderer requires @0@ which was not found. Or, you can use \'-Dbackend-tdm=false\'.'.format(name))
+               endif
+               deps_renderer_gl += d
+       endforeach
+
+       config_h.set('HAVE_TBM', '1')
+endif
+
 plugin_gl = shared_library(
        'gl-renderer',
        srcs_renderer_gl,
index c862ecc47bd17341cea579a494925162164bd5cc..5413a534f5cb91f37b763981621222dc1891fb6b 100644 (file)
@@ -50,10 +50,16 @@ option(
        value: true,
        description: 'Weston backend: fbdev'
 )
+option(
+       'backend-tdm',
+       type: 'boolean',
+       value: true,
+       description: 'Weston backend: TDM(Tizen Display Manager)'
+)
 option(
        'backend-default',
        type: 'combo',
-       choices: [ 'auto', 'drm', 'wayland', 'x11', 'fbdev', 'headless' ],
+       choices: [ 'auto', 'drm', 'wayland', 'x11', 'fbdev', 'headless', 'tdm' ],
        value: 'drm',
        description: 'Default backend when no parent display server detected'
 )
index d61e1290cdeb255d93a269c646ba5fe49d3312c8..aba4e49fed88a602465157a96e694dcbc1afee68 100644 (file)
@@ -1,4 +1,4 @@
-%global apiver 9
+%global apiver 8
 %global meson_prob 0
 
 Name:           weston
@@ -180,6 +180,7 @@ export LDFLAGS="%{?build_ldflags} -Wl,-z,undefs"
 %{_libdir}/libweston-%{apiver}/wayland-backend.so
 #%{_libdir}/libweston-%{apiver}/x11-backend.so
 #%{_libdir}/libweston-%{apiver}/xwayland.so
+%{_libdir}/libweston-%{apiver}/tdm-backend.so
 %{_libdir}/libweston-%{apiver}.so.0*
 %{_libdir}/libweston-desktop-%{apiver}.so.0*
 
index 5e1ce4793cec6c30d6084ab4c8c502d918633d6a..041c929f83e58eb31414f6c6abe92c64cd1f410e 100644 (file)
 #include <string.h>
 #include <stdlib.h>
 #include <libweston/zalloc.h>
-
-#ifdef HAVE_MEMFD_CREATE
 #include <sys/mman.h>
-#endif
 
 #include "os-compatibility.h"