backend/tdm: Add ds_tdm_output_hwc 17/278217/1
authorChangyeon Lee <cyeon.lee@samsung.com>
Thu, 16 Jun 2022 02:52:13 +0000 (11:52 +0900)
committerSooChan Lim <sc1.lim@samsung.com>
Mon, 18 Jul 2022 05:58:56 +0000 (14:58 +0900)
ds_tdm_output_hwc composites surface or buffer if hardware layer of output
can use direct scan-out(bypass compositing at output buffer).

ds_tdm_output_hwc has is consists of ds_tdm_output_hwc_window.
for using hardware compositing, display-server should set compositing
infomation of ds_tdm_output_hwc_window

it would be nice to ds_tdm_output_hwc can display all ds_tdm_output_hwc_window
by direct scan-out but it has restrictions.
output has limited number of hardware layer and hardware layer has
hw restrictions(buffer size, transform, format, etc).
if ds_tdm_output_hwc_window can not be display by direct scan-out,
display-server should composite it at output buffer.

ds_tdm_output_hwc_window is updated at output when ds_output_hwc_validate and
ds_output_commit is called because ds_tdm_output_hwc is component of ds_output.

Change-Id: If78e862e4acbcc1ae465fe8315fc478f888657b3

include/libds-tizen/backend/tdm.h
include/libds-tizen/backend/tdm_output_hwc.h [new file with mode: 0644]
include/libds-tizen/tbm_server.h
src/backend/tdm/meson.build
src/backend/tdm/output.c
src/backend/tdm/tdm.h
src/backend/tdm/tdm_output_hwc.c [new file with mode: 0644]
src/backend/tdm/tdm_output_hwc.h [new file with mode: 0644]

index 8c5c605..33422ba 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <libds/backend.h>
 #include <libds/buffer.h>
+#include <libds/output.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -10,8 +11,15 @@ extern "C" {
 
 struct ds_tdm_output;
 
+struct ds_tdm_output_hwc;
+
 struct ds_tdm_buffer_queue;
 
+struct ds_tdm_box {
+    int x, y;
+    int width, height;
+};
+
 struct ds_backend *
 ds_tdm_backend_create(struct wl_display *display);
 
@@ -31,6 +39,9 @@ void
 ds_tdm_buffer_queue_add_acquirable_listener(struct ds_tdm_buffer_queue *queue,
         struct wl_listener *listener);
 
+struct ds_tdm_output_hwc *
+ds_tdm_output_get_hwc(struct ds_tdm_output *output);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/libds-tizen/backend/tdm_output_hwc.h b/include/libds-tizen/backend/tdm_output_hwc.h
new file mode 100644 (file)
index 0000000..abde223
--- /dev/null
@@ -0,0 +1,74 @@
+#ifndef LIBDS_TIZEN_BACKEND_TDM_OUTPUT_HWC_H
+#define LIBDS_TIZEN_BACKEND_TDM_OUTPUT_HWC_H
+
+#include <libds-tizen/backend/tdm.h>
+#include <libds/buffer.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ds_tdm_output_hwc;
+
+struct ds_tdm_output_hwc_window;
+
+enum ds_tdm_output_hwc_window_composition {
+    DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_NONE,
+    DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_CLIENT,
+    DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_DEVICE,
+    DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_CURSOR,
+    DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_VIDEO,
+};
+
+WL_EXPORT void
+ds_tdm_output_hwc_set_enabled(struct ds_tdm_output_hwc *hwc, bool enabled);
+
+WL_EXPORT bool
+ds_tdm_output_hwc_validate(struct ds_tdm_output_hwc *hwc,
+        struct ds_tdm_output_hwc_window **composited_windows, uint32_t num_windows, uint32_t *num_changed);
+
+WL_EXPORT bool
+ds_tdm_output_hwc_get_changed_composition(struct ds_tdm_output_hwc *hwc, uint32_t *num_changed,
+        struct ds_tdm_output_hwc_window **changed_windows);
+
+WL_EXPORT bool
+ds_tdm_output_hwc_accept_validation(struct ds_tdm_output_hwc *hwc);
+
+WL_EXPORT struct ds_tdm_output_hwc_window *
+ds_tdm_output_hwc_window_create(struct ds_tdm_output_hwc *hwc);
+
+WL_EXPORT void
+ds_tdm_output_hwc_window_destroy(struct ds_tdm_output_hwc_window *hwc_window);
+
+WL_EXPORT bool
+ds_tdm_output_hwc_window_set_buffer(struct ds_tdm_output_hwc_window *hwc_window,
+        struct ds_buffer *buffer);
+
+WL_EXPORT void
+ds_tdm_output_hwc_window_set_src_box(struct ds_tdm_output_hwc_window *hwc_window,
+        const struct ds_tdm_box *src_box);
+
+WL_EXPORT void
+ds_tdm_output_hwc_window_set_transform(struct ds_tdm_output_hwc_window *hwc_window,
+        enum wl_output_transform transform);
+
+WL_EXPORT void
+ds_tdm_output_hwc_window_set_position(struct ds_tdm_output_hwc_window *hwc_window,
+        int x, int y);
+
+WL_EXPORT void
+ds_tdm_output_hwc_window_set_dest_size(struct ds_tdm_output_hwc_window *hwc_window,
+        int width, int height);
+
+WL_EXPORT void
+ds_tdm_output_hwc_window_set_composition(struct ds_tdm_output_hwc_window *hwc_window,
+        enum ds_tdm_output_hwc_window_composition composition);
+
+WL_EXPORT enum ds_tdm_output_hwc_window_composition
+ds_tdm_output_hwc_window_get_composition(struct ds_tdm_output_hwc_window *hwc_window);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
index f1cc97d..6e3b148 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <wayland-server.h>
 #include <tbm_surface.h>
