summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compositor/main.c68
-rw-r--r--include/libweston/libweston.h1
-rw-r--r--libweston/backend-tdm/meson.build35
-rw-r--r--libweston/backend-tdm/tdm-internal.h179
-rw-r--r--libweston/backend-tdm/tdm.c1280
-rw-r--r--libweston/compositor.c1
-rw-r--r--libweston/meson.build1
-rw-r--r--libweston/renderer-gl/gl-renderer-internal.h2
-rw-r--r--libweston/renderer-gl/gl-renderer.c107
-rw-r--r--libweston/renderer-gl/meson.build12
-rw-r--r--meson_options.txt8
-rw-r--r--packaging/weston.spec3
-rw-r--r--shared/os-compatibility.c3
13 files changed, 1695 insertions, 5 deletions
diff --git a/compositor/main.c b/compositor/main.c
index 8eb8a470..035ee323 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -643,6 +643,9 @@ usage(int error_code)
#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"
" -i, --idle-time=SECS\tIdle time in seconds\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;
diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h
index dc54b170..d1916692 100644
--- a/include/libweston/libweston.h
+++ b/include/libweston/libweston.h
@@ -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
index 00000000..bec333da
--- /dev/null
+++ b/libweston/backend-tdm/meson.build
@@ -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
index 00000000..6fb8818d
--- /dev/null
+++ b/libweston/backend-tdm/tdm-internal.h
@@ -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
index 00000000..3140ca35
--- /dev/null
+++ b/libweston/backend-tdm/tdm.c
@@ -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;
+}
diff --git a/libweston/compositor.c b/libweston/compositor.c
index dbc64927..1e7c2b94 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -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
diff --git a/libweston/meson.build b/libweston/meson.build
index 08d23ecd..d989b287 100644
--- a/libweston/meson.build
+++ b/libweston/meson.build
@@ -240,3 +240,4 @@ subdir('backend-headless')
subdir('backend-rdp')
subdir('backend-wayland')
subdir('backend-x11')
+subdir('backend-tdm')
diff --git a/libweston/renderer-gl/gl-renderer-internal.h b/libweston/renderer-gl/gl-renderer-internal.h
index 00f617a9..5f8bac10 100644
--- a/libweston/renderer-gl/gl-renderer-internal.h
+++ b/libweston/renderer-gl/gl-renderer-internal.h
@@ -115,6 +115,8 @@ struct gl_renderer {
bool has_wait_sync;
PFNEGLWAITSYNCKHRPROC wait_sync;
+
+ void *wl_tbm_server;
};
static inline struct gl_renderer *
diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c
index 6c435507..967827e6 100644
--- a/libweston/renderer-gl/gl-renderer.c
+++ b/libweston/renderer-gl/gl-renderer.c
@@ -58,6 +58,11 @@
#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:
diff --git a/libweston/renderer-gl/meson.build b/libweston/renderer-gl/meson.build
index 374e65ba..2d0d3938 100644
--- a/libweston/renderer-gl/meson.build
+++ b/libweston/renderer-gl/meson.build
@@ -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,
diff --git a/meson_options.txt b/meson_options.txt
index c862ecc4..5413a534 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -51,9 +51,15 @@ option(
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'
)
diff --git a/packaging/weston.spec b/packaging/weston.spec
index d61e1290..aba4e49f 100644
--- a/packaging/weston.spec
+++ b/packaging/weston.spec
@@ -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*
diff --git a/shared/os-compatibility.c b/shared/os-compatibility.c
index 5e1ce479..041c929f 100644
--- a/shared/os-compatibility.c
+++ b/shared/os-compatibility.c
@@ -34,10 +34,7 @@
#include <string.h>
#include <stdlib.h>
#include <libweston/zalloc.h>
-
-#ifdef HAVE_MEMFD_CREATE
#include <sys/mman.h>
-#endif
#include "os-compatibility.h"