--- /dev/null
+/*
+ * 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;
+}