+/**************************************************************************
+ *
+ * libtdm
+ *
+ * Copyright 2018 Samsung Electronics co., Ltd. All Rights Reserved.
+ *
+ * Contact: SooChan Lim <sc1.lim@samsung.com>,
+ * Boram Park <boram1288.park@samsung.com>,
+ * Changyeon Lee <cyeon.lee@samsung.com>,
+ * Sangjin Lee <lsj119@samsung.com>
+ *
+ * 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, sub license, 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 NON-INFRINGEMENT.
+ * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS 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.
+ *
+**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "tdm_private.h"
+
+#define HWC_FUNC_ENTRY() \
+ tdm_private_display *private_display; \
+ tdm_private_output *private_output; \
+ tdm_private_hwc *private_hwc; \
+ tdm_error ret = TDM_ERROR_NONE; /* default TDM_ERROR_NONE */\
+ TDM_RETURN_VAL_IF_FAIL(hwc != NULL, TDM_ERROR_INVALID_PARAMETER); \
+ private_hwc = (tdm_private_hwc*)hwc; \
+ private_output = private_hwc->private_output; \
+ TDM_RETURN_VAL_IF_FAIL(private_output != NULL, TDM_ERROR_INVALID_PARAMETER); \
+ private_display = private_output->private_display
+
+#define HWC_FUNC_ENTRY_ERROR() \
+ tdm_private_display *private_display; \
+ tdm_private_output *private_output; \
+ tdm_private_hwc *private_hwc; \
+ tdm_error ret = TDM_ERROR_NONE; /* default TDM_ERROR_NONE */\
+ TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(hwc != NULL, TDM_ERROR_INVALID_PARAMETER, NULL); \
+ private_hwc = (tdm_private_hwc*)hwc; \
+ private_output = private_hwc->private_output; \
+ TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(private_output != NULL, TDM_ERROR_INVALID_PARAMETER, NULL); \
+ private_display = private_output->private_display
+
+#define HWC_FUNC_ENTRY_VOID_RETURN() \
+ tdm_private_display *private_display; \
+ tdm_private_output *private_output; \
+ tdm_private_hwc *private_hwc; \
+ tdm_error ret = TDM_ERROR_NONE; /* default TDM_ERROR_NONE */\
+ TDM_RETURN_IF_FAIL(hwc != NULL); \
+ private_hwc = (tdm_private_hwc*)hwc; \
+ private_output = private_hwc->private_output; \
+ TDM_RETURN_IF_FAIL(private_output != NULL); \
+ private_display = private_output->private_display
+
+
+static tdm_private_hwc_window *
+_tdm_hwc_find_private_hwc_window(tdm_private_hwc *private_hwc, tdm_hwc_window *hwc_window_backend)
+{
+ tdm_private_hwc_window *private_hwc_window = NULL;
+
+ LIST_FOR_EACH_ENTRY(private_hwc_window, &private_hwc->hwc_window_list, link) {
+ if (private_hwc_window->hwc_window_backend == hwc_window_backend)
+ return private_hwc_window;
+ }
+
+ return NULL;
+}
+
+static void
+_tdm_hwc_thread_cb_commit(tdm_private_display *private_display, void *object,
+ tdm_thread_cb_base *cb_base, void *user_data)
+{
+ tdm_thread_cb_hwc_commit *hwc_commit = (tdm_thread_cb_hwc_commit *)cb_base;
+ tdm_private_hwc_commit_handler *hwc_commit_handler = hwc_commit->base.data;
+ tdm_private_hwc *private_hwc = object;
+
+ TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
+
+ if (!hwc_commit_handler)
+ return;
+
+ assert(hwc_commit_handler->owner_tid == syscall(SYS_gettid));
+
+ tdm_thread_cb_remove(private_hwc, TDM_THREAD_CB_HWC_COMMIT, hwc_commit_handler,
+ _tdm_hwc_thread_cb_commit, NULL);
+
+ LIST_DEL(&hwc_commit_handler->link);
+
+ if (tdm_debug_module & TDM_DEBUG_COMMIT) {
+ TDM_INFO("----------------------------------------- hwc(%d) committed", private_hwc->index);
+ TDM_INFO("handler(%p)", hwc_commit_handler);
+ }
+
+ if (hwc_commit_handler->func) {
+ _pthread_mutex_unlock(&private_display->lock);
+ hwc_commit_handler->func(private_hwc,
+ hwc_commit->sequence,
+ hwc_commit->tv_sec,
+ hwc_commit->tv_usec,
+ hwc_commit_handler->user_data);
+ _pthread_mutex_lock(&private_display->lock);
+ }
+
+ free(hwc_commit_handler);
+
+ if (tdm_debug_module & TDM_DEBUG_COMMIT)
+ TDM_INFO("-----------------------------------------...");
+}
+
+static void
+_tdm_hwc_cb_commit(tdm_hwc *hwc_backend, unsigned int sequence,
+ unsigned int tv_sec, unsigned int tv_usec, void *user_data)
+{
+ tdm_private_hwc_commit_handler *hwc_commit_handler = user_data;
+ tdm_private_hwc *private_hwc;
+ tdm_thread_cb_hwc_commit hwc_commit;
+ tdm_error ret;
+
+ if (hwc_commit_handler)
+ private_hwc = hwc_commit_handler->private_hwc;
+ else
+ private_hwc = tdm_display_find_private_hwc(tdm_display_get(), hwc_backend);
+
+ memset(&hwc_commit, 0, sizeof hwc_commit);
+ hwc_commit.base.type = TDM_THREAD_CB_HWC_COMMIT;
+ hwc_commit.base.length = sizeof hwc_commit;
+ hwc_commit.base.object_stamp = private_hwc->stamp;
+ hwc_commit.base.data = hwc_commit_handler;
+ hwc_commit.base.sync = 0;
+ hwc_commit.sequence = sequence;
+ hwc_commit.tv_sec = tv_sec;
+ hwc_commit.tv_usec = tv_usec;
+
+ ret = tdm_thread_cb_call(private_hwc, &hwc_commit.base, 1);
+ TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE);
+}
+
+INTERN tdm_error
+tdm_hwc_init(tdm_private_display *private_display)
+{
+ tdm_thread_cb_set_find_func(TDM_THREAD_CB_HWC_COMMIT, tdm_display_find_hwc_stamp);
+
+ return TDM_ERROR_NONE;
+}
+
+EXTERN tdm_hwc_window *
+tdm_hwc_create_window(tdm_hwc *hwc, tdm_error *error)
+{
+ tdm_hwc_window *hwc_window = NULL;
+
+ HWC_FUNC_ENTRY_ERROR();
+
+ _pthread_mutex_lock(&private_display->lock);
+
+ hwc_window = (tdm_hwc_window *)tdm_hwc_window_create_internal(private_hwc, error);
+
+ _pthread_mutex_unlock(&private_display->lock);
+
+ return hwc_window;
+}
+
+EXTERN tdm_error
+tdm_hwc_get_supported_formats(tdm_hwc *hwc, const tbm_format **formats, int *count)
+{
+ tdm_private_module *private_module;
+ tdm_func_hwc *func_hwc;
+
+ HWC_FUNC_ENTRY();
+
+ TDM_RETURN_VAL_IF_FAIL(formats != NULL, TDM_ERROR_INVALID_PARAMETER);
+ TDM_RETURN_VAL_IF_FAIL(count != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+ _pthread_mutex_lock(&private_display->lock);
+
+ private_module = private_output->private_module;
+ func_hwc = &private_module->func_hwc;
+
+ if (!func_hwc->hwc_get_supported_formats) {
+ /* LCOV_EXCL_START */
+ _pthread_mutex_unlock(&private_display->lock);
+ TDM_WRN("not implemented!!");
+ return TDM_ERROR_NOT_IMPLEMENTED;
+ /* LCOV_EXCL_STOP */
+ }
+
+ ret = func_hwc->hwc_get_supported_formats(private_hwc->hwc_backend, formats, count);
+
+ _pthread_mutex_unlock(&private_display->lock);
+
+ return ret;
+}
+
+EXTERN tdm_error
+tdm_hwc_get_available_properties(tdm_hwc *hwc, const tdm_prop **props, int *count)
+{
+ tdm_private_module *private_module;
+ tdm_func_hwc *func_hwc = NULL;
+
+ HWC_FUNC_ENTRY();
+
+ TDM_RETURN_VAL_IF_FAIL(props != NULL, TDM_ERROR_INVALID_PARAMETER);
+ TDM_RETURN_VAL_IF_FAIL(count != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+ _pthread_mutex_lock(&private_display->lock);
+
+ private_module = private_output->private_module;
+ func_hwc = &private_module->func_hwc;
+
+ if (!func_hwc->hwc_get_available_properties) {
+ /* LCOV_EXCL_START */
+ _pthread_mutex_unlock(&private_display->lock);
+ TDM_WRN("not implemented!!");
+ return TDM_ERROR_NOT_IMPLEMENTED;
+ /* LCOV_EXCL_STOP */
+ }
+
+ ret = func_hwc->hwc_get_available_properties(private_hwc->hwc_backend, props, count);
+
+ _pthread_mutex_unlock(&private_display->lock);
+
+ return ret;
+}
+
+EXTERN tbm_surface_queue_h
+tdm_hwc_get_client_target_buffer_queue(tdm_hwc *hwc, tdm_error *error)
+{
+ tdm_private_module *private_module;
+ tdm_func_hwc *func_hwc = NULL;
+ tbm_surface_queue_h queue = NULL;
+
+ HWC_FUNC_ENTRY_ERROR();
+
+ _pthread_mutex_lock(&private_display->lock);
+
+ private_module = private_hwc->private_module;
+ func_hwc = &private_module->func_hwc;
+
+ if (!func_hwc->hwc_get_client_target_buffer_queue) {
+ /* LCOV_EXCL_START */
+ _pthread_mutex_unlock(&private_display->lock);
+ TDM_WRN("not implemented!!");
+ return NULL;
+ /* LCOV_EXCL_STOP */
+ }
+
+ queue = func_hwc->hwc_get_client_target_buffer_queue(private_hwc->hwc_backend, error);
+
+ _pthread_mutex_unlock(&private_display->lock);
+
+ return queue;
+}
+
+EXTERN tdm_error
+tdm_hwc_set_client_target_buffer(tdm_hwc *hwc, tbm_surface_h target_buffer, tdm_region damage)
+{
+ tdm_private_module *private_module;
+ tdm_func_hwc *func_hwc = NULL;
+
+ HWC_FUNC_ENTRY();
+
+ _pthread_mutex_lock(&private_display->lock);
+
+ if (tdm_debug_dump & TDM_DUMP_FLAG_WINDOW) {
+ /* LCOV_EXCL_START */
+ char str[TDM_PATH_LEN];
+ static int i;
+ snprintf(str, TDM_PATH_LEN, "target_window_%03d", i++);
+ tdm_helper_dump_buffer_str(target_buffer, tdm_debug_dump_dir, str);
+ /* LCOV_EXCL_STOP */
+ }
+
+ private_module = private_hwc->private_module;
+ func_hwc = &private_module->func_hwc;
+
+ if (!func_hwc->hwc_set_client_target_buffer) {
+ /* LCOV_EXCL_START */
+ _pthread_mutex_unlock(&private_display->lock);
+ TDM_WRN("not implemented!!");
+ return TDM_ERROR_NOT_IMPLEMENTED;
+ /* LCOV_EXCL_STOP */
+ }
+
+ ret = func_hwc->hwc_set_client_target_buffer(private_hwc->hwc_backend, target_buffer, damage);
+
+ _pthread_mutex_unlock(&private_display->lock);
+
+ return ret;
+}
+
+
+EXTERN tdm_error
+tdm_hwc_validate(tdm_hwc *hwc, tdm_hwc_window **composited_wnds, uint32_t num_wnds, uint32_t *num_types)
+{
+ tdm_private_module *private_module;
+ tdm_func_hwc *func_hwc = NULL;
+ tdm_private_hwc_window **composited_wnds_frontend = NULL;
+ tdm_hwc_window **composited_wnds_backend = NULL;
+ int i;
+
+ HWC_FUNC_ENTRY();
+
+ TDM_RETURN_VAL_IF_FAIL(num_types != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+ _pthread_mutex_lock(&private_display->lock);
+
+ private_module = private_hwc->private_module;
+ func_hwc = &private_module->func_hwc;
+
+ if (!func_hwc->hwc_validate) {
+ /* LCOV_EXCL_START */
+ _pthread_mutex_unlock(&private_display->lock);
+ TDM_WRN("not implemented!!");
+ return TDM_ERROR_NOT_IMPLEMENTED;
+ /* LCOV_EXCL_STOP */
+ }
+
+ if (num_wnds == 0) {
+ ret = func_hwc->hwc_validate(private_hwc->hwc_backend, NULL, 0, num_types);
+
+ _pthread_mutex_unlock(&private_display->lock);
+ return ret;
+ }
+
+ composited_wnds_backend = calloc(num_wnds, sizeof(tdm_hwc_window *));
+ if (!composited_wnds_backend) {
+ /* LCOV_EXCL_START */
+ _pthread_mutex_unlock(&private_display->lock);
+ return TDM_ERROR_OUT_OF_MEMORY;
+ /* LCOV_EXCL_STOP */
+ }
+
+ composited_wnds_frontend = (tdm_private_hwc_window **)composited_wnds;
+
+ for (i = 0; i < num_wnds; i++)
+ composited_wnds_backend[i] = composited_wnds_frontend[i]->hwc_window_backend;
+
+ ret = func_hwc->hwc_validate(private_hwc->hwc_backend, composited_wnds_backend,
+ num_wnds, num_types);
+
+ free(composited_wnds_backend);
+
+ _pthread_mutex_unlock(&private_display->lock);
+
+ return ret;
+}
+
+EXTERN tdm_error
+tdm_hwc_get_changed_composition_types(tdm_hwc *hwc, uint32_t *num_elements,
+ tdm_hwc_window **hwc_window,
+ tdm_hwc_window_composition *composition_types)
+{
+ tdm_private_module *private_module;
+ tdm_func_hwc *func_hwc = NULL;
+ tdm_private_hwc_window * private_hwc_window = NULL;
+ int i = 0;
+
+ HWC_FUNC_ENTRY();
+
+ TDM_RETURN_VAL_IF_FAIL(num_elements != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+ _pthread_mutex_lock(&private_display->lock);
+
+ private_module = private_hwc->private_module;
+ func_hwc = &private_module->func_hwc;
+
+ if (!func_hwc->hwc_get_changed_composition_types) {
+ /* LCOV_EXCL_START */
+ _pthread_mutex_unlock(&private_display->lock);
+ TDM_WRN("not implemented!!");
+ return TDM_ERROR_NOT_IMPLEMENTED;
+ /* LCOV_EXCL_STOP */
+ }
+
+ ret = func_hwc->hwc_get_changed_composition_types(private_hwc->hwc_backend,
+ num_elements, hwc_window, composition_types);
+ if (ret != TDM_ERROR_NONE) {
+ /* LCOV_EXCL_START */
+ _pthread_mutex_unlock(&private_display->lock);
+ return ret;
+ /* LCOV_EXCL_STOP */
+ }
+
+ if (hwc_window == NULL || composition_types == NULL) {
+ _pthread_mutex_unlock(&private_display->lock);
+ return TDM_ERROR_NONE;
+ }
+
+ for (i = 0; i < *num_elements; i++) {
+ private_hwc_window = _tdm_hwc_find_private_hwc_window(private_hwc, hwc_window[i]);
+ if (private_hwc_window == NULL) {
+ /* LCOV_EXCL_START */
+ TDM_ERR("failed! This should never happen!");
+ tdm_hwc_window_destroy_internal(private_hwc_window);
+ *num_elements = 0;
+ _pthread_mutex_unlock(&private_display->lock);
+ return TDM_ERROR_OPERATION_FAILED;
+ /* LCOV_EXCL_STOP */
+ }
+
+ hwc_window[i] = (tdm_hwc_window*)private_hwc_window;
+ }
+
+ _pthread_mutex_unlock(&private_display->lock);
+
+ return ret;
+}
+
+EXTERN tdm_error
+tdm_hwc_accept_changes(tdm_hwc *hwc)
+{
+ tdm_private_module *private_module;
+ tdm_func_hwc *func_hwc = NULL;
+
+ HWC_FUNC_ENTRY();
+
+ _pthread_mutex_lock(&private_display->lock);
+
+ private_module = private_hwc->private_module;
+ func_hwc = &private_module->func_hwc;
+
+ if (!func_hwc->hwc_validate) {
+ /* LCOV_EXCL_START */
+ _pthread_mutex_unlock(&private_display->lock);
+ TDM_WRN("not implemented!!");
+ return TDM_ERROR_NOT_IMPLEMENTED;
+ /* LCOV_EXCL_STOP */
+ }
+
+ ret = func_hwc->hwc_accept_changes(private_hwc->hwc_backend);
+
+ _pthread_mutex_unlock(&private_display->lock);
+
+ return ret;
+}
+
+EXTERN tdm_error
+tdm_hwc_commit(tdm_hwc *hwc, int sync, tdm_hwc_commit_handler func, void *user_data)
+{
+ tdm_private_module *private_module;
+ tdm_func_hwc *func_hwc = NULL;
+ tdm_private_hwc_commit_handler *hwc_commit_handler = NULL;
+
+ HWC_FUNC_ENTRY();
+
+ _pthread_mutex_lock(&private_display->lock);
+
+ private_module = private_hwc->private_module;
+ func_hwc = &private_module->func_hwc;
+
+ if (!func_hwc->hwc_commit) {
+ /* LCOV_EXCL_START */
+ TDM_WRN("not implemented!!");
+ _pthread_mutex_unlock(&private_display->lock);
+ return TDM_ERROR_NOT_IMPLEMENTED;
+ /* LCOV_EXCL_STOP */
+ }
+
+//TODO: I am not sure yet whether we have to check the dpms at hwc_commit.
+#if 0
+ /* check if the dpms is off */
+ if (TDM_OUTPUT_DPMS_VSYNC_IS_OFF(private_output->current_dpms_value)) {
+ TDM_ERR("hwc(%d) dpms: %s", private_hwc->index,
+ tdm_dpms_str(private_output->current_dpms_value));
+ _pthread_mutex_unlock(&private_display->lock);
+ return TDM_ERROR_DPMS_OFF;
+ }
+#endif
+
+ if (tdm_debug_module & TDM_DEBUG_COMMIT)
+ TDM_INFO("hwc(%d) commit", private_hwc->index);
+
+ if (!private_hwc->regist_commit_cb) {
+ private_hwc->regist_commit_cb = 1;
+ ret = func_hwc->hwc_set_commit_handler(private_hwc->hwc_backend, _tdm_hwc_cb_commit);
+ /* LCOV_EXCL_START */
+ if (ret != TDM_ERROR_NONE) {
+ private_hwc->regist_commit_cb = 0;
+ TDM_ERR("hwc(%d) fail to set hwc_set_commit_handler", private_hwc->index);
+ _pthread_mutex_unlock(&private_display->lock);
+ return ret;
+ /* LCOV_EXCL_STOP */
+ }
+ }
+
+ hwc_commit_handler = calloc(1, sizeof(tdm_private_hwc_commit_handler));
+ if (!hwc_commit_handler) {
+ /* LCOV_EXCL_START */
+ TDM_ERR("failed: alloc memory");
+ _pthread_mutex_unlock(&private_display->lock);
+ return TDM_ERROR_OUT_OF_MEMORY;
+ /* LCOV_EXCL_STOP */
+ }
+
+ ret = tdm_thread_cb_add(private_hwc, TDM_THREAD_CB_HWC_COMMIT, hwc_commit_handler,
+ _tdm_hwc_thread_cb_commit, NULL);
+ if (ret != TDM_ERROR_NONE) {
+ TDM_ERR("tdm_thread_cb_add failed");
+ free(hwc_commit_handler);
+ return ret;
+ }
+
+ LIST_ADDTAIL(&hwc_commit_handler->link, &private_hwc->hwc_commit_handler_list);
+ hwc_commit_handler->private_hwc = private_hwc;
+ hwc_commit_handler->func = func;
+ hwc_commit_handler->user_data = user_data;
+ hwc_commit_handler->owner_tid = syscall(SYS_gettid);
+
+ ret = func_hwc->hwc_commit(private_hwc->hwc_backend, sync, hwc_commit_handler);
+ TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, commit_failed);
+
+ if (tdm_debug_module & TDM_DEBUG_COMMIT)
+ TDM_INFO("hwc(%d) backend commit: handle(%p) func(%p) user_data(%p)",
+ private_hwc->index, hwc_commit_handler, func, user_data);
+
+ _pthread_mutex_unlock(&private_display->lock);
+
+ return ret;
+
+commit_failed:
+ /* LCOV_EXCL_START */
+ if (hwc_commit_handler) {
+ tdm_thread_cb_remove(private_hwc, TDM_THREAD_CB_HWC_COMMIT, hwc_commit_handler,
+ _tdm_hwc_thread_cb_commit, NULL);
+ LIST_DEL(&hwc_commit_handler->link);
+ free(hwc_commit_handler);
+ }
+
+ _pthread_mutex_unlock(&private_display->lock);
+
+ return ret;
+ /* LCOV_EXCL_STOP */
+}
\ No newline at end of file