First draft for tdm backend 14/278014/1
authorSeunghun Lee <shiin.lee@samsung.com>
Wed, 23 Feb 2022 04:39:35 +0000 (13:39 +0900)
committerSooChan Lim <sc1.lim@samsung.com>
Mon, 18 Jul 2022 05:07:44 +0000 (14:07 +0900)
Change-Id: I5d73b20242a1e865713834f785a7fa0ff1f3ed11

include/libds/interfaces/output.h
include/libds/output.h
packaging/libds.spec
src/libds/backend/meson.build
src/libds/backend/tdm/backend.c [new file with mode: 0644]
src/libds/backend/tdm/meson.build [new file with mode: 0644]
src/libds/backend/tdm/output.c [new file with mode: 0644]
src/libds/backend/tdm/tdm.h [new file with mode: 0644]
src/libds/output.c

index cacb927..9b95db1 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <stdint.h>
 #include <wayland-server.h>
+#include <libds/output.h>
 #include <libds/backend.h>
 #include <libds/buffer.h>
 
@@ -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 {
index ea8e276..d2454fc 100644 (file)
@@ -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);
index 8b7c5a7..f68b266 100644 (file)
@@ -14,6 +14,8 @@ BuildRequires:  pkgconfig(wayland-protocols)
 BuildRequires:  pkgconfig(pixman-1)
 BuildRequires:  pkgconfig(libdrm)
 
+BuildRequires:  pkgconfig(libtdm)
+
 %description
 Wayland Compositor Library
 
index b135534..e095efd 100644 (file)
@@ -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 (file)
index 0000000..582fe62
--- /dev/null
@@ -0,0 +1,162 @@
+#include <assert.h>
+#include <stdlib.h>
+
+#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 (file)
index 0000000..9a0b041
--- /dev/null
@@ -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 (file)
index 0000000..74a0e29
--- /dev/null
@@ -0,0 +1,145 @@
+#include <assert.h>
+#include <stdlib.h>
+
+#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 (file)
index 0000000..3a8990b
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef DS_BACKEND_TDM_H
+#define DS_BACKEND_TDM_H
+
+#include <time.h>
+#include <tdm.h>
+
+#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
index 21d4ff8..f5cd356 100644 (file)
@@ -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)