+#include <libds/buffer.h>
 
 #ifdef __cplusplus
 extern "C" {
index 932559f..7a7c25f 100644 (file)
@@ -2,6 +2,7 @@ libds_tizen_files += files(
   'backend.c',
   'output.c',
   'tdm_buffer_queue.c',
+  'tdm_output_hwc.c',
 )
 
 libtdm = dependency('libtdm', required: true)
index 089c067..5e874df 100644 (file)
@@ -7,6 +7,7 @@
 #include "libds-tizen/allocator/tbm.h"
 
 #include "tdm.h"
+#include "tdm_output_hwc.h"
 #include "tdm_buffer_queue.h"
 
 static const struct ds_output_interface tdm_output_iface;
@@ -44,6 +45,12 @@ ds_tdm_output_get_buffer_queue(struct ds_tdm_output *output)
     return output->queue;
 }
 
+WL_EXPORT struct ds_tdm_output_hwc *
+ds_tdm_output_get_hwc(struct ds_tdm_output *output)
+{
+    return output->hwc;
+}
+
 struct ds_tdm_output *
 create_tdm_output(struct ds_tdm_backend *tdm, tdm_output *tdm_output)
 {
@@ -265,6 +272,9 @@ output_destroy(struct ds_tdm_output *output)
     if (output->queue)
         buffer_queue_destroy(output->queue);
 
+    if (output->hwc)
+        ds_tdm_output_hwc_destroy(output->hwc);
+
     free(output);
 }
 
@@ -316,6 +326,18 @@ output_init_modes(struct ds_tdm_output *output)
 }
 
 static void
+output_hwc_commit_handler(struct wl_listener *listener, void *data)
+{
+    struct ds_tdm_output *output;
+
+    output = wl_container_of(listener, output, hwc_commit_handler_listener);
+
+    output_update_front_buffer(output);
+
+    wl_signal_emit(&output->base.events.frame, &output->base);
+}
+
+static void
 output_init_hwc(struct ds_tdm_output *output)
 {
     tdm_error err;
@@ -325,6 +347,15 @@ output_init_hwc(struct ds_tdm_output *output)
         ds_err("Could not get tdm_hwc: output(%p)", output);
         return;
     }
+
+    output->hwc = ds_tdm_output_hwc_create(output->tdm.hwc);
+    if (!output->hwc) {
+        ds_err("Could not create ds_dm_hwc: output(%p)", output);
+        return;
+    }
+
+    output->hwc_commit_handler_listener.notify = output_hwc_commit_handler;
+    ds_tdm_output_hwc_add_commit_handler_listener(output->hwc, &output->hwc_commit_handler_listener);
 }
 
 static bool
