diff options
-rw-r--r-- | compositor/main.c | 68 | ||||
-rw-r--r-- | include/libweston/libweston.h | 1 | ||||
-rw-r--r-- | libweston/backend-tdm/meson.build | 35 | ||||
-rw-r--r-- | libweston/backend-tdm/tdm-internal.h | 179 | ||||
-rw-r--r-- | libweston/backend-tdm/tdm.c | 1280 | ||||
-rw-r--r-- | libweston/compositor.c | 1 | ||||
-rw-r--r-- | libweston/meson.build | 1 | ||||
-rw-r--r-- | libweston/renderer-gl/gl-renderer-internal.h | 2 | ||||
-rw-r--r-- | libweston/renderer-gl/gl-renderer.c | 107 | ||||
-rw-r--r-- | libweston/renderer-gl/meson.build | 12 | ||||
-rw-r--r-- | meson_options.txt | 8 | ||||
-rw-r--r-- | packaging/weston.spec | 3 | ||||
-rw-r--r-- | shared/os-compatibility.c | 3 |
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" |