From 2dcbbdaeb989750e1fce697f02205f57602a2066 Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Wed, 23 Feb 2022 13:39:35 +0900 Subject: [PATCH] First draft for tdm backend Change-Id: I2f0deeeb9990495907f9e805e6e43401fb28e008 --- include/libds/interfaces/output.h | 13 ++- include/libds/output.h | 14 ++++ packaging/libds.spec | 2 + src/libds/backend/meson.build | 1 + src/libds/backend/tdm/backend.c | 162 ++++++++++++++++++++++++++++++++++++++ src/libds/backend/tdm/meson.build | 8 ++ src/libds/backend/tdm/output.c | 145 ++++++++++++++++++++++++++++++++++ src/libds/backend/tdm/tdm.h | 53 +++++++++++++ src/libds/output.c | 33 ++++++++ 9 files changed, 423 insertions(+), 8 deletions(-) create mode 100644 src/libds/backend/tdm/backend.c create mode 100644 src/libds/backend/tdm/meson.build create mode 100644 src/libds/backend/tdm/output.c create mode 100644 src/libds/backend/tdm/tdm.h diff --git a/include/libds/interfaces/output.h b/include/libds/interfaces/output.h index cacb927..9b95db1 100644 --- a/include/libds/interfaces/output.h +++ b/include/libds/interfaces/output.h @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -28,18 +29,11 @@ struct ds_output_interface bool (*commit)(struct ds_output *output); }; -struct ds_output_mode { - struct wl_list link; - int32_t width, height; - int32_t refresh; - bool preferred; -}; - struct ds_output_state { enum ds_output_state_field committed; struct ds_buffer *buffer; - struct ds_output_mode *mode; + const struct ds_output_mode *mode; bool enabled; }; @@ -54,8 +48,11 @@ struct ds_output struct wl_global *global; struct ds_buffer *back_buffer, *front_buffer; + const struct ds_output_mode *current_mode; struct ds_output_state pending; + struct wl_list modes; + struct wl_listener display_destroy; struct { diff --git a/include/libds/output.h b/include/libds/output.h index ea8e276..d2454fc 100644 --- a/include/libds/output.h +++ b/include/libds/output.h @@ -6,6 +6,13 @@ struct ds_output; +struct ds_output_mode { + int32_t width, height; + int32_t refresh; + bool preferred; + struct wl_list link; +}; + void ds_output_destroy(struct ds_output *output); @@ -15,6 +22,13 @@ ds_output_commit(struct ds_output *output); void ds_output_attach_buffer(struct ds_output *output, struct ds_buffer *buffer); +const struct ds_output_mode * +ds_output_preferred_mode(struct ds_output *output); + +void +ds_output_set_mode(struct ds_output *output, + const struct ds_output_mode *mode); + void ds_output_add_destroy_listener(struct ds_output *output, struct wl_listener *listener); diff --git a/packaging/libds.spec b/packaging/libds.spec index 8b7c5a7..f68b266 100644 --- a/packaging/libds.spec +++ b/packaging/libds.spec @@ -14,6 +14,8 @@ BuildRequires: pkgconfig(wayland-protocols) BuildRequires: pkgconfig(pixman-1) BuildRequires: pkgconfig(libdrm) +BuildRequires: pkgconfig(libtdm) + %description Wayland Compositor Library diff --git a/src/libds/backend/meson.build b/src/libds/backend/meson.build index b135534..e095efd 100644 --- a/src/libds/backend/meson.build +++ b/src/libds/backend/meson.build @@ -3,3 +3,4 @@ libds_files += files( ) subdir('wayland') +subdir('tdm') diff --git a/src/libds/backend/tdm/backend.c b/src/libds/backend/tdm/backend.c new file mode 100644 index 0000000..582fe62 --- /dev/null +++ b/src/libds/backend/tdm/backend.c @@ -0,0 +1,162 @@ +#include +#include + +#include "libds/log.h" +#include "tdm.h" + +static const struct ds_backend_interface tdm_backend_iface; +static void tdm_backend_handle_display_destroy(struct wl_listener *listener, + void *data); +static void tdm_backend_destroy(struct ds_tdm_backend *tdm); +static int tdm_backend_handle_tdm_event(int fd, uint32_t mask, void *data); + +WL_EXPORT struct ds_backend * +ds_tdm_backend_create(struct wl_display *display) +{ + struct ds_tdm_backend *tdm; + struct wl_event_loop *loop; + tdm_error err; + + ds_inf("Initializing TDM backend"); + + tdm = calloc(1, sizeof *tdm); + if (!tdm) + return NULL; + + ds_backend_init(&tdm->base, &tdm_backend_iface); + + tdm->wl_display = display; + tdm->clock = CLOCK_MONOTONIC; // FIXME + wl_list_init(&tdm->outputs); + + tdm->tdm_display = tdm_display_init(&err); + if (err != TDM_ERROR_NONE) { + ds_err("Could not initialize tdm_display"); + goto err_display; + } + + err = tdm_display_get_fd(tdm->tdm_display, &tdm->fd); + if (err != TDM_ERROR_NONE || tdm->fd < 0) { + ds_err("Could not get fd from tdm_display: err(%d)", err); + goto err_fd; + } + + loop = wl_display_get_event_loop(display); + tdm->tdm_event = wl_event_loop_add_fd(loop, tdm->fd, WL_EVENT_READABLE, + tdm_backend_handle_tdm_event, tdm); + if (!tdm->tdm_event) { + ds_err("could not add fd event handler for tdm event"); + goto err_event; + } + + tdm->display_destroy.notify = tdm_backend_handle_display_destroy; + wl_display_add_destroy_listener(display, &tdm->display_destroy); + + return &tdm->base; + +err_event: + close(tdm->fd); +err_fd: + tdm_display_deinit(tdm->tdm_display); +err_display: + free(tdm); + + return NULL; +} + +struct ds_tdm_backend * +tdm_backend_from_backend(struct ds_backend *backend) +{ + assert(backend->iface == &tdm_backend_iface); + return (struct ds_tdm_backend *)backend; +} + +static void +tdm_backend_destroy(struct ds_tdm_backend *tdm) +{ + wl_list_remove(&tdm->display_destroy.link); + wl_event_source_remove(tdm->tdm_event); + close(tdm->fd); + tdm_display_deinit(tdm->tdm_display); + free(tdm); +} + +static bool +tdm_backend_scan_outputs(struct ds_tdm_backend *tdm) +{ + struct ds_tdm_output *output; + tdm_output *tdm_output; + tdm_error err; + int num_outputs, i; + + err = tdm_display_get_output_count(tdm->tdm_display, &num_outputs); + if (err != TDM_ERROR_NONE) { + ds_err("Could not get number of outputs: err(%d)", err); + return false; + } + + for (i = 0; i < num_outputs; i++) { + tdm_output = tdm_display_get_output(tdm->tdm_display, i, &err); + if (err != TDM_ERROR_NONE || !tdm_output) { + ds_err("Could not get output from tdm_display: index(%d) err(%d)", + i, err); + continue; + } + + output = create_tdm_output(tdm, tdm_output); + if (!output) { + ds_err("Could not create output: index(%d)", i); + continue; + } + + wl_list_insert(&tdm->outputs, &output->link); + + wl_signal_emit(&tdm->base.events.new_output, &output->base); + } + + return true; +} + +static bool +tdm_backend_iface_start(struct ds_backend *backend) +{ + struct ds_tdm_backend *tdm; + + tdm = tdm_backend_from_backend(backend); + return tdm_backend_scan_outputs(tdm); +} + +static void +tdm_backend_iface_destroy(struct ds_backend *backend) +{ + struct ds_tdm_backend *tdm; + + tdm = tdm_backend_from_backend(backend); + tdm_backend_destroy(tdm); +} + +static const struct ds_backend_interface tdm_backend_iface = { + .start = tdm_backend_iface_start, + .destroy = tdm_backend_iface_destroy, + .get_drm_fd = NULL, +}; + +static void +tdm_backend_handle_display_destroy(struct wl_listener *listener, void *data) +{ + struct ds_tdm_backend *tdm; + + tdm = wl_container_of(listener, tdm, display_destroy); + tdm_backend_destroy(tdm); +} + +static int +tdm_backend_handle_tdm_event(int fd, uint32_t mask, void *data) +{ + struct ds_tdm_backend *tdm; + + tdm = data; + tdm_display_handle_events(tdm->tdm_display); + + return 0; +} diff --git a/src/libds/backend/tdm/meson.build b/src/libds/backend/tdm/meson.build new file mode 100644 index 0000000..9a0b041 --- /dev/null +++ b/src/libds/backend/tdm/meson.build @@ -0,0 +1,8 @@ +libds_files += files( + 'backend.c', + 'output.c', +) + +libds_deps += [ + dependency('libtdm', required: true), +] diff --git a/src/libds/backend/tdm/output.c b/src/libds/backend/tdm/output.c new file mode 100644 index 0000000..74a0e29 --- /dev/null +++ b/src/libds/backend/tdm/output.c @@ -0,0 +1,145 @@ +#include +#include + +#include "libds/log.h" +#include "tdm.h" + +static const struct ds_output_interface tdm_output_iface; +static bool tdm_output_init_modes(struct ds_tdm_output *output); +static void tdm_output_destroy(struct ds_tdm_output *output); + +struct ds_tdm_output * +create_tdm_output(struct ds_tdm_backend *tdm, tdm_output *tdm_output) +{ + struct ds_tdm_output *output; + tdm_output_conn_status conn; + tdm_error err; + + output = calloc(1, sizeof *output); + if (!output) + return NULL; + + ds_output_init(&output->base, &tdm->base, &tdm_output_iface, + tdm->wl_display); + + output->tdm = tdm; + output->tdm_output = tdm_output; + + err = tdm_output_get_conn_status(tdm_output, &conn); + if (err != TDM_ERROR_NONE) { + ds_err("Could not get connection status of tdm output(%p): err(%d)", + tdm_output, err); + goto err_status; + } + + if (conn == TDM_OUTPUT_CONN_STATUS_CONNECTED) { + output->status = DS_TDM_OUTPUT_CONNECTED; + + if (!tdm_output_init_modes(output)) { + ds_err("Could not initialize modes of tdm output(%p)", + tdm_output); + goto err_status; + } + } + else if (conn == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) { + output->status = DS_TDM_OUTPUT_DISCONNECTED; + } + + ds_inf("TDM output(%p) created.", output); + + return output; + +err_status: + free(output); + + return NULL; +} + +static struct ds_tdm_output * +tdm_output_from_output(struct ds_output *ds_output) +{ + assert(ds_output->iface == &tdm_output_iface); + return (struct ds_tdm_output *)ds_output; +} + +static void +tdm_output_iface_destroy(struct ds_output *ds_output) +{ + struct ds_tdm_output *output; + + output = tdm_output_from_output(ds_output); + tdm_output_destroy(output); +} + +static bool +tdm_output_iface_commit(struct ds_output *ds_output) +{ + // TODO + return true; +} + +static const struct ds_output_interface tdm_output_iface = +{ + .destroy = tdm_output_iface_destroy, + .commit = tdm_output_iface_commit, +}; + +static void +tdm_output_destroy(struct ds_tdm_output *output) +{ + struct ds_tdm_output_mode *mode, *mode_tmp; + + wl_list_for_each_safe(mode, mode_tmp, &output->base.modes, base.link) { + wl_list_remove(&mode->base.link); + free(mode); + } + + free(output); +} + +static bool +tdm_output_init_modes(struct ds_tdm_output *output) +{ + struct ds_tdm_output_mode *mode; + const tdm_output_mode *tdm_modes, *tdm_mode; + tdm_error err; + int num_modes, i; + + err = tdm_output_get_available_modes(output->tdm_output, &tdm_modes, + &num_modes); + if (err != TDM_ERROR_NONE) { + ds_err("Could not get available modes: output(%p)", output); + return false; + } + + ds_inf("Detected modes:"); + + for (i = 0; i < num_modes; i++) { + tdm_mode = &tdm_modes[i]; + + mode = calloc(1, sizeof *mode); + if (!mode) { + ds_err("Could not allocate memory"); + continue; + } + + mode->tdm_mode = tdm_mode; + mode->base.width = tdm_mode->hdisplay; + mode->base.height = tdm_mode->vdisplay; + mode->base.refresh = (int32_t)tdm_mode->vrefresh; // FIXME + + if (tdm_mode->type & TDM_OUTPUT_MODE_TYPE_PREFERRED) + mode->base.preferred = true; + + ds_inf(" %dx%d@%d %s", mode->base.width, mode->base.height, + mode->base.refresh, + mode->base.preferred ? "(preferred)" : ""); + + if (tdm_mode->type & TDM_OUTPUT_MODE_TYPE_DEFAULT) + wl_list_insert(&output->base.modes, &mode->base.link); + else + wl_list_insert(output->base.modes.prev, &mode->base.link); + } + + return true; +} diff --git a/src/libds/backend/tdm/tdm.h b/src/libds/backend/tdm/tdm.h new file mode 100644 index 0000000..3a8990b --- /dev/null +++ b/src/libds/backend/tdm/tdm.h @@ -0,0 +1,53 @@ +#ifndef DS_BACKEND_TDM_H +#define DS_BACKEND_TDM_H + +#include +#include + +#include "libds/interfaces/backend.h" +#include "libds/interfaces/output.h" + +enum ds_tdm_output_status { + DS_TDM_OUTPUT_DISCONNECTED, + DS_TDM_OUTPUT_CONNECTED, +}; + +struct ds_tdm_output_mode { + struct ds_output_mode base; + const tdm_output_mode *tdm_mode; +}; + +struct ds_tdm_backend +{ + struct ds_backend base; + + tdm_display *tdm_display; + struct wl_display *wl_display; + struct wl_event_source *tdm_event; + + struct wl_list outputs; // ds_tdm_output.link + + struct wl_listener display_destroy; + + clockid_t clock; + int fd; +}; + +struct ds_tdm_output +{ + struct ds_output base; + + struct ds_tdm_backend *tdm; + tdm_output *tdm_output; + + struct wl_list link; + + enum ds_tdm_output_status status; +}; + +struct ds_tdm_backend *tdm_backend_from_backend(struct ds_backend *backend); + +struct ds_tdm_output *create_tdm_output(struct ds_tdm_backend *tdm, + tdm_output *tdm_output); + +#endif diff --git a/src/libds/output.c b/src/libds/output.c index 21d4ff8..f5cd356 100644 --- a/src/libds/output.c +++ b/src/libds/output.c @@ -20,6 +20,8 @@ ds_output_init(struct ds_output *output, struct ds_backend *backend, output->iface = iface; output->display = display; + wl_list_init(&output->modes); + wl_signal_init(&output->events.destroy); wl_signal_init(&output->events.frame); wl_signal_init(&output->events.commit); @@ -65,6 +67,37 @@ ds_output_attach_buffer(struct ds_output *output, struct ds_buffer *buffer) output->pending.buffer = ds_buffer_lock(buffer); } +WL_EXPORT const struct ds_output_mode * +ds_output_preferred_mode(struct ds_output *output) +{ + struct ds_output_mode *mode; + + if (wl_list_empty(&output->modes)) + return NULL; + + wl_list_for_each(mode, &output->modes, link) { + if (mode->preferred) + return mode; + } + + // No preferred mode, choose the first one + return wl_container_of(output->modes.next, mode, link); +} + +WL_EXPORT void +ds_output_set_mode(struct ds_output *output, const struct ds_output_mode *mode) +{ + output->pending.mode = NULL; + output->pending.committed &= ~DS_OUTPUT_STATE_MODE; + + if (output->current_mode == mode) { + return; + } + + output->pending.committed |= DS_OUTPUT_STATE_MODE; + output->pending.mode = mode; +} + WL_EXPORT void ds_output_add_destroy_listener(struct ds_output *output, struct wl_listener *listener) -- 2.7.4