@@ -352,8 +383,6 @@ output_set_pending_fb(struct ds_tdm_output *output,
     struct ds_tdm_queue_buffer *queue_buffer;
     struct ds_tdm_buffer *buffer;
     tbm_surface_h surface = NULL;
-    tdm_region fb_damage;
-    tdm_error err;
 
     if (output->queue) {
         queue_buffer = buffer_queue_find_buffer(output->queue, ds_buffer);
@@ -371,10 +400,7 @@ output_set_pending_fb(struct ds_tdm_output *output,
         surface = buffer->surface;
     }
 
-    memset(&fb_damage, 0, sizeof(fb_damage));
-    err = tdm_hwc_set_client_target_buffer(output->tdm.hwc,
-            surface, fb_damage);
-    if (err != TDM_ERROR_NONE) {
+    if (!ds_tdm_output_hwc_set_client_target_buffer(output->hwc, surface)) {
         ds_err("Could not set hwc client target buffer");
         ds_buffer_unlock(ds_buffer);
         return false;
@@ -385,38 +411,10 @@ output_set_pending_fb(struct ds_tdm_output *output,
     return true;
 }
 
-static void
-output_hwc_commit_handler(tdm_hwc *hwc, unsigned int sequence,
-        unsigned int tv_sec, unsigned int tv_usec, void *user_data)
-{
-    struct ds_tdm_output *output = user_data;
-
-    output_update_front_buffer(output);
-
-    wl_signal_emit(&output->base.events.frame, &output->base);
-}
-
 static bool
 output_hwc_commit(struct ds_tdm_output *output)
 {
-    tdm_error err;
-    uint32_t num_changes;
-
-    err = tdm_hwc_validate(output->tdm.hwc, NULL, 0, &num_changes);
-    if (err != TDM_ERROR_NONE) {
-        ds_err("Could not hwc validate");
-        return false;
-    }
-
-    err = tdm_hwc_accept_validation(output->tdm.hwc);
-    if (err != TDM_ERROR_NONE) {
-        ds_err("Could not hwc accept validation");
-        return false;
-    }
-
-    err = tdm_hwc_commit(output->tdm.hwc, 0, output_hwc_commit_handler,
-            output);
-    if (err != TDM_ERROR_NONE) {
+    if (!ds_tdm_output_hwc_commit(output->hwc)) {
         ds_err("Could not hwc commit");
         return false;
     }
index a151f01..7c00202 100644 (file)
@@ -48,6 +48,9 @@ struct ds_tdm_output
         tdm_hwc *hwc;
     } tdm;
 
+    struct ds_tdm_output_hwc *hwc;
+    struct wl_listener hwc_commit_handler_listener;
+
     struct wl_list link;
 
     enum ds_tdm_output_status status;
diff --git a/src/backend/tdm/tdm_output_hwc.c b/src/backend/tdm/tdm_output_hwc.c
new file mode 100644 (file)
index 0000000..8db2984
--- /dev/null
@@ -0,0 +1,636 @@
+#include <stdint.h>
+#include <wayland-server.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <tdm.h>
+#include <libds/log.h>
+#include <libds-tizen/tbm_server.h>
+
+#include "tdm_output_hwc.h"
+
+static void
+hwc_window_update_front_buffer(struct ds_tdm_output_hwc_window *hwc_window)
+{
+    if (hwc_window->front_buffer == hwc_window->back_buffer) {
+        if (hwc_window->back_buffer) {
+            ds_buffer_unlock(hwc_window->back_buffer);
+            hwc_window->back_buffer = NULL;
+        }
+
+        return;
+    }
+
+    ds_tdm_output_hwc_window_lock(hwc_window);
+
+    if (hwc_window->front_buffer) {
+        ds_buffer_unlock(hwc_window->front_buffer);
+        hwc_window->front_buffer = NULL;
+        ds_tdm_output_hwc_window_unlock(hwc_window);
+    }
+
+    hwc_window->front_buffer = hwc_window->back_buffer;
+    hwc_window->back_buffer = NULL;
+
+    if (hwc_window->front_buffer)
+        ds_tdm_output_hwc_window_lock(hwc_window);
+
+    ds_tdm_output_hwc_window_unlock(hwc_window);
+}
+
+static void
+hwc_window_attach_back_buffer(struct ds_tdm_output_hwc_window *hwc_window,
+        struct ds_buffer *buffer)
+{
+    if (hwc_window->back_buffer) {
+        ds_buffer_unlock(hwc_window->back_buffer);
+        hwc_window->back_buffer = NULL;
+    }
+
+    if (buffer)
+        hwc_window->back_buffer = ds_buffer_lock(buffer);
+}
+
+static unsigned int
+get_horizontal_get(tbm_surface_h tsurface)
+{
+    unsigned int horizontal = 0;
+    tbm_surface_info_s surf_info;
+
+    tbm_surface_get_info(tsurface, &surf_info);
+
+    switch (surf_info.format) {
+        case TBM_FORMAT_YUV420:
+        case TBM_FORMAT_YVU420:
+        case TBM_FORMAT_YUV422:
+        case TBM_FORMAT_YVU422:
+        case TBM_FORMAT_NV12:
+        case TBM_FORMAT_NV21:
+            horizontal = surf_info.planes[0].stride;
+            break;
+        case TBM_FORMAT_YUYV:
+        case TBM_FORMAT_UYVY:
+            horizontal = surf_info.planes[0].stride >> 1;
+            break;
+        case TBM_FORMAT_XRGB8888:
+        case TBM_FORMAT_XBGR8888:
+        case TBM_FORMAT_RGBX8888:
+        case TBM_FORMAT_BGRX8888:
+        case TBM_FORMAT_ARGB8888:
+        case TBM_FORMAT_ABGR8888:
+        case TBM_FORMAT_RGBA8888:
+        case TBM_FORMAT_BGRA8888:
+        case TBM_FORMAT_XRGB2101010:
+        case TBM_FORMAT_XBGR2101010:
+        case TBM_FORMAT_RGBX1010102:
+        case TBM_FORMAT_BGRX1010102:
+        case TBM_FORMAT_ARGB2101010:
+        case TBM_FORMAT_ABGR2101010:
+        case TBM_FORMAT_RGBA1010102:
+        case TBM_FORMAT_BGRA1010102:
+            horizontal = surf_info.planes[0].stride >> 2;
+            break;
+        default:
+            ds_err("not supported format");
+    }
+
+    return horizontal;
+}
+
+static tdm_transform
+get_tdm_transform(enum wl_output_transform output_transform)
+{
+    switch (output_transform) {
+        case WL_OUTPUT_TRANSFORM_90:
+            return TDM_TRANSFORM_90;
+        case WL_OUTPUT_TRANSFORM_180:
+            return TDM_TRANSFORM_180;
+        case WL_OUTPUT_TRANSFORM_270:
+            return TDM_TRANSFORM_270;
+        case WL_OUTPUT_TRANSFORM_FLIPPED:
+            return TDM_TRANSFORM_FLIPPED;
+        case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+            return TDM_TRANSFORM_FLIPPED_90;
+        case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+            return TDM_TRANSFORM_FLIPPED_180;
+        case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+            return TDM_TRANSFORM_FLIPPED_270;
+           case WL_OUTPUT_TRANSFORM_NORMAL:
+        default:
+            return TDM_TRANSFORM_NORMAL;
+    }
+}
+
+static tdm_hwc_window_composition
+get_tdm_composition(enum ds_tdm_output_hwc_window_composition composition)
+{
+    switch (composition) {
+        case DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_CLIENT:
+            return TDM_HWC_WIN_COMPOSITION_CLIENT;
+        case DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_CURSOR:
+            return TDM_HWC_WIN_COMPOSITION_CURSOR;
+        case DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_DEVICE:
+            return TDM_HWC_WIN_COMPOSITION_DEVICE;
+        case DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_VIDEO:
+            return TDM_HWC_WIN_COMPOSITION_VIDEO;
+        case DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_NONE:
+        default:
+            return TDM_HWC_WIN_COMPOSITION_NONE;
+    }
+}
+
+static enum ds_tdm_output_hwc_window_composition
+get_composition(tdm_hwc_window_composition composition)
+{
+    switch (composition) {
+        case TDM_HWC_WIN_COMPOSITION_CLIENT:
+            return DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_CLIENT;
+        case TDM_HWC_WIN_COMPOSITION_CURSOR:
+            return DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_CURSOR;
+        case TDM_HWC_WIN_COMPOSITION_DEVICE:
+            return DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_DEVICE;
+        case TDM_HWC_WIN_COMPOSITION_VIDEO:
+            return DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_VIDEO;
+        case TDM_HWC_WIN_COMPOSITION_NONE:
+        default:
+            return DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_NONE;
+    }
+}
+
+struct ds_tdm_output_hwc *
+ds_tdm_output_hwc_create(tdm_hwc *thwc)
+{
+    struct ds_tdm_output_hwc *hwc;
+
+    hwc = calloc(1, sizeof *hwc);
+    if (!hwc)
+        return NULL;
+
+    hwc->thwc = thwc;
+
+    wl_list_init(&hwc->hwc_windows);
+    wl_signal_init(&hwc->events.commit_handler);
+    wl_signal_init(&hwc->events.destroy);
+
+    return hwc;
+}
+
+void
+ds_tdm_output_hwc_destroy(struct ds_tdm_output_hwc *hwc)
+{
+    wl_signal_emit(&hwc->events.destroy, hwc);
+
+    free(hwc);
+}
+
+bool
+ds_tdm_output_hwc_set_client_target_buffer(struct ds_tdm_output_hwc *hwc, tbm_surface_h tsurface)
+{
+    tdm_error terr;
+    tdm_region fb_damage;
+
+    memset(&fb_damage, 0, sizeof(fb_damage));
+
+    hwc->target_buffer_info.src_config.pos.x = 0;
+    hwc->target_buffer_info.src_config.pos.y = 0;
+    hwc->target_buffer_info.src_config.pos.w = tbm_surface_get_width(tsurface);
+    hwc->target_buffer_info.src_config.pos.h = tbm_surface_get_height(tsurface);
+    hwc->target_buffer_info.src_config.size.h = get_horizontal_get(tsurface);
+    hwc->target_buffer_info.src_config.size.v = tbm_surface_get_height(tsurface);
+    hwc->target_buffer_info.src_config.format = tbm_surface_get_format(tsurface);
+
+    hwc->target_buffer_info.dst_pos.x = 0;
+    hwc->target_buffer_info.dst_pos.y = 0;
+    hwc->target_buffer_info.dst_pos.w = tbm_surface_get_width(tsurface);
+    hwc->target_buffer_info.dst_pos.h = tbm_surface_get_height(tsurface);
+
+    hwc->target_buffer_info.transform = TDM_TRANSFORM_NORMAL;
+
+    tdm_hwc_set_client_target_buffer_info(hwc->thwc, &hwc->target_buffer_info);
+
+    terr = tdm_hwc_set_client_target_buffer(hwc->thwc, tsurface, fb_damage);
+    if (terr != TDM_ERROR_NONE) {
+        ds_err("Could not set hwc client target buffer");
+        return false;
+    }
+
+    return true;
+}
+
+void
+ds_tdm_output_hwc_window_set_accepted_composition(struct ds_tdm_output_hwc_window *hwc_window,
+        enum ds_tdm_output_hwc_window_composition composition)
+{
+    if (hwc_window->accepted_composition == composition)
+        return;
+
+    hwc_window->accepted_composition = composition;
+}
+
+static void
+hwc_window_free(struct ds_tdm_output_hwc_window *hwc_window)
+{
+    if (hwc_window->buffer)
+        ds_buffer_unlock(hwc_window->buffer);
+
+    if (hwc_window->set_buffer)
+        ds_buffer_unlock(hwc_window->set_buffer);
+
+    if (hwc_window->front_buffer)
+        ds_buffer_unlock(hwc_window->front_buffer);
+
+    if (hwc_window->back_buffer)
+        ds_buffer_unlock(hwc_window->back_buffer);
+
+    wl_list_remove(&hwc_window->link);
+
+    tdm_hwc_window_destroy(hwc_window->twindow);
+
+    free(hwc_window);
+}
+
+static void
+hwc_window_consider_destroy(struct ds_tdm_output_hwc_window *hwc_window)
+{
+    if (!hwc_window->dropped || hwc_window->n_locks > 0)
+        return;
+
+    hwc_window_free(hwc_window);
+}
+
+struct ds_tdm_output_hwc_window *
+ds_tdm_output_hwc_window_lock(struct ds_tdm_output_hwc_window *hwc_window)
+{
+    hwc_window->n_locks++;
+    ds_dbg("hwc_window(%p) n_locks(%zu)", hwc_window, hwc_window->n_locks);
+    return hwc_window;
+}
+
+void
+ds_tdm_output_hwc_window_unlock(struct ds_tdm_output_hwc_window *hwc_window)
+{
+    hwc_window->n_locks--;
+    ds_dbg("hwc_window(%p) n_locks(%zu)", hwc_window, hwc_window->n_locks);
+
+    hwc_window_consider_destroy(hwc_window);
+}
+
+WL_EXPORT void
+ds_tdm_output_hwc_set_enabled(struct ds_tdm_output_hwc *hwc, bool enabled)
+{
+    if (hwc->enabled == enabled)
+        return;
+
+    hwc->enabled = enabled;
+}
+
+WL_EXPORT struct ds_tdm_output_hwc_window *
+ds_tdm_output_hwc_window_create(struct ds_tdm_output_hwc *hwc)
+{
+    tdm_error terr;
+    tdm_hwc_window *thwc_window;
+    struct ds_tdm_output_hwc_window *hwc_window;
+
+    hwc_window = calloc(1, sizeof *hwc_window);
+    if (!hwc_window)
+        return NULL;
+
+    thwc_window = tdm_hwc_create_window(hwc->thwc, &terr);
+    if (terr != TDM_ERROR_NONE) {
+        ds_err("Could not create tdm_hwc_window error:%d", terr);
+        free(hwc_window);
+        return NULL;
+    }
+
+    hwc_window->twindow = thwc_window;
+
+    wl_list_insert(hwc->hwc_windows.prev, &hwc_window->link);
+
+    return hwc_window;
+}
+
+WL_EXPORT void
+ds_tdm_output_hwc_window_destroy(struct ds_tdm_output_hwc_window *hwc_window)
+{
+    hwc_window->dropped = true;
+    ds_dbg("hwc_window(%p) dropped: n_locks(%zu)", hwc_window, hwc_window->n_locks);
+    hwc_window_consider_destroy(hwc_window);
+}
+
+WL_EXPORT bool
+ds_tdm_output_hwc_window_set_buffer(struct ds_tdm_output_hwc_window *hwc_window,
+        struct ds_buffer *buffer)
+{
+    if (hwc_window->buffer == buffer)
+        return true;
+
+    if (hwc_window->buffer) {
+        ds_buffer_unlock(hwc_window->set_buffer);
+        hwc_window->buffer = NULL;
+    }
+
+    if (buffer)
+        hwc_window->buffer = ds_buffer_lock(buffer);
+
+    return true;
+}
+
+WL_EXPORT void
+ds_tdm_output_hwc_window_set_src_box(struct ds_tdm_output_hwc_window *hwc_window,
+        const struct ds_tdm_box *src_box)
+{
+    if (src_box != NULL && !memcmp(&hwc_window->src_box, src_box, sizeof(*src_box)))
+        return;
+
+    if (src_box != NULL) {
+        memcpy(&hwc_window->src_box, src_box, sizeof(*src_box));
+    } else {
+        memset(&hwc_window->src_box, 0, sizeof(hwc_window->src_box));
+    }
+}
+
+WL_EXPORT void
+ds_tdm_output_hwc_window_set_transform(struct ds_tdm_output_hwc_window *hwc_window,
+        enum wl_output_transform transform)
+{
+    if (hwc_window->transform == transform)
+        return;
+
+    hwc_window->transform = transform;
+}
+
+WL_EXPORT void
+ds_tdm_output_hwc_window_set_position(struct ds_tdm_output_hwc_window *hwc_window,
+        int x, int y)
+{
+    if ((hwc_window->x == x) && (hwc_window->y == y))
+        return;
+
+    hwc_window->x = x;
+    hwc_window->y = y;
+}
+
+WL_EXPORT void
+ds_tdm_output_hwc_window_set_dest_size(struct ds_tdm_output_hwc_window *hwc_window,
+        int width, int height)
+{
+    if ((hwc_window->dest_width == width) && (hwc_window->dest_height == height))
+        return;
+
+    hwc_window->dest_width = width;
+    hwc_window->dest_height = height;
+}
+
+WL_EXPORT void
+ds_tdm_output_hwc_window_set_composition(struct ds_tdm_output_hwc_window *hwc_window,
+        enum ds_tdm_output_hwc_window_composition composition)
+{
+    if (hwc_window->composition == composition)
+        return;
+
+    hwc_window->composition = composition;
+}
+
+WL_EXPORT enum ds_tdm_output_hwc_window_composition
+ds_tdm_output_hwc_window_get_composition(struct ds_tdm_output_hwc_window *hwc_window)
+{
+    return hwc_window->composition;
+}
+
+static void
+hwc_window_update(struct ds_tdm_output_hwc_window *hwc_window)
+{
+    tdm_error terr;
+    tbm_surface_h tsurface = NULL;
+    tdm_hwc_window_info tinfo = {0, };
+    struct ds_tbm_client_buffer *client_buffer;
+
+    if (hwc_window->buffer) {
+        client_buffer = ds_tbm_client_buffer_from_buffer(hwc_window->buffer);
+        if (client_buffer)
+            tsurface = ds_tbm_client_buffer_get_tbm_surface(client_buffer);
+    }
+
+    if (hwc_window->set_buffer != hwc_window->buffer) {
+        if (hwc_window->set_buffer) {
+            ds_buffer_unlock(hwc_window->set_buffer);
+            hwc_window->set_buffer = NULL;
+        }
+
+        if (hwc_window->buffer)
+            hwc_window->set_buffer = ds_buffer_lock(hwc_window->buffer);
+
+        terr = tdm_hwc_window_set_buffer(hwc_window->twindow, tsurface);
+        if (terr != TDM_ERROR_NONE)
+            ds_err("Could not set buffer hwc_window:%p tdm_error:%d", hwc_window, terr);
+    }
+
+    if (tsurface) {
+        tinfo.src_config.pos.x = 0;
+        tinfo.src_config.pos.y = 0;
+        tinfo.src_config.pos.w = tbm_surface_get_width(tsurface);
+        tinfo.src_config.pos.h = tbm_surface_get_height(tsurface);
+        tinfo.src_config.size.h = get_horizontal_get(tsurface);
+        tinfo.src_config.size.v = tbm_surface_get_height(tsurface);
+        tinfo.src_config.format = tbm_surface_get_format(tsurface);
+        tinfo.dst_pos.x = hwc_window->x;
+        tinfo.dst_pos.y = hwc_window->y;
+        tinfo.dst_pos.w = hwc_window->dest_width;
+        tinfo.dst_pos.h = hwc_window->dest_height;
+        tinfo.transform = get_tdm_transform(hwc_window->transform);
+    }
+
+    if (memcmp(&hwc_window->tinfo, &tinfo, sizeof tinfo)) {
+        memcpy(&hwc_window->tinfo, &tinfo, sizeof tinfo);
+
+        terr = tdm_hwc_window_set_info(hwc_window->twindow, &hwc_window->tinfo);
+        if (terr != TDM_ERROR_NONE)
+            ds_err("Could not set info hwc_window:%p tdm_error:%d", hwc_window, terr);
+    }
+
+    terr = tdm_hwc_window_set_composition_type(hwc_window->twindow,
+            get_tdm_composition(hwc_window->composition));
+    if (terr != TDM_ERROR_NONE)
+        ds_err("Could not set composition type hwc_window:%p tdm_error:%d", hwc_window, terr);
+}
+
+WL_EXPORT bool
+ds_tdm_output_hwc_validate(struct ds_tdm_output_hwc *hwc,
+        struct ds_tdm_output_hwc_window **composited_windows, uint32_t num_windows, uint32_t *num_changed)
+{
+    tdm_error terr;
+    tdm_hwc_window **compositied_hwc_windows = NULL;
+    struct ds_tdm_output_hwc_window *hwc_window;
+    int i;
+
+    wl_list_for_each(hwc_window, &hwc->hwc_windows, link)
+        hwc_window_update(hwc_window);
+
+    if (num_windows > 0) {
+        compositied_hwc_windows = calloc(num_windows, sizeof(tdm_hwc_window *));
+        if (!compositied_hwc_windows)
+            return false;
+
+        for (i = 0; i < num_windows; i++)
+            compositied_hwc_windows[i] = composited_windows[i]->twindow;
+    }
+
+    terr = tdm_hwc_validate(hwc->thwc, compositied_hwc_windows, num_windows, num_changed);
+    if (terr != TDM_ERROR_NONE) {
+        if (compositied_hwc_windows)
+            free(compositied_hwc_windows);
+        ds_err("Could not hwc validate");
+        return false;
+    }
+
+    if (compositied_hwc_windows)
+        free(compositied_hwc_windows);
+
+    hwc->validate_num_changed = *num_changed;
+
+    return true;
+}
+
+static struct ds_tdm_output_hwc_window *
+hwc_get_hwc_window_from_tdm_hwc_window(struct ds_tdm_output_hwc *hwc, tdm_hwc_window *thwc_window)
+{
+    struct ds_tdm_output_hwc_window *hwc_window;
+
+    wl_list_for_each(hwc_window, &hwc->hwc_windows, link) {
+        if (hwc_window->twindow == thwc_window)
+            return hwc_window;
+    }
+
+    return NULL;
+}
+
+WL_EXPORT bool
+ds_tdm_output_hwc_get_changed_composition(struct ds_tdm_output_hwc *hwc, uint32_t *num_changed,
+        struct ds_tdm_output_hwc_window **changed_windows)
+{
+    tdm_hwc_window **changed_thwc_windows = NULL;
+    tdm_hwc_window_composition *compositions = NULL;
+    struct ds_tdm_output_hwc_window *hwc_window;
+    tdm_error terr;
+    int i;
+
+    if (!hwc->validate_num_changed) {
+        *num_changed = 0;
+        return true;
+    }
+
+    changed_thwc_windows = calloc(hwc->validate_num_changed, sizeof *changed_thwc_windows);
+    if (!changed_thwc_windows)
+        return false;
+
+    compositions = calloc(hwc->validate_num_changed, sizeof *compositions);
+    if (!compositions) {
+        free(changed_thwc_windows);
+        return false;
+    }
+
+    terr = tdm_hwc_get_changed_composition_types(hwc->thwc, &hwc->validate_num_changed,
+            changed_thwc_windows, compositions);
+    if (terr != TDM_ERROR_NONE) {
+        ds_err("Could not hwc get changed composition types");
+        free(changed_thwc_windows);
+        free(compositions);
+        return false;
+    }
+
+    for (i = 0; i < hwc->validate_num_changed; i++) {
+        if (i >= *num_changed)
+            break;
+
+        hwc_window = hwc_get_hwc_window_from_tdm_hwc_window(hwc, changed_thwc_windows[i]);
+        if (!hwc_window)
+            continue;
+
+        ds_tdm_output_hwc_window_set_composition(hwc_window, get_composition(compositions[i]));
+        changed_windows[i] = hwc_window;
+    }
+
+    free(changed_thwc_windows);
+    free(compositions);
+
+    *num_changed = i;
+
+    return true;
+}
+
+WL_EXPORT bool
+ds_tdm_output_hwc_accept_validation(struct ds_tdm_output_hwc *hwc)
+{
+    tdm_error terr;
+    struct ds_tdm_output_hwc_window *hwc_window;
+    enum ds_tdm_output_hwc_window_composition composition;
+
+    terr = tdm_hwc_accept_validation(hwc->thwc);
+    if (terr != TDM_ERROR_NONE) {
+        ds_err("Could not hwc accept validation");
+        return false;
+    }
+
+    wl_list_for_each(hwc_window, &hwc->hwc_windows, link) {
+        composition = ds_tdm_output_hwc_window_get_composition(hwc_window);
+        ds_tdm_output_hwc_window_set_accepted_composition(hwc_window,
+                composition);
+        if ((composition == DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_DEVICE) ||
+            (composition == DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_VIDEO)) {
+            hwc_window_attach_back_buffer(hwc_window, hwc_window->set_buffer);
+        } else {
+            hwc_window_attach_back_buffer(hwc_window, NULL);
+        }
+    }
+
+    return true;
+}
+
+static void
+ds_tdm_output_hwc_commit_handler(tdm_hwc *thwc, unsigned int sequence,
+        unsigned int tv_sec, unsigned int tv_usec, void *user_data)
+{
+    struct ds_tdm_output_hwc *hwc = user_data;
+    struct ds_tdm_output_hwc_window *hwc_window, *tmp;
+
+    wl_list_for_each_safe(hwc_window, tmp, &hwc->hwc_windows, link)
+        hwc_window_update_front_buffer(hwc_window);
+
+    wl_signal_emit(&hwc->events.commit_handler, hwc);
+}
+
+bool
+ds_tdm_output_hwc_commit(struct ds_tdm_output_hwc *hwc)
+{
+    tdm_error terr;
+    uint32_t num_changes;
+
+    if (!hwc->enabled) {
+        terr = tdm_hwc_validate(hwc->thwc, NULL, 0, &num_changes);
+        if (terr != TDM_ERROR_NONE) {
+            ds_err("Could not hwc validate");
+            return false;
+        }
+
+        terr = tdm_hwc_accept_validation(hwc->thwc);
+        if (terr != TDM_ERROR_NONE) {
+            ds_err("Could not hwc accept validation");
+            return false;
+        }
+    }
+
+    terr = tdm_hwc_commit(hwc->thwc, 0, ds_tdm_output_hwc_commit_handler, hwc);
+    if (terr != TDM_ERROR_NONE) {
+        ds_err("Could not hwc commit");
+        return false;
+    }
+
+    return true;
+}
+
+void
+ds_tdm_output_hwc_add_commit_handler_listener(struct ds_tdm_output_hwc *hwc,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&hwc->events.commit_handler, listener);
+}
diff --git a/src/backend/tdm/tdm_output_hwc.h b/src/backend/tdm/tdm_output_hwc.h
new file mode 100644 (file)
index 0000000..07b3699
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef DS_TIZEN_BACKEND_TDM_OUTPUT_HWC_H
+#define DS_TIZEN_BACKEND_TDM_OUTPUT_HWC_H
+
+#include <tdm.h>
+#include <libds/surface.h>
+#include <libds/buffer.h>
+
+#include <libds-tizen/backend/tdm.h>
+#include <libds-tizen/backend/tdm_output_hwc.h>
+
+struct ds_tdm_output_hwc_window {
+    struct wl_list link;
+
+    tdm_hwc *hwc;
+
+    size_t n_locks;
+    bool dropped;
+
+    tdm_hwc_window *twindow;
+    tdm_hwc_window_info tinfo;
+
+    enum ds_tdm_output_hwc_window_composition composition;
+    enum ds_tdm_output_hwc_window_composition accepted_composition;
+
+    struct ds_tdm_box src_box;
+    int x, y;
+    int dest_width, dest_height;
+    enum wl_output_transform transform;
+    struct ds_buffer *buffer;
+
+    struct ds_buffer *set_buffer;
+    struct ds_buffer *back_buffer, *front_buffer;
+};
+
+struct ds_tdm_output_hwc {
+    struct ds_tdm_output *tdm_output;
+
+    tdm_hwc *thwc;
+    tdm_hwc_window_info target_buffer_info;
+    bool enabled;
+
+    struct {
+        struct wl_signal destroy;
+        struct wl_signal commit_handler;
+    } events;
+
+    uint32_t validate_num_changed;
+
+    struct wl_list hwc_windows;
+};
+
+struct ds_tdm_output_hwc *
+ds_tdm_output_hwc_create(tdm_hwc *thwc);
+
+void
+ds_tdm_output_hwc_destroy(struct ds_tdm_output_hwc *hwc);
+
+bool
+ds_tdm_output_hwc_set_client_target_buffer(struct ds_tdm_output_hwc *hwc,
+        tbm_surface_h tsurface);
+
+bool
+ds_tdm_output_hwc_commit(struct ds_tdm_output_hwc *hwc);
+
+void
+ds_tdm_output_hwc_add_commit_handler_listener(struct ds_tdm_output_hwc *hwc,
+        struct wl_listener *listener);
+
+void
+ds_tdm_output_hwc_window_set_accepted_composition(struct ds_tdm_output_hwc_window *hwc_window,
+        enum ds_tdm_output_hwc_window_composition composition);
+
+void
+ds_tdm_output_hwc_window_set_changed_composition(struct ds_tdm_output_hwc_window *hwc_window,
+        enum ds_tdm_output_hwc_window_composition composition);
+
+struct ds_tdm_output_hwc_window *
+ds_tdm_output_hwc_window_lock(struct ds_tdm_output_hwc_window *hwc_window);
+
+void
+ds_tdm_output_hwc_window_unlock(struct ds_tdm_output_hwc_window *hwc_window);
+
+#endif