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