From 633c7139cfeb77dd009c0e99fb0d33498009b056 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Mon, 27 Jun 2016 21:53:28 +0900 Subject: [PATCH 01/16] add tdm_layer_get_buffer_flags There can be the layer which can set and display the specific buffer with the specific flags. Therefore, tdm user has to know the buffer flags to create the buffer which can be set to the specified layer. Change-Id: I1df658f4cbb4ca6019a7df1fe72c77db3d6db401 --- include/tdm.h | 9 +++++++++ include/tdm_backend.h | 9 ++++++++- src/tdm_display.c | 23 +++++++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/include/tdm.h b/include/tdm.h index 4af34d7..6ba4996 100644 --- a/include/tdm.h +++ b/include/tdm.h @@ -653,6 +653,15 @@ tdm_capture * tdm_layer_create_capture(tdm_layer *layer, tdm_error *error); /** + * @brief Get buffer flags from a layer object + * @param[in] layer A layer object + * @param[out] flags a buffer flags value + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +tdm_error +tdm_layer_get_buffer_flags(tdm_layer *layer, unsigned int *flags); + +/** * @brief Destroy a pp object * @param[in] pp A pp object * @see tdm_display_create_pp diff --git a/include/tdm_backend.h b/include/tdm_backend.h index 8967b52..0256e92 100644 --- a/include/tdm_backend.h +++ b/include/tdm_backend.h @@ -621,6 +621,14 @@ typedef struct _tdm_func_layer { */ tdm_capture *(*layer_create_capture)(tdm_layer *layer, tdm_error *error); + /** + * @brief Get buffer flags which the layer can support. + * @param[in] layer A layer object + * @param[out] flags The buffer flags which should be the tbm_bo flags + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*layer_get_buffer_flags)(tdm_layer *layer, unsigned int *flags); + void (*reserved1)(void); void (*reserved2)(void); void (*reserved3)(void); @@ -628,7 +636,6 @@ typedef struct _tdm_func_layer { void (*reserved5)(void); void (*reserved6)(void); void (*reserved7)(void); - void (*reserved8)(void); } tdm_func_layer; /** diff --git a/src/tdm_display.c b/src/tdm_display.c index e691501..17c02aa 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -1839,3 +1839,26 @@ tdm_layer_create_capture(tdm_layer *layer, tdm_error *error) return capture; } + +EXTERN tdm_error +tdm_layer_get_buffer_flags(tdm_layer *layer, unsigned int *flags) +{ + tdm_func_layer *func_layer; + LAYER_FUNC_ENTRY(); + + _pthread_mutex_lock(&private_display->lock); + + func_layer = &private_display->func_layer; + + if (!func_layer->layer_get_buffer_flags) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + ret = func_layer->layer_get_buffer_flags(private_layer->layer_backend, flags); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} -- 2.7.4 From ee90ba719ca9dfe34a3bc2fc58a9ba93a0d95ab9 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 22 Jun 2016 13:15:39 +0900 Subject: [PATCH 02/16] correct the retry condition of poll() Change-Id: I7b2cdd80ccc903811ed791dead112932b887469b --- client/tdm_client.h | 2 +- src/tdm_display.c | 2 +- src/tdm_thread.c | 2 +- tools/tdm_test_client.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/tdm_client.h b/client/tdm_client.h index a6a7a09..fff8808 100644 --- a/client/tdm_client.h +++ b/client/tdm_client.h @@ -113,7 +113,7 @@ tdm_client_destroy(tdm_client *client); * while(1) { * ret = poll(&fds, 1, -1); * if (ret < 0) { - * if (errno == EBUSY) + * if (errno == EINTR || errno == EAGAIN) * continue; * else { * //error handling diff --git a/src/tdm_display.c b/src/tdm_display.c index 17c02aa..cab04a6 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -417,7 +417,7 @@ tdm_display_handle_events(tdm_display *dpy) TDM_INFO("fd(%d) polling in", fd); while (poll(&fds, 1, -1) < 0) { - if (errno == EBUSY) /* normal case */ + if (errno == EINTR || errno == EAGAIN) /* normal case */ continue; else { TDM_ERR("poll failed: %m"); diff --git a/src/tdm_thread.c b/src/tdm_thread.c index 0d24561..3ae6d0b 100644 --- a/src/tdm_thread.c +++ b/src/tdm_thread.c @@ -99,7 +99,7 @@ _tdm_thread_main(void *data) TDM_INFO("fd(%d) polling out", fd); if (ret < 0) { - if (errno == EBUSY) /* normal case */ + if (errno == EINTR || errno == EAGAIN) /* normal case */ continue; else { TDM_ERR("poll failed: %m"); diff --git a/tools/tdm_test_client.c b/tools/tdm_test_client.c index 499d1f5..7268628 100644 --- a/tools/tdm_test_client.c +++ b/tools/tdm_test_client.c @@ -121,7 +121,7 @@ main(int argc, char *argv[]) if (!sync) { ret = poll(&fds, 1, -1); if (ret < 0) { - if (errno == EBUSY) /* normal case */ + if (errno == EINTR || errno == EAGAIN) /* normal case */ continue; else { printf("poll failed: %m\n"); -- 2.7.4 From 3c28bfc94c9f680bc7bb5f8c2ca2aaa4b7b5081c Mon Sep 17 00:00:00 2001 From: Boram Park Date: Mon, 27 Jun 2016 13:35:24 +0900 Subject: [PATCH 03/16] add tdm_common.h to share enumeration and structure with client Change-Id: I6c64905cb8363cc99f3742fe40934872d5d2cb33 --- include/Makefile.am | 1 + include/tdm.h | 8 --- include/tdm_common.h | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/tdm_types.h | 67 +------------------------- packaging/libtdm.spec | 2 + src/tdm_capture.c | 2 +- src/tdm_display.c | 49 ++----------------- src/tdm_macro.h | 38 +++++++++++++++ src/tdm_private.h | 9 +--- src/tdm_server.c | 4 +- 10 files changed, 181 insertions(+), 130 deletions(-) create mode 100644 include/tdm_common.h diff --git a/include/Makefile.am b/include/Makefile.am index b47ee00..aa1d502 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,6 +1,7 @@ libtdmincludedir = ${includedir} libtdminclude_HEADERS = \ tdm.h \ + tdm_common.h \ tdm_types.h \ tdm_list.h \ tdm_log.h \ diff --git a/include/tdm.h b/include/tdm.h index 6ba4996..47afc19 100644 --- a/include/tdm.h +++ b/include/tdm.h @@ -65,14 +65,6 @@ typedef enum { } tdm_display_capability; /** - * @brief The output change enumeration of #tdm_output_change_handler - */ -typedef enum { - TDM_OUTPUT_CHANGE_CONNECTION = (1 << 0), /**< connection chagne */ - TDM_OUTPUT_CHANGE_DPMS = (1 << 1), /**< dpms change */ -} tdm_output_change_type; - -/** * @brief The output change handler * @details This handler will be called when the status of a output object is * changed in runtime. diff --git a/include/tdm_common.h b/include/tdm_common.h new file mode 100644 index 0000000..79cdf69 --- /dev/null +++ b/include/tdm_common.h @@ -0,0 +1,131 @@ +/************************************************************************** + * + * libtdm + * + * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Contact: Eunchul Kim , + * JinYoung Jeon , + * Taeheon Kim , + * YoungJun Cho , + * SooChan Lim , + * Boram Park + * + * 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. + * +**************************************************************************/ + +#ifndef _TDM_COMMON_H_ +#define _TDM_COMMON_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define TDM_NAME_LEN 64 + +/** + * @file tdm_common.h + * @brief The header file which defines Enumerations and Structures for TDM + * frontend, backend and client. + */ + +/** + * @brief The error enumeration + */ +typedef enum { + TDM_ERROR_NONE = 0, /**< none */ + TDM_ERROR_BAD_REQUEST = -1, /**< bad request */ + TDM_ERROR_OPERATION_FAILED = -2, /**< operaion failed */ + TDM_ERROR_INVALID_PARAMETER = -3, /**< wrong input parameter */ + TDM_ERROR_PERMISSION_DENIED = -4, /**< access denied */ + TDM_ERROR_BUSY = -5, /**< hardware resource busy */ + TDM_ERROR_OUT_OF_MEMORY = -6, /**< no free memory */ + TDM_ERROR_BAD_MODULE = -7, /**< bad backend module */ + TDM_ERROR_NOT_IMPLEMENTED = -8, /**< not implemented */ + TDM_ERROR_NO_CAPABILITY = -9, /**< no capability */ + TDM_ERROR_DPMS_OFF = -10, /**< dpms off */ +} tdm_error; + +/** + * @brief The output change enumeration of #tdm_output_change_handler + */ +typedef enum { + TDM_OUTPUT_CHANGE_CONNECTION = (1 << 0), /**< connection chagne */ + TDM_OUTPUT_CHANGE_DPMS = (1 << 1), /**< dpms change */ +} tdm_output_change_type; + +/** + * @brief The output connection status enumeration + */ +typedef enum { + TDM_OUTPUT_CONN_STATUS_DISCONNECTED, /**< output disconnected */ + TDM_OUTPUT_CONN_STATUS_CONNECTED, /**< output connected */ + TDM_OUTPUT_CONN_STATUS_MODE_SETTED, /**< output connected and setted a mode */ +} tdm_output_conn_status; + +/** + * @brief The DPMS enumeration + * @details bit compatible with the libdrm definitions. + */ +typedef enum { + TDM_OUTPUT_DPMS_ON, /**< On */ + TDM_OUTPUT_DPMS_STANDBY, /**< StandBy */ + TDM_OUTPUT_DPMS_SUSPEND, /**< Suspend */ + TDM_OUTPUT_DPMS_OFF, /**< Off */ +} tdm_output_dpms; + +/** + * @brief The size structure + */ +typedef struct _tdm_size { + unsigned int h; /**< width */ + unsigned int v; /**< height */ +} tdm_size; + +/** + * @brief The pos structure + */ +typedef struct _tdm_pos { + unsigned int x; + unsigned int y; + unsigned int w; + unsigned int h; +} tdm_pos; + +/** + * @brief The value union + */ +typedef union { + void *ptr; + int32_t s32; + uint32_t u32; + int64_t s64; + uint64_t u64; +} tdm_value; + +#ifdef __cplusplus +} +#endif + +#endif /* _TDM_COMMON_H_ */ diff --git a/include/tdm_types.h b/include/tdm_types.h index a6b68fa..53988f2 100755 --- a/include/tdm_types.h +++ b/include/tdm_types.h @@ -42,8 +42,6 @@ extern "C" { #endif -#define TDM_NAME_LEN 64 - /** * @file tdm_types.h * @brief The header file which defines Enumerations and Structures for frontend and backend. @@ -59,21 +57,7 @@ extern "C" { * @endcode */ -/** - * @brief The error enumeration - */ -typedef enum { - TDM_ERROR_NONE = 0, /**< none */ - TDM_ERROR_BAD_REQUEST = -1, /**< bad request */ - TDM_ERROR_OPERATION_FAILED = -2, /**< operaion failed */ - TDM_ERROR_INVALID_PARAMETER = -3, /**< wrong input parameter */ - TDM_ERROR_PERMISSION_DENIED = -4, /**< access denied */ - TDM_ERROR_BUSY = -5, /**< hardware resource busy */ - TDM_ERROR_OUT_OF_MEMORY = -6, /**< no free memory */ - TDM_ERROR_BAD_MODULE = -7, /**< bad backend module */ - TDM_ERROR_NOT_IMPLEMENTED = -8, /**< not implemented */ - TDM_ERROR_NO_CAPABILITY = -9, /**< no capability */ -} tdm_error; +#include /** * @brief The transform enumeration(rotate, flip) @@ -91,15 +75,6 @@ typedef enum { /** * @brief The output connection status enumeration - */ -typedef enum { - TDM_OUTPUT_CONN_STATUS_DISCONNECTED, /**< output disconnected */ - TDM_OUTPUT_CONN_STATUS_CONNECTED, /**< output connected */ - TDM_OUTPUT_CONN_STATUS_MODE_SETTED, /**< output connected and setted a mode */ -} tdm_output_conn_status; - -/** - * @brief The output connection status enumeration * @details bit compatible with the libdrm definitions. */ typedef enum { @@ -123,17 +98,6 @@ typedef enum { } tdm_output_type; /** - * @brief The DPMS enumeration - * @details bit compatible with the libdrm definitions. - */ -typedef enum { - TDM_OUTPUT_DPMS_ON, /**< On */ - TDM_OUTPUT_DPMS_STANDBY, /**< StandBy */ - TDM_OUTPUT_DPMS_SUSPEND, /**< Suspend */ - TDM_OUTPUT_DPMS_OFF, /**< Off */ -} tdm_output_dpms; - -/** * @brief The layer capability enumeration * @details * A layer can have one of CURSOR, PRIMARY and OVERLAY capability. And a layer @@ -253,35 +217,6 @@ typedef struct _tdm_prop { } tdm_prop; /** - * @brief The size structure - */ -typedef struct _tdm_size { - unsigned int h; /**< width */ - unsigned int v; /**< height */ -} tdm_size; - -/** - * @brief The pos structure - */ -typedef struct _tdm_pos { - unsigned int x; - unsigned int y; - unsigned int w; - unsigned int h; -} tdm_pos; - -/** - * @brief The value union - */ -typedef union { - void *ptr; - int32_t s32; - uint32_t u32; - int64_t s64; - uint64_t u64; -} tdm_value; - -/** * @brief The info config structure */ typedef struct _tdm_info_config { diff --git a/packaging/libtdm.spec b/packaging/libtdm.spec index 11cec9a..28bb692 100644 --- a/packaging/libtdm.spec +++ b/packaging/libtdm.spec @@ -35,6 +35,7 @@ Tizen Display Manager Client Library Summary: Client library for Tizen Display Manager Group: Development/Libraries Requires: libtdm-client = %{version} +Requires: libtdm-devel %description client-devel Tizen Display Manager Client Library headers @@ -100,6 +101,7 @@ rm -f %{_unitdir_user}/default.target.wants/tdm-socket-user.path %manifest %{name}.manifest %defattr(-,root,root,-) %{_includedir}/tdm.h +%{_includedir}/tdm_common.h %{_includedir}/tdm_backend.h %{_includedir}/tdm_helper.h %{_includedir}/tdm_list.h diff --git a/src/tdm_capture.c b/src/tdm_capture.c index ea67044..8f562ab 100644 --- a/src/tdm_capture.c +++ b/src/tdm_capture.c @@ -403,7 +403,7 @@ tdm_capture_commit(tdm_capture *capture) private_output = private_capture->private_output; if (private_output->current_dpms_value > TDM_OUTPUT_DPMS_ON) { TDM_ERR("output(%d) dpms: %s", private_output->pipe, - dpms_str(private_output->current_dpms_value)); + tdm_dpms_str(private_output->current_dpms_value)); _pthread_mutex_unlock(&private_display->lock); return TDM_ERROR_BAD_REQUEST; } diff --git a/src/tdm_display.c b/src/tdm_display.c index cab04a6..2c09442 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -102,47 +102,6 @@ private_output = private_layer->private_output; \ private_display = private_output->private_display -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) - -struct type_name { - int type; - const char *name; -}; - -#define type_name_fn(res) \ -const char * res##_str(int type) \ -{ \ - unsigned int i; \ - for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \ - if (res##_names[i].type == type) \ - return res##_names[i].name; \ - } \ - return "(invalid)"; \ -} - -struct type_name dpms_names[] = { - { TDM_OUTPUT_DPMS_ON, "on" }, - { TDM_OUTPUT_DPMS_STANDBY, "standby" }, - { TDM_OUTPUT_DPMS_SUSPEND, "suspend" }, - { TDM_OUTPUT_DPMS_OFF, "off" }, -}; - -INTERN type_name_fn(dpms) - -struct type_name status_names[] = { - { TDM_OUTPUT_CONN_STATUS_DISCONNECTED, "disconnected" }, - { TDM_OUTPUT_CONN_STATUS_CONNECTED, "connected" }, - { TDM_OUTPUT_CONN_STATUS_MODE_SETTED, "mode_setted" }, -}; - -INTERN type_name_fn(status) - -INTERN const char* -tdm_get_dpms_str(tdm_output_dpms dpms_value) -{ - return dpms_str(dpms_value); -} - INTERN tdm_error _tdm_display_lock(tdm_display *dpy, const char *func) { @@ -988,7 +947,7 @@ tdm_output_wait_vblank(tdm_output *output, int interval, int sync, if (private_output->current_dpms_value > TDM_OUTPUT_DPMS_ON) { TDM_ERR("output(%d) dpms: %s", private_output->pipe, - dpms_str(private_output->current_dpms_value)); + tdm_dpms_str(private_output->current_dpms_value)); _pthread_mutex_unlock(&private_display->lock); return TDM_ERROR_BAD_REQUEST; } @@ -1082,7 +1041,7 @@ tdm_output_commit(tdm_output *output, int sync, tdm_output_commit_handler func, if (private_output->current_dpms_value > TDM_OUTPUT_DPMS_ON) { TDM_ERR("output(%d) dpms: %s", private_output->pipe, - dpms_str(private_output->current_dpms_value)); + tdm_dpms_str(private_output->current_dpms_value)); _pthread_mutex_unlock(&private_display->lock); return TDM_ERROR_BAD_REQUEST; } @@ -1245,10 +1204,10 @@ tdm_output_call_change_handler_internal(tdm_private_output *private_output, if (!tdm_thread_in_display_thread(syscall(SYS_gettid))) { if (type & TDM_OUTPUT_CHANGE_CONNECTION) TDM_INFO("output(%d) changed: %s (%d)", - private_output->pipe, status_str(value.u32), value.u32); + private_output->pipe, tdm_status_str(value.u32), value.u32); if (type & TDM_OUTPUT_CHANGE_DPMS) TDM_INFO("output(%d) changed: dpms %s (%d)", - private_output->pipe, dpms_str(value.u32), value.u32); + private_output->pipe, tdm_dpms_str(value.u32), value.u32); } if (LIST_IS_EMPTY(change_handler_list)) diff --git a/src/tdm_macro.h b/src/tdm_macro.h index b87b4a0..659ead3 100644 --- a/src/tdm_macro.h +++ b/src/tdm_macro.h @@ -41,6 +41,8 @@ #include #include +#include + #ifdef __cplusplus extern "C" { #endif @@ -117,6 +119,42 @@ extern "C" { #define FOURCC_STR(id) C(id, 0), C(id, 8), C(id, 16), C(id, 24) #define FOURCC_ID(str) FOURCC(((char*)str)[0], ((char*)str)[1], ((char*)str)[2], ((char*)str)[3]) + +#define TDM_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +struct tdm_type_name { + int type; + const char *name; +}; + +#define TDM_TYPE_NAME_FN(res) \ +static inline const char * tdm_##res##_str(int type) \ +{ \ + unsigned int i; \ + for (i = 0; i < TDM_ARRAY_SIZE(tdm_##res##_names); i++) { \ + if (tdm_##res##_names[i].type == type) \ + return tdm_##res##_names[i].name; \ + } \ + return "(invalid)"; \ +} + +static struct tdm_type_name tdm_dpms_names[] = { + { TDM_OUTPUT_DPMS_ON, "on" }, + { TDM_OUTPUT_DPMS_STANDBY, "standby" }, + { TDM_OUTPUT_DPMS_SUSPEND, "suspend" }, + { TDM_OUTPUT_DPMS_OFF, "off" }, +}; + +TDM_TYPE_NAME_FN(dpms) + +static struct tdm_type_name tdm_status_names[] = { + { TDM_OUTPUT_CONN_STATUS_DISCONNECTED, "disconnected" }, + { TDM_OUTPUT_CONN_STATUS_CONNECTED, "connected" }, + { TDM_OUTPUT_CONN_STATUS_MODE_SETTED, "mode_setted" }, +}; + +TDM_TYPE_NAME_FN(status) + #ifdef __cplusplus } #endif diff --git a/src/tdm_private.h b/src/tdm_private.h index db7426e..bdb1a20 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -84,11 +85,6 @@ extern int tdm_debug_thread; #define TDM_TRACE_END() #endif -#define prototype_name_fn(res) const char * res##_str(int type) - -prototype_name_fn(dpms); -prototype_name_fn(status); - typedef enum { TDM_CAPTURE_TARGET_OUTPUT, TDM_CAPTURE_TARGET_LAYER, @@ -304,9 +300,6 @@ typedef struct _tdm_buffer_info { struct list_head link; } tdm_buffer_info; -const char* -tdm_get_dpms_str(tdm_output_dpms dpms_value); - int tdm_display_check_module_abi(tdm_private_display *private_display, int abimaj, int abimin); diff --git a/src/tdm_server.c b/src/tdm_server.c index 1233deb..b71c2a2 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -321,8 +321,8 @@ _tdm_server_client_cb_wait_vblank(struct wl_client *client, if (dpms_value != TDM_OUTPUT_DPMS_ON && !sw_timer) { wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_DPMS_OFF, - "dpms '%s'", tdm_get_dpms_str(dpms_value)); - TDM_ERR("dpms '%s'", tdm_get_dpms_str(dpms_value)); + "dpms '%s'", tdm_dpms_str(dpms_value)); + TDM_ERR("dpms '%s'", tdm_dpms_str(dpms_value)); return; } -- 2.7.4 From 6708e849656ae248c39792318f65135870b51730 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Mon, 27 Jun 2016 13:37:44 +0900 Subject: [PATCH 04/16] support the tdm dynamic fps and the dpms on/off event Change-Id: I3fdc26887c19b5ddc66a5a4c1d7eca64cfa9f013 --- client/Makefile.am | 3 +- client/tdm_client.c | 673 ++++++++++++++++++++++++++++++----- client/tdm_client.h | 93 +++-- client/tdm_client_types.h | 112 ++++++ include/tdm_backend.h | 15 + include/tdm_list.h | 5 + include/tdm_types.h | 15 - packaging/libtdm.spec | 1 + protocol/tdm.xml | 72 ++-- src/Makefile.am | 1 + src/tdm_display.c | 23 +- src/tdm_private.h | 26 +- src/tdm_server.c | 516 +++++++++++++-------------- src/tdm_vblank.c | 879 ++++++++++++++++++++++++++++++++++++++++++++++ tools/Makefile.am | 3 +- tools/tdm_test_client.c | 355 +++++++++++++++++-- 16 files changed, 2303 insertions(+), 489 deletions(-) create mode 100644 client/tdm_client_types.h mode change 100755 => 100644 include/tdm_types.h create mode 100644 src/tdm_vblank.c diff --git a/client/Makefile.am b/client/Makefile.am index 14192b0..b41c0c6 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -1,6 +1,7 @@ libtdm_clientincludedir = ${includedir} libtdm_clientinclude_HEADERS = \ - tdm_client.h + tdm_client.h \ + tdm_client_types.h pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libtdm-client.pc diff --git a/client/tdm_client.c b/client/tdm_client.c index 2560a87..e3679d2 100644 --- a/client/tdm_client.c +++ b/client/tdm_client.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "tdm_client.h" #include "tdm_log.h" @@ -50,23 +51,202 @@ int tdm_debug; +typedef struct _tdm_private_client_vblank tdm_private_client_vblank; + typedef struct _tdm_private_client { struct wl_display *display; struct wl_registry *registry; struct wl_tdm *tdm; - struct wl_tdm_client *tdm_client; - struct list_head vblank_list; + struct list_head output_list; + + tdm_private_client_vblank *temp_vblank; } tdm_private_client; -typedef struct _tdm_client_vblank_info { +typedef struct _tdm_private_client_output { + tdm_private_client *private_client; + + char name[TDM_NAME_LEN]; + struct wl_tdm_output *output; + int width; + int height; + int refresh; + tdm_output_conn_status connection; + tdm_output_dpms dpms; + struct list_head vblank_list; + struct list_head change_handler_list; + + unsigned int req_id; + struct list_head link; +} tdm_private_client_output; + +struct _tdm_private_client_vblank { + tdm_private_client_output *private_output; + struct wl_tdm_vblank *vblank; + struct list_head wait_list; + + unsigned int sync; + unsigned int fps; + int offset; + unsigned int enable_fake; + + unsigned int started; + + struct list_head link; +}; + +typedef struct _tdm_client_output_handler_info { + tdm_private_client_output *private_output; + + tdm_client_output_change_handler func; + void *user_data; + + struct list_head link; +} tdm_client_output_handler_info; + +typedef struct _tdm_client_wait_info { + tdm_private_client_vblank *private_vblank; + tdm_client_vblank_handler func; + void *user_data; + + unsigned int req_id; unsigned int req_sec; unsigned int req_usec; - void *user_data; int need_free; -} tdm_client_vblank_info; + + struct list_head link; +} tdm_client_wait_info; + +static void +_tdm_client_vblank_cb_done(void *data, struct wl_tdm_vblank *wl_tdm_vblank, + uint32_t req_id, uint32_t sequence, uint32_t tv_sec, + uint32_t tv_usec, uint32_t error) +{ + tdm_private_client_vblank *private_vblank = data; + tdm_client_wait_info *w = NULL, *ww = NULL; + + TDM_RETURN_IF_FAIL(private_vblank != NULL); + + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->wait_list, link) { + if (w->req_id != req_id) + continue; + + if (w->func) + w->func(private_vblank, error, sequence, tv_sec, tv_usec, w->user_data); + + if (w->need_free) { + LIST_DEL(&w->link); + free(w); + } else + w->need_free = 1; + return; + } +} + +static const struct wl_tdm_vblank_listener tdm_client_vblank_listener = { + _tdm_client_vblank_cb_done, +}; + +static void +_tdm_client_output_destroy(tdm_private_client_output *private_output) +{ + tdm_private_client_vblank *v = NULL, *vv = NULL; + tdm_client_output_handler_info *h = NULL, *hh = NULL; + + LIST_DEL(&private_output->link); + + LIST_FOR_EACH_ENTRY_SAFE(v, vv, &private_output->vblank_list, link) { + TDM_ERR("vblanks SHOULD be destroyed first!"); + LIST_DEL(&v->link); + v->private_output = NULL; + } + + LIST_FOR_EACH_ENTRY_SAFE(h, hh, &private_output->change_handler_list, link) { + LIST_DEL(&h->link); + free(h); + } + + wl_tdm_output_destroy(private_output->output); + + free(private_output); +} + +static void +_tdm_client_output_cb_mode(void *data, struct wl_tdm_output *wl_tdm_output, + uint32_t width, uint32_t height, uint32_t refresh) +{ + tdm_private_client_output *private_output = (tdm_private_client_output*)data; + + TDM_RETURN_IF_FAIL(private_output != NULL); + + private_output->width = width; + private_output->height = height; + private_output->refresh = refresh; + + TDM_DBG("private_output(%p) wl_tbm_output@%d width(%d) height(%d) refresh(%d)", + private_output, wl_proxy_get_id((struct wl_proxy*)private_output->output), + width, height, refresh); +} + +static void +_tdm_client_output_cb_connection(void *data, struct wl_tdm_output *wl_tdm_output, uint32_t value) +{ + tdm_private_client_output *private_output = (tdm_private_client_output*)data; + tdm_client_output_handler_info *h = NULL; + tdm_value v; + + TDM_RETURN_IF_FAIL(private_output != NULL); + + if (private_output->connection == value) + return; + + private_output->connection = value; + + TDM_DBG("private_output(%p) wl_tbm_output@%d connection(%d)", + private_output, + wl_proxy_get_id((struct wl_proxy*)private_output->output), + value); + + v.u32 = value; + LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) { + if (h->func) + h->func(private_output, TDM_OUTPUT_CHANGE_CONNECTION, v, h->user_data); + } +} + +static void +_tdm_client_output_cb_dpms(void *data, struct wl_tdm_output *wl_tdm_output, uint32_t value) +{ + tdm_private_client_output *private_output = (tdm_private_client_output*)data; + tdm_client_output_handler_info *h = NULL; + tdm_value v; + + TDM_RETURN_IF_FAIL(private_output != NULL); + + if (private_output->dpms == value) + return; + + private_output->dpms = value; + + TDM_DBG("private_output(%p) wl_tbm_output@%d dpms(%d)", + private_output, + wl_proxy_get_id((struct wl_proxy*)private_output->output), + value); + + v.u32 = value; + LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) { + if (h->func) + h->func(private_output, TDM_OUTPUT_CHANGE_DPMS, v, h->user_data); + } +} + +static const struct wl_tdm_output_listener tdm_client_output_listener = { + _tdm_client_output_cb_mode, + _tdm_client_output_cb_connection, + _tdm_client_output_cb_dpms, +}; static void _tdm_client_cb_global(void *data, struct wl_registry *registry, @@ -95,7 +275,7 @@ static const struct wl_registry_listener tdm_client_registry_listener = { }; tdm_client* -tdm_client_create(tdm_client_error *error) +tdm_client_create(tdm_error *error) { tdm_private_client *private_client; const char *debug; @@ -104,14 +284,16 @@ tdm_client_create(tdm_client_error *error) if (debug && (strstr(debug, "1"))) tdm_debug = 1; - private_client = calloc(1, sizeof * private_client); + private_client = calloc(1, sizeof *private_client); if (!private_client) { TDM_ERR("alloc failed"); if (error) - *error = TDM_CLIENT_ERROR_OUT_OF_MEMORY; + *error = TDM_ERROR_OUT_OF_MEMORY; return NULL; } + LIST_INITHEAD(&private_client->output_list); + private_client->display = wl_display_connect("tdm-socket"); TDM_GOTO_IF_FAIL(private_client->display != NULL, create_failed); @@ -125,19 +307,14 @@ tdm_client_create(tdm_client_error *error) /* check global objects */ TDM_GOTO_IF_FAIL(private_client->tdm != NULL, create_failed); - LIST_INITHEAD(&private_client->vblank_list); - - private_client->tdm_client = wl_tdm_create_client(private_client->tdm); - TDM_GOTO_IF_FAIL(private_client->tdm_client != NULL, create_failed); - if (error) - *error = TDM_CLIENT_ERROR_NONE; + *error = TDM_ERROR_NONE; return (tdm_client*)private_client; create_failed: tdm_client_destroy((tdm_client*)private_client); if (error) - *error = TDM_CLIENT_ERROR_OPERATION_FAILED; + *error = TDM_ERROR_OPERATION_FAILED; return NULL; } @@ -145,19 +322,18 @@ void tdm_client_destroy(tdm_client *client) { tdm_private_client *private_client = (tdm_private_client*)client; - tdm_client_vblank_info *v = NULL, *vv = NULL; + tdm_private_client_output *o = NULL, *oo = NULL; if (!private_client) return; - LIST_FOR_EACH_ENTRY_SAFE(v, vv, &private_client->vblank_list, link) { - LIST_DEL(&v->link); - wl_tdm_vblank_destroy(v->vblank); - free(v); + if (private_client->temp_vblank) + tdm_client_vblank_destroy(private_client->temp_vblank); + + LIST_FOR_EACH_ENTRY_SAFE(o, oo, &private_client->output_list, link) { + _tdm_client_output_destroy(o); } - if (private_client->tdm_client) - wl_tdm_client_destroy(private_client->tdm_client); if (private_client->tdm) wl_tdm_destroy(private_client->tdm); if (private_client->registry) @@ -168,129 +344,446 @@ tdm_client_destroy(tdm_client *client) free(private_client); } -tdm_client_error +tdm_error tdm_client_get_fd(tdm_client *client, int *fd) { tdm_private_client *private_client; - TDM_RETURN_VAL_IF_FAIL(client != NULL, TDM_CLIENT_ERROR_INVALID_PARAMETER); - TDM_RETURN_VAL_IF_FAIL(fd != NULL, TDM_CLIENT_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(client != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(fd != NULL, TDM_ERROR_INVALID_PARAMETER); private_client = (tdm_private_client*)client; *fd = wl_display_get_fd(private_client->display); if (*fd < 0) - return TDM_CLIENT_ERROR_OPERATION_FAILED; + return TDM_ERROR_OPERATION_FAILED; - return TDM_CLIENT_ERROR_NONE; + return TDM_ERROR_NONE; } -tdm_client_error +tdm_error tdm_client_handle_events(tdm_client *client) { tdm_private_client *private_client; - TDM_RETURN_VAL_IF_FAIL(client != NULL, TDM_CLIENT_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(client != NULL, TDM_ERROR_INVALID_PARAMETER); private_client = (tdm_private_client*)client; wl_display_dispatch(private_client->display); - return TDM_CLIENT_ERROR_NONE; + return TDM_ERROR_NONE; } +typedef struct _tdm_client_vblank_temp { + tdm_client_vblank_handler2 func; + void *user_data; +} tdm_client_vblank_temp; + static void -_tdm_client_cb_vblank_done(void *data, struct wl_tdm_vblank *vblank, - uint32_t sequence, uint32_t tv_sec, uint32_t tv_usec) +_tdm_client_vblank_handler_temp(tdm_client_vblank *vblank, tdm_error error, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, void *user_data) { - tdm_client_vblank_info *vblank_info = (tdm_client_vblank_info*)data; + tdm_client_vblank_temp *vblank_temp = user_data; - TDM_RETURN_IF_FAIL(vblank_info != NULL); + TDM_RETURN_IF_FAIL(vblank_temp != NULL); - if (vblank_info->vblank != vblank) - TDM_NEVER_GET_HERE(); + if (vblank_temp->func) + vblank_temp->func(sequence, tv_sec, tv_usec, vblank_temp->user_data); - TDM_DBG("vblank_info(%p) wl_tbm_vblank@%d", vblank_info, - wl_proxy_get_id((struct wl_proxy *)vblank)); - - if (vblank_info->func) - vblank_info->func(sequence, tv_sec, tv_usec, vblank_info->user_data); - - if (vblank_info->need_free) { - LIST_DEL(&vblank_info->link); - free(vblank_info); - } else { - vblank_info->need_free = 1; - } + free(vblank_temp); } -static const struct wl_tdm_vblank_listener tdm_client_vblank_listener = { - _tdm_client_cb_vblank_done, -}; - -tdm_client_error +tdm_error tdm_client_wait_vblank(tdm_client *client, char *name, int sw_timer, int interval, int sync, - tdm_client_vblank_handler func, void *user_data) + tdm_client_vblank_handler2 func, void *user_data) { tdm_private_client *private_client = (tdm_private_client*)client; - tdm_client_vblank_info *vblank_info; - struct timespec tp; - int ret = 0; + tdm_client_output *output; + tdm_client_vblank_temp *vblank_temp; + tdm_error ret; + + TDM_RETURN_VAL_IF_FAIL(private_client != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER); + + if (!private_client->temp_vblank) { + output = tdm_client_get_output(client, name, &ret); + TDM_RETURN_VAL_IF_FAIL(output != NULL, ret); + + private_client->temp_vblank = tdm_client_output_create_vblank(output, &ret); + TDM_RETURN_VAL_IF_FAIL(private_client->temp_vblank != NULL, ret); + } + + tdm_client_vblank_set_enable_fake(private_client->temp_vblank, sw_timer); + tdm_client_vblank_set_sync(private_client->temp_vblank, sync); + + vblank_temp = calloc(1, sizeof *vblank_temp); + TDM_RETURN_VAL_IF_FAIL(vblank_temp != NULL, TDM_ERROR_OUT_OF_MEMORY); + + vblank_temp->func = func; + vblank_temp->user_data = user_data; - TDM_RETURN_VAL_IF_FAIL(name != NULL, TDM_CLIENT_ERROR_INVALID_PARAMETER); - TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_CLIENT_ERROR_INVALID_PARAMETER); - TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_CLIENT_ERROR_INVALID_PARAMETER); - TDM_RETURN_VAL_IF_FAIL(private_client != NULL, TDM_CLIENT_ERROR_INVALID_PARAMETER); - TDM_RETURN_VAL_IF_FAIL(private_client->tdm_client != NULL, TDM_CLIENT_ERROR_INVALID_PARAMETER); + return tdm_client_vblank_wait(private_client->temp_vblank, interval, _tdm_client_vblank_handler_temp, vblank_temp); +} + +tdm_client_output* +tdm_client_get_output(tdm_client *client, char *name, tdm_error *error) +{ + tdm_private_client *private_client; + tdm_private_client_output *private_output = NULL; - vblank_info = calloc(1, sizeof * vblank_info); - if (!vblank_info) { + if (error) + *error = TDM_ERROR_NONE; + + if (!client) { + TDM_ERR("'!client' failed"); + if (error) + *error = TDM_ERROR_INVALID_PARAMETER; + return NULL; + } + + private_client = (tdm_private_client*)client; + + if (!name) + name = "primary"; + + LIST_FOR_EACH_ENTRY(private_output, &private_client->output_list, link) { + if (!strncmp(private_output->name, name, TDM_NAME_LEN)) + return (tdm_client_output*)private_output; + } + + private_output = calloc(1, sizeof *private_output); + if (!private_output) { TDM_ERR("alloc failed"); - return TDM_CLIENT_ERROR_OUT_OF_MEMORY; + if (error) + *error = TDM_ERROR_OUT_OF_MEMORY; + return NULL; } - clock_gettime(CLOCK_MONOTONIC, &tp); + private_output->private_client = private_client; - vblank_info->req_sec = (unsigned int)tp.tv_sec; - vblank_info->req_usec = (unsigned int)(tp.tv_nsec / 1000); - vblank_info->need_free = (sync) ? 0 : 1; + snprintf(private_output->name, TDM_NAME_LEN, "%s", name); + private_output->output = wl_tdm_create_output(private_client->tdm, private_output->name); + if (!private_output->output) { + TDM_ERR("couldn't create output resource"); + free(private_output); + if (error) + *error = TDM_ERROR_OUT_OF_MEMORY; + return NULL; + } + + LIST_INITHEAD(&private_output->vblank_list); + LIST_INITHEAD(&private_output->change_handler_list); + LIST_ADDTAIL(&private_output->link, &private_client->output_list); + + wl_tdm_output_add_listener(private_output->output, + &tdm_client_output_listener, private_output); + wl_display_roundtrip(private_client->display); + + return (tdm_client_output*)private_output; +} + +tdm_error +tdm_client_output_add_change_handler(tdm_client_output *output, + tdm_client_output_change_handler func, + void *user_data) +{ + tdm_private_client_output *private_output; + tdm_client_output_handler_info *h; + + TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_output = (tdm_private_client_output*)output; + + h = calloc(1, sizeof *h); + TDM_RETURN_VAL_IF_FAIL(h != NULL, TDM_ERROR_OUT_OF_MEMORY); + + h->private_output = private_output; + h->func = func; + h->user_data = user_data; + LIST_ADDTAIL(&h->link, &private_output->change_handler_list); + + return TDM_ERROR_NOT_IMPLEMENTED; +} + +void +tdm_client_output_remove_change_handler(tdm_client_output *output, + tdm_client_output_change_handler func, + void *user_data) +{ + tdm_private_client_output *private_output; + tdm_client_output_handler_info *h = NULL; + + TDM_RETURN_IF_FAIL(output != NULL); + TDM_RETURN_IF_FAIL(func != NULL); + + private_output = (tdm_private_client_output*)output; + + LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) { + if (h->func != func || h->user_data != user_data) + continue; + + LIST_DEL(&h->link); + free(h); + return; + } +} + +tdm_error +tdm_client_output_get_refresh_rate(tdm_client_output *output, unsigned int *refresh) +{ + tdm_private_client_output *private_output; + + TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(refresh != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_output = (tdm_private_client_output*)output; + + *refresh = private_output->refresh; + + return TDM_ERROR_NONE; +} + +tdm_error +tdm_client_output_get_conn_status(tdm_client_output *output, tdm_output_conn_status *status) +{ + tdm_private_client_output *private_output; + + TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(status != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_output = (tdm_private_client_output*)output; + + *status = private_output->connection; + + return TDM_ERROR_NONE; +} - vblank_info->vblank = - wl_tdm_client_wait_vblank(private_client->tdm_client, - name, sw_timer, interval, - vblank_info->req_sec, vblank_info->req_usec); - if (!vblank_info->vblank) { +tdm_error +tdm_client_output_get_dpms(tdm_client_output *output, tdm_output_dpms *dpms) +{ + tdm_private_client_output *private_output; + + TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(dpms != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_output = (tdm_private_client_output*)output; + + *dpms = private_output->dpms; + + return TDM_ERROR_NONE; +} + +tdm_client_vblank* +tdm_client_output_create_vblank(tdm_client_output *output, tdm_error *error) +{ + tdm_private_client_output *private_output; + tdm_private_client_vblank *private_vblank; + + if (error) + *error = TDM_ERROR_NONE; + + if (!output) { + TDM_ERR("'!output' failed"); + if (error) + *error = TDM_ERROR_INVALID_PARAMETER; + return NULL; + } + + private_output = (tdm_private_client_output*)output; + + private_vblank = calloc(1, sizeof *private_vblank); + if (!private_vblank) { + TDM_ERR("alloc failed"); + if (error) + *error = TDM_ERROR_OUT_OF_MEMORY; + return NULL; + } + + private_vblank->private_output = private_output; + + private_vblank->vblank = wl_tdm_output_create_vblank(private_output->output); + if (!private_vblank->vblank) { TDM_ERR("couldn't create vblank resource"); - free(vblank_info); - return TDM_CLIENT_ERROR_OUT_OF_MEMORY; + free(private_vblank); + if (error) + *error = TDM_ERROR_OUT_OF_MEMORY; + return NULL; + } + + /* initial value */ + private_vblank->fps = private_output->refresh; + private_vblank->offset = 0; + private_vblank->enable_fake = 0; + + LIST_INITHEAD(&private_vblank->wait_list); + LIST_ADDTAIL(&private_vblank->link, &private_output->vblank_list); + + wl_tdm_vblank_add_listener(private_vblank->vblank, + &tdm_client_vblank_listener, private_vblank); + + return (tdm_client_vblank*)private_vblank; +} + +void +tdm_client_vblank_destroy(tdm_client_vblank *vblank) +{ + tdm_private_client_vblank *private_vblank; + tdm_client_wait_info *w = NULL, *ww = NULL; + + TDM_RETURN_IF_FAIL(vblank != NULL); + + private_vblank = vblank; + LIST_DEL(&private_vblank->link); + + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->wait_list, link) { + LIST_DEL(&w->link); + free(w); } - TDM_DBG("vblank_info(%p) wl_tbm_vblank@%d", vblank_info, - wl_proxy_get_id((struct wl_proxy *)vblank_info->vblank)); + wl_tdm_vblank_destroy(private_vblank->vblank); + + free(private_vblank); +} + +tdm_error +tdm_client_vblank_set_sync(tdm_client_vblank *vblank, unsigned int sync) +{ + tdm_private_client_vblank *private_vblank; + + TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_vblank = vblank; + private_vblank->sync = sync; + + return TDM_ERROR_NONE; +} + +tdm_error +tdm_client_vblank_set_fps(tdm_client_vblank *vblank, unsigned int fps) +{ + tdm_private_client_vblank *private_vblank; + + TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(fps > 0, TDM_ERROR_INVALID_PARAMETER); + + private_vblank = vblank; + TDM_RETURN_VAL_IF_FAIL(private_vblank->started == 0, TDM_ERROR_BAD_REQUEST); + + if (private_vblank->fps == fps) + return TDM_ERROR_NONE; + private_vblank->fps = fps; + + wl_tdm_vblank_set_fps(private_vblank->vblank, fps); + + return TDM_ERROR_NONE; +} + +tdm_error +tdm_client_vblank_set_offset(tdm_client_vblank *vblank, int offset_ms) +{ + tdm_private_client_vblank *private_vblank; - wl_tdm_vblank_add_listener(vblank_info->vblank, - &tdm_client_vblank_listener, vblank_info); + TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_vblank = vblank; + TDM_RETURN_VAL_IF_FAIL(private_vblank->started == 0, TDM_ERROR_BAD_REQUEST); + + if (private_vblank->offset == offset_ms) + return TDM_ERROR_NONE; + private_vblank->offset = offset_ms; + + wl_tdm_vblank_set_offset(private_vblank->vblank, offset_ms); + + return TDM_ERROR_NONE; +} + +tdm_error +tdm_client_vblank_set_enable_fake(tdm_client_vblank *vblank, unsigned int enable_fake) +{ + tdm_private_client_vblank *private_vblank; + + TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_vblank = vblank; + + if (private_vblank->enable_fake == enable_fake) + return TDM_ERROR_NONE; + private_vblank->enable_fake = enable_fake; + + wl_tdm_vblank_set_enable_fake(private_vblank->vblank, enable_fake); + + return TDM_ERROR_NONE; +} + +tdm_error +tdm_client_vblank_wait(tdm_client_vblank *vblank, unsigned int interval, tdm_client_vblank_handler func, void *user_data) +{ + tdm_private_client *private_client; + tdm_private_client_output *private_output; + tdm_private_client_vblank *private_vblank; + tdm_client_wait_info *w; + struct timespec tp; + int ret = 0; + + TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER); + /* can't support "interval 0" and "getting current_msc" things because + * there is a socket communication between TDM client and server. It's impossible + * to return the current msc or sequence immediately. + */ + TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_ERROR_INVALID_PARAMETER); + + private_vblank = vblank; + private_output = private_vblank->private_output; + private_client = private_output->private_client; + + if (!private_vblank->started) + private_vblank->started = 1; + + if (private_output->dpms != TDM_OUTPUT_DPMS_ON && !private_vblank->enable_fake) { + TDM_INFO("dpms off"); + return TDM_ERROR_DPMS_OFF; + } + + w = calloc(1, sizeof *w); + if (!w) { + TDM_ERR("alloc failed"); + return TDM_ERROR_OUT_OF_MEMORY; + } + + w->private_vblank = private_vblank; + w->func = func; + w->user_data = user_data; + + LIST_ADDTAIL(&w->link, &private_vblank->wait_list); + + clock_gettime(CLOCK_MONOTONIC, &tp); + w->req_id = ++private_output->req_id; + w->req_sec = (unsigned int)tp.tv_sec; + w->req_usec = (unsigned int)(tp.tv_nsec / 1000); + w->need_free = (private_vblank->sync) ? 0 : 1; - vblank_info->func = func; - vblank_info->user_data = user_data; - LIST_ADDTAIL(&vblank_info->link, &private_client->vblank_list); + wl_tdm_vblank_wait_vblank(private_vblank->vblank, interval, w->req_id, w->req_sec, w->req_usec); - if (!sync) { + if (!private_vblank->sync) { wl_display_flush(private_client->display); - return TDM_CLIENT_ERROR_NONE; + return TDM_ERROR_NONE; } - while (ret != -1 && !vblank_info->need_free) + while (ret != -1 && !w->need_free) ret = wl_display_dispatch(private_client->display); clock_gettime(CLOCK_MONOTONIC, &tp); TDM_DBG("block during %d us", ((unsigned int)(tp.tv_sec * 1000000) + (unsigned int)(tp.tv_nsec / 1000)) - - (vblank_info->req_sec * 1000000 + vblank_info->req_usec)); + - (w->req_sec * 1000000 + w->req_usec)); - LIST_DEL(&vblank_info->link); - free(vblank_info); + LIST_DEL(&w->link); + free(w); - return TDM_CLIENT_ERROR_NONE; + return TDM_ERROR_NONE; } diff --git a/client/tdm_client.h b/client/tdm_client.h index fff8808..2f1c64f 100644 --- a/client/tdm_client.h +++ b/client/tdm_client.h @@ -49,39 +49,16 @@ extern "C" { * @endcode */ -/** - * @brief The client error enumeration - */ -typedef enum { - TDM_CLIENT_ERROR_NONE = 0, /**< none */ - TDM_CLIENT_ERROR_OPERATION_FAILED = -1, /**< operaion failed */ - TDM_CLIENT_ERROR_INVALID_PARAMETER = -2, /**< wrong input parameter */ - TDM_CLIENT_ERROR_PERMISSION_DENIED = -3, /**< access denied */ - TDM_CLIENT_ERROR_OUT_OF_MEMORY = -4, /**< no free memory */ - TDM_CLIENT_ERROR_DPMS_OFF = -5, /**< dpms off */ -} tdm_client_error; - -/** - * @brief The TDM client object - */ -typedef void *tdm_client; - -/** - * @brief The client vblank handler - * @see #tdm_client_wait_vblank - */ -typedef void -(*tdm_client_vblank_handler)(unsigned int sequence, unsigned int tv_sec, - unsigned int tv_usec, void *user_data); +#include /** * @brief Create a TDM client object. - * @param[out] error #TDM_CLIENT_ERROR_NONE if success. Otherwise, error value. + * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. * @return A TDM client object if success. Otherwise, NULL. * @see #tdm_client_destroy */ tdm_client* -tdm_client_create(tdm_client_error *error); +tdm_client_create(tdm_error *error); /** * @brief Destroy a TDM client object @@ -95,14 +72,14 @@ tdm_client_destroy(tdm_client *client); * @brief Get the file descriptor * @param[in] client A TDM client object * @param[out] fd The file descriptor - * @return #TDM_CLIENT_ERROR_NONE if success. Otherwise, error value. + * @return #TDM_ERROR_NONE if success. Otherwise, error value. * @see #tdm_client_handle_events * @par Example * @code * #include //for a client of TDM * * err = tdm_client_get_fd(client, &fd); - * if (err != TDM_CLIENT_ERROR_NONE) { + * if (err != TDM_ERROR_NONE) { * //error handling * } * @@ -121,26 +98,27 @@ tdm_client_destroy(tdm_client *client); * } * * err = tdm_client_handle_events(client); - * if (err != TDM_CLIENT_ERROR_NONE) { + * if (err != TDM_ERROR_NONE) { * //error handling * } * } * @endcode */ -tdm_client_error +tdm_error tdm_client_get_fd(tdm_client *client, int *fd); /** * @brief Handle the events of the given file descriptor * @param[in] client A TDM client object - * @return #TDM_CLIENT_ERROR_NONE if success. Otherwise, error value. + * @return #TDM_ERROR_NONE if success. Otherwise, error value. * @see #tdm_client_get_fd */ -tdm_client_error +tdm_error tdm_client_handle_events(tdm_client *client); /** * @brief Wait for VBLANK + * @deprecated * @details After interval vblanks, a client vblank handler will be called. * If 'sw_timer' param is 1 in case of DPMS off, TDM will use the SW timer and * call a client vblank handler. Otherwise, this function will return error. @@ -151,13 +129,58 @@ tdm_client_handle_events(tdm_client *client); * @param[in] sync 0: asynchronous, 1:synchronous * @param[in] func A client vblank handler * @param[in] user_data The user data - * @return #TDM_CLIENT_ERROR_NONE if success. Otherwise, error value. + * @return #TDM_ERROR_NONE if success. Otherwise, error value. * @see #tdm_client_vblank_handler */ -tdm_client_error +tdm_error tdm_client_wait_vblank(tdm_client *client, char *name, int sw_timer, int interval, int sync, - tdm_client_vblank_handler func, void *user_data); + tdm_client_vblank_handler2 func, void *user_data); + + +tdm_client_output* +tdm_client_get_output(tdm_client *client, char *name, tdm_error *error); + +tdm_error +tdm_client_output_add_change_handler(tdm_client_output *output, + tdm_client_output_change_handler func, + void *user_data); + +void +tdm_client_output_remove_change_handler(tdm_client_output *output, + tdm_client_output_change_handler func, + void *user_data); + +tdm_error +tdm_client_output_get_refresh_rate(tdm_client_output *output, unsigned int *refresh); + +tdm_error +tdm_client_output_get_conn_status(tdm_client_output *output, tdm_output_conn_status *status); + +tdm_error +tdm_client_output_get_dpms(tdm_client_output *output, tdm_output_dpms *dpms); + +tdm_client_vblank* +tdm_client_output_create_vblank(tdm_client_output *output, tdm_error *error); + +void +tdm_client_vblank_destroy(tdm_client_vblank *vblank); + +tdm_error +tdm_client_vblank_set_sync(tdm_client_vblank *vblank, unsigned int sync); + +tdm_error +tdm_client_vblank_set_fps(tdm_client_vblank *vblank, unsigned int fps); + +tdm_error +tdm_client_vblank_set_offset(tdm_client_vblank *vblank, int offset_ms); + +tdm_error +tdm_client_vblank_set_enable_fake(tdm_client_vblank *vblank, unsigned int enable_fake); + +tdm_error +tdm_client_vblank_wait(tdm_client_vblank *vblank, unsigned int interval, tdm_client_vblank_handler func, void *user_data); + #ifdef __cplusplus } diff --git a/client/tdm_client_types.h b/client/tdm_client_types.h new file mode 100644 index 0000000..f6f6355 --- /dev/null +++ b/client/tdm_client_types.h @@ -0,0 +1,112 @@ +/************************************************************************** + * + * libtdm + * + * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Contact: Eunchul Kim , + * JinYoung Jeon , + * Taeheon Kim , + * YoungJun Cho , + * SooChan Lim , + * Boram Park + * + * 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. + * +**************************************************************************/ + +#ifndef _TDM_CLIENT_TYPES_H_ +#define _TDM_CLIENT_TYPES_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file tdm_client_types.h + * @brief The header file which defines Enumerations and Structures for client. + */ + +#include + +/** + * @deprecated + */ +typedef tdm_error tdm_client_error; + +/** + * @brief The client error enumeration + * @deprecated + */ +enum { + TDM_CLIENT_ERROR_NONE = 0, /**< none */ + TDM_CLIENT_ERROR_OPERATION_FAILED = -1, /**< operaion failed */ + TDM_CLIENT_ERROR_INVALID_PARAMETER = -2, /**< wrong input parameter */ + TDM_CLIENT_ERROR_PERMISSION_DENIED = -3, /**< access denied */ + TDM_CLIENT_ERROR_OUT_OF_MEMORY = -4, /**< no free memory */ + TDM_CLIENT_ERROR_DPMS_OFF = -5, /**< dpms off */ +}; + +/** + * @deprecated + */ +typedef void +(*tdm_client_vblank_handler2)(unsigned int sequence, unsigned int tv_sec, + unsigned int tv_usec, void *user_data); + +/** + * @brief The TDM client object + */ +typedef void tdm_client; + +/** + * @brief The TDM client output object + */ +typedef void tdm_client_output; + +/** + * @brief The TDM client vblank object + */ +typedef void tdm_client_vblank; + +typedef void +(*tdm_client_output_change_handler)(tdm_client_output *output, + tdm_output_change_type type, + tdm_value value, + void *user_data); + +/** + * @brief The client vblank handler + * @see #tdm_client_wait_vblank + */ +typedef void +(*tdm_client_vblank_handler)(tdm_client_vblank *vblank, + tdm_error error, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + void *user_data); + +#ifdef __cplusplus +} +#endif + +#endif /* _TDM_CLIENT_TYPES_H_ */ diff --git a/include/tdm_backend.h b/include/tdm_backend.h index 0256e92..77b1e9b 100644 --- a/include/tdm_backend.h +++ b/include/tdm_backend.h @@ -788,6 +788,21 @@ typedef struct _tdm_func_capture { void (*reserved8)(void); } tdm_func_capture; +/** + * @brief The tdm event source + */ +typedef void tdm_event_loop_source; + +/** + * @brief The fd source handler + */ +typedef tdm_error (*tdm_event_loop_fd_handler)(int fd, tdm_event_loop_mask mask, void *user_data); + +/** + * @brief The timer source handler + */ +typedef tdm_error (*tdm_event_loop_timer_handler)(void *user_data); + #define TDM_BACKEND_MINOR_VERSION_MASK 0x0000FFFF #define TDM_BACKEND_MAJOR_VERSION_MASK 0xFFFF0000 #define TDM_BACKEND_GET_ABI_MINOR(v) ((v) & TDM_BACKEND_MINOR_VERSION_MASK) diff --git a/include/tdm_list.h b/include/tdm_list.h index 2fd1db0..7475894 100644 --- a/include/tdm_list.h +++ b/include/tdm_list.h @@ -126,6 +126,11 @@ static inline int list_length(struct list_head *item) &pos->member != (head); \ pos = container_of(pos->member.next, pos, member)) +#define LIST_FOR_EACH_ENTRY_REV(pos, head, member) \ + for (pos = container_of((head)->prev, pos, member); \ + &pos->member != (head); \ + pos = container_of(pos->member.prev, pos, member)) + #define LIST_FOR_EACH_ENTRY_SAFE(pos, storage, head, member) \ for (pos = container_of((head)->next, pos, member), \ storage = container_of(pos->member.next, pos, member); \ diff --git a/include/tdm_types.h b/include/tdm_types.h old mode 100755 new mode 100644 index 53988f2..050dad8 --- a/include/tdm_types.h +++ b/include/tdm_types.h @@ -297,21 +297,6 @@ typedef void (*tdm_output_commit_handler)(tdm_output *output, unsigned int seque unsigned int tv_sec, unsigned int tv_usec, void *user_data); -/** - * @brief The tdm event source - */ -typedef void tdm_event_loop_source; - -/** - * @brief The fd source handler - */ -typedef tdm_error (*tdm_event_loop_fd_handler)(int fd, tdm_event_loop_mask mask, void *user_data); - -/** - * @brief The timer source handler - */ -typedef tdm_error (*tdm_event_loop_timer_handler)(void *user_data); - #ifdef __cplusplus } #endif diff --git a/packaging/libtdm.spec b/packaging/libtdm.spec index 28bb692..52c83b0 100644 --- a/packaging/libtdm.spec +++ b/packaging/libtdm.spec @@ -120,6 +120,7 @@ rm -f %{_unitdir_user}/default.target.wants/tdm-socket-user.path %manifest %{name}.manifest %defattr(-,root,root,-) %{_includedir}/tdm_client.h +%{_includedir}/tdm_client_types.h %{_libdir}/pkgconfig/libtdm-client.pc %{_libdir}/libtdm-client.so diff --git a/protocol/tdm.xml b/protocol/tdm.xml index 7564670..308fe8c 100644 --- a/protocol/tdm.xml +++ b/protocol/tdm.xml @@ -8,42 +8,33 @@ TDM uses the wayland protocol to communicate between tdm client and tdm server. - - + + + - + - - TDM uses the wayland protocol to communicate between tdm client and tdm server. - + + + + + - - - - - + + + - - + + + - - When wl_tdm_client.wait_vblank is called, wl_tdm_vblank resource is created. - And when TDM server gets the HW vblank, TDM server will send the 'done' - event of wl_tdm_vblank interface to let the TDM client get the HW vblank. - If 'sw_timer' param is 1, TDM server will use the SW timer in case of DPMS off. - And TDM server will create the fake vblank event and send it to TDM client. - Otherwise, TDM server will return 'dpms_off' error. - + + - - - - - @@ -51,11 +42,40 @@ + + + + + + + + + + + + + + + + + + + When wl_tdm_vblank.wait_vblank is called, TDM server will send the wl_tdm_vblank.done + event which is aligned with HW vblank. Even if DPMS is off, it will send + wl_tdm_vblank.done events by using SW timer. + + + + + + + + diff --git a/src/Makefile.am b/src/Makefile.am index 245c2fa..4d62318 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,6 +14,7 @@ libtdm_la_SOURCES = \ $(top_srcdir)/protocol/tdm-protocol.c \ tdm_backend.c \ tdm_server.c \ + tdm_vblank.c \ tdm_event_loop.c \ tdm_thread.c \ tdm_helper.c \ diff --git a/src/tdm_display.c b/src/tdm_display.c index 2c09442..c3bd904 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -491,7 +491,7 @@ tdm_output_cb_status(tdm_output *output_backend, tdm_output_conn_status status, tdm_output_call_change_handler_internal(private_output, &private_output->change_handler_list_sub, TDM_OUTPUT_CHANGE_CONNECTION, - value); + value, 0); ret = tdm_thread_send_cb(private_display->private_loop, &output_status.base); TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE); @@ -506,7 +506,7 @@ tdm_output_cb_status(tdm_output *output_backend, tdm_output_conn_status status, tdm_output_call_change_handler_internal(private_output, &private_output->change_handler_list_main, TDM_OUTPUT_CHANGE_CONNECTION, - value); + value, 0); } EXTERN tdm_error @@ -521,12 +521,6 @@ tdm_output_add_change_handler(tdm_output *output, pthread_mutex_lock(&private_display->lock); - if (!private_output->regist_change_cb) { - _pthread_mutex_unlock(&private_display->lock); - TDM_ERR("not implemented!!"); - return TDM_ERROR_NOT_IMPLEMENTED; - } - change_handler = calloc(1, sizeof(tdm_private_change_handler)); if (!change_handler) { TDM_ERR("failed: alloc memory"); @@ -1138,7 +1132,13 @@ tdm_output_set_dpms(tdm_output *output, tdm_output_dpms dpms_value) tdm_output_call_change_handler_internal(private_output, &private_output->change_handler_list_main, TDM_OUTPUT_CHANGE_DPMS, - value); + value, 0); + + //TODO: safe? We can't take care of the user_data of callback functions. + tdm_output_call_change_handler_internal(private_output, + &private_output->change_handler_list_sub, + TDM_OUTPUT_CHANGE_DPMS, + value, 1); } _pthread_mutex_unlock(&private_display->lock); @@ -1192,7 +1192,8 @@ INTERN void tdm_output_call_change_handler_internal(tdm_private_output *private_output, struct list_head *change_handler_list, tdm_output_change_type type, - tdm_value value) + tdm_value value, + int no_check_thread_id) { tdm_private_display *private_display; tdm_private_change_handler *change_handler = NULL; @@ -1214,7 +1215,7 @@ tdm_output_call_change_handler_internal(tdm_private_output *private_output, return; LIST_FOR_EACH_ENTRY(change_handler, change_handler_list, link) { - if (change_handler->owner_tid != syscall(SYS_gettid)) + if (!no_check_thread_id && change_handler->owner_tid != syscall(SYS_gettid)) TDM_NEVER_GET_HERE(); _pthread_mutex_unlock(&private_display->lock); diff --git a/src/tdm_private.h b/src/tdm_private.h index bdb1a20..05fb7a9 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -331,7 +332,8 @@ void tdm_output_call_change_handler_internal(tdm_private_output *private_output, struct list_head *change_handler_list, tdm_output_change_type type, - tdm_value value); + tdm_value value, + int no_check_thread_id); tdm_private_pp * tdm_pp_create_internal(tdm_private_display *private_display, tdm_error *error); @@ -511,6 +513,28 @@ tdm_error tdm_display_update_output(tdm_private_display *private_display, tdm_output *output_backend, int pipe); + +/** + * @brief The tdm vblank object + */ +typedef void tdm_vblank; + +typedef void (*tdm_vblank_handler)(tdm_vblank *vblank, tdm_error error, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, void *user_data); + +tdm_vblank* +tdm_vblank_create(tdm_display *dpy, tdm_output *output, tdm_error *error); +void +tdm_vblank_destroy(tdm_vblank *vblank); +tdm_error +tdm_vblank_set_fps(tdm_vblank *vblank, unsigned int fps); +tdm_error +tdm_vblank_set_offset(tdm_vblank *vblank, int offset); +tdm_error +tdm_vblank_set_enable_fake(tdm_vblank *vblank, unsigned int enable_fake); +tdm_error +tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec, unsigned int interval, tdm_vblank_handler func, void *user_data); + #ifdef __cplusplus } #endif diff --git a/src/tdm_server.c b/src/tdm_server.c index b71c2a2..bb8ff6c 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -52,173 +52,150 @@ struct _tdm_private_server { tdm_private_loop *private_loop; - struct list_head client_list; - struct list_head vblank_list; + struct list_head output_list; + struct list_head wait_list; }; -typedef struct _tdm_server_client_info { +typedef struct _tdm_server_output_info { struct list_head link; tdm_private_server *private_server; struct wl_resource *resource; - - tdm_output *vblank_output; - double vblank_gap; - unsigned int last_tv_sec; - unsigned int last_tv_usec; -} tdm_server_client_info; + tdm_output *output; + struct list_head vblank_list; +} tdm_server_output_info; typedef struct _tdm_server_vblank_info { struct list_head link; - tdm_server_client_info *client_info; + tdm_server_output_info *output_info; struct wl_resource *resource; - unsigned int req_sec; - unsigned int req_usec; - - tdm_event_loop_source *timer_source; - unsigned int timer_target_sec; - unsigned int timer_target_usec; + tdm_vblank *vblank; } tdm_server_vblank_info; -static tdm_private_server *keep_private_server; -static int tdm_debug_server; - -static void -_tdm_server_send_done(tdm_server_vblank_info *vblank_info, unsigned int sequence, - unsigned int tv_sec, unsigned int tv_usec); +typedef struct _tdm_server_wait_info { + struct list_head link; + tdm_server_vblank_info *vblank_info; -static tdm_error -_tdm_server_cb_timer(void *user_data) -{ - tdm_server_vblank_info *vblank_info = (tdm_server_vblank_info*)user_data; + unsigned int req_id; +} tdm_server_wait_info; - _tdm_server_send_done(vblank_info, 0, - vblank_info->timer_target_sec, - vblank_info->timer_target_usec); +static tdm_private_server *keep_private_server; - return TDM_ERROR_NONE; -} +static void destroy_wait(tdm_server_wait_info *wait_info); -static tdm_error -_tdm_server_update_timer(tdm_server_vblank_info *vblank_info, int interval) +static tdm_output* +_tdm_server_find_output(tdm_private_server *private_server, const char *name) { - tdm_server_client_info *client_info = vblank_info->client_info; - tdm_private_server *private_server = client_info->private_server; tdm_private_loop *private_loop = private_server->private_loop; - unsigned long last, prev_req, req, curr, next; - unsigned int ms_delay; - tdm_error ret; + tdm_output *found = NULL; - ret = tdm_display_lock(private_loop->dpy); - TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret); - - if (!vblank_info->timer_source) { - vblank_info->timer_source = - tdm_event_loop_add_timer_handler(private_loop->dpy, - _tdm_server_cb_timer, - vblank_info, - NULL); - if (!vblank_info->timer_source) { - TDM_ERR("couldn't add timer"); - tdm_display_unlock(private_loop->dpy); - return TDM_ERROR_OPERATION_FAILED; - } - } + if (!strncasecmp(name, "primary", 7) || !strncasecmp(name, "default", 7)) + found = tdm_display_get_output(private_loop->dpy, 0, NULL); - last = (unsigned long)client_info->last_tv_sec * 1000000 + client_info->last_tv_usec; - req = (unsigned long)vblank_info->req_sec * 1000000 + vblank_info->req_usec; - curr = tdm_helper_get_time_in_micros(); + if (!found) { + int count = 0, i; - prev_req = last + (unsigned int)((req - last) / client_info->vblank_gap) * client_info->vblank_gap; - next = prev_req + (unsigned long)(client_info->vblank_gap * interval); + tdm_display_get_output_count(private_loop->dpy, &count); - while (next < curr) - next += (unsigned long)client_info->vblank_gap; + for (i = 0; i < count; i++) { + tdm_output *output = tdm_display_get_output(private_loop->dpy, i, NULL); + tdm_output_conn_status status; + const char *model = NULL; + tdm_error ret; - TDM_DBG("last(%.6lu) req(%.6lu) curr(%.6lu) prev_req(%.6lu) next(%.6lu)", - last, req, curr, prev_req, next); + ret = tdm_output_get_conn_status(output, &status); + if (ret || status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) + continue; - ms_delay = (unsigned int)ceil((double)(next - curr) / 1000); - if (ms_delay == 0) - ms_delay = 1; + ret = tdm_output_get_model_info(output, NULL, &model, NULL); + if (ret || !model) + continue; - TDM_DBG("delay(%lu) ms_delay(%d)", next - curr, ms_delay); + if (strncmp(model, name, TDM_NAME_LEN)) + continue; - ret = tdm_event_loop_source_timer_update(vblank_info->timer_source, ms_delay); - if (ret != TDM_ERROR_NONE) { - tdm_event_loop_source_remove(vblank_info->timer_source); - vblank_info->timer_source = NULL; - tdm_display_unlock(private_loop->dpy); - TDM_ERR("couldn't update timer"); - return TDM_ERROR_OPERATION_FAILED; + found = output; + break; + } } - TDM_DBG("timer tick: %d us", (int)(next - last)); - - vblank_info->timer_target_sec = next / 1000000; - vblank_info->timer_target_usec = next % 1000000; - - tdm_display_unlock(private_loop->dpy); - - return TDM_ERROR_NONE; + return found; } static void -_tdm_server_send_done(tdm_server_vblank_info *vblank_info, unsigned int sequence, - unsigned int tv_sec, unsigned int tv_usec) +_tdm_server_send_done(tdm_server_wait_info *wait_info, tdm_error error, + unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec) { - tdm_server_vblank_info *found; - tdm_server_client_info *client_info; - unsigned long vtime = (tv_sec * 1000000) + tv_usec; - unsigned long curr = tdm_helper_get_time_in_micros(); + tdm_server_wait_info *found; + tdm_server_vblank_info *vblank_info; if (!keep_private_server) return; - LIST_FIND_ITEM(vblank_info, &keep_private_server->vblank_list, - tdm_server_vblank_info, link, found); + LIST_FIND_ITEM(wait_info, &keep_private_server->wait_list, + tdm_server_wait_info, link, found); if (!found) { - TDM_DBG("vblank_info(%p) is destroyed", vblank_info); + TDM_DBG("wait_info(%p) is destroyed", wait_info); return; } - client_info = vblank_info->client_info; - client_info->last_tv_sec = tv_sec; - client_info->last_tv_usec = tv_usec; - - TDM_DBG("wl_tdm_vblank@%d done. tv(%lu) curr(%lu)", - wl_resource_get_id(vblank_info->resource), vtime, curr); + TDM_DBG("req_id(%d) done", wait_info->req_id); - if (tdm_debug_server) { - if (curr - vtime > 1000) /* 1ms */ - TDM_WRN("delay: %d us", (int)(curr - vtime)); - } + vblank_info = wait_info->vblank_info; + wl_tdm_vblank_send_done(vblank_info->resource, wait_info->req_id, + sequence, tv_sec, tv_usec, error); + destroy_wait(wait_info); +} - wl_tdm_vblank_send_done(vblank_info->resource, sequence, tv_sec, tv_usec); - wl_resource_destroy(vblank_info->resource); +static void +_tdm_server_cb_vblank(tdm_vblank *vblank, tdm_error error, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, void *user_data) +{ + _tdm_server_send_done((tdm_server_wait_info*)user_data, error, sequence, tv_sec, tv_usec); } static void -_tdm_server_cb_output_vblank(tdm_output *output, unsigned int sequence, - unsigned int tv_sec, unsigned int tv_usec, - void *user_data) +_tdm_server_cb_output_change(tdm_output *output, tdm_output_change_type type, + tdm_value value, void *user_data) { - tdm_server_vblank_info *vblank_info = (tdm_server_vblank_info*)user_data; + tdm_server_output_info *output_info = user_data; + + TDM_RETURN_IF_FAIL(output_info != NULL); + + switch (type) { + case TDM_OUTPUT_CHANGE_DPMS: + wl_tdm_output_send_dpms(output_info->resource, value.u32); + break; + case TDM_OUTPUT_CHANGE_CONNECTION: + wl_tdm_output_send_connection(output_info->resource, value.u32); + break; + default: + break; + } +} - _tdm_server_send_done(vblank_info, sequence, tv_sec, tv_usec); +static void +destroy_wait(tdm_server_wait_info *wait_info) +{ + LIST_DEL(&wait_info->link); + free(wait_info); } static void destroy_vblank_callback(struct wl_resource *resource) { tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource); + tdm_server_wait_info *w = NULL, *ww = NULL; + + TDM_RETURN_IF_FAIL(vblank_info != NULL); - if (vblank_info->timer_source) { - tdm_private_server *private_server = vblank_info->client_info->private_server; + if (vblank_info->vblank) + tdm_vblank_destroy(vblank_info->vblank); - tdm_display_lock(private_server->private_loop->dpy); - tdm_event_loop_source_remove(vblank_info->timer_source); - tdm_display_unlock(private_server->private_loop->dpy); + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &keep_private_server->wait_list, link) { + if (w->vblank_info == vblank_info) { + destroy_wait(w); + } } LIST_DEL(&vblank_info->link); @@ -226,213 +203,217 @@ destroy_vblank_callback(struct wl_resource *resource) } static void -_tdm_server_client_cb_destroy(struct wl_client *client, struct wl_resource *resource) +_tdm_server_vblank_cb_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void -_tdm_server_client_cb_wait_vblank(struct wl_client *client, - struct wl_resource *resource, - uint32_t id, const char *name, - int32_t sw_timer, int32_t interval, - uint32_t req_sec, uint32_t req_usec) +_tdm_server_vblank_cb_set_fps(struct wl_client *client, struct wl_resource *resource, uint32_t fps) { - tdm_server_client_info *client_info = wl_resource_get_user_data(resource); - tdm_private_server *private_server = client_info->private_server; - tdm_private_loop *private_loop = private_server->private_loop; - tdm_server_vblank_info *vblank_info; - struct wl_resource *vblank_resource; - tdm_output *found = NULL; - tdm_output_dpms dpms_value = TDM_OUTPUT_DPMS_ON; - tdm_error ret; - const char *model; - - TDM_DBG("The tdm client requests vblank"); + tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource); - if (tdm_debug_server) { - unsigned long curr = tdm_helper_get_time_in_micros(); - unsigned long reqtime = (req_sec * 1000000) + req_usec; - if (curr - reqtime > 1000) /* 1ms */ - TDM_WRN("delay(req): %d us", (int)(curr - reqtime)); - } + tdm_vblank_set_fps(vblank_info->vblank, fps); +} - if (client_info->vblank_output) { - model = NULL; - ret = tdm_output_get_model_info(client_info->vblank_output, NULL, &model, NULL); - if (model && !strncmp(model, name, TDM_NAME_LEN)) - found = client_info->vblank_output; - } +static void +_tdm_server_vblank_cb_set_offset(struct wl_client *client, struct wl_resource *resource, int32_t offset) +{ + tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource); - if (!strncmp(name, "primary", TDM_NAME_LEN)) - found = tdm_display_get_output(private_loop->dpy, 0, NULL); + tdm_vblank_set_offset(vblank_info->vblank, offset); +} - if (!found) { - int count = 0, i; +static void +_tdm_server_vblank_cb_set_enable_fake(struct wl_client *client, struct wl_resource *resource, uint32_t enable_fake) +{ + tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource); - tdm_display_get_output_count(private_loop->dpy, &count); + tdm_vblank_set_enable_fake(vblank_info->vblank, enable_fake); +} - for (i = 0; i < count; i++) { - tdm_output *output = tdm_display_get_output(private_loop->dpy, i, NULL); - tdm_output_conn_status status; +static void +_tdm_server_vblank_cb_wait_vblank(struct wl_client *client, struct wl_resource *resource, + uint32_t interval, uint32_t req_id, uint32_t req_sec, uint32_t req_usec) +{ + tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource); + tdm_server_output_info *output_info = vblank_info->output_info; + tdm_private_server *private_server = output_info->private_server; + tdm_server_wait_info *wait_info; + tdm_error ret; - ret = tdm_output_get_conn_status(output, &status); - if (ret || status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) - continue; + wait_info = calloc(1, sizeof *wait_info); + if (!wait_info) { + TDM_ERR("alloc failed"); + ret = TDM_ERROR_OUT_OF_MEMORY; + goto wait_failed; + } - model = NULL; - ret = tdm_output_get_model_info(output, NULL, &model, NULL); - if (ret || !model) - continue; + LIST_ADDTAIL(&wait_info->link, &private_server->wait_list); + wait_info->vblank_info = vblank_info; + wait_info->req_id = req_id; - if (strncmp(model, name, TDM_NAME_LEN)) - continue; + TDM_DBG("req_id(%d) wait", req_id); - found = output; - break; - } - } + ret = tdm_vblank_wait(vblank_info->vblank, req_sec, req_usec, interval, _tdm_server_cb_vblank, wait_info); + TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, wait_failed); - if (!found) { - wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_INVALID_NAME, - "There is no '%s' output", name); - TDM_ERR("There is no '%s' output", name); - return; - } + return; +wait_failed: + wl_tdm_vblank_send_done(vblank_info->resource, req_id, 0, 0, 0, ret); +} - if (client_info->vblank_output != found) { - const tdm_output_mode *mode = NULL; +static const struct wl_tdm_vblank_interface tdm_vblank_implementation = { + _tdm_server_vblank_cb_destroy, + _tdm_server_vblank_cb_set_fps, + _tdm_server_vblank_cb_set_offset, + _tdm_server_vblank_cb_set_enable_fake, + _tdm_server_vblank_cb_wait_vblank, +}; - client_info->vblank_output = found; +static void +_tdm_server_output_cb_destroy(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} - tdm_output_get_mode(client_info->vblank_output, &mode); - if (!mode || mode->vrefresh <= 0) { - wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_OPERATION_FAILED, - "couldn't get mode of %s", name); - TDM_ERR("couldn't get mode of %s", name); - return; - } +static void +_tdm_server_output_cb_create_vblank(struct wl_client *client, struct wl_resource *resource, uint32_t id) +{ + tdm_server_output_info *output_info = wl_resource_get_user_data(resource); + tdm_private_server *private_server = output_info->private_server; + tdm_private_loop *private_loop = private_server->private_loop; + struct wl_resource *vblank_resource; + tdm_vblank *vblank; + tdm_server_vblank_info *vblank_info; - client_info->vblank_gap = (double)1000000 / mode->vrefresh; - TDM_INFO("vblank_gap(%.6lf)", client_info->vblank_gap); + vblank_resource = + wl_resource_create(client, &wl_tdm_vblank_interface, + wl_resource_get_version(resource), id); + if (!vblank_resource) { + wl_resource_post_no_memory(resource); + TDM_ERR("wl_resource_create failed"); + return; } - tdm_output_get_dpms(found, &dpms_value); - - if (dpms_value != TDM_OUTPUT_DPMS_ON && !sw_timer) { - wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_DPMS_OFF, - "dpms '%s'", tdm_dpms_str(dpms_value)); - TDM_ERR("dpms '%s'", tdm_dpms_str(dpms_value)); + vblank = tdm_vblank_create(private_loop->dpy, output_info->output, NULL); + if (!vblank) { + wl_resource_post_no_memory(resource); + wl_resource_destroy(vblank_resource); + TDM_ERR("tdm_vblank_create failed"); return; } - vblank_info = calloc(1, sizeof * vblank_info); + vblank_info = calloc(1, sizeof *vblank_info); if (!vblank_info) { wl_resource_post_no_memory(resource); + wl_resource_destroy(vblank_resource); + tdm_vblank_destroy(vblank); TDM_ERR("alloc failed"); return; } - TDM_DBG("wl_tdm_vblank@%d output(%s) interval(%d)", id, name, interval); - - vblank_resource = - wl_resource_create(client, &wl_tdm_vblank_interface, - wl_resource_get_version(resource), id); - if (!vblank_resource) { - wl_resource_post_no_memory(resource); - TDM_ERR("wl_resource_create failed"); - goto free_info; - } - + LIST_ADDTAIL(&vblank_info->link, &output_info->vblank_list); + vblank_info->output_info = output_info; vblank_info->resource = vblank_resource; - vblank_info->client_info = client_info; - vblank_info->req_sec = req_sec; - vblank_info->req_usec = req_usec; - - if (dpms_value == TDM_OUTPUT_DPMS_ON) { - ret = tdm_output_wait_vblank(found, interval, 0, - _tdm_server_cb_output_vblank, vblank_info); - if (ret != TDM_ERROR_NONE) { - wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_OPERATION_FAILED, - "couldn't wait vblank for %s", name); - TDM_ERR("couldn't wait vblank for %s", name); - goto destroy_resource; - } - } else if (sw_timer) { - ret = _tdm_server_update_timer(vblank_info, interval); - if (ret != TDM_ERROR_NONE) { - wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_OPERATION_FAILED, - "couldn't update timer for %s", name); - TDM_ERR("couldn't update timer for %s", name); - goto destroy_resource; - } - } else { - wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_OPERATION_FAILED, - "bad implementation"); - TDM_NEVER_GET_HERE(); - goto destroy_resource; - } + vblank_info->vblank = vblank; - wl_resource_set_implementation(vblank_resource, NULL, vblank_info, - destroy_vblank_callback); + wl_resource_set_implementation(vblank_resource, &tdm_vblank_implementation, + vblank_info, destroy_vblank_callback); - LIST_ADDTAIL(&vblank_info->link, &private_server->vblank_list); return; -destroy_resource: - wl_resource_destroy(vblank_resource); -free_info: - free(vblank_info); } -static const struct wl_tdm_client_interface tdm_client_implementation = { - _tdm_server_client_cb_destroy, - _tdm_server_client_cb_wait_vblank, +static const struct wl_tdm_output_interface tdm_output_implementation = { + _tdm_server_output_cb_destroy, + _tdm_server_output_cb_create_vblank, }; static void -destroy_client_callback(struct wl_resource *resource) +destroy_output_callback(struct wl_resource *resource) { - tdm_server_client_info *client_info = wl_resource_get_user_data(resource); - LIST_DEL(&client_info->link); - free(client_info); + tdm_server_output_info *output_info = wl_resource_get_user_data(resource); + tdm_server_vblank_info *v = NULL, *vv = NULL; + + TDM_RETURN_IF_FAIL(output_info != NULL); + + tdm_output_remove_change_handler(output_info->output, + _tdm_server_cb_output_change, output_info); + + LIST_FOR_EACH_ENTRY_SAFE(v, vv, &output_info->vblank_list, link) { + wl_resource_destroy(v->resource); + } + + LIST_DEL(&output_info->link); + free(output_info); } static void -_tdm_server_cb_create_client(struct wl_client *client, - struct wl_resource *resource, uint32_t id) +_tdm_server_cb_create_output(struct wl_client *client, struct wl_resource *resource, + const char *name, uint32_t id) { tdm_private_server *private_server = wl_resource_get_user_data(resource); - tdm_server_client_info *client_info; - struct wl_resource *client_resource; + tdm_server_output_info *output_info; + struct wl_resource *output_resource = NULL; + tdm_output *output; + const tdm_output_mode *mode = NULL; + tdm_output_dpms dpms_value; + tdm_output_conn_status status; + + output = _tdm_server_find_output(private_server, name); + if (!output) { + TDM_ERR("There is no '%s' output", name); + wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, + "There is no '%s' output", name); + return; + } - client_info = calloc(1, sizeof * client_info); - if (!client_info) { - wl_resource_post_no_memory(resource); - TDM_ERR("alloc failed"); + tdm_output_get_mode(output, &mode); + if (!mode) { + TDM_ERR("no mode for '%s' output", name); + wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, + "no mode for '%s' output", name); return; } - client_resource = - wl_resource_create(client, &wl_tdm_client_interface, + tdm_output_get_dpms(output, &dpms_value); + tdm_output_get_conn_status(output, &status); + + output_resource = + wl_resource_create(client, &wl_tdm_output_interface, wl_resource_get_version(resource), id); - if (!client_resource) { + if (!output_resource) { wl_resource_post_no_memory(resource); - free(client_info); TDM_ERR("wl_resource_create failed"); return; } - client_info->private_server = private_server; - client_info->resource = client_resource; + output_info = calloc(1, sizeof * output_info); + if (!output_info) { + wl_resource_post_no_memory(resource); + wl_resource_destroy(output_resource); + TDM_ERR("alloc failed"); + return; + } + + LIST_ADDTAIL(&output_info->link, &private_server->output_list); + output_info->private_server = private_server; + output_info->resource = output_resource; + output_info->output = output; + LIST_INITHEAD(&output_info->vblank_list); - wl_resource_set_implementation(client_resource, &tdm_client_implementation, - client_info, destroy_client_callback); + tdm_output_add_change_handler(output, _tdm_server_cb_output_change, output_info); - LIST_ADDTAIL(&client_info->link, &private_server->client_list); + wl_resource_set_implementation(output_resource, &tdm_output_implementation, + output_info, destroy_output_callback); + + wl_tdm_output_send_mode(output_resource, mode->hdisplay, mode->vdisplay, mode->vrefresh); + wl_tdm_output_send_dpms(output_resource, dpms_value); + wl_tdm_output_send_connection(output_resource, status); } static const struct wl_tdm_interface tdm_implementation = { - _tdm_server_cb_create_client, + _tdm_server_cb_create_output, }; static void @@ -456,11 +437,6 @@ INTERN tdm_error tdm_server_init(tdm_private_loop *private_loop) { tdm_private_server *private_server; - const char *debug; - - debug = getenv("TDM_DEBUG_SERVER"); - if (debug && (strstr(debug, "1"))) - tdm_debug_server = 1; if (private_loop->private_server) return TDM_ERROR_NONE; @@ -479,8 +455,8 @@ tdm_server_init(tdm_private_loop *private_loop) return TDM_ERROR_OUT_OF_MEMORY; } - LIST_INITHEAD(&private_server->client_list); - LIST_INITHEAD(&private_server->vblank_list); + LIST_INITHEAD(&private_server->output_list); + LIST_INITHEAD(&private_server->wait_list); if (!wl_global_create(private_loop->wl_display, &wl_tdm_interface, 1, private_server, _tdm_server_bind)) { @@ -499,8 +475,8 @@ tdm_server_init(tdm_private_loop *private_loop) INTERN void tdm_server_deinit(tdm_private_loop *private_loop) { - tdm_server_vblank_info *v = NULL, *vv = NULL; - tdm_server_client_info *c = NULL, *cc = NULL; + tdm_server_output_info *o = NULL, *oo = NULL; + tdm_server_wait_info *w = NULL, *ww = NULL; tdm_private_server *private_server; if (!private_loop->private_server) @@ -508,12 +484,12 @@ tdm_server_deinit(tdm_private_loop *private_loop) private_server = private_loop->private_server; - LIST_FOR_EACH_ENTRY_SAFE(v, vv, &private_server->vblank_list, link) { - wl_resource_destroy(v->resource); + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_server->wait_list, link) { + destroy_wait(w); } - LIST_FOR_EACH_ENTRY_SAFE(c, cc, &private_server->client_list, link) { - wl_resource_destroy(c->resource); + LIST_FOR_EACH_ENTRY_SAFE(o, oo, &private_server->output_list, link) { + wl_resource_destroy(o->resource); } free(private_server); diff --git a/src/tdm_vblank.c b/src/tdm_vblank.c new file mode 100644 index 0000000..f5ec5f9 --- /dev/null +++ b/src/tdm_vblank.c @@ -0,0 +1,879 @@ +/************************************************************************** + * + * libtdm + * + * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Contact: Eunchul Kim , + * JinYoung Jeon , + * Taeheon Kim , + * YoungJun Cho , + * SooChan Lim , + * Boram Park + * + * 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.h" +#include "tdm_private.h" +#include "tdm_list.h" + +/* CAUTION: + * - tdm vblank doesn't care about thread things. + * - DO NOT use the TDM internal functions here. + * However, the internal function which does lock/unlock the mutex of + * private_display in itself can be called. + * - DO NOT use the tdm_private_display structure here. + */ + +/* TDM vblank + * - aligned by HW vblank + * - use a tdm_event_loop_source object only. + */ + +#define VER(fmt,arg...) TDM_ERR("[%p] "fmt, private_vblank, ##arg) +#define VWR(fmt,arg...) TDM_WRN("[%p] "fmt, private_vblank, ##arg) +#define VIN(fmt,arg...) TDM_INFO("[%p] "fmt, private_vblank, ##arg) +#define VDB(fmt,arg...) TDM_DBG("[%p] "fmt, private_vblank, ##arg) + +typedef struct _tdm_vblank_wait_info tdm_vblank_wait_info; + +typedef struct _tdm_private_vblank { + struct list_head link; + + tdm_display *dpy; + tdm_output *output; + tdm_output_dpms dpms; + unsigned int vrefresh; + + unsigned int check_HW_or_SW; + unsigned int fps; + int offset; + unsigned int enable_fake; + + double vblank_gap; + unsigned int last_seq; + unsigned int last_tv_sec; + unsigned int last_tv_usec; + + /* for HW */ + double HW_vblank_gap; + unsigned int HW_enable; + unsigned int HW_quotient; + struct list_head HW_wait_list; + + /* for SW */ + tdm_event_loop_source *SW_timer; + struct list_head SW_pending_wait_list; + struct list_head SW_wait_list; +#if 0 + tdm_vblank_wait_info *SW_align_wait; + double SW_align_offset; + unsigned int SW_align_sec; + unsigned int SW_align_usec; +#endif +} tdm_private_vblank; + +struct _tdm_vblank_wait_info { + struct list_head link; + struct list_head valid_link; + + unsigned int stamp; + + unsigned int req_sec; + unsigned int req_usec; + unsigned int interval; + + tdm_vblank_handler func; + void *user_data; + tdm_private_vblank *private_vblank; + + /* target_sec can be 0 when last_tv_sec is 0 because we can't calculate + * target_sec without last_tv_sec. So we have to call tdm_output_wait_vblank + * to fill last_tv_sec at the first time. + */ + unsigned int target_sec; + unsigned int target_usec; + unsigned int target_seq; + int target_hw_interval; +}; + +static struct list_head vblank_list; +static struct list_head valid_wait_list; +static unsigned int vblank_list_inited; +static unsigned int stamp = 0; + +static tdm_error _tdm_vblank_wait_SW(tdm_vblank_wait_info *wait_info); +#if 0 +static void _tdm_vblank_sw_timer_align(tdm_private_vblank *private_vblank); +#endif + +#if 0 +static void +_print_list(struct list_head *list) +{ + tdm_vblank_wait_info *w = NULL; + + LIST_FOR_EACH_ENTRY(w, list, link) { + printf(" %d", w->interval); + } + printf("\n"); +} +#endif + +static inline unsigned int +_tdm_vblank_check_valid(tdm_vblank_wait_info *wait_info) +{ + tdm_vblank_wait_info *w = NULL; + + if (!wait_info) + return 0; + + LIST_FOR_EACH_ENTRY(w, &valid_wait_list, valid_link) { + if (w->stamp == wait_info->stamp) + return 1; + } + + return 0; +} + +static inline unsigned int +_tdm_vblank_find_wait(tdm_vblank_wait_info *wait_info, struct list_head *list) +{ + tdm_vblank_wait_info *w = NULL; + + if (!wait_info) + return 0; + + LIST_FOR_EACH_ENTRY(w, list, link) { + if (w->stamp == wait_info->stamp) + return 1; + } + + return 0; +} + +static inline void +_tdm_vblank_insert_wait(tdm_vblank_wait_info *wait_info, struct list_head *list) +{ + tdm_vblank_wait_info *w = NULL; + + LIST_FOR_EACH_ENTRY_REV(w, list, link) { + /* If last_tv_sec == 0, we can't calculate target_sec. */ + if (wait_info->target_sec == 0) { + if (w->interval > wait_info->interval) + continue; + list = &w->link; + break; + } else { + if (w->target_sec > wait_info->target_sec) + continue; + if (w->target_usec > wait_info->target_usec) + continue; + list = &w->link; + } + break; + } + + LIST_ADDTAIL(&wait_info->link, list->next); +} + +static void +_tdm_vblank_change_to_SW(tdm_private_vblank *private_vblank) +{ + tdm_vblank_wait_info *w = NULL, *ww = NULL; + + VIN("Change to SW"); + + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->HW_wait_list, link) { + LIST_DEL(&w->link); + _tdm_vblank_wait_SW(w); + } +} + +static void +_tdm_vblank_free_HW_wait(tdm_private_vblank *private_vblank, tdm_error error, unsigned int call_cb) +{ + tdm_vblank_wait_info *w = NULL, *ww = NULL; + + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->HW_wait_list, link) { + LIST_DEL(&w->link); + LIST_DEL(&w->valid_link); + + if (call_cb && w->func) + w->func(private_vblank, error, 0, 0, 0, w->user_data); + + free(w); + } +} + +static void +_tdm_vblank_cb_output_change(tdm_output *output, tdm_output_change_type type, + tdm_value value, void *user_data) +{ + tdm_private_vblank *private_vblank = user_data; + + TDM_RETURN_IF_FAIL(private_vblank != NULL); + + switch (type) { + case TDM_OUTPUT_CHANGE_DPMS: + if (private_vblank->dpms == value.u32) + break; + VIN("dpms %s", tdm_dpms_str(value.u32)); + private_vblank->dpms = value.u32; + private_vblank->check_HW_or_SW = 1; + if (private_vblank->dpms != TDM_OUTPUT_DPMS_ON) { +#if 0 + if (private_vblank->SW_align_wait) { + LIST_DEL(&private_vblank->SW_align_wait->valid_link); + free(private_vblank->SW_align_wait); + private_vblank->SW_align_wait = NULL; + } +#endif + + if (private_vblank->enable_fake) + _tdm_vblank_change_to_SW(private_vblank); + else + _tdm_vblank_free_HW_wait(private_vblank, TDM_ERROR_DPMS_OFF, 1); + } + break; + case TDM_OUTPUT_CHANGE_CONNECTION: + VIN("output %s", tdm_status_str(value.u32)); + if (value.u32 == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) + _tdm_vblank_free_HW_wait(private_vblank, 0, 0); + break; + default: + break; + } +} + +tdm_vblank* +tdm_vblank_create(tdm_display *dpy, tdm_output *output, tdm_error *error) +{ + tdm_private_vblank *private_vblank; + const tdm_output_mode *mode = NULL; + tdm_output_dpms dpms = TDM_OUTPUT_DPMS_ON; + tdm_error ret; + + TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(dpy != NULL, TDM_ERROR_INVALID_PARAMETER, NULL); + TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(output != NULL, TDM_ERROR_INVALID_PARAMETER, NULL); + + if (error) + *error = TDM_ERROR_NONE; + + if (!vblank_list_inited) { + LIST_INITHEAD(&vblank_list); + LIST_INITHEAD(&valid_wait_list); + vblank_list_inited = 1; + } + + tdm_output_get_mode(output, &mode); + if (!mode) { + if (error) + *error = TDM_ERROR_OPERATION_FAILED; + TDM_ERR("no mode"); + return NULL; + } + + tdm_output_get_dpms(output, &dpms); + + private_vblank = calloc(1, sizeof *private_vblank); + if (!private_vblank) { + if (error) + *error = TDM_ERROR_OUT_OF_MEMORY; + VER("alloc failed"); + return NULL; + } + + tdm_output_add_change_handler(output, _tdm_vblank_cb_output_change, private_vblank); + + private_vblank->dpy = dpy; + private_vblank->output = output; + private_vblank->dpms = dpms; + private_vblank->vrefresh = mode->vrefresh; + private_vblank->HW_vblank_gap = (double)1000000 / private_vblank->vrefresh; + + private_vblank->check_HW_or_SW = 1; + private_vblank->fps = mode->vrefresh; + + LIST_INITHEAD(&private_vblank->HW_wait_list); + + LIST_INITHEAD(&private_vblank->SW_pending_wait_list); + LIST_INITHEAD(&private_vblank->SW_wait_list); + + LIST_ADD(&private_vblank->link, &vblank_list); + + VDB("created. vrefresh(%d) dpms(%d)", + private_vblank->vrefresh, private_vblank->dpms); + + return (tdm_vblank*)private_vblank; +} + +void +tdm_vblank_destroy(tdm_vblank *vblank) +{ + tdm_private_vblank *private_vblank = vblank; + tdm_vblank_wait_info *w = NULL, *ww = NULL; + + if (!private_vblank) + return; + + LIST_DEL(&private_vblank->link); + +#if 0 + if (private_vblank->SW_align_wait) { + LIST_DEL(&private_vblank->SW_align_wait->valid_link); + free(private_vblank->SW_align_wait); + } +#endif + + if (private_vblank->SW_timer) { + if (tdm_display_lock(private_vblank->dpy) == TDM_ERROR_NONE) { + tdm_event_loop_source_remove(private_vblank->SW_timer); + tdm_display_unlock(private_vblank->dpy); + } + } + + tdm_output_remove_change_handler(private_vblank->output, + _tdm_vblank_cb_output_change, private_vblank); + + _tdm_vblank_free_HW_wait(private_vblank, 0, 0); + + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_pending_wait_list, link) { + LIST_DEL(&w->link); + LIST_DEL(&w->valid_link); + free(w); + } + + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_wait_list, link) { + LIST_DEL(&w->link); + LIST_DEL(&w->valid_link); + free(w); + } + + VIN("destroyed"); + + free(private_vblank); +} + +tdm_error +tdm_vblank_set_fps(tdm_vblank *vblank, unsigned int fps) +{ + tdm_private_vblank *private_vblank = vblank; + + TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(fps > 0, TDM_ERROR_INVALID_PARAMETER); + + if (private_vblank->fps == fps) + return TDM_ERROR_NONE; + + private_vblank->fps = fps; + private_vblank->check_HW_or_SW = 1; + + VDB("fps(%d)", private_vblank->fps); + + return TDM_ERROR_NONE; +} + +tdm_error +tdm_vblank_set_offset(tdm_vblank *vblank, int offset) +{ + tdm_private_vblank *private_vblank = vblank; + + TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_INVALID_PARAMETER); + + if (private_vblank->offset == offset) + return TDM_ERROR_NONE; + + private_vblank->offset = offset; + private_vblank->check_HW_or_SW = 1; + + VDB("offset(%d)", private_vblank->offset); + + return TDM_ERROR_NONE; +} + +tdm_error +tdm_vblank_set_enable_fake(tdm_vblank *vblank, unsigned int enable_fake) +{ + tdm_private_vblank *private_vblank = vblank; + + TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_INVALID_PARAMETER); + + if (private_vblank->enable_fake == enable_fake) + return TDM_ERROR_NONE; + + private_vblank->enable_fake = enable_fake; + + VDB("enable_fake(%d)", private_vblank->enable_fake); + + return TDM_ERROR_NONE; +} + +static void +_tdm_vblank_cb_vblank_HW(tdm_output *output, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, + void *user_data) +{ + tdm_vblank_wait_info *wait_info = user_data; + tdm_private_vblank *private_vblank; + + if (!_tdm_vblank_check_valid(wait_info)) { + TDM_DBG("can't find wait(%p) from valid_wait_list", wait_info); + return; + } + + private_vblank = wait_info->private_vblank; + TDM_RETURN_IF_FAIL(private_vblank != NULL); + + if (!_tdm_vblank_find_wait(wait_info, &private_vblank->HW_wait_list)) { + VDB("can't find wait(%p)", wait_info); + return; + } + + LIST_DEL(&wait_info->link); + LIST_DEL(&wait_info->valid_link); + + private_vblank->last_seq = wait_info->target_seq; + private_vblank->last_tv_sec = tv_sec; + private_vblank->last_tv_usec = tv_usec; + + if (wait_info->func) + wait_info->func(private_vblank, TDM_ERROR_NONE, private_vblank->last_seq, + tv_sec, tv_usec, wait_info->user_data); + + VDB("wait(%p) done", wait_info); + + free(wait_info); +} + +static tdm_error +_tdm_vblank_wait_HW(tdm_vblank_wait_info *wait_info) +{ + tdm_private_vblank *private_vblank = wait_info->private_vblank; + tdm_error ret; + + TDM_RETURN_VAL_IF_FAIL(wait_info->target_hw_interval > 0, TDM_ERROR_OPERATION_FAILED); + + _tdm_vblank_insert_wait(wait_info, &private_vblank->HW_wait_list); + + ret = tdm_output_wait_vblank(private_vblank->output, wait_info->target_hw_interval, 0, + _tdm_vblank_cb_vblank_HW, wait_info); + + if (ret != TDM_ERROR_NONE) { + VER("wait(%p) failed", wait_info); + LIST_DEL(&wait_info->link); + return ret; + } + + VDB("wait(%p) waiting", wait_info); + + return TDM_ERROR_NONE; +} + +static tdm_error +_tdm_vblank_cb_vblank_SW(void *user_data) +{ + tdm_private_vblank *private_vblank = user_data; + tdm_vblank_wait_info *first_wait_info = NULL, *w = NULL, *ww = NULL; + + TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_OPERATION_FAILED); + + first_wait_info = container_of(private_vblank->SW_wait_list.next, first_wait_info, link); + TDM_RETURN_VAL_IF_FAIL(first_wait_info != NULL, TDM_ERROR_OPERATION_FAILED); + + VDB("wait(%p) done", first_wait_info); + + private_vblank->last_seq = first_wait_info->target_seq; + private_vblank->last_tv_sec = first_wait_info->target_sec; + private_vblank->last_tv_usec = first_wait_info->target_usec; + + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_wait_list, link) { + if (w->target_sec != first_wait_info->target_sec || + w->target_usec != first_wait_info->target_usec) + break; + + LIST_DEL(&w->link); + LIST_DEL(&w->valid_link); + + if (w->func) + w->func(private_vblank, TDM_ERROR_NONE, w->target_seq, + w->target_sec, w->target_usec, + w->user_data); + + free(w); + } + + return TDM_ERROR_NONE; +} + +static void +_tdm_vblank_cb_vblank_SW_first(tdm_output *output, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, + void *user_data) +{ + tdm_vblank_wait_info *wait_info = user_data; + tdm_private_vblank *private_vblank; + tdm_vblank_wait_info *w = NULL, *ww = NULL; + unsigned int min_interval = 0; + unsigned long last; + + if (!_tdm_vblank_check_valid(wait_info)) + return; + + private_vblank = wait_info->private_vblank; + TDM_RETURN_IF_FAIL(private_vblank != NULL); + + w = container_of((&private_vblank->SW_pending_wait_list)->next, w, link); + TDM_RETURN_IF_FAIL(w != NULL); + + VDB("wait(%p) done", w); + + min_interval = w->interval; + + last = (unsigned long)tv_sec * 1000000 + tv_usec; + last -= private_vblank->offset * 1000; + + private_vblank->last_seq = min_interval; + private_vblank->last_tv_sec = last / 1000000; + private_vblank->last_tv_usec = last % 1000000; + + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_pending_wait_list, link) { + if (w->interval == min_interval) { + LIST_DEL(&w->link); + LIST_DEL(&w->valid_link); + + if (w->func) + w->func(private_vblank, TDM_ERROR_NONE, private_vblank->last_seq, + tv_sec, tv_usec, w->user_data); + free(w); + } else { + LIST_DEL(&w->link); + w->interval -= min_interval; + _tdm_vblank_wait_SW(w); + } + } +} + +static tdm_error +_tdm_vblank_sw_timer_update(tdm_private_vblank *private_vblank) +{ + tdm_vblank_wait_info *first_wait_info = NULL; + unsigned long curr, target; + int ms_delay; + tdm_error ret; + + first_wait_info = container_of(private_vblank->SW_wait_list.next, first_wait_info, link); + curr = tdm_helper_get_time_in_micros(); + target = first_wait_info->target_sec * 1000000 + first_wait_info->target_usec; + + if (target < curr) + ms_delay = 1; + else + ms_delay = ceil((double)(target - curr) / 1000); + + if (ms_delay < 1) + ms_delay = 1; + + VDB("wait(%p) curr(%4lu) target(%4lu) ms_delay(%d)", + first_wait_info, curr, target, ms_delay); + + ret = tdm_display_lock(private_vblank->dpy); + TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret); + + if (!private_vblank->SW_timer) { + private_vblank->SW_timer = + tdm_event_loop_add_timer_handler(private_vblank->dpy, + _tdm_vblank_cb_vblank_SW, + private_vblank, + &ret); + if (!private_vblank->SW_timer) { + tdm_display_unlock(private_vblank->dpy); + VER("couldn't add timer"); + return ret; + } + VIN("Use SW vblank"); + } + + ret = tdm_event_loop_source_timer_update(private_vblank->SW_timer, ms_delay); + if (ret != TDM_ERROR_NONE) { + tdm_display_unlock(private_vblank->dpy); + VER("couldn't update timer"); + return ret; + } + + tdm_display_unlock(private_vblank->dpy); + + return TDM_ERROR_NONE; +} + +#if 0 +static void +_tdm_vblank_cb_vblank_align(tdm_output *output, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, + void *user_data) +{ + tdm_vblank_wait_info *align_info = user_data; + tdm_private_vblank *private_vblank; + unsigned int diff_sec, diff_usec; + + if (!_tdm_vblank_check_valid(align_info)) + return; + + private_vblank = align_info->private_vblank; + TDM_RETURN_IF_FAIL(private_vblank != NULL); + + private_vblank->SW_align_wait = NULL; + private_vblank->SW_align_sec = tv_sec; + private_vblank->SW_align_usec = tv_usec; + + LIST_DEL(&align_info->valid_link); + + if (tv_usec > align_info->req_usec) { + diff_usec = tv_usec - align_info->req_usec; + diff_sec = tv_sec - align_info->req_sec; + } else { + diff_usec = 1000000 + tv_usec - align_info->req_usec; + diff_sec = tv_sec - align_info->req_sec - 1; + } + + private_vblank->SW_align_offset = (double)(1000000 - diff_sec * 1000000 - diff_usec) / private_vblank->vrefresh; + + free(align_info); + + /* align vblank continously only if non HW and DPMS on */ + if (!private_vblank->HW_enable && private_vblank->dpms == TDM_OUTPUT_DPMS_ON) + _tdm_vblank_sw_timer_align(private_vblank); +} + +static void +_tdm_vblank_sw_timer_align(tdm_private_vblank *private_vblank) +{ + tdm_vblank_wait_info *align_info; + unsigned long curr; + tdm_error ret; + + if (private_vblank->SW_align_wait) + return; + + TDM_RETURN_IF_FAIL(private_vblank->dpms == TDM_OUTPUT_DPMS_ON); + + align_info = calloc(1, sizeof *align_info); + if (!align_info) { + VER("alloc failed"); + return; + } + + LIST_ADDTAIL(&align_info->valid_link, &valid_wait_list); + align_info->stamp = ++stamp; + align_info->private_vblank = private_vblank; + + curr = tdm_helper_get_time_in_micros(); + align_info->req_sec = curr / 1000000; + align_info->req_usec = curr % 1000000; + + ret = tdm_output_wait_vblank(private_vblank->output, private_vblank->vrefresh, 0, + _tdm_vblank_cb_vblank_align, align_info); + if (ret != TDM_ERROR_NONE) { + LIST_DEL(&align_info->valid_link); + free(align_info); + return; + } + + private_vblank->SW_align_wait = align_info; +} +#endif + +static tdm_error +_tdm_vblank_wait_SW(tdm_vblank_wait_info *wait_info) +{ + tdm_private_vblank *private_vblank = wait_info->private_vblank; + tdm_error ret; + + if (private_vblank->last_tv_sec == 0 && private_vblank->dpms == TDM_OUTPUT_DPMS_ON) { + unsigned int do_wait = LIST_IS_EMPTY(&private_vblank->SW_pending_wait_list); + _tdm_vblank_insert_wait(wait_info, &private_vblank->SW_pending_wait_list); + if (do_wait) { + ret = tdm_output_wait_vblank(private_vblank->output, 1, 0, + _tdm_vblank_cb_vblank_SW_first, wait_info); + if (ret != TDM_ERROR_NONE) { + LIST_DEL(&wait_info->link); + return ret; + } + VDB("wait(%p) waiting", wait_info); + } + return TDM_ERROR_NONE; + } + + TDM_RETURN_VAL_IF_FAIL(wait_info->target_sec > 0, TDM_ERROR_OPERATION_FAILED); + + _tdm_vblank_insert_wait(wait_info, &private_vblank->SW_wait_list); + + ret = _tdm_vblank_sw_timer_update(private_vblank); + if (ret != TDM_ERROR_NONE) { + VER("couldn't update sw timer"); + return ret; + } + + return TDM_ERROR_NONE; +} + +static void +_tdm_vblank_calculate_target(tdm_vblank_wait_info *wait_info) +{ + tdm_private_vblank *private_vblank = wait_info->private_vblank; + unsigned long last, prev, req, curr, target; + unsigned int skip = 0; + + curr = tdm_helper_get_time_in_micros(); + + if (!private_vblank->HW_enable) { + if (private_vblank->last_tv_sec == 0) { + /* If last == 0 and DPMS == on, we will use HW vblank to sync with HW vblank. */ + if (private_vblank->dpms == TDM_OUTPUT_DPMS_ON) { + return; + } else { + private_vblank->last_tv_sec = curr / 1000000; + private_vblank->last_tv_usec = curr % 1000000; + } + } + } + + /* last can be 0 when HW enable. But it doesn't matter if HW enable. */ + if (!private_vblank->HW_enable) + TDM_RETURN_IF_FAIL(private_vblank->last_tv_sec != 0); + + last = (unsigned long)private_vblank->last_tv_sec * 1000000 + private_vblank->last_tv_usec; + req = (unsigned long)wait_info->req_sec * 1000000 + wait_info->req_usec; + skip = (unsigned int)((req - last) / private_vblank->vblank_gap); + prev = last + skip * private_vblank->vblank_gap; + + if (private_vblank->last_seq == 0) + skip = 0; + + skip += wait_info->interval; + + if (private_vblank->HW_enable) { + unsigned int hw_skip = (unsigned int)((curr - prev) / private_vblank->HW_vblank_gap); + + wait_info->target_hw_interval = wait_info->interval * private_vblank->HW_quotient; + wait_info->target_hw_interval -= hw_skip; + + if (wait_info->target_hw_interval < 1) + wait_info->target_hw_interval = 1; + + target = prev + wait_info->target_hw_interval * private_vblank->HW_vblank_gap; + } else { + target = prev + (unsigned long)(private_vblank->vblank_gap * wait_info->interval); + + while (target < curr) { + target += (unsigned long)private_vblank->vblank_gap; + skip++; + } + } + + VDB("target_seq(%d) last_seq(%d) skip(%d)", + wait_info->target_seq, private_vblank->last_seq, skip); + +#if 0 + target -= (private_vblank->SW_align_offset * skip * private_vblank->HW_quotient); +#endif + + wait_info->target_seq = private_vblank->last_seq + skip; + wait_info->target_sec = target / 1000000; + wait_info->target_usec = target % 1000000; + + VDB("wait(%p) last(%4lu) req(%4lu) prev(%4lu) curr(%4lu) skip(%d) hw_interval(%d) target(%4lu,%4lu)", + wait_info, last, req - last, prev - last, curr - last, + skip, wait_info->target_hw_interval, target, target - last); +} + +tdm_error +tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec, + unsigned int interval, tdm_vblank_handler func, void *user_data) +{ + tdm_private_vblank *private_vblank = vblank; + tdm_vblank_wait_info *wait_info; + tdm_error ret; + + TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER); + + if (private_vblank->dpms != TDM_OUTPUT_DPMS_ON && !private_vblank->enable_fake) { + VIN("can't wait a vblank because of DPMS off"); + return TDM_ERROR_DPMS_OFF; + } + +#if 0 + if (!private_vblank->SW_align_wait && private_vblank->dpms == TDM_OUTPUT_DPMS_ON) + _tdm_vblank_sw_timer_align(private_vblank); +#endif + + if (private_vblank->check_HW_or_SW) { + private_vblank->check_HW_or_SW = 0; + private_vblank->vblank_gap = (double)1000000 / private_vblank->fps; + private_vblank->HW_quotient = private_vblank->vrefresh / private_vblank->fps; + + if (private_vblank->dpms == TDM_OUTPUT_DPMS_ON && + !(private_vblank->vrefresh % private_vblank->fps)) { + private_vblank->HW_enable = 1; + VIN("Use HW vblank"); + } else { + private_vblank->HW_enable = 0; + VIN("Use SW vblank"); + } + } + + wait_info = calloc(1, sizeof *wait_info); + if (!wait_info) { + VER("alloc failed"); + return TDM_ERROR_OUT_OF_MEMORY; + } + + LIST_ADDTAIL(&wait_info->valid_link, &valid_wait_list); + wait_info->stamp = ++stamp; + wait_info->req_sec = req_sec; + wait_info->req_usec = req_usec; + wait_info->interval = interval; + wait_info->func = func; + wait_info->user_data = user_data; + wait_info->private_vblank = private_vblank; + + _tdm_vblank_calculate_target(wait_info); + + if (private_vblank->HW_enable) + ret = _tdm_vblank_wait_HW(wait_info); + else + ret = _tdm_vblank_wait_SW(wait_info); + + if (ret != TDM_ERROR_NONE) { + LIST_DEL(&wait_info->valid_link); + free(wait_info); + return ret; + } + + return TDM_ERROR_NONE; +} diff --git a/tools/Makefile.am b/tools/Makefile.am index 4863833..05edfb5 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -8,7 +8,8 @@ tdm_test_client_LDFLAGS = ${LDFLAGS} tdm_test_client_CFLAGS = \ $(TDM_CFLAGS) \ -I$(top_srcdir)/include \ - -I$(top_srcdir)/client + -I$(top_srcdir)/client \ + -I$(top_srcdir)/src tdm_test_client_LDADD = \ $(TDM_LIBS) \ $(top_builddir)/client/libtdm-client.la diff --git a/tools/tdm_test_client.c b/tools/tdm_test_client.c index 7268628..2b61c03 100644 --- a/tools/tdm_test_client.c +++ b/tools/tdm_test_client.c @@ -34,72 +34,303 @@ **************************************************************************/ #include +#include #include #include #include #include #include +#include "tdm_macro.h" + #include #include -static int +int tdm_debug; + +typedef struct _tdm_test_client_arg { + char output_name[512]; + int fps; + int sync; + int interval; + int offset; + int enable_fake; +} tdm_test_client_arg; + +typedef struct _tdm_test_client { + tdm_test_client_arg args; + + int do_query; + int do_vblank; + int waiting; + + tdm_client *client; +} tdm_test_client; + +struct typestrings { + int type; + char string[512]; +}; + +struct optstrings { + int type; + char opt[512]; + char desc[512]; + char arg[512]; + char ex[512]; +}; + +enum { + OPT_QRY, + OPT_TST, + OPT_GNR, +}; + +static struct typestrings typestrs[] = { + {OPT_QRY, "Query"}, + {OPT_TST, "Test"}, + {OPT_GNR, "General"}, +}; + +static struct optstrings optstrs[] = { + {OPT_QRY, "qo", "output objects info", "", "primary"}, + {OPT_TST, "v", "vblank test", "[,][@][#][+][*fake]", "primary,0@60#1+0*1"}, +}; + +#define DELIM "!@#^&*+-|," + +static char* +strtostr(char *buf, int len, char *str, char *delim) +{ + char *end; + end = strpbrk(str, delim); + if (end) + len = ((end - str + 1) < len) ? (end - str + 1) : len; + else { + int l = strlen(str); + len = ((l + 1) < len) ? (l + 1) : len; + } + snprintf(buf, len, "%s", str); + return str + len - 1; +} + +static void +usage(char *app_name) +{ + int type_size = sizeof(typestrs) / sizeof(struct typestrings); + int opt_size = sizeof(optstrs) / sizeof(struct optstrings); + int t; + + printf("usage: %s \n\n", app_name); + + for (t = 0; t < type_size; t++) { + int o, f = 1; + + for (o = 0; o < opt_size; o++) + if (optstrs[o].type == typestrs[t].type) { + if (f == 1) + printf(" %s options:\n\n", typestrs[t].string); + printf("\t-%s\t%s\n", optstrs[o].opt, optstrs[o].desc); + printf("\t\t%s\n", optstrs[o].arg); + printf("\t\tex) %s\n", optstrs[o].ex); + f = 0; + } + } + + exit(0); +} + +//"" +static void +parse_arg_qo(tdm_test_client *data, char *p) +{ + strtostr(data->args.output_name, 512, p, DELIM); +} + +//"[,][@][#][+][*fake]" +static void +parse_arg_v(tdm_test_client *data, char *p) +{ + char *end = p; + + end = strtostr(data->args.output_name, 512, p, DELIM); + + if (*end == ',') { + p = end + 1; + data->args.sync = strtol(p, &end, 10); + } + + if (*end == '@') { + p = end + 1; + data->args.fps = strtol(p, &end, 10); + } + + if (*end == '#') { + p = end + 1; + data->args.interval = strtol(p, &end, 10); + } + + if (*end == '+' || *end == '-') { + p = end; + data->args.offset = strtol(p, &end, 10); + } + + if (*end == '*') { + p = end + 1; + data->args.enable_fake= strtol(p, &end, 10); + } +} + +static void +parse_args(tdm_test_client *data, int argc, char *argv[]) +{ + int size = sizeof(optstrs) / sizeof(struct optstrings); + int i, j = 0; + + if (argc < 2) { + usage(argv[0]); + exit(1); + } + + memset(data, 0, sizeof *data); + data->args.interval = 1; + + for (i = 1; i < argc; i++) { + for (j = 0; j < size; j++) { + if (!strncmp(argv[i]+1, "qo", 512)) { + data->do_query = 1; + parse_arg_qo(data, argv[++i]); + break; + } else if (!strncmp(argv[i]+1, "v", 512)) { + data->do_vblank = 1; + parse_arg_v(data, argv[++i]); + break; + } else { + usage(argv[0]); + exit(1); + } + } + } +} + +static unsigned long get_time_in_micros(void) { struct timespec tp; if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) - return (int)(tp.tv_sec * 1000000) + (tp.tv_nsec / 1000L); + return (unsigned long)(tp.tv_sec * 1000000) + (tp.tv_nsec / 1000L); return 0; } static void -_client_vblank_handler(unsigned int sequence, unsigned int tv_sec, - unsigned int tv_usec, void *user_data) +_client_vblank_handler(tdm_client_vblank *vblank, tdm_error error, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, void *user_data) { - int client, vblank; - static int prev = 0; + tdm_test_client *data = user_data; + unsigned long cur, vbl; + static unsigned long p_vbl = 0; + + data->waiting = 0; + + if (error == TDM_ERROR_DPMS_OFF) { + printf("exit: dpms off\n"); + exit(1); + } + + if (error != TDM_ERROR_NONE) { + printf("exit: error(%d)\n", error); + exit(1); + } - client = get_time_in_micros(); - vblank = tv_sec * 1000000 + tv_usec; + cur = get_time_in_micros(); + vbl = (unsigned long)tv_sec * (unsigned long)1000000 + (unsigned long)tv_usec; - if (vblank - prev > 16966 || vblank - prev < 16366) /* +0.3 ~ -0.3 ms */ - printf("vblank : %d us\n", vblank - prev); + printf("vblank : %ld us vbl(%lu)\n", vbl - p_vbl, vbl); - if (client - vblank > 2000) /* 2ms */ - printf("kernel -> tdm-client: %d us\n", client - vblank); + if (cur - vbl > 2000) /* 2ms */ + printf("kernel -> tdm-client: %ld us\n", cur - vbl); - prev = vblank; + if (tdm_debug) { + static unsigned long p_cur = 0; + printf("vblank event interval: %ld %ld\n", + vbl - p_vbl, cur - p_cur); + p_cur = cur; + } + + p_vbl = vbl; } +static char *conn_str[3] = {"disconnected", "connected", "mode_setted"}; +static char *dpms_str[4] = {"on", "standy", "suspend", "off"}; -int -main(int argc, char *argv[]) +static void +_client_output_handler(tdm_client_output *output, tdm_output_change_type type, + tdm_value value, void *user_data) { - tdm_client *client; - tdm_client_error error; + if (type == TDM_OUTPUT_CHANGE_CONNECTION) + printf("output %s.\n", conn_str[value.u32]); + else if (type == TDM_OUTPUT_CHANGE_DPMS) + printf("dpms %s.\n", dpms_str[value.u32]); +} + +static void +do_query(tdm_test_client *data) +{ + tdm_client_output *output; + tdm_output_conn_status status; + tdm_output_dpms dpms; + unsigned int refresh; + tdm_error error; + + output = tdm_client_get_output(data->client, NULL, &error); + if (error != TDM_ERROR_NONE) { + printf("tdm_client_get_output failed\n"); + return; + } + + tdm_client_output_get_conn_status(output, &status); + tdm_client_output_get_dpms(output, &dpms); + tdm_client_output_get_refresh_rate(output, &refresh); + + printf("tdm_output \"%s\"\n", data->args.output_name); + printf("\tstatus : %s\n", conn_str[status]); + printf("\tdpms : %s\n", dpms_str[dpms]); + printf("\trefresh : %d\n", refresh); +} + +static void +do_vblank(tdm_test_client *data) +{ + tdm_client_output *output; + tdm_client_vblank *vblank = NULL; + tdm_error error; int fd = -1; struct pollfd fds; - int sync; - if (argc < 2) { - printf("Usage: %s\n", argv[0]); - printf("\t%s 0 : non-sync\n", argv[0]); - printf("\t%s 1 : sync\n", argv[0]); - exit(1); + output = tdm_client_get_output(data->client, NULL, &error); + if (error != TDM_ERROR_NONE) { + printf("tdm_client_get_output failed\n"); + return; } - sync = atoi(argv[1]); + tdm_client_output_add_change_handler(output, _client_output_handler, NULL); - client = tdm_client_create(&error); - if (error != TDM_CLIENT_ERROR_NONE) { - printf("tdm_client_create failed\n"); - exit(1); + vblank = tdm_client_output_create_vblank(output, &error); + if (error != TDM_ERROR_NONE) { + printf("tdm_client_output_create_vblank failed\n"); + return; } - error = tdm_client_get_fd(client, &fd); - if (error != TDM_CLIENT_ERROR_NONE || fd < 0) { + tdm_client_vblank_set_enable_fake(vblank, data->args.enable_fake); + tdm_client_vblank_set_sync(vblank, data->args.sync); + if (data->args.fps > 0) + tdm_client_vblank_set_fps(vblank, data->args.fps); + tdm_client_vblank_set_offset(vblank, data->args.offset); + + error = tdm_client_get_fd(data->client, &fd); + if (error != TDM_ERROR_NONE || fd < 0) { printf("tdm_client_get_fd failed\n"); goto done; } @@ -111,14 +342,21 @@ main(int argc, char *argv[]) while (1) { int ret; - error = tdm_client_wait_vblank(client, "unknown-0", 1, 1, sync, - _client_vblank_handler, NULL); - if (error != TDM_CLIENT_ERROR_NONE) { - printf("tdm_client_wait_vblank failed\n"); - goto done; + if (!data->waiting) { + error = tdm_client_vblank_wait(vblank, data->args.interval, + _client_vblank_handler, data); + if (error == TDM_ERROR_DPMS_OFF) { + printf("tdm_client_vblank_wait failed (dpms off)\n"); + goto done; + } + if (error != TDM_ERROR_NONE) { + printf("tdm_client_vblank_wait failed (error: %d)\n", error); + goto done; + } + data->waiting = 1; } - if (!sync) { + if (!data->args.sync) { ret = poll(&fds, 1, -1); if (ret < 0) { if (errno == EINTR || errno == EAGAIN) /* normal case */ @@ -129,13 +367,52 @@ main(int argc, char *argv[]) } } - error = tdm_client_handle_events(client); - if (error != TDM_CLIENT_ERROR_NONE) + error = tdm_client_handle_events(data->client); + if (error != TDM_ERROR_NONE) { printf("tdm_client_handle_events failed\n"); + goto done; + } } } done: - tdm_client_destroy(client); + if (vblank) + tdm_client_vblank_destroy(vblank); +} + +static tdm_test_client ttc_data; + +int +main(int argc, char *argv[]) +{ + tdm_test_client *data = &ttc_data; + tdm_error error; + const char *debug; + + debug = getenv("TDM_DEBUG"); + if (debug && (strstr(debug, "1"))) + tdm_debug = 1; + + parse_args(data, argc, argv); + + printf("sync(%d) fps(%d) interval(%d) offset(%d) enable_fake(%d)\n", + data->args.sync, data->args.fps, data->args.interval, + data->args.offset, data->args.enable_fake); + + data->client = tdm_client_create(&error); + if (error != TDM_ERROR_NONE) { + printf("tdm_client_create failed\n"); + goto done; + } + + if (data->do_query) + do_query(data); + if (data->do_vblank) + do_vblank(data); + +done: + if (data->client) + tdm_client_destroy(data->client); + return 0; } -- 2.7.4 From 84496bc63961000e56ffc27cfb49307fe15104d5 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Thu, 30 Jun 2016 21:33:01 +0900 Subject: [PATCH 05/16] enhance doxgen document Change-Id: Ie3e787772461d0c7922627176b8be5b9dc394305 --- client/tdm_client.h | 198 +++++++++++++++++++++++++++++++++++++++++++++- client/tdm_client_types.h | 6 +- doc/Doxyfile | 5 +- protocol/tdm.xml | 19 +++-- 4 files changed, 217 insertions(+), 11 deletions(-) diff --git a/client/tdm_client.h b/client/tdm_client.h index 2f1c64f..c64391e 100644 --- a/client/tdm_client.h +++ b/client/tdm_client.h @@ -117,7 +117,7 @@ tdm_error tdm_client_handle_events(tdm_client *client); /** - * @brief Wait for VBLANK + * @brief @b Deprecated. Wait for VBLANK. * @deprecated * @details After interval vblanks, a client vblank handler will be called. * If 'sw_timer' param is 1 in case of DPMS off, TDM will use the SW timer and @@ -137,47 +137,241 @@ tdm_client_wait_vblank(tdm_client *client, char *name, int sw_timer, int interval, int sync, tdm_client_vblank_handler2 func, void *user_data); - +/** + * @brief Get the client output object which has the given name. + * @details + * The client output name can be @b 'primary' or @b 'default' to get the main output. + * @param[in] client The TDM client object + * @param[in] name The name of the TDM client output object + * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. + * @return A client output object if success. Otherwise, NULL. + */ tdm_client_output* tdm_client_get_output(tdm_client *client, char *name, tdm_error *error); +/** + * @brief Add the client output change handler + * @details The handler will be called when the status of a + * client output object is changed. connection, DPMS, etc. + * @param[in] output The client output object + * @param[in] func The client output change handler + * @param[in] user_data The user data + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @par Example + * @code + * #include //for a client of TDM + * + * static void + * _client_output_handler(tdm_client_output *output, tdm_output_change_type type, + * tdm_value value, void *user_data) + * { + * char *conn_str[3] = {"disconnected", "connected", "mode_setted"}; + * char *dpms_str[4] = {"on", "standy", "suspend", "off"}; + * + * if (type == TDM_OUTPUT_CHANGE_CONNECTION) + * printf("output %s.\n", conn_str[value.u32]); + * else if (type == TDM_OUTPUT_CHANGE_DPMS) + * printf("dpms %s.\n", dpms_str[value.u32]); + * } + * ... + * tdm_client_output_add_change_handler(output, _client_output_handler, NULL); + * ... + * tdm_client_output_remove_change_handler(output, _client_output_handler, NULL); + * + * @endcode + */ tdm_error tdm_client_output_add_change_handler(tdm_client_output *output, tdm_client_output_change_handler func, void *user_data); +/** + * @brief Remove the client output change handler + * @param[in] output The client output object + * @param[in] func The client output change handler + * @param[in] user_data The user data + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ void tdm_client_output_remove_change_handler(tdm_client_output *output, tdm_client_output_change_handler func, void *user_data); +/** + * @brief Get the vertical refresh rate of the given client output + * @param[in] output The client output object + * @param[out] refresh The refresh rate + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ tdm_error tdm_client_output_get_refresh_rate(tdm_client_output *output, unsigned int *refresh); +/** + * @brief Get the connection status of the given client output + * @param[in] output The client output object + * @param[out] status The connection status + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ tdm_error tdm_client_output_get_conn_status(tdm_client_output *output, tdm_output_conn_status *status); +/** + * @brief Get the DPMS value of the given client output + * @param[in] output The client output object + * @param[out] dpms The DPMS value + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ tdm_error tdm_client_output_get_dpms(tdm_client_output *output, tdm_output_dpms *dpms); +/** + * @brief Create the client vblank object of the given client output + * @param[in] output The client output object + * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. + * @return A client vblank object if success. Otherwise, NULL. + */ tdm_client_vblank* tdm_client_output_create_vblank(tdm_client_output *output, tdm_error *error); +/** + * @brief Destroy the client vblank object + * @param[in] vblank The client vblank object + */ void tdm_client_vblank_destroy(tdm_client_vblank *vblank); +/** + * @brief Set the sync value to the client vblank object + * @details + * If sync == 1, the user client vblank handler of #tdm_client_vblank_wait + * will be called before #tdm_client_vblank_wait returns the result. If sync == 0, + * the user client vblank handler of #tdm_client_vblank_wait will be called + * asynchronously after #tdm_client_vblank_wait returns. Default is @b asynchronous. + * @param[in] vblank The client vblank object + * @param[in] sync 0: asynchronous, 1:synchronous + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ tdm_error tdm_client_vblank_set_sync(tdm_client_vblank *vblank, unsigned int sync); +/** + * @brief Set the fps to the client vblank object + * @details Default is the @b vertical @b refresh @b rate of the given client output. + * @param[in] vblank The client vblank object + * @param[in] fps more than 0 + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ tdm_error tdm_client_vblank_set_fps(tdm_client_vblank *vblank, unsigned int fps); +/** + * @brief Set the offset(milli-second) to the client vblank object + * @details Default is @b 0. + * @param[in] vblank The client vblank object + * @param[in] offset_ms the offset(milli-second) + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ tdm_error tdm_client_vblank_set_offset(tdm_client_vblank *vblank, int offset_ms); +/** + * @brief Enable/Disable the fake vblank to the client vblank object + * @details + * If enable_fake == 0, #tdm_client_vblank_wait will return TDM_ERROR_DPMS_OFF + * when DPMS off. Otherwise, #tdm_client_vblank_wait will return TDM_ERROR_NONE + * as success. Once #tdm_client_vblank_wait returns TDM_ERROR_NONE, the user client + * vblank handler(#tdm_client_vblank_handler) SHOULD be called after the given + * interval of #tdm_client_vblank_wait. Default is @b disable. + * @param[in] vblank The client vblank object + * @param[in] enable_fake 1:enable, 0:disable + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ tdm_error tdm_client_vblank_set_enable_fake(tdm_client_vblank *vblank, unsigned int enable_fake); +/** + * @brief Wait for a vblank + * @details + * This function will return TDM_ERROR_DPMS_OFF when DPMS off. However, + * #tdm_client_vblank_wait will return TDM_ERROR_NONE as success if + * #tdm_client_vblank_set_enable_fake sets true. Once #tdm_client_vblank_wait + * returns TDM_ERROR_NONE, the user client vblank handler(#tdm_client_vblank_handler) + * SHOULD be called after the given interval. + * @param[in] vblank The client vblank object + * @param[in] interval The vblank interval + * @param[in] func The user client vblank handler + * @param[in] user_data The user data + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @par Example + * @code + * #include //for a client of TDM + * + * static void + * _client_vblank_handler(tdm_client_vblank *vblank, tdm_error error, unsigned int sequence, + * unsigned int tv_sec, unsigned int tv_usec, void *user_data) + * { + * if (error != TDM_ERROR_NONE) + * //error handling + * } + * + * { + * tdm_client_output *output; + * tdm_client_vblank *vblank; + * tdm_error error; + * struct pollfd fds; + * int fd = -1; + * + * cliet = tdm_client_create(&error); + * if (error != TDM_ERROR_NONE) + * //error handling + * + * output = tdm_client_get_output(client, NULL, &error); + * if (error != TDM_ERROR_NONE) + * //error handling + * + * vblank = tdm_client_output_create_vblank(output, &error); + * if (error != TDM_ERROR_NONE) + * //error handling + * + * tdm_client_vblank_set_enable_fake(vblank, enable_fake); //default: disable + * tdm_client_vblank_set_sync(vblank, 0); //default: async + * tdm_client_vblank_set_fps(vblank, fps); //default: refresh rate of output + * tdm_client_vblank_set_offset(vblank, offset); //default: 0 + * + * error = tdm_client_get_fd(data->client, &fd); + * if (error != TDM_ERROR_NONE) + * //error handling + * + * fds.events = POLLIN; + * fds.fd = fd; + * fds.revents = 0; + * + * while (1) { + * int ret; + * + * error = tdm_client_vblank_wait(vblank, interval, + * _client_vblank_handler, NULL); + * if (error != TDM_ERROR_NONE) + * //error handling + * + * ret = poll(&fds, 1, -1); + * if (ret < 0) { + * if (errno == EINTR || errno == EAGAIN) // normal case + * continue; + * else + * //error handling + * } + * + * error = tdm_client_handle_events(client); + * if (error != TDM_ERROR_NONE) + * //error handling + * } + * + * tdm_client_vblank_destroy(vblank); + * tdm_client_destroy(client); + * } + * @endcode + */ tdm_error tdm_client_vblank_wait(tdm_client_vblank *vblank, unsigned int interval, tdm_client_vblank_handler func, void *user_data); diff --git a/client/tdm_client_types.h b/client/tdm_client_types.h index f6f6355..94807ec 100644 --- a/client/tdm_client_types.h +++ b/client/tdm_client_types.h @@ -87,6 +87,10 @@ typedef void tdm_client_output; */ typedef void tdm_client_vblank; +/** + * @brief The client output handler + * @see #tdm_client_output_add_change_handler, #tdm_client_output_remove_change_handler + */ typedef void (*tdm_client_output_change_handler)(tdm_client_output *output, tdm_output_change_type type, @@ -95,7 +99,7 @@ typedef void /** * @brief The client vblank handler - * @see #tdm_client_wait_vblank + * @see #tdm_client_vblank_wait */ typedef void (*tdm_client_vblank_handler)(tdm_client_vblank *vblank, diff --git a/doc/Doxyfile b/doc/Doxyfile index cd3a6a7..a5738ce 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -104,7 +104,10 @@ INPUT = \ include/tdm_helper.h \ include/tdm_list.h \ include/tdm_log.h \ - include/tdm_types.h + include/tdm_types.h \ + include/tdm_common.h \ + client/tdm_client_types.h \ + client/tdm_client.h INPUT_ENCODING = UTF-8 FILE_PATTERNS = RECURSIVE = NO diff --git a/protocol/tdm.xml b/protocol/tdm.xml index 308fe8c..cb161b5 100644 --- a/protocol/tdm.xml +++ b/protocol/tdm.xml @@ -9,13 +9,17 @@ - + + + When the DPMS of connection status of the output is changed, TDM server + will send wl_tdm_output.mode and wl_tdm_output.dpms event. + @@ -43,9 +47,9 @@ - - - + + + @@ -65,9 +69,10 @@ - When wl_tdm_vblank.wait_vblank is called, TDM server will send the wl_tdm_vblank.done - event which is aligned with HW vblank. Even if DPMS is off, it will send - wl_tdm_vblank.done events by using SW timer. + When wl_tdm_vblank.wait_vblank is called, TDM server will send the + wl_tdm_vblank.done event after interval vblanks. If enable_fake == 1, + TDM server will send the done event always. Otherwise, TDM server will + send the done event with TDM error value. -- 2.7.4 From 7eb26154bfe6d2476f804b823fdca0a6380e6acb Mon Sep 17 00:00:00 2001 From: Boram Park Date: Thu, 30 Jun 2016 21:35:16 +0900 Subject: [PATCH 06/16] version up to 1.3.0 Change-Id: I25b313e3692f2a3137fb6a2c38da48062089a582 --- configure.ac | 2 +- doc/tdm_doc.h | 2 +- packaging/libtdm.spec | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 66a57ec..21d8163 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ AC_PREREQ([2.60]) m4_define([tdm_major_version], [1]) -m4_define([tdm_minor_version], [2]) +m4_define([tdm_minor_version], [3]) m4_define([tdm_micro_version], [0]) m4_define([tdm_version], [tdm_major_version.tdm_minor_version.tdm_micro_version]) diff --git a/doc/tdm_doc.h b/doc/tdm_doc.h index 1abca27..afe08eb 100644 --- a/doc/tdm_doc.h +++ b/doc/tdm_doc.h @@ -39,7 +39,7 @@ /** * @mainpage TDM * @author Boram Park, boram1288.park@samsung.com - * @version 1.2.0 + * @version 1.3.0 * @par Introduction * TDM stands for Tizen Display Manager. It's the display HAL layer for tizen * display server. It offers the frontend APIs(@ref tdm.h) for a frontend user diff --git a/packaging/libtdm.spec b/packaging/libtdm.spec index 52c83b0..2e6bf7c 100644 --- a/packaging/libtdm.spec +++ b/packaging/libtdm.spec @@ -1,5 +1,5 @@ Name: libtdm -Version: 1.2.0 +Version: 1.3.0 Release: 0 Summary: User Library of Tizen Display Manager Group: Development/Libraries -- 2.7.4 From 9bb4229a5ec62035519ea54869efeae4bf88c876 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Thu, 30 Jun 2016 22:14:08 +0900 Subject: [PATCH 07/16] fix syntax error Change-Id: I2f32e699218c0bf28089fd6c9ab33d80f21e5f9a --- tools/tdm_test_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/tdm_test_client.c b/tools/tdm_test_client.c index 2b61c03..f14b6fe 100644 --- a/tools/tdm_test_client.c +++ b/tools/tdm_test_client.c @@ -309,7 +309,7 @@ do_vblank(tdm_test_client *data) int fd = -1; struct pollfd fds; - output = tdm_client_get_output(data->client, NULL, &error); + output = tdm_client_get_output(data->client, data->args.output_name, &error); if (error != TDM_ERROR_NONE) { printf("tdm_client_get_output failed\n"); return; -- 2.7.4 From 7df15762d374d94810105e73f01b193dd295e559 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Tue, 5 Jul 2016 15:05:06 +0900 Subject: [PATCH 08/16] support dlog for debugging Change-Id: If8ebfbbe9158ca8313f9f32351762e3e3a4d4b5a --- Makefile.am | 2 +- client/Makefile.am | 2 +- client/tdm_client.c | 7 --- common/Makefile.am | 12 ++++ common/tdm_log.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 3 +- include/tdm_log.h | 127 ++++++++--------------------------------- src/Makefile.am | 2 +- src/tdm.c | 7 --- tools/tdm_test_client.c | 18 +----- 10 files changed, 187 insertions(+), 140 deletions(-) create mode 100644 common/Makefile.am create mode 100644 common/tdm_log.c diff --git a/Makefile.am b/Makefile.am index 393ba5c..1908008 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = . include protocol client src tools +SUBDIRS = . include protocol common src client tools pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libtdm.pc diff --git a/client/Makefile.am b/client/Makefile.am index b41c0c6..f3b29c8 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -9,7 +9,7 @@ pkgconfig_DATA = libtdm-client.pc libtdm_client_la_LTLIBRARIES = libtdm-client.la libtdm_client_ladir = $(libdir) libtdm_client_la_LDFLAGS = $(LDFLAGS) -libtdm_client_la_LIBADD = $(TDM_CLIENT_LIBS) +libtdm_client_la_LIBADD = $(TDM_CLIENT_LIBS) ../common/libtdm-common.la libtdm_client_la_CFLAGS = \ $(CFLAGS) \ $(WARN_CFLAGS) \ diff --git a/client/tdm_client.c b/client/tdm_client.c index e3679d2..f03ae63 100644 --- a/client/tdm_client.c +++ b/client/tdm_client.c @@ -49,8 +49,6 @@ #include "tdm_list.h" #include "tdm-client-protocol.h" -int tdm_debug; - typedef struct _tdm_private_client_vblank tdm_private_client_vblank; typedef struct _tdm_private_client { @@ -278,11 +276,6 @@ tdm_client* tdm_client_create(tdm_error *error) { tdm_private_client *private_client; - const char *debug; - - debug = getenv("TDM_DEBUG"); - if (debug && (strstr(debug, "1"))) - tdm_debug = 1; private_client = calloc(1, sizeof *private_client); if (!private_client) { diff --git a/common/Makefile.am b/common/Makefile.am new file mode 100644 index 0000000..e3f2dbe --- /dev/null +++ b/common/Makefile.am @@ -0,0 +1,12 @@ +noinst_LTLIBRARIES = libtdm-common.la + +libtdm_common_la_SOURCES = tdm_log.c +libtdm_common_la_LIBADD = ${LDFLAGS} $(TDM_LDFLAGS) +libtdm_common_la_LDFLAGS = ${LDFLAGS} $(TDM_LDFLAGS) +libtdm_common_la_CFLAGS = \ + $(CFLAGS) \ + $(WARN_CFLAGS) \ + $(TDM_CFLAGS) \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/protocol \ + -I$(top_srcdir)/src diff --git a/common/tdm_log.c b/common/tdm_log.c new file mode 100644 index 0000000..2c8b0c6 --- /dev/null +++ b/common/tdm_log.c @@ -0,0 +1,147 @@ +/************************************************************************** + * + * libtdm + * + * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Contact: Eunchul Kim , + * JinYoung Jeon , + * Taeheon Kim , + * YoungJun Cho , + * SooChan Lim , + * Boram Park + * + * 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 +#include +#include +#include +#include +#include + +#include "tdm.h" +#include "tdm_log.h" +#include "tdm_macro.h" + +//#define TDM_CONFIG_ASSERT + +#define LOG_MAX_LEN 4076 + +#define COLOR_RED "\x1b[31m" /* for error */ +#define COLOR_YELLOW "\x1b[33m" /* for warning */ +#define COLOR_GREEN "\x1b[32m" /* for info */ +#define COLOR_RESET "\x1b[0m" + +#undef LOG_TAG +#define LOG_TAG "TDM" + +static unsigned int dlog_enable; +static unsigned int debug_enable; + +static unsigned int need_check_env = 1; + +static void +_tdm_log_check_env(void) +{ + const char *str; + + str = getenv("TDM_DEBUG"); + if (str && (strstr(str, "1"))) + debug_enable = 1; + + str = getenv("TDM_DLOG"); + if (str && (strstr(str, "1"))) + dlog_enable = 1; +} + +EXTERN void +tdm_log_enable_dlog(unsigned int enable) +{ + dlog_enable = enable; +} + +EXTERN void +tdm_log_enable_debug(unsigned int enable) +{ + debug_enable = enable; +} + +EXTERN void +tdm_log_print(int level, const char *fmt, ...) +{ + va_list arg; + + if (need_check_env) { + need_check_env = 0; + _tdm_log_check_env(); + } + + if (level > 3 && !debug_enable) + return; + + if (dlog_enable) { + log_priority dlog_prio; + switch (level) { + case TDM_LOG_LEVEL_ERR: + dlog_prio = DLOG_ERROR; + break; + case TDM_LOG_LEVEL_WRN: + dlog_prio = DLOG_WARN; + break; + case TDM_LOG_LEVEL_INFO: + dlog_prio = DLOG_INFO; + break; + case TDM_LOG_LEVEL_DBG: + dlog_prio = DLOG_DEBUG; + break; + default: + return; + } + va_start(arg, fmt); + dlog_vprint(dlog_prio, LOG_TAG, fmt, arg); + va_end(arg); + } else { + struct timespec ts; + char *lvl_str[] = {"TDM_NON", "TDM_ERR", "TDM_WRN", "TDM_INF", "TDM_DBG"}; + char *color[] = {COLOR_RESET, COLOR_RED, COLOR_YELLOW, COLOR_GREEN, COLOR_RESET}; + + clock_gettime(CLOCK_MONOTONIC, &ts); + + printf("%s", color[level]); + printf("[%s]", lvl_str[level]); + printf(COLOR_RESET"[%d.%06d]", (int)ts.tv_sec, (int)ts.tv_nsec / 1000); + va_start(arg, fmt); + vprintf(fmt, arg); + va_end(arg); + } + +#ifdef TDM_CONFIG_ASSERT + if (level < 3) + assert(0); +#endif +} diff --git a/configure.ac b/configure.ac index 21d8163..c2bb484 100644 --- a/configure.ac +++ b/configure.ac @@ -78,9 +78,10 @@ AC_OUTPUT([ libtdm.pc include/Makefile protocol/Makefile + common/Makefile + src/Makefile client/libtdm-client.pc client/Makefile - src/Makefile tools/Makefile]) echo "" diff --git a/include/tdm_log.h b/include/tdm_log.h index c55acf6..69c2729 100644 --- a/include/tdm_log.h +++ b/include/tdm_log.h @@ -36,132 +36,49 @@ #ifndef _TDM_LOG_H_ #define _TDM_LOG_H_ +#include +#include #ifdef __cplusplus extern "C" { #endif -#include -#include -#include -#include - - /** * @file tdm_log.h * @brief The header file to print logs in frontend and backend modules * @details - * The TDM debug log can be enable by setting "TDM_DEBUG" enviroment + * The TDM debug log can be enable by setting "TDM_DEBUG" enviroment. And also, + * the TDM dlog can be enable by setting "TDM_DLOG" enviroment. * @par Example * @code * $ export TDM_DEBUG=1 * @endcode */ -extern int tdm_debug; - -//#define TDM_CONFIG_DLOG -//#define TDM_CONFIG_ASSERT - -#undef TDM_ASSERT -#ifdef TDM_CONFIG_ASSERT -#define TDM_ASSERT(o) assert(o) -#else -#define TDM_ASSERT(o) -#endif - -#ifdef TDM_CONFIG_DLOG - -#include - -#ifdef LOG_TAG -#undef LOG_TAG -#endif -#define LOG_TAG "TDM" -#define TDM_DBG(fmt, args...) \ - do { \ - if (tdm_debug) { \ - struct timespec ts; \ - clock_gettime(CLOCK_MONOTONIC, &ts); \ - LOGD("[%d.%06d] "fmt"\n", (int)ts.tv_sec, (int)ts.tv_nsec / 1000, ##args); \ - printf("[TDM_DBG][%d.%06d][%d][%s %d] "fmt"\n", (int)ts.tv_sec, \ - (int)ts.tv_nsec / 1000, (int)syscall(SYS_gettid), __func__, __LINE__, ##args); \ - } \ - } while (0) - -#define TDM_INFO(fmt, args...) \ - do { \ - struct timespec ts; \ - clock_gettime(CLOCK_MONOTONIC, &ts); \ - LOGI("[%d.%06d] "fmt"\n", (int)ts.tv_sec, (int)ts.tv_nsec / 1000, ##args); \ - printf("[TDM_INF][%d.%06d][%d][%s %d] "fmt"\n", (int)ts.tv_sec, \ - (int)ts.tv_nsec / 1000, (int)syscall(SYS_gettid), __func__, __LINE__, ##args); \ - } while (0) - -#define TDM_WRN(fmt, args...) \ - do { \ - struct timespec ts; \ - clock_gettime(CLOCK_MONOTONIC, &ts); \ - LOGI("[%d.%06d] "fmt"\n", (int)ts.tv_sec, (int)ts.tv_nsec / 1000, ##args); \ - printf("[TDM_WRN][%d.%06d][%d][%s %d] "fmt"\n", (int)ts.tv_sec, \ - (int)ts.tv_nsec / 1000, (int)syscall(SYS_gettid), __func__, __LINE__, ##args); \ - } while (0) - -#define TDM_ERR(fmt, args...) \ - do { \ - struct timespec ts; \ - clock_gettime(CLOCK_MONOTONIC, &ts); \ - LOGE("[%d.%06d] "fmt"\n", (int)ts.tv_sec, (int)ts.tv_nsec / 1000, ##args); \ - printf("[TDM_ERR][%d.%06d][%d][%s %d] "fmt"\n", (int)ts.tv_sec, \ - (int)ts.tv_nsec / 1000, (int)syscall(SYS_gettid), __func__, __LINE__, ##args); \ - } while (0) +enum { + TDM_LOG_LEVEL_NONE, + TDM_LOG_LEVEL_ERR, + TDM_LOG_LEVEL_WRN, + TDM_LOG_LEVEL_INFO, + TDM_LOG_LEVEL_DBG, +}; -#else /* TDM_CONFIG_DLOG */ - -#include - -#define COLOR_RED "\x1b[31m" /* for error */ -#define COLOR_YELLOW "\x1b[33m" /* for warning */ -#define COLOR_GREEN "\x1b[32m" /* for info */ -#define COLOR_RESET "\x1b[0m" +void tdm_log_enable_dlog(unsigned int enable); +void tdm_log_enable_debug(unsigned int enable); +void tdm_log_print(int level, const char *fmt, ...); #define TDM_DBG(fmt, args...) \ - do { \ - if (tdm_debug) { \ - struct timespec ts; \ - clock_gettime(CLOCK_MONOTONIC, &ts); \ - printf("[TDM_DBG][%d.%06d][%d][%s %d] "fmt"\n", (int)ts.tv_sec, \ - (int)ts.tv_nsec / 1000, (int)syscall(SYS_gettid), __func__, __LINE__, ##args); \ - } \ - } while (0) - + tdm_log_print(TDM_LOG_LEVEL_DBG, "[%d][%s %d]"fmt"\n", \ + (int)syscall(SYS_gettid), __FUNCTION__, __LINE__, ##args) #define TDM_INFO(fmt, args...) \ - do { \ - struct timespec ts; \ - clock_gettime(CLOCK_MONOTONIC, &ts); \ - printf(COLOR_GREEN"[TDM_INF]"COLOR_RESET"[%d.%06d][%d][%s %d] "fmt"\n", (int)ts.tv_sec, \ - (int)ts.tv_nsec / 1000, (int)syscall(SYS_gettid), __func__, __LINE__, ##args); \ - } while (0) - + tdm_log_print(TDM_LOG_LEVEL_INFO, "[%d][%s %d]"fmt"\n", \ + (int)syscall(SYS_gettid), __FUNCTION__, __LINE__, ##args) #define TDM_WRN(fmt, args...) \ - do { \ - struct timespec ts; \ - clock_gettime(CLOCK_MONOTONIC, &ts); \ - printf(COLOR_YELLOW"[TDM_WRN]"COLOR_RESET"[%d.%06d][%d][%s %d] "fmt"\n", (int)ts.tv_sec, \ - (int)ts.tv_nsec / 1000, (int)syscall(SYS_gettid), __func__, __LINE__, ##args); \ - TDM_ASSERT(0); \ - } while (0) - + tdm_log_print(TDM_LOG_LEVEL_WRN, "[%d][%s %d]"fmt"\n", \ + (int)syscall(SYS_gettid), __FUNCTION__, __LINE__, ##args) #define TDM_ERR(fmt, args...) \ - do { \ - struct timespec ts; \ - clock_gettime(CLOCK_MONOTONIC, &ts); \ - printf(COLOR_RED"[TDM_ERR]"COLOR_RESET"[%d.%06d][%d][%s %d] "fmt"\n", (int)ts.tv_sec, \ - (int)ts.tv_nsec / 1000, (int)syscall(SYS_gettid), __func__, __LINE__, ##args); \ - TDM_ASSERT(0); \ - } while (0) - -#endif /* TDM_CONFIG_DLOG */ + tdm_log_print(TDM_LOG_LEVEL_ERR, "[%d][%s %d]"fmt"\n", \ + (int)syscall(SYS_gettid), __FUNCTION__, __LINE__, ##args) #ifdef __cplusplus } diff --git a/src/Makefile.am b/src/Makefile.am index 4d62318..900d3e2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,7 @@ libtdm_la_LTLIBRARIES = libtdm.la libtdm_ladir = $(libdir) libtdm_la_LDFLAGS = -version-number 1:0:0 -no-undefined -libtdm_la_LIBADD = $(TDM_LIBS) -ldl -lpthread +libtdm_la_LIBADD = $(TDM_LIBS) -ldl -lpthread ../common/libtdm-common.la libtdm_la_CFLAGS = \ $(CFLAGS) \ $(WARN_CFLAGS) \ diff --git a/src/tdm.c b/src/tdm.c index be9d26b..9653d8f 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -665,7 +665,6 @@ tdm_display_update(tdm_display *dpy) #define SUFFIX_MODULE ".so" #define DEFAULT_MODULE "libtdm-default"SUFFIX_MODULE -int tdm_debug; int tdm_debug_buffer; int tdm_debug_thread; int tdm_debug_mutex; @@ -892,10 +891,6 @@ tdm_display_init(tdm_error *error) return g_private_display; } - debug = getenv("TDM_DEBUG"); - if (debug && (strstr(debug, "1"))) - tdm_debug = 1; - debug = getenv("TDM_DEBUG_BUFFER"); if (debug && (strstr(debug, "1"))) tdm_debug_buffer = 1; @@ -975,7 +970,6 @@ failed_event: failed_mutex_init: free(private_display); failed_alloc: - tdm_debug = 0; tdm_debug_buffer = 0; if (error) *error = ret; @@ -1018,7 +1012,6 @@ tdm_display_deinit(tdm_display *dpy) pthread_mutex_destroy(&private_display->lock); free(private_display); g_private_display = NULL; - tdm_debug = 0; tdm_debug_buffer = 0; _pthread_mutex_unlock(&gLock); diff --git a/tools/tdm_test_client.c b/tools/tdm_test_client.c index f14b6fe..4a8e583 100644 --- a/tools/tdm_test_client.c +++ b/tools/tdm_test_client.c @@ -41,12 +41,8 @@ #include #include -#include "tdm_macro.h" - #include -#include - -int tdm_debug; +#include "tdm_macro.h" typedef struct _tdm_test_client_arg { char output_name[512]; @@ -252,13 +248,6 @@ _client_vblank_handler(tdm_client_vblank *vblank, tdm_error error, unsigned int if (cur - vbl > 2000) /* 2ms */ printf("kernel -> tdm-client: %ld us\n", cur - vbl); - if (tdm_debug) { - static unsigned long p_cur = 0; - printf("vblank event interval: %ld %ld\n", - vbl - p_vbl, cur - p_cur); - p_cur = cur; - } - p_vbl = vbl; } @@ -387,11 +376,6 @@ main(int argc, char *argv[]) { tdm_test_client *data = &ttc_data; tdm_error error; - const char *debug; - - debug = getenv("TDM_DEBUG"); - if (debug && (strstr(debug, "1"))) - tdm_debug = 1; parse_args(data, argc, argv); -- 2.7.4 From bac98a645dc507a923680978ff6b45bb0efa0726 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Fri, 8 Jul 2016 17:27:59 +0900 Subject: [PATCH 10/16] correct the wrong list operation Change-Id: If257e99a78646298593fb03c53002d67abf293bd --- src/tdm_buffer.c | 3 +++ src/tdm_pp.c | 10 +++++++--- src/tdm_vblank.c | 47 ++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/tdm_buffer.c b/src/tdm_buffer.c index 3c48e0e..93bbd61 100644 --- a/src/tdm_buffer.c +++ b/src/tdm_buffer.c @@ -263,6 +263,9 @@ tdm_buffer_list_get_first_entry(struct list_head *list) TDM_RETURN_VAL_IF_FAIL(list != NULL, NULL); + if (LIST_IS_EMPTY(list)) + return NULL; + buf_info = container_of((list)->next, buf_info, link); return buf_info->buffer; diff --git a/src/tdm_pp.c b/src/tdm_pp.c index 9075c8b..17ea9df 100644 --- a/src/tdm_pp.c +++ b/src/tdm_pp.c @@ -139,9 +139,13 @@ tdm_pp_cb_done(tdm_pp *pp_backend, tbm_surface_h src, tbm_surface_h dst, if (tdm_debug_buffer) TDM_INFO("pp(%p) done: src(%p) dst(%p)", private_pp, src, dst); - first_entry = container_of((&private_pp->buffer_list)->next, pp_buffer, link); - if (first_entry->src != src || first_entry->dst != dst) - TDM_ERR("buffer(%p,%p) is skipped", first_entry->src, first_entry->dst); + if (!LIST_IS_EMPTY(&private_pp->buffer_list)) { + first_entry = container_of((&private_pp->buffer_list)->next, pp_buffer, link); + if (first_entry->src != src || first_entry->dst != dst) + TDM_ERR("buffer(%p,%p) is skipped", first_entry->src, first_entry->dst); + } else { + TDM_NEVER_GET_HERE(); + } if ((pp_buffer = _tdm_pp_find_tbm_buffers(&private_pp->buffer_list, src, dst))) { LIST_DEL(&pp_buffer->link); diff --git a/src/tdm_vblank.c b/src/tdm_vblank.c index f5ec5f9..4af896f 100644 --- a/src/tdm_vblank.c +++ b/src/tdm_vblank.c @@ -180,25 +180,36 @@ static inline void _tdm_vblank_insert_wait(tdm_vblank_wait_info *wait_info, struct list_head *list) { tdm_vblank_wait_info *w = NULL; + tdm_vblank_wait_info *found = NULL; - LIST_FOR_EACH_ENTRY_REV(w, list, link) { + if (LIST_IS_EMPTY(list)) { + LIST_ADDTAIL(&wait_info->link, list); + return; + } + + LIST_FOR_EACH_ENTRY(w, list, link) { /* If last_tv_sec == 0, we can't calculate target_sec. */ if (wait_info->target_sec == 0) { - if (w->interval > wait_info->interval) + if (w->interval <= wait_info->interval) { + found = w; continue; - list = &w->link; - break; + } } else { - if (w->target_sec > wait_info->target_sec) + if (w->target_sec < wait_info->target_sec) { + found = w; continue; - if (w->target_usec > wait_info->target_usec) + } + if (w->target_sec == wait_info->target_sec && w->target_usec <= wait_info->target_usec) { + found = w; continue; - list = &w->link; + } } - break; } - LIST_ADDTAIL(&wait_info->link, list->next); + if (found) + LIST_ADD(&wait_info->link, &found->link); + else + LIST_ADDTAIL(&wait_info->link, list->next); } static void @@ -502,6 +513,11 @@ _tdm_vblank_cb_vblank_SW(void *user_data) TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_OPERATION_FAILED); + if (LIST_IS_EMPTY(&private_vblank->SW_wait_list)) { + VER("no wait_info"); + return TDM_ERROR_OPERATION_FAILED; + } + first_wait_info = container_of(private_vblank->SW_wait_list.next, first_wait_info, link); TDM_RETURN_VAL_IF_FAIL(first_wait_info != NULL, TDM_ERROR_OPERATION_FAILED); @@ -547,6 +563,11 @@ _tdm_vblank_cb_vblank_SW_first(tdm_output *output, unsigned int sequence, private_vblank = wait_info->private_vblank; TDM_RETURN_IF_FAIL(private_vblank != NULL); + if (LIST_IS_EMPTY(&private_vblank->SW_pending_wait_list)) { + VER("no wait_info"); + return; + } + w = container_of((&private_vblank->SW_pending_wait_list)->next, w, link); TDM_RETURN_IF_FAIL(w != NULL); @@ -586,6 +607,11 @@ _tdm_vblank_sw_timer_update(tdm_private_vblank *private_vblank) int ms_delay; tdm_error ret; + if (LIST_IS_EMPTY(&private_vblank->SW_wait_list)) { + VER("no wait_info"); + return TDM_ERROR_OPERATION_FAILED; + } + first_wait_info = container_of(private_vblank->SW_wait_list.next, first_wait_info, link); curr = tdm_helper_get_time_in_micros(); target = first_wait_info->target_sec * 1000000 + first_wait_info->target_usec; @@ -734,6 +760,7 @@ _tdm_vblank_wait_SW(tdm_vblank_wait_info *wait_info) ret = _tdm_vblank_sw_timer_update(private_vblank); if (ret != TDM_ERROR_NONE) { + LIST_DEL(&wait_info->link); VER("couldn't update sw timer"); return ret; } @@ -853,6 +880,7 @@ tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec, return TDM_ERROR_OUT_OF_MEMORY; } + LIST_INITHEAD(&wait_info->link); LIST_ADDTAIL(&wait_info->valid_link, &valid_wait_list); wait_info->stamp = ++stamp; wait_info->req_sec = req_sec; @@ -870,6 +898,7 @@ tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec, ret = _tdm_vblank_wait_SW(wait_info); if (ret != TDM_ERROR_NONE) { + LIST_DEL(&wait_info->link); LIST_DEL(&wait_info->valid_link); free(wait_info); return ret; -- 2.7.4 From f2653134995c5a4b918eb1ed5ac09efb35acf77a Mon Sep 17 00:00:00 2001 From: Boram Park Date: Sat, 9 Jul 2016 17:58:04 +0900 Subject: [PATCH 11/16] Use timer to call the output change callback of the sub-thread. The output change callback of tdm_server and tdm_vblank was called in the main thread. And it made the multi thread issue. If we use the timer, we can call the sub-thread's output change callback in sub-thread. And, after tdm_event_loop_deinit, we don't worry about thread things because it's finalized. Change-Id: I69013b302673551b887384325b14c94e3a53646c --- src/tdm.c | 11 +++++-- src/tdm_display.c | 82 ++++++++++++++++++++++------------------------------ src/tdm_event_loop.c | 6 ++++ src/tdm_macro.h | 2 +- src/tdm_private.h | 15 +++++----- src/tdm_server.c | 1 + src/tdm_thread.c | 10 +++++++ src/tdm_vblank.c | 32 +++++++++++--------- 8 files changed, 87 insertions(+), 72 deletions(-) diff --git a/src/tdm.c b/src/tdm.c index 9653d8f..8a74ae2 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -185,6 +185,9 @@ _tdm_display_destroy_private_output(tdm_private_output *private_output) _tdm_display_destroy_caps_output(&private_output->caps); + if (private_output->dpms_changed_timer) + tdm_event_loop_source_remove(private_output->dpms_changed_timer); + private_output->stamp = 0; free(private_output); } @@ -993,9 +996,13 @@ tdm_display_deinit(tdm_display *dpy) return; } + /* dont move the position of lock/unlock. all resource should be protected + * during destroying. after tdm_event_loop_deinit, we don't worry about thread + * things because it's finalized. + */ _pthread_mutex_lock(&private_display->lock); - tdm_event_loop_deinit(private_display); + _pthread_mutex_unlock(&private_display->lock); _tdm_display_destroy_private_display(private_display); _tdm_display_unload_module(private_display); @@ -1007,8 +1014,6 @@ tdm_display_deinit(tdm_display *dpy) tdm_helper_set_fd("TDM_DRM_MASTER_FD", -1); - _pthread_mutex_unlock(&private_display->lock); - pthread_mutex_destroy(&private_display->lock); free(private_display); g_private_display = NULL; diff --git a/src/tdm_display.c b/src/tdm_display.c index c3bd904..aa29226 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -102,46 +102,6 @@ private_output = private_layer->private_output; \ private_display = private_output->private_display -INTERN tdm_error -_tdm_display_lock(tdm_display *dpy, const char *func) -{ - tdm_private_display *private_display = (tdm_private_display*)dpy; - int ret; - - if (tdm_debug_mutex) - TDM_INFO("mutex lock: %s", func); - - ret = pthread_mutex_trylock(&private_display->lock); - if (ret < 0) { - if (ret == EBUSY) - TDM_ERR("mutex lock busy: %s", func); - else - TDM_ERR("mutex lock failed: %s(%m)", func); - return TDM_ERROR_OPERATION_FAILED; - } - - pthread_mutex_lock(&tdm_mutex_check_lock); - tdm_mutex_locked = 1; - pthread_mutex_unlock(&tdm_mutex_check_lock); - - return TDM_ERROR_NONE; -} - -INTERN void -_tdm_display_unlock(tdm_display *dpy, const char *func) -{ - tdm_private_display *private_display = (tdm_private_display*)dpy; - - if (tdm_debug_mutex) - TDM_INFO("mutex unlock: %s", func); - - pthread_mutex_lock(&tdm_mutex_check_lock); - tdm_mutex_locked = 0; - pthread_mutex_unlock(&tdm_mutex_check_lock); - - pthread_mutex_unlock(&private_display->lock); -} - EXTERN tdm_error tdm_display_get_capabilities(tdm_display *dpy, tdm_display_capability *capabilities) @@ -940,10 +900,10 @@ tdm_output_wait_vblank(tdm_output *output, int interval, int sync, _pthread_mutex_lock(&private_display->lock); if (private_output->current_dpms_value > TDM_OUTPUT_DPMS_ON) { - TDM_ERR("output(%d) dpms: %s", private_output->pipe, + TDM_WRN("output(%d) dpms: %s", private_output->pipe, tdm_dpms_str(private_output->current_dpms_value)); _pthread_mutex_unlock(&private_display->lock); - return TDM_ERROR_BAD_REQUEST; + return TDM_ERROR_DPMS_OFF; } func_output = &private_display->func_output; @@ -1037,7 +997,7 @@ tdm_output_commit(tdm_output *output, int sync, tdm_output_commit_handler func, TDM_ERR("output(%d) dpms: %s", private_output->pipe, tdm_dpms_str(private_output->current_dpms_value)); _pthread_mutex_unlock(&private_display->lock); - return TDM_ERROR_BAD_REQUEST; + return TDM_ERROR_DPMS_OFF; } ret = _tdm_output_commit(output, sync, func, user_data); @@ -1097,6 +1057,21 @@ tdm_output_get_mode(tdm_output *output, const tdm_output_mode **mode) return ret; } +static tdm_error +_tdm_output_dpms_changed_timeout(void *user_data) +{ + tdm_private_output *private_output = user_data; + tdm_value value; + + value.u32 = private_output->current_dpms_value; + tdm_output_call_change_handler_internal(private_output, + &private_output->change_handler_list_sub, + TDM_OUTPUT_CHANGE_DPMS, + value, 0); + + return TDM_ERROR_NONE; +} + EXTERN tdm_error tdm_output_set_dpms(tdm_output *output, tdm_output_dpms dpms_value) { @@ -1113,6 +1088,17 @@ tdm_output_set_dpms(tdm_output *output, tdm_output_dpms dpms_value) return TDM_ERROR_NONE; } + if (!private_output->dpms_changed_timer) { + private_output->dpms_changed_timer = + tdm_event_loop_add_timer_handler(private_output->private_display, + _tdm_output_dpms_changed_timeout, private_output, NULL); + if (!private_output->dpms_changed_timer) { + TDM_ERR("can't create dpms timer!!"); + _pthread_mutex_unlock(&private_display->lock); + return TDM_ERROR_OUT_OF_MEMORY; + } + } + func_output = &private_display->func_output; if (!func_output->output_set_dpms) { @@ -1134,11 +1120,11 @@ tdm_output_set_dpms(tdm_output *output, tdm_output_dpms dpms_value) TDM_OUTPUT_CHANGE_DPMS, value, 0); - //TODO: safe? We can't take care of the user_data of callback functions. - tdm_output_call_change_handler_internal(private_output, - &private_output->change_handler_list_sub, - TDM_OUTPUT_CHANGE_DPMS, - value, 1); + if (!LIST_IS_EMPTY(&private_output->change_handler_list_sub)) { + ret = tdm_event_loop_source_timer_update(private_output->dpms_changed_timer, 1); + if (ret != TDM_ERROR_NONE) + TDM_NEVER_GET_HERE(); + } } _pthread_mutex_unlock(&private_display->lock); diff --git a/src/tdm_event_loop.c b/src/tdm_event_loop.c index 51a8f55..c068c09 100644 --- a/src/tdm_event_loop.c +++ b/src/tdm_event_loop.c @@ -147,7 +147,11 @@ tdm_event_loop_deinit(tdm_private_display *private_display) if (!private_display->private_loop) return; + /* after tdm_thread_deinit, we don't worry about thread things because it's finalized */ tdm_thread_deinit(private_display->private_loop); + + + _pthread_mutex_unlock(&private_display->lock); tdm_server_deinit(private_display->private_loop); if (private_display->private_loop->backend_source) @@ -158,6 +162,8 @@ tdm_event_loop_deinit(tdm_private_display *private_display) free(private_display->private_loop); private_display->private_loop = NULL; + + _pthread_mutex_lock(&private_display->lock); } INTERN void diff --git a/src/tdm_macro.h b/src/tdm_macro.h index 659ead3..3983c68 100644 --- a/src/tdm_macro.h +++ b/src/tdm_macro.h @@ -102,7 +102,7 @@ extern "C" { } \ } -#define TDM_NEVER_GET_HERE() TDM_ERR("** NEVER GET HERE **") +#define TDM_NEVER_GET_HERE() TDM_WRN("** NEVER GET HERE **") #define TDM_SNPRINTF(p, len, fmt, ARG...) \ do { \ diff --git a/src/tdm_private.h b/src/tdm_private.h index 05fb7a9..a9ad25e 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -170,6 +170,9 @@ struct _tdm_private_output { struct list_head change_handler_list_sub; void **layers_ptr; + + /* TODO: temp solution for handling DPMS things in sub-htread */ + tdm_event_loop_source *dpms_changed_timer; }; struct _tdm_private_layer { @@ -495,19 +498,17 @@ extern int tdm_dump_enable; static inline int TDM_MUTEX_IS_LOCKED(void) { int ret; + /* if thread is not running, we don't need to consider mutex things. */ + if (!tdm_thread_is_running()) + return 1; pthread_mutex_lock(&tdm_mutex_check_lock); ret = (tdm_mutex_locked == 1); pthread_mutex_unlock(&tdm_mutex_check_lock); return ret; } -tdm_error -_tdm_display_lock(tdm_display *dpy, const char *func); -void -_tdm_display_unlock(tdm_display *dpy, const char *func); - -#define tdm_display_lock(dpy) _tdm_display_lock(dpy, __FUNCTION__) -#define tdm_display_unlock(dpy) _tdm_display_unlock(dpy, __FUNCTION__) +#define tdm_display_lock(dpy) _pthread_mutex_lock(&((tdm_private_display *)dpy)->lock) +#define tdm_display_unlock(dpy) _pthread_mutex_unlock(&((tdm_private_display *)dpy)->lock) tdm_error tdm_display_update_output(tdm_private_display *private_display, diff --git a/src/tdm_server.c b/src/tdm_server.c index bb8ff6c..7c18228 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -48,6 +48,7 @@ * However, the internal function which does lock/unlock the mutex of * private_display in itself can be called. * - DO NOT use the tdm_private_display structure here. + * - The callback function things can be called in main thread. */ struct _tdm_private_server { diff --git a/src/tdm_thread.c b/src/tdm_thread.c index 3ae6d0b..1a9a853 100644 --- a/src/tdm_thread.c +++ b/src/tdm_thread.c @@ -173,13 +173,23 @@ tdm_thread_init(tdm_private_loop *private_loop) INTERN void tdm_thread_deinit(tdm_private_loop *private_loop) { + tdm_private_display *private_display; + TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED()); if (!private_loop->private_thread) return; pthread_cancel(private_loop->private_thread->event_thread); + + private_display = private_loop->dpy; + + /* before falling into the block of pthread_join, we have to unlock the mutex + * for subthread to use the mutex. + */ + _pthread_mutex_unlock(&private_display->lock); pthread_join(private_loop->private_thread->event_thread, NULL); + _pthread_mutex_lock(&private_display->lock); if (private_loop->private_thread->pipe[0] >= 0) close(private_loop->private_thread->pipe[0]); diff --git a/src/tdm_vblank.c b/src/tdm_vblank.c index 4af896f..30ed7df 100644 --- a/src/tdm_vblank.c +++ b/src/tdm_vblank.c @@ -42,11 +42,9 @@ #include "tdm_list.h" /* CAUTION: - * - tdm vblank doesn't care about thread things. - * - DO NOT use the TDM internal functions here. - * However, the internal function which does lock/unlock the mutex of - * private_display in itself can be called. - * - DO NOT use the tdm_private_display structure here. + * tdm vblank doesn't care about thread things. + * However, to use tdm_event_loop_xxx functions, have to use the internal function. + * So need to lock/unlock the mutex of private_display. */ /* TDM vblank @@ -362,10 +360,9 @@ tdm_vblank_destroy(tdm_vblank *vblank) #endif if (private_vblank->SW_timer) { - if (tdm_display_lock(private_vblank->dpy) == TDM_ERROR_NONE) { - tdm_event_loop_source_remove(private_vblank->SW_timer); - tdm_display_unlock(private_vblank->dpy); - } + tdm_display_lock(private_vblank->dpy); + tdm_event_loop_source_remove(private_vblank->SW_timer); + tdm_display_unlock(private_vblank->dpy); } tdm_output_remove_change_handler(private_vblank->output, @@ -495,7 +492,7 @@ _tdm_vblank_wait_HW(tdm_vblank_wait_info *wait_info) _tdm_vblank_cb_vblank_HW, wait_info); if (ret != TDM_ERROR_NONE) { - VER("wait(%p) failed", wait_info); + VWR("wait(%p) failed", wait_info); LIST_DEL(&wait_info->link); return ret; } @@ -627,8 +624,7 @@ _tdm_vblank_sw_timer_update(tdm_private_vblank *private_vblank) VDB("wait(%p) curr(%4lu) target(%4lu) ms_delay(%d)", first_wait_info, curr, target, ms_delay); - ret = tdm_display_lock(private_vblank->dpy); - TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret); + tdm_display_lock(private_vblank->dpy); if (!private_vblank->SW_timer) { private_vblank->SW_timer = @@ -745,6 +741,10 @@ _tdm_vblank_wait_SW(tdm_vblank_wait_info *wait_info) if (do_wait) { ret = tdm_output_wait_vblank(private_vblank->output, 1, 0, _tdm_vblank_cb_vblank_SW_first, wait_info); + if (ret == TDM_ERROR_DPMS_OFF) { + TDM_WRN("use SW"); + goto use_sw; + } if (ret != TDM_ERROR_NONE) { LIST_DEL(&wait_info->link); return ret; @@ -754,6 +754,7 @@ _tdm_vblank_wait_SW(tdm_vblank_wait_info *wait_info) return TDM_ERROR_NONE; } +use_sw: TDM_RETURN_VAL_IF_FAIL(wait_info->target_sec > 0, TDM_ERROR_OPERATION_FAILED); _tdm_vblank_insert_wait(wait_info, &private_vblank->SW_wait_list); @@ -892,8 +893,13 @@ tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec, _tdm_vblank_calculate_target(wait_info); - if (private_vblank->HW_enable) + if (private_vblank->HW_enable) { ret = _tdm_vblank_wait_HW(wait_info); + if (ret == TDM_ERROR_DPMS_OFF) { + TDM_WRN("try to use SW"); + ret = _tdm_vblank_wait_SW(wait_info); + } + } else ret = _tdm_vblank_wait_SW(wait_info); -- 2.7.4 From 1cb4014a89cc18b7b5d4f6b62c933a6337dc15bf Mon Sep 17 00:00:00 2001 From: Boram Park Date: Mon, 11 Jul 2016 09:22:16 +0900 Subject: [PATCH 12/16] remove non-effective code. no need lock/unlock after tdm_thread_deinit Change-Id: I032304d085e7153ff43489b74bb9e2567ccd8abb --- src/tdm_event_loop.c | 5 ----- src/tdm_thread.c | 1 - 2 files changed, 6 deletions(-) diff --git a/src/tdm_event_loop.c b/src/tdm_event_loop.c index c068c09..500bba9 100644 --- a/src/tdm_event_loop.c +++ b/src/tdm_event_loop.c @@ -149,9 +149,6 @@ tdm_event_loop_deinit(tdm_private_display *private_display) /* after tdm_thread_deinit, we don't worry about thread things because it's finalized */ tdm_thread_deinit(private_display->private_loop); - - - _pthread_mutex_unlock(&private_display->lock); tdm_server_deinit(private_display->private_loop); if (private_display->private_loop->backend_source) @@ -162,8 +159,6 @@ tdm_event_loop_deinit(tdm_private_display *private_display) free(private_display->private_loop); private_display->private_loop = NULL; - - _pthread_mutex_lock(&private_display->lock); } INTERN void diff --git a/src/tdm_thread.c b/src/tdm_thread.c index 1a9a853..b9490a5 100644 --- a/src/tdm_thread.c +++ b/src/tdm_thread.c @@ -189,7 +189,6 @@ tdm_thread_deinit(tdm_private_loop *private_loop) */ _pthread_mutex_unlock(&private_display->lock); pthread_join(private_loop->private_thread->event_thread, NULL); - _pthread_mutex_lock(&private_display->lock); if (private_loop->private_thread->pipe[0] >= 0) close(private_loop->private_thread->pipe[0]); -- 2.7.4 From 9c1914294ab107e39c4d14b6dad470db47a03528 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Tue, 5 Jul 2016 17:25:37 +0900 Subject: [PATCH 13/16] fix wrong log Change-Id: I9e724f9b5cb7a1c7b9fa48eb7d67b742181cd6d6 --- src/tdm_display.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/tdm_display.c b/src/tdm_display.c index aa29226..fd07d00 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -1331,6 +1331,8 @@ EXTERN tdm_error tdm_layer_set_info(tdm_layer *layer, tdm_info_layer *info) { tdm_func_layer *func_layer; + char fmtstr[128]; + LAYER_FUNC_ENTRY(); TDM_RETURN_VAL_IF_FAIL(info != NULL, TDM_ERROR_INVALID_PARAMETER); @@ -1350,11 +1352,16 @@ tdm_layer_set_info(tdm_layer *layer, tdm_info_layer *info) return TDM_ERROR_NOT_IMPLEMENTED; } - TDM_INFO("layer(%p) info: src(%dx%d %d,%d %dx%d %c%c%c%c) dst(%d,%d %dx%d) trans(%d)", + if (info->src_config.format) + snprintf(fmtstr, 128, "%c%c%c%c", FOURCC_STR(info->src_config.format)); + else + snprintf(fmtstr, 128, "NONE"); + + TDM_INFO("layer(%p) info: src(%dx%d %d,%d %dx%d %s) dst(%d,%d %dx%d) trans(%d)", private_layer, info->src_config.size.h, info->src_config.size.v, info->src_config.pos.x, info->src_config.pos.y, info->src_config.pos.w, info->src_config.pos.h, - FOURCC_STR(info->src_config.format), + fmtstr, info->dst_pos.x, info->dst_pos.y, info->dst_pos.w, info->dst_pos.h, info->transform); -- 2.7.4 From 5ecd7fb59c26446e6f7a83cd2278b0cecd5393bf Mon Sep 17 00:00:00 2001 From: Boram Park Date: Mon, 4 Jul 2016 13:35:19 +0900 Subject: [PATCH 14/16] add tdm-dbg and td-test-server for debugging Change-Id: I381c9c517256df37ec2238e42815fd3b8618caa4 --- client/Makefile.am | 21 + client/tdm_client.c | 2 +- client/tdm_dbg.c | 155 ++++++ common/tdm_log.c | 22 +- include/tdm.h | 12 + include/tdm_common.h | 130 +++++ include/tdm_helper.h | 10 + include/tdm_log.h | 1 + include/tdm_types.h | 129 ----- packaging/libtdm.spec | 2 + protocol/tdm.xml | 8 + src/Makefile.am | 1 + src/tdm.c | 74 ++- src/tdm_capture.c | 8 + src/tdm_dbg_server.c | 409 ++++++++++++++ src/tdm_display.c | 37 +- src/tdm_event_loop.c | 4 + src/tdm_helper.c | 269 +++++++++- src/tdm_macro.h | 135 ++++- src/tdm_pp.c | 15 + src/tdm_private.h | 17 +- src/tdm_server.c | 14 + tools/Makefile.am | 16 +- tools/buffers.c | 950 +++++++++++++++++++++++++++++++++ tools/buffers.h | 39 ++ tools/tdm_test_client.c | 114 ++-- tools/tdm_test_server.c | 1345 +++++++++++++++++++++++++++++++++++++++++++++++ 27 files changed, 3722 insertions(+), 217 deletions(-) create mode 100644 client/tdm_dbg.c create mode 100644 src/tdm_dbg_server.c create mode 100644 tools/buffers.c create mode 100644 tools/buffers.h create mode 100644 tools/tdm_test_server.c diff --git a/client/Makefile.am b/client/Makefile.am index f3b29c8..739c2f1 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -1,3 +1,6 @@ +########################################################################## +### tdm-client library +########################################################################## libtdm_clientincludedir = ${includedir} libtdm_clientinclude_HEADERS = \ tdm_client.h \ @@ -22,3 +25,21 @@ libtdm_client_la_CFLAGS = \ libtdm_client_la_SOURCES = \ $(top_srcdir)/protocol/tdm-protocol.c \ tdm_client.c + +########################################################################## +### tdm-dbg +########################################################################## +bin_PROGRAMS = \ + tdm-dbg + +#tdm-dbg +tdm_dbg_SOURCES = \ + $(top_srcdir)/protocol/tdm-protocol.c \ + tdm_dbg.c +tdm_dbg_LDFLAGS = ${LDFLAGS} +tdm_dbg_LDADD = $(TDM_CLIENT_LIBS) ../common/libtdm-common.la +tdm_dbg_CFLAGS = \ + $(TDM_CFLAGS) \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/protocol \ + -I$(top_srcdir)/src diff --git a/client/tdm_client.c b/client/tdm_client.c index f03ae63..b7a80c1 100644 --- a/client/tdm_client.c +++ b/client/tdm_client.c @@ -253,7 +253,7 @@ _tdm_client_cb_global(void *data, struct wl_registry *registry, { tdm_private_client *private_client = data; - if (strcmp(interface, "wl_tdm") == 0) { + if (strncmp(interface, "wl_tdm", 6) == 0) { private_client->tdm = wl_registry_bind(registry, name, &wl_tdm_interface, version); TDM_RETURN_IF_FAIL(private_client->tdm != NULL); diff --git a/client/tdm_dbg.c b/client/tdm_dbg.c new file mode 100644 index 0000000..a6fc0a7 --- /dev/null +++ b/client/tdm_dbg.c @@ -0,0 +1,155 @@ +/************************************************************************** + * + * libtdm + * + * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Contact: Eunchul Kim , + * JinYoung Jeon , + * Taeheon Kim , + * YoungJun Cho , + * SooChan Lim , + * Boram Park + * + * 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 +#include +#include +#include + +#undef exit_if_fail +#define exit_if_fail(cond) { \ + if (!(cond)) { \ + printf("'%s' failed. (line:%d)\n", #cond, __LINE__); \ + exit(0); \ + } \ +} + +typedef struct _tdm_dbg_info { + struct wl_display *display; + struct wl_registry *registry; + struct wl_tdm *tdm; +} tdm_dbg_info; + +static tdm_dbg_info td_info; +static int done; + +static void +_tdm_dbg_cb_debug_done(void *data, struct wl_tdm *wl_tdm, const char *message) +{ + printf("%s", message); + + done = 1; +} + +static const struct wl_tdm_listener tdm_dbg_listener = { + _tdm_dbg_cb_debug_done, +}; + +static void +_tdm_dbg_cb_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, + uint32_t version) +{ + tdm_dbg_info *info = data; + + if (strncmp(interface, "wl_tdm", 6) == 0) { + info->tdm = wl_registry_bind(registry, name, &wl_tdm_interface, version); + exit_if_fail(info->tdm != NULL); + wl_tdm_add_listener(info->tdm, &tdm_dbg_listener, info); + wl_display_flush(info->display); + } +} + +static void +_tdm_dbg_cb_global_remove(void *data, struct wl_registry *registry, uint32_t name) +{ +} + +static const struct wl_registry_listener tdm_dbg_registry_listener = { + _tdm_dbg_cb_global, + _tdm_dbg_cb_global_remove +}; + +int +main(int argc, char ** argv) +{ + tdm_dbg_info *info = &td_info; + int i, ret = 0; + char cwd[1024]; + char options[1024]; + int bufsize = sizeof(options); + char *str_buf = options; + int *len_buf = &bufsize; + const char *xdg; + + xdg = (const char*)getenv("XDG_RUNTIME_DIR"); + if (!xdg) { + char buf[32]; + snprintf(buf, sizeof(buf), "/run"); + + ret = setenv("XDG_RUNTIME_DIR", (const char*)buf, 1); + exit_if_fail(ret == 0); + } + + info->display = wl_display_connect("tdm-socket"); + exit_if_fail(info->display != NULL); + + info->registry = wl_display_get_registry(info->display); + exit_if_fail(info->registry != NULL); + + wl_registry_add_listener(info->registry, + &tdm_dbg_registry_listener, info); + wl_display_roundtrip(info->display); + exit_if_fail(info->tdm != NULL); + + TDM_SNPRINTF(str_buf, len_buf, "%d ", getpid()); + + if (!getcwd(cwd, sizeof(cwd))) + snprintf(cwd, sizeof(cwd), "/tmp"); + TDM_SNPRINTF(str_buf, len_buf, "%s ", cwd); + + for (i = 0; i < argc; i++) + TDM_SNPRINTF(str_buf, len_buf, "%s ", argv[i]); + + done = 0; + + wl_tdm_debug(info->tdm, options); + + while (!done && ret >= 0) + ret = wl_display_dispatch(info->display); + + if (info->tdm) + wl_tdm_destroy(info->tdm); + if (info->registry) + wl_registry_destroy(info->registry); + if (info->display) + wl_display_disconnect(info->display); + + return 0; +} diff --git a/common/tdm_log.c b/common/tdm_log.c index 2c8b0c6..c843c9b 100644 --- a/common/tdm_log.c +++ b/common/tdm_log.c @@ -61,7 +61,7 @@ #define LOG_TAG "TDM" static unsigned int dlog_enable; -static unsigned int debug_enable; +static unsigned int debug_level = TDM_LOG_LEVEL_INFO; static unsigned int need_check_env = 1; @@ -69,10 +69,15 @@ static void _tdm_log_check_env(void) { const char *str; + char *end; + + str = getenv("TDM_DEBUG_LEVEL"); + if (str) + debug_level = strtol(str, &end, 10); str = getenv("TDM_DEBUG"); if (str && (strstr(str, "1"))) - debug_enable = 1; + debug_level = TDM_LOG_LEVEL_DBG; str = getenv("TDM_DLOG"); if (str && (strstr(str, "1"))) @@ -88,7 +93,16 @@ tdm_log_enable_dlog(unsigned int enable) EXTERN void tdm_log_enable_debug(unsigned int enable) { - debug_enable = enable; + if (enable) + debug_level = TDM_LOG_LEVEL_DBG; + else + debug_level = TDM_LOG_LEVEL_INFO; +} + +EXTERN void +tdm_log_set_debug_level(int level) +{ + debug_level = level; } EXTERN void @@ -101,7 +115,7 @@ tdm_log_print(int level, const char *fmt, ...) _tdm_log_check_env(); } - if (level > 3 && !debug_enable) + if (level > debug_level) return; if (dlog_enable) { diff --git a/include/tdm.h b/include/tdm.h index 47afc19..fa0a7f0 100644 --- a/include/tdm.h +++ b/include/tdm.h @@ -123,6 +123,18 @@ tdm_error tdm_display_handle_events(tdm_display *dpy); /** + * @brief Get the information of the TDM backend module. + * @param[in] dpy A display object + * @param[out] name The name of the TDM backend module + * @param[out] vendor The vendor of the TDM backend module + * @param[out] version The version of the TDM backend module + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +tdm_error +tdm_display_get_backend_info(tdm_display *dpy, const char **name, + const char **vendor, int *major, int *minor); + +/** * @brief Get the capabilities of a display object. * @details A frontend user can get whether TDM supports pp/capture functionality with this function. * @param[in] dpy A display object diff --git a/include/tdm_common.h b/include/tdm_common.h index 79cdf69..fae1224 100644 --- a/include/tdm_common.h +++ b/include/tdm_common.h @@ -43,6 +43,7 @@ extern "C" { #endif #define TDM_NAME_LEN 64 +#define TDM_PATH_LEN 1024 /** * @file tdm_common.h @@ -68,6 +69,76 @@ typedef enum { } tdm_error; /** + * @brief The transform enumeration(rotate, flip) + */ +typedef enum { + TDM_TRANSFORM_NORMAL = 0, /**< no transform */ + TDM_TRANSFORM_90 = 1, /**< rotate 90 */ + TDM_TRANSFORM_180 = 2, /**< rotate 180 */ + TDM_TRANSFORM_270 = 3, /**< rotate 270 */ + TDM_TRANSFORM_FLIPPED = 4, /**< no rotate and horizontal flip */ + TDM_TRANSFORM_FLIPPED_90 = 5, /**< rotate 90 and horizontal flip */ + TDM_TRANSFORM_FLIPPED_180 = 6, /**< rotate 180 and horizontal flip */ + TDM_TRANSFORM_FLIPPED_270 = 7, /**< rotate 270 and horizontal flip */ +} tdm_transform; + +/** + * @brief The layer capability enumeration + * @details + * A layer can have one of CURSOR, PRIMARY and OVERLAY capability. And a layer + * also can have one of GRAPHIC and VIDEO capability. And a layer also can have + * SCALE and TRANSFORM capability.\n + * @par Example + * @code + * //For example + * capabilities = TDM_LAYER_CAPABILITY_PRIMARY | TDM_LAYER_CAPABILITY_GRAPHIC; + * capabilities = TDM_LAYER_CAPABILITY_OVERLAY | TDM_LAYER_CAPABILITY_GRAPHIC | TDM_LAYER_CAPABILITY_SCALE; + * capabilities = TDM_LAYER_CAPABILITY_OVERLAY | TDM_LAYER_CAPABILITY_GRAPHIC | TDM_LAYER_CAPABILITY_SCALE | TDM_LAYER_CAPABILITY_TRANSFORM; + * capabilities = TDM_LAYER_CAPABILITY_CURSOR | TDM_LAYER_CAPABILITY_GRAPHIC; + * capabilities = TDM_LAYER_CAPABILITY_OVERLAY | TDM_LAYER_CAPABILITY_VIDEO; + * @endcode + * @remark + * - When a video plays, in most of cases, video buffers will be displayed to + * a GRAPHIC layer after converting RGB buffers via PP. In this case, a backend + * module doesn't need to offer VIDEO layer. + * - But in case that s vendor wants to handle a video by their own way, + * a backend module offer VIDEO layers. And a display server will pass a video + * buffer to a VIDEO layer without converting. + */ +typedef enum { + TDM_LAYER_CAPABILITY_CURSOR = (1 << 0), /**< cursor */ + TDM_LAYER_CAPABILITY_PRIMARY = (1 << 1), /**< primary */ + TDM_LAYER_CAPABILITY_OVERLAY = (1 << 2), /**< overlay */ + TDM_LAYER_CAPABILITY_GRAPHIC = (1 << 4), /**< graphic */ + TDM_LAYER_CAPABILITY_VIDEO = (1 << 5), /**< video */ + TDM_LAYER_CAPABILITY_SCALE = (1 << 8), /**< if a layer has scale capability */ + TDM_LAYER_CAPABILITY_TRANSFORM = (1 << 9), /**< if a layer has transform capability */ + TDM_LAYER_CAPABILITY_SCANOUT = (1 << 10), /**< if a layer allows a scanout buffer only */ + TDM_LAYER_CAPABILITY_RESEVED_MEMORY = (1 << 11), /**< if a layer allows a reserved buffer only */ + TDM_LAYER_CAPABILITY_NO_CROP = (1 << 12), /**< if a layer has no cropping capability */ +} tdm_layer_capability; + +/** + * @brief The pp capability enumeration + */ +typedef enum { + TDM_PP_CAPABILITY_SYNC = (1 << 0), /**< The pp device supports synchronous operation */ + TDM_PP_CAPABILITY_ASYNC = (1 << 1), /**< The pp device supports asynchronous operation */ + TDM_PP_CAPABILITY_SCALE = (1 << 4), /**< The pp device supports scale operation */ + TDM_PP_CAPABILITY_TRANSFORM = (1 << 5), /**< The pp device supports transform operation */ +} tdm_pp_capability; + +/** + * @brief The capture capability enumeration + */ +typedef enum { + TDM_CAPTURE_CAPABILITY_OUTPUT = (1 << 0), /**< The capture device supports to dump a output */ + TDM_CAPTURE_CAPABILITY_LAYER = (1 << 1), /**< The capture device supports to dump a layer */ + TDM_CAPTURE_CAPABILITY_SCALE = (1 << 4), /**< The capture device supports scale operation */ + TDM_CAPTURE_CAPABILITY_TRANSFORM = (1 << 5), /**< The capture device supports transform operation */ +} tdm_capture_capability; + +/** * @brief The output change enumeration of #tdm_output_change_handler */ typedef enum { @@ -85,6 +156,30 @@ typedef enum { } tdm_output_conn_status; /** + * @brief The output connection status enumeration + * @details bit compatible with the libdrm definitions. + */ +typedef enum { + TDM_OUTPUT_TYPE_Unknown, /**< unknown */ + TDM_OUTPUT_TYPE_VGA, /**< VGA connection */ + TDM_OUTPUT_TYPE_DVII, /**< DVII connection */ + TDM_OUTPUT_TYPE_DVID, /**< DVID connection */ + TDM_OUTPUT_TYPE_DVIA, /**< DVIA connection */ + TDM_OUTPUT_TYPE_Composite, /**< Composite connection */ + TDM_OUTPUT_TYPE_SVIDEO, /**< SVIDEO connection */ + TDM_OUTPUT_TYPE_LVDS, /**< LVDS connection */ + TDM_OUTPUT_TYPE_Component, /**< Component connection */ + TDM_OUTPUT_TYPE_9PinDIN, /**< 9PinDIN connection */ + TDM_OUTPUT_TYPE_DisplayPort, /**< DisplayPort connection */ + TDM_OUTPUT_TYPE_HDMIA, /**< HDMIA connection */ + TDM_OUTPUT_TYPE_HDMIB, /**< HDMIB connection */ + TDM_OUTPUT_TYPE_TV, /**< TV connection */ + TDM_OUTPUT_TYPE_eDP, /**< eDP connection */ + TDM_OUTPUT_TYPE_VIRTUAL, /**< Virtual connection for WiFi Display */ + TDM_OUTPUT_TYPE_DSI, /**< DSI connection */ +} tdm_output_type; + +/** * @brief The DPMS enumeration * @details bit compatible with the libdrm definitions. */ @@ -96,6 +191,41 @@ typedef enum { } tdm_output_dpms; /** + * @brief The output mode type enumeration + * @details bit compatible with the libdrm definitions. + */ +typedef enum { + TDM_OUTPUT_MODE_TYPE_BUILTIN = (1 << 0), + TDM_OUTPUT_MODE_TYPE_CLOCK_C = ((1 << 1) | TDM_OUTPUT_MODE_TYPE_BUILTIN), + TDM_OUTPUT_MODE_TYPE_CRTC_C = ((1 << 2) | TDM_OUTPUT_MODE_TYPE_BUILTIN), + TDM_OUTPUT_MODE_TYPE_PREFERRED = (1 << 3), + TDM_OUTPUT_MODE_TYPE_DEFAULT = (1 << 4), + TDM_OUTPUT_MODE_TYPE_USERDEF = (1 << 5), + TDM_OUTPUT_MODE_TYPE_DRIVER = (1 << 6), +} tdm_output_mode_type; + +/** + * @brief The output mode flag enumeration + * @details bit compatible with the libdrm definitions. + */ +typedef enum { + TDM_OUTPUT_MODE_FLAG_PHSYNC = (1 << 0), + TDM_OUTPUT_MODE_FLAG_NHSYNC = (1 << 1), + TDM_OUTPUT_MODE_FLAG_PVSYNC = (1 << 2), + TDM_OUTPUT_MODE_FLAG_NVSYNC = (1 << 3), + TDM_OUTPUT_MODE_FLAG_INTERLACE = (1 << 4), + TDM_OUTPUT_MODE_FLAG_DBLSCAN = (1 << 5), + TDM_OUTPUT_MODE_FLAG_CSYNC = (1 << 6), + TDM_OUTPUT_MODE_FLAG_PCSYNC = (1 << 7), + TDM_OUTPUT_MODE_FLAG_NCSYNC = (1 << 8), + TDM_OUTPUT_MODE_FLAG_HSKEW = (1 << 9), /* hskew provided */ + TDM_OUTPUT_MODE_FLAG_BCAST = (1 << 10), + TDM_OUTPUT_MODE_FLAG_PIXMUX = (1 << 11), + TDM_OUTPUT_MODE_FLAG_DBLCLK = (1 << 12), + TDM_OUTPUT_MODE_FLAG_CLKDIV2 = (1 << 13), +} tdm_output_mode_flag; + +/** * @brief The size structure */ typedef struct _tdm_size { diff --git a/include/tdm_helper.h b/include/tdm_helper.h index e98ad9f..d526981 100644 --- a/include/tdm_helper.h +++ b/include/tdm_helper.h @@ -144,6 +144,16 @@ tdm_error tdm_helper_capture_output(tdm_output *output, tbm_surface_h dst_buffer, int x, int y, int w, int h, tdm_helper_capture_handler func, void *data); + +/** + * @brief Fill the display information to the reply buffer as string. + * @param[in] dpy A display object + * @param[out] reply the string buffer to be filled by this function. + * @param[out] len the length of the reply buffer + */ +void +tdm_helper_get_display_information(tdm_display *dpy, char *reply, int *len); + #ifdef __cplusplus } #endif diff --git a/include/tdm_log.h b/include/tdm_log.h index 69c2729..19abde5 100644 --- a/include/tdm_log.h +++ b/include/tdm_log.h @@ -65,6 +65,7 @@ enum { void tdm_log_enable_dlog(unsigned int enable); void tdm_log_enable_debug(unsigned int enable); +void tdm_log_set_debug_level(int level); void tdm_log_print(int level, const char *fmt, ...); #define TDM_DBG(fmt, args...) \ diff --git a/include/tdm_types.h b/include/tdm_types.h index 050dad8..6073646 100644 --- a/include/tdm_types.h +++ b/include/tdm_types.h @@ -59,135 +59,6 @@ extern "C" { #include -/** - * @brief The transform enumeration(rotate, flip) - */ -typedef enum { - TDM_TRANSFORM_NORMAL = 0, /**< no transform */ - TDM_TRANSFORM_90 = 1, /**< rotate 90 */ - TDM_TRANSFORM_180 = 2, /**< rotate 180 */ - TDM_TRANSFORM_270 = 3, /**< rotate 270 */ - TDM_TRANSFORM_FLIPPED = 4, /**< no rotate and horizontal flip */ - TDM_TRANSFORM_FLIPPED_90 = 5, /**< rotate 90 and horizontal flip */ - TDM_TRANSFORM_FLIPPED_180 = 6, /**< rotate 180 and horizontal flip */ - TDM_TRANSFORM_FLIPPED_270 = 7, /**< rotate 270 and horizontal flip */ -} tdm_transform; - -/** - * @brief The output connection status enumeration - * @details bit compatible with the libdrm definitions. - */ -typedef enum { - TDM_OUTPUT_TYPE_Unknown, /**< unknown */ - TDM_OUTPUT_TYPE_VGA, /**< VGA connection */ - TDM_OUTPUT_TYPE_DVII, /**< DVII connection */ - TDM_OUTPUT_TYPE_DVID, /**< DVID connection */ - TDM_OUTPUT_TYPE_DVIA, /**< DVIA connection */ - TDM_OUTPUT_TYPE_Composite, /**< Composite connection */ - TDM_OUTPUT_TYPE_SVIDEO, /**< SVIDEO connection */ - TDM_OUTPUT_TYPE_LVDS, /**< LVDS connection */ - TDM_OUTPUT_TYPE_Component, /**< Component connection */ - TDM_OUTPUT_TYPE_9PinDIN, /**< 9PinDIN connection */ - TDM_OUTPUT_TYPE_DisplayPort, /**< DisplayPort connection */ - TDM_OUTPUT_TYPE_HDMIA, /**< HDMIA connection */ - TDM_OUTPUT_TYPE_HDMIB, /**< HDMIB connection */ - TDM_OUTPUT_TYPE_TV, /**< TV connection */ - TDM_OUTPUT_TYPE_eDP, /**< eDP connection */ - TDM_OUTPUT_TYPE_VIRTUAL, /**< Virtual connection for WiFi Display */ - TDM_OUTPUT_TYPE_DSI, /**< DSI connection */ -} tdm_output_type; - -/** - * @brief The layer capability enumeration - * @details - * A layer can have one of CURSOR, PRIMARY and OVERLAY capability. And a layer - * also can have one of GRAPHIC and VIDEO capability. And a layer also can have - * SCALE and TRANSFORM capability.\n - * @par Example - * @code - * //For example - * capabilities = TDM_LAYER_CAPABILITY_PRIMARY | TDM_LAYER_CAPABILITY_GRAPHIC; - * capabilities = TDM_LAYER_CAPABILITY_OVERLAY | TDM_LAYER_CAPABILITY_GRAPHIC | TDM_LAYER_CAPABILITY_SCALE; - * capabilities = TDM_LAYER_CAPABILITY_OVERLAY | TDM_LAYER_CAPABILITY_GRAPHIC | TDM_LAYER_CAPABILITY_SCALE | TDM_LAYER_CAPABILITY_TRANSFORM; - * capabilities = TDM_LAYER_CAPABILITY_CURSOR | TDM_LAYER_CAPABILITY_GRAPHIC; - * capabilities = TDM_LAYER_CAPABILITY_OVERLAY | TDM_LAYER_CAPABILITY_VIDEO; - * @endcode - * @remark - * - When a video plays, in most of cases, video buffers will be displayed to - * a GRAPHIC layer after converting RGB buffers via PP. In this case, a backend - * module doesn't need to offer VIDEO layer. - * - But in case that s vendor wants to handle a video by their own way, - * a backend module offer VIDEO layers. And a display server will pass a video - * buffer to a VIDEO layer without converting. - */ -typedef enum { - TDM_LAYER_CAPABILITY_CURSOR = (1 << 0), /**< cursor */ - TDM_LAYER_CAPABILITY_PRIMARY = (1 << 1), /**< primary */ - TDM_LAYER_CAPABILITY_OVERLAY = (1 << 2), /**< overlay */ - TDM_LAYER_CAPABILITY_GRAPHIC = (1 << 4), /**< graphic */ - TDM_LAYER_CAPABILITY_VIDEO = (1 << 5), /**< video */ - TDM_LAYER_CAPABILITY_SCALE = (1 << 8), /**< if a layer has scale capability */ - TDM_LAYER_CAPABILITY_TRANSFORM = (1 << 9), /**< if a layer has transform capability */ - TDM_LAYER_CAPABILITY_SCANOUT = (1 << 10), /**< if a layer allows a scanout buffer only */ - TDM_LAYER_CAPABILITY_RESEVED_MEMORY = (1 << 11), /**< if a layer allows a reserved buffer only */ - TDM_LAYER_CAPABILITY_NO_CROP = (1 << 12), /**< if a layer has no cropping capability */ -} tdm_layer_capability; - -/** - * @brief The pp capability enumeration - */ -typedef enum { - TDM_PP_CAPABILITY_SYNC = (1 << 0), /**< The pp device supports synchronous operation */ - TDM_PP_CAPABILITY_ASYNC = (1 << 1), /**< The pp device supports asynchronous operation */ - TDM_PP_CAPABILITY_SCALE = (1 << 4), /**< The pp device supports scale operation */ - TDM_PP_CAPABILITY_TRANSFORM = (1 << 5), /**< The pp device supports transform operation */ -} tdm_pp_capability; - -/** - * @brief The capture capability enumeration - */ -typedef enum { - TDM_CAPTURE_CAPABILITY_OUTPUT = (1 << 0), /**< The capture device supports to dump a output */ - TDM_CAPTURE_CAPABILITY_LAYER = (1 << 1), /**< The capture device supports to dump a layer */ - TDM_CAPTURE_CAPABILITY_SCALE = (1 << 4), /**< The capture device supports scale operation */ - TDM_CAPTURE_CAPABILITY_TRANSFORM = (1 << 5), /**< The capture device supports transform operation */ -} tdm_capture_capability; - -/** - * @brief The output mode type enumeration - * @details bit compatible with the libdrm definitions. - */ -typedef enum { - TDM_OUTPUT_MODE_TYPE_BUILTIN = (1 << 0), - TDM_OUTPUT_MODE_TYPE_CLOCK_C = ((1 << 1) | TDM_OUTPUT_MODE_TYPE_BUILTIN), - TDM_OUTPUT_MODE_TYPE_CRTC_C = ((1 << 2) | TDM_OUTPUT_MODE_TYPE_BUILTIN), - TDM_OUTPUT_MODE_TYPE_PREFERRED = (1 << 3), - TDM_OUTPUT_MODE_TYPE_DEFAULT = (1 << 4), - TDM_OUTPUT_MODE_TYPE_USERDEF = (1 << 5), - TDM_OUTPUT_MODE_TYPE_DRIVER = (1 << 6), -} tdm_output_mode_type; - -/** - * @brief The output mode flag enumeration - * @details bit compatible with the libdrm definitions. - */ -typedef enum { - TDM_OUTPUT_MODE_FLAG_PHSYNC = (1 << 0), - TDM_OUTPUT_MODE_FLAG_NHSYNC = (1 << 1), - TDM_OUTPUT_MODE_FLAG_PVSYNC = (1 << 2), - TDM_OUTPUT_MODE_FLAG_NVSYNC = (1 << 3), - TDM_OUTPUT_MODE_FLAG_INTERLACE = (1 << 4), - TDM_OUTPUT_MODE_FLAG_DBLSCAN = (1 << 5), - TDM_OUTPUT_MODE_FLAG_CSYNC = (1 << 6), - TDM_OUTPUT_MODE_FLAG_PCSYNC = (1 << 7), - TDM_OUTPUT_MODE_FLAG_NCSYNC = (1 << 8), - TDM_OUTPUT_MODE_FLAG_HSKEW = (1 << 9), /* hskew provided */ - TDM_OUTPUT_MODE_FLAG_BCAST = (1 << 10), - TDM_OUTPUT_MODE_FLAG_PIXMUX = (1 << 11), - TDM_OUTPUT_MODE_FLAG_DBLCLK = (1 << 12), - TDM_OUTPUT_MODE_FLAG_CLKDIV2 = (1 << 13), -} tdm_output_mode_flag; - typedef enum { TDM_EVENT_LOOP_READABLE = (1 << 0), TDM_EVENT_LOOP_WRITABLE = (1 << 1), diff --git a/packaging/libtdm.spec b/packaging/libtdm.spec index 2e6bf7c..6e1472d 100644 --- a/packaging/libtdm.spec +++ b/packaging/libtdm.spec @@ -92,6 +92,7 @@ rm -f %{_unitdir_user}/default.target.wants/tdm-socket-user.path %defattr(-,root,root,-) %{TZ_SYS_RO_SHARE}/license/%{name} %{_libdir}/libtdm.so.* +%{_bindir}/tdm-dbg %{_unitdir}/tdm-socket.path %{_unitdir}/tdm-socket.service %{_unitdir_user}/tdm-socket-user.path @@ -126,6 +127,7 @@ rm -f %{_unitdir_user}/default.target.wants/tdm-socket-user.path %files tools %manifest %{name}.manifest +%{_bindir}/tdm-test-server %{_bindir}/tdm-test-client %changelog diff --git a/protocol/tdm.xml b/protocol/tdm.xml index cb161b5..e338958 100644 --- a/protocol/tdm.xml +++ b/protocol/tdm.xml @@ -8,11 +8,19 @@ TDM uses the wayland protocol to communicate between tdm client and tdm server. + + + + + + + + diff --git a/src/Makefile.am b/src/Makefile.am index 900d3e2..2af9638 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -22,4 +22,5 @@ libtdm_la_SOURCES = \ tdm_display.c \ tdm_pp.c \ tdm_capture.c \ + tdm_dbg_server.c \ tdm.c diff --git a/src/tdm.c b/src/tdm.c index 8a74ae2..0bbd7a7 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -386,7 +386,7 @@ _tdm_display_update_layer(tdm_private_display *private_display, private_layer = calloc(1, sizeof(tdm_private_layer)); TDM_RETURN_VAL_IF_FAIL(private_layer != NULL, TDM_ERROR_OUT_OF_MEMORY); - LIST_ADD(&private_layer->link, &private_output->layer_list); + LIST_ADDTAIL(&private_layer->link, &private_output->layer_list); private_layer->private_display = private_display; private_layer->private_output = private_output; private_layer->layer_backend = layer_backend; @@ -671,6 +671,7 @@ tdm_display_update(tdm_display *dpy) int tdm_debug_buffer; int tdm_debug_thread; int tdm_debug_mutex; +int tdm_debug_dump; static tdm_private_display *g_private_display; static pthread_mutex_t gLock = PTHREAD_MUTEX_INITIALIZER; @@ -678,41 +679,47 @@ static pthread_mutex_t gLock = PTHREAD_MUTEX_INITIALIZER; static tdm_error _tdm_display_check_module(tdm_backend_module *module) { - const char *name; - const char *vendor; int major, minor; TDM_INFO("TDM ABI version : %d.%d", TDM_MAJOR_VERSION, TDM_MINOR_VERSION); - name = module->name ? module->name : "unknown"; - vendor = module->vendor ? module->vendor : "unknown"; + if (!module->name) { + TDM_ERR("TDM backend doesn't have name"); + return TDM_ERROR_BAD_MODULE; + } + + if (!module->vendor) { + TDM_ERR("TDM backend doesn't have vendor"); + return TDM_ERROR_BAD_MODULE; + } + major = TDM_BACKEND_GET_ABI_MAJOR(module->abi_version); minor = TDM_BACKEND_GET_ABI_MINOR(module->abi_version); - TDM_INFO("TDM module name: %s", name); - TDM_INFO("'%s' vendor: %s", name, vendor); - TDM_INFO("'%s' version: %d.%d", name, major, minor); + TDM_INFO("TDM module name: %s", module->name); + TDM_INFO("'%s' vendor: %s", module->name, module->vendor); + TDM_INFO("'%s' version: %d.%d", module->name, major, minor); if (major != TDM_MAJOR_VERSION) { TDM_ERR("'%s' major version mismatch, %d != %d", - name, major, TDM_MAJOR_VERSION); + module->name, major, TDM_MAJOR_VERSION); return TDM_ERROR_BAD_MODULE; } if (minor > TDM_MINOR_VERSION) { TDM_ERR("'%s' minor version(%d) is newer than %d", - name, minor, TDM_MINOR_VERSION); + module->name, minor, TDM_MINOR_VERSION); return TDM_ERROR_BAD_MODULE; } if (!module->init) { - TDM_ERR("'%s' doesn't have init function", name); + TDM_ERR("'%s' doesn't have init function", module->name); return TDM_ERROR_BAD_MODULE; } if (!module->deinit) { - TDM_ERR("'%s' doesn't have deinit function", name); + TDM_ERR("'%s' doesn't have deinit function", module->name); return TDM_ERROR_BAD_MODULE; } @@ -898,6 +905,10 @@ tdm_display_init(tdm_error *error) if (debug && (strstr(debug, "1"))) tdm_debug_buffer = 1; + debug = getenv("TDM_DEBUG_DUMP"); + if (debug) + tdm_display_enable_dump(debug); + debug = getenv("TDM_DEBUG_THREAD"); if (debug && (strstr(debug, "1"))) tdm_debug_thread = 1; @@ -1038,3 +1049,42 @@ tdm_display_check_module_abi(tdm_private_display *private_display, int abimaj, i return 1; } +INTERN void +tdm_display_enable_debug(char *debug, int enable) +{ + if (!strncmp(debug, "TDM_DEBUG_BUFFER", 16)) + tdm_debug_buffer = enable; + else if (!strncmp(debug, "TDM_DEBUG_THREAD", 16)) + tdm_debug_thread = enable; + else if (!strncmp(debug, "TDM_DEBUG_MUTEX", 15)) + tdm_debug_mutex = enable; +} + +INTERN void +tdm_display_enable_dump(const char *dump_str) +{ + char temp[1024]; + char *arg; + char *end; + int flags = 0; + + snprintf(temp, sizeof(temp), "%s", dump_str); + arg = strtok_r(temp, ",", &end); + while (arg) { + if (!strncmp(arg, "all", 3)) { + flags = TDM_DUMP_FLAG_LAYER|TDM_DUMP_FLAG_PP|TDM_DUMP_FLAG_CAPTURE; + break; + } + else if (!strncmp(arg, "layer", 5)) + flags |= TDM_DUMP_FLAG_LAYER; + else if (!strncmp(arg, "pp", 2)) + flags |= TDM_DUMP_FLAG_PP; + else if (!strncmp(arg, "capture", 7)) + flags |= TDM_DUMP_FLAG_CAPTURE; + else if (!strncmp(arg, "none", 4)) + flags = 0; + arg = strtok_r(NULL, ",", &end); + } + + tdm_debug_dump = flags; +} diff --git a/src/tdm_capture.c b/src/tdm_capture.c index 8f562ab..cc79f62 100644 --- a/src/tdm_capture.c +++ b/src/tdm_capture.c @@ -40,6 +40,7 @@ #include "tdm.h" #include "tdm_backend.h" #include "tdm_private.h" +#include "tdm_helper.h" #define CAPTURE_FUNC_ENTRY() \ tdm_func_capture *func_capture; \ @@ -104,6 +105,13 @@ tdm_capture_cb_done(tdm_capture *capture_backend, tbm_surface_h buffer, if (private_capture->owner_tid != syscall(SYS_gettid)) TDM_NEVER_GET_HERE(); + if (tdm_debug_dump & TDM_DUMP_FLAG_CAPTURE) { + char str[TDM_PATH_LEN]; + static int i; + snprintf(str, TDM_PATH_LEN, "capture_%03d", i++); + tdm_helper_dump_buffer_str(buffer, str); + } + if (tdm_debug_buffer) TDM_INFO("capture(%p) done: %p", private_capture, buffer); diff --git a/src/tdm_dbg_server.c b/src/tdm_dbg_server.c new file mode 100644 index 0000000..76f895f --- /dev/null +++ b/src/tdm_dbg_server.c @@ -0,0 +1,409 @@ +/************************************************************************** + * + * libtdm + * + * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Contact: Eunchul Kim , + * JinYoung Jeon , + * Taeheon Kim , + * YoungJun Cho , + * SooChan Lim , + * Boram Park + * + * 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. + * +**************************************************************************/ + +#include +#include +#include +#include + +#include "tdm.h" +#include "tdm_private.h" +#include "tdm_helper.h" +#include "tdm_log.h" + +#define TDM_DBG_SERVER_ARGS_MAX 32 + +static void _tdm_dbg_server_usage(char *app_name, char *reply, int *len); + +static void +_tdm_dbg_server_query(unsigned int pid, char *cwd, int argc, char *argv[], char *reply, int *len, tdm_display *dpy) +{ + tdm_helper_get_display_information(dpy, reply, len); +} + +static void +_tdm_dbg_server_dpms(unsigned int pid, char *cwd, int argc, char *argv[], char *reply, int *len, tdm_display *dpy) +{ + tdm_output *output; + int output_idx, dpms_value; + char *arg; + char *end; + tdm_error ret; + + if (argc < 3) { + _tdm_dbg_server_usage(argv[0], reply, len); + return; + } + + arg = argv[2]; + output_idx = strtol(arg, &end, 10); + if (*end != ':') { + TDM_SNPRINTF(reply, len, "failed: no onoff value\n"); + return; + } + + arg = end + 1; + dpms_value = strtol(arg, &end, 10); + + output = tdm_display_get_output(dpy, output_idx, &ret); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE && output != NULL); + + ret = tdm_output_set_dpms(output, dpms_value); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + + TDM_SNPRINTF(reply, len, "done: DPMS %s\n", tdm_dpms_str(dpms_value)); +} + +static void +_tdm_dbg_server_debug(unsigned int pid, char *cwd, int argc, char *argv[], char *reply, int *len, tdm_display *dpy) +{ + int value; + char *arg; + char *end; + + if (argc < 3) { + _tdm_dbg_server_usage(argv[0], reply, len); + return; + } + + arg = argv[2]; + value = strtol(arg, &end, 10); + + tdm_log_set_debug_level(value); + + value = !!value; + tdm_log_enable_debug(value); + + TDM_SNPRINTF(reply, len, "debug '%s'\n", (value) ? "on" : "off"); + + if (argc > 3) { + char temp[TDM_PATH_LEN]; + char *arg; + char *end; + + snprintf(temp, TDM_PATH_LEN, "%s", argv[3]); + + arg = strtok_r(temp, TDM_DELIM, &end); + while (arg) { + tdm_display_enable_debug(arg, value); + if (value) + TDM_SNPRINTF(reply, len, "debuging '%s'...\n", arg); + arg = strtok_r(NULL, TDM_DELIM, &end); + } + } +} + +static void +_tdm_dbg_server_log_path(unsigned int pid, char *cwd, int argc, char *argv[], char *reply, int *len, tdm_display *dpy) +{ + static int old_stdout = -1; + char fd_name[TDM_PATH_LEN]; + int log_fd = -1; + FILE *log_fl; + char *path; + + if (argc < 3) { + _tdm_dbg_server_usage(argv[0], reply, len); + return; + } + + if (old_stdout == -1) + old_stdout = dup(STDOUT_FILENO); + + path = argv[2]; + TDM_DBG_RETURN_IF_FAIL(path != NULL); + + tdm_log_enable_dlog(0); + + if (!strncmp(path, "dlog", 4)) { + tdm_log_enable_dlog(1); + goto done; + } else if (!strncmp(path, "console", 7)) + snprintf(fd_name, TDM_PATH_LEN, "/proc/%d/fd/1", pid); + else { + if (path[0] == '/') + snprintf(fd_name, TDM_PATH_LEN, "%s", path); + else { + if (cwd) + snprintf(fd_name, TDM_PATH_LEN, "%s/%s", cwd, path); + else + snprintf(fd_name, TDM_PATH_LEN, "%s", path); + } + } + + log_fl = fopen(fd_name, "a"); + if (!log_fl) { + TDM_SNPRINTF(reply, len, "failed: open file(%s)\n", fd_name); + return; + } + + fflush(stderr); + close(STDOUT_FILENO); + + setvbuf(log_fl, NULL, _IOLBF, 512); + log_fd = fileno(log_fl); + + dup2(log_fd, STDOUT_FILENO); + fclose(log_fl); +done: + TDM_SNPRINTF(reply, len, "log path: '%s'\n", path); +} + +static void +_tdm_dbg_server_prop(unsigned int pid, char *cwd, int argc, char *argv[], char *reply, int *len, tdm_display *dpy) +{ + tdm_output *output; + tdm_output *layer = NULL; + int output_idx, layer_idx = -1; + int cnt, i, done = 0; + tdm_value value; + char temp[TDM_PATH_LEN]; + char *prop_name; + char *arg; + char *end; + tdm_error ret; + const tdm_prop *props; + + if (argc < 3) { + _tdm_dbg_server_usage(argv[0], reply, len); + return; + } + + snprintf(temp, TDM_PATH_LEN, "%s", argv[2]); + arg = temp; + + output_idx = strtol(arg, &end, 10); + if (*end == ',') { + arg = end + 1; + layer_idx = strtol(arg, &end, 10); + } + + if (*end != ':') { + TDM_SNPRINTF(reply, len, "failed: no prop_name\n"); + return; + } + + arg = end + 1; + prop_name = strtok_r(arg, ",", &end); + + if (*end == '\0') { + TDM_SNPRINTF(reply, len, "failed: no value\n"); + return; + } + + arg = strtok_r(NULL, TDM_DELIM, &end); + value.u32 = strtol(arg, &end, 10); + + output = tdm_display_get_output(dpy, output_idx, &ret); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE && output != NULL); + + if (layer_idx != -1) { + layer = tdm_output_get_layer(output, layer_idx, &ret); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE && layer != NULL); + } + + if (layer) { + ret = tdm_layer_get_available_properties(layer, &props, &cnt); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + + for (i = 0; i < cnt; i++) { + if (!strncmp(props[i].name, prop_name, TDM_NAME_LEN)) { + ret = tdm_layer_set_property(layer, props[i].id, value); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + done = 1; + break; + } + } + } else { + ret = tdm_output_get_available_properties(output, &props, &cnt); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + + for (i = 0; i < cnt; i++) { + if (!strncmp(props[i].name, prop_name, TDM_NAME_LEN)) { + ret = tdm_output_set_property(output, props[i].id, value); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + done = 1; + break; + } + } + } + + if (done) + TDM_SNPRINTF(reply, len, "done: %s:%d \n", prop_name, value.u32); + else + TDM_SNPRINTF(reply, len, "no '%s' propperty \n", prop_name); +} + +static void +_tdm_dbg_server_dump(unsigned int pid, char *cwd, int argc, char *argv[], char *reply, int *len, tdm_display *dpy) +{ + if (argc < 3) { + _tdm_dbg_server_usage(argv[0], reply, len); + return; + } + + tdm_display_enable_dump((const char*)argv[2]); + + TDM_SNPRINTF(reply, len, "%s done\n", argv[2]); +} + +static struct { + const char *opt; + void (*func)(unsigned int pid, char *cwd, int argc, char *argv[], char *reply, int *len, tdm_display *dpy); + const char *desc; + const char *arg; + const char *ex; +} option_proc[] = { + { + "info", _tdm_dbg_server_query, + "show tdm output, layer information", NULL, NULL + }, + { + "dpms", _tdm_dbg_server_dpms, + "set output dpms", ":", "0:3 or 0:0" + }, + { + "debug", _tdm_dbg_server_debug, + "enable the debug level log", + "", + "0 or 1" + }, + { + "log_path", _tdm_dbg_server_log_path, + "set the log path (console,dlog,filepath)", + "", + "console" + }, + { + "prop", _tdm_dbg_server_prop, + "set the property of a output or a layer", + "[,]:,", + NULL + }, + { + "dump", _tdm_dbg_server_dump, + "dump buffers (type: layer, pp, capture, none)", + "[,[,...]]", + NULL + }, +}; + +static void +_tdm_dbg_server_usage(char *app_name, char *reply, int *len) +{ + int opt_size = sizeof(option_proc) / sizeof(option_proc[0]); + int i; + + TDM_SNPRINTF(reply, len, "usage: %s \n\n", app_name); + + for (i = 0; i < opt_size; i++) { + TDM_SNPRINTF(reply, len, "\t-%s\t%s\n", option_proc[i].opt, option_proc[i].desc); + if (option_proc[i].arg) + TDM_SNPRINTF(reply, len, "\t\t %s\n", option_proc[i].arg); + if (option_proc[i].ex) + TDM_SNPRINTF(reply, len, "\t\t ex) %s\n", option_proc[i].ex); + TDM_SNPRINTF(reply, len, "\n"); + } +} + +static void +_tdm_dbg_server_command(unsigned int pid, char *cwd, tdm_display *dpy, int argc, char *argv[], char *reply, int *len) +{ + int opt_size = sizeof(option_proc) / sizeof(option_proc[0]); + int i; + + if (argc < 2) { + _tdm_dbg_server_usage(argv[0], reply, len); + return; + } + + for (i = 0; i < opt_size; i++) { + if (argv[1][0] == '-' && !strncmp(argv[1] + 1, option_proc[i].opt, 32)) { + if (option_proc[i].func) { + option_proc[i].func(pid, cwd, argc, argv, reply, len, dpy); + return; + } else { + TDM_SNPRINTF(reply, len, "'%s' not implemented.\n", argv[1]); + return; + } + } + } + + _tdm_dbg_server_usage(argv[0], reply, len); + return; +} + + +INTERN void +tdm_dbg_server_command(tdm_display *dpy, const char *options, char *reply, int *len) +{ + unsigned int pid; + char cwd[1024]; + int argc = 0; + char *argv[TDM_DBG_SERVER_ARGS_MAX] = {0,}; + char temp[1024]; + char *arg; + char *end = NULL, *e; + + snprintf(temp, sizeof(temp), "%s", options); + + arg = strtok_r(temp, " ", &end); + if (!arg) { + TDM_SNPRINTF(reply, len, "no pid for tdm-dbg"); + return; + } + pid = strtol(arg, &e, 10); + + arg = strtok_r(NULL, " ", &end); + if (!arg) { + TDM_SNPRINTF(reply, len, "no cwd for tdm-dbg"); + return; + } + snprintf(cwd, sizeof(cwd), "%s", arg); + + TDM_DBG("pid(%d) cwd(%s)", pid, cwd); + + argv[argc] = strtok_r(NULL, " ", &end); + while (argv[argc]) { + argc++; + if (argc == TDM_DBG_SERVER_ARGS_MAX) { + TDM_SNPRINTF(reply, len, "too many arguments for tdm-dbg"); + break; + } + argv[argc] = strtok_r(NULL, " ", &end); + } + + _tdm_dbg_server_command(pid, cwd, dpy, argc, argv, reply, len); +} diff --git a/src/tdm_display.c b/src/tdm_display.c index fd07d00..fd4dff6 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -355,6 +355,32 @@ tdm_display_handle_events(tdm_display *dpy) return ret; } +EXTERN tdm_error +tdm_display_get_backend_info(tdm_display *dpy, const char **name, + const char **vendor, int *major, int *minor) +{ + tdm_backend_module *module_data; + + DISPLAY_FUNC_ENTRY(); + + _pthread_mutex_lock(&private_display->lock); + + module_data = private_display->module_data; + + if (name) + *name = module_data->name; + if (vendor) + *vendor = module_data->vendor; + if (major) + *major = TDM_BACKEND_GET_ABI_MAJOR(module_data->abi_version); + if (minor) + *minor = TDM_BACKEND_GET_ABI_MINOR(module_data->abi_version); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + EXTERN tdm_pp * tdm_display_create_pp(tdm_display *dpy, tdm_error *error) { @@ -1430,6 +1456,15 @@ tdm_layer_set_buffer(tdm_layer *layer, tbm_surface_h buffer) _pthread_mutex_lock(&private_display->lock); + if (tdm_debug_dump & TDM_DUMP_FLAG_LAYER && + !(private_layer->caps.capabilities & TDM_LAYER_CAPABILITY_VIDEO)) { + char str[TDM_PATH_LEN]; + static int i; + snprintf(str, TDM_PATH_LEN, "layer_%d_%d_%03d", + private_output->pipe, private_layer->caps.zpos, i++); + tdm_helper_dump_buffer_str(buffer, str); + } + func_layer = &private_display->func_layer; if (private_layer->usable) @@ -1447,7 +1482,7 @@ tdm_layer_set_buffer(tdm_layer *layer, tbm_surface_h buffer) TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE); /* dump buffer */ - if (tdm_dump_enable) + if (tdm_dump_enable && !(private_layer->caps.capabilities & TDM_LAYER_CAPABILITY_VIDEO)) _tdm_layer_dump_buffer(layer, buffer); if (ret == TDM_ERROR_NONE) { diff --git a/src/tdm_event_loop.c b/src/tdm_event_loop.c index 500bba9..4496806 100644 --- a/src/tdm_event_loop.c +++ b/src/tdm_event_loop.c @@ -379,6 +379,10 @@ _tdm_event_loop_timer_func(void *data) private_display = timer_source->private_display; + /* TDM event_loop function is actually for TDM backend module. When we call the + * backend's functions, we have to lock the mutex. TDM backend shouldn't consider + * mutex things. + */ _pthread_mutex_lock(&private_display->lock); timer_source->func(timer_source->user_data); _pthread_mutex_unlock(&private_display->lock); diff --git a/src/tdm_helper.c b/src/tdm_helper.c index 4172fe8..b9ad240 100644 --- a/src/tdm_helper.c +++ b/src/tdm_helper.c @@ -51,7 +51,7 @@ #define PNG_DEPTH 8 -static const char *dump_prefix[2] = {"png", "yuv"}; +static const char *file_exts[2] = {"png", "yuv"}; int tdm_dump_enable; @@ -168,12 +168,41 @@ _tdm_helper_dump_png(const char *file, const void *data, int width, fclose(fp); } +INTERN void +tdm_helper_dump_buffer_str(tbm_surface_h buffer, const char *str) +{ + tbm_surface_info_s info; + const char *dir = "/tmp/dump-tdm"; + const char *ext; + char file[TDM_PATH_LEN]; + int ret, bw; + + TDM_RETURN_IF_FAIL(buffer != NULL); + TDM_RETURN_IF_FAIL(str != NULL); + + ret = tbm_surface_get_info(buffer, &info); + TDM_RETURN_IF_FAIL(ret == TBM_SURFACE_ERROR_NONE); + + if (info.format == TBM_FORMAT_ARGB8888 || info.format == TBM_FORMAT_XRGB8888) { + ext = file_exts[0]; + bw = info.planes[0].stride >> 2; + } else { + ext = file_exts[1]; + bw = info.planes[0].stride; + } + + snprintf(file, TDM_PATH_LEN, "%s/%c%c%c%c_%dx%d_%s.%s", + dir, FOURCC_STR(info.format), bw, info.height, str, ext); + + tdm_helper_dump_buffer(buffer, file); +} + EXTERN void tdm_helper_dump_buffer(tbm_surface_h buffer, const char *file) { tbm_surface_info_s info; int len, ret; - const char *prefix; + const char *ext; TDM_RETURN_IF_FAIL(buffer != NULL); TDM_RETURN_IF_FAIL(file != NULL); @@ -183,11 +212,11 @@ tdm_helper_dump_buffer(tbm_surface_h buffer, const char *file) len = strnlen(file, 1024); if (info.format == TBM_FORMAT_ARGB8888 || info.format == TBM_FORMAT_XRGB8888) - prefix = dump_prefix[0]; + ext = file_exts[0]; else - prefix = dump_prefix[1]; + ext = file_exts[1]; - if (strncmp(file + (len - 3), prefix, 3)) { + if (strncmp(file + (len - 3), ext, 3)) { TDM_ERR("can't dump to '%s' file", file + (len - 3)); tbm_surface_unmap(buffer); return; @@ -462,3 +491,233 @@ tdm_helper_capture_output(tdm_output *output, tbm_surface_h dst_buffer, return TDM_ERROR_NONE; } +EXTERN void +tdm_helper_get_display_information(tdm_display *dpy, char *reply, int *len) +{ + const char *name, *vendor; + int major, minor; + tdm_error ret; + int i, count; + tdm_output *output; + const tdm_prop *props; + int min_w, min_h, max_w, max_h, preferred_align; + const tbm_format *formats; + tdm_display_capability display_caps; + + ret = tdm_display_get_backend_info(dpy, &name, &vendor, &major, &minor); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + + TDM_SNPRINTF(reply, len, "TDM backend name: %s\n", name); + TDM_SNPRINTF(reply, len, "TDM backend vendor: %s\n", vendor); + TDM_SNPRINTF(reply, len, "TDM backend version: %d.%d\n\n", major, minor); + + ret = tdm_display_get_output_count(dpy, &count); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + + TDM_SNPRINTF(reply, len, "[Output information]\n"); + TDM_SNPRINTF(reply, len, "-------------------------------------------------------------------------------------------\n"); + TDM_SNPRINTF(reply, len, "idx maker model name type status dpms subpix prefer min max phy\n"); + TDM_SNPRINTF(reply, len, "-------------------------------------------------------------------------------------------\n"); + + for (i = 0; i < count; i++) { + /* idx maker model name type status dpms subpix prefer min max phy */ + const char *maker, *model, *name; + tdm_output_type type; + tdm_output_conn_status status; + unsigned int subpixel; + unsigned int mmWidth, mmHeight; + tdm_output_dpms dpms; + const tdm_output_mode *mode, *modes; + int j, cnt; + + output = tdm_display_get_output(dpy, i, &ret); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + ret = tdm_output_get_model_info(output, &maker, &model, &name); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + ret = tdm_output_get_output_type(output, &type); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + ret = tdm_output_get_conn_status(output, &status); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + ret = tdm_output_get_dpms(output, &dpms); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + ret = tdm_output_get_subpixel(output, &subpixel); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + ret = tdm_output_get_available_size(output, &min_w, &min_h, &max_w, &max_h, &preferred_align); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + ret = tdm_output_get_physical_size(output, &mmWidth, &mmHeight); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + + TDM_SNPRINTF(reply, len, "%d %s %s %s %s %s %s %d %d %dx%d %dx%d %dx%d\n", + i, maker, model, name, tdm_conn_str(type), tdm_status_str(status), + tdm_dpms_str(dpms), subpixel, preferred_align, + min_w, min_h, max_w, max_h, mmWidth, mmHeight); + + ret = tdm_output_get_mode(output, &mode); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + + ret = tdm_output_get_available_modes(output, &modes, &cnt); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + + TDM_SNPRINTF(reply, len, "\t%d modes:\n", cnt); + + if (cnt > 0) { + TDM_SNPRINTF(reply, len, "\t\tname refresh (Hz) hdisp hss hse htot vdisp vss vse vtot\n"); + for (j = 0; j < cnt; j++) { + char *current = (mode == modes + j) ? "*" : " "; + TDM_SNPRINTF(reply, len, "\t\t%s%s %d %d %d %d %d %d %d %d %d ", + current, + modes[j].name, + modes[j].vrefresh, + modes[j].hdisplay, + modes[j].hsync_start, + modes[j].hsync_end, + modes[j].htotal, + modes[j].vdisplay, + modes[j].vsync_start, + modes[j].vsync_end, + modes[j].vtotal); + tdm_mode_flag_str(modes[j].flags, &reply, len); + TDM_SNPRINTF(reply, len, " "); + tdm_mode_type_str(modes[j].type, &reply, len); + TDM_SNPRINTF(reply, len, "\n"); + } + } + + ret = tdm_output_get_available_properties(output, &props, &cnt); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + + TDM_SNPRINTF(reply, len, "\t%d properties:\n", cnt); + if (cnt > 0) { + TDM_SNPRINTF(reply, len, "\t\tname idx value\n"); + for (j = 0; j < cnt; j++) { + tdm_value value; + ret = tdm_output_get_property(output, props[j].id, &value); + TDM_SNPRINTF(reply, len, "\t\t%s %d %d\n", + props[j].name, + props[j].id, + value.u32); + } + } + TDM_SNPRINTF(reply, len, "\n"); + } + + TDM_SNPRINTF(reply, len, "[Layer information]\n"); + for (i = 0; i < count; i++) { + int j, cnt; + + output = tdm_display_get_output(dpy, i, &ret); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + + ret = tdm_output_get_layer_count(output, &cnt); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + + if (cnt > 0) { + TDM_SNPRINTF(reply, len, "-----------------------------------------------------\n"); + TDM_SNPRINTF(reply, len, "idx output zpos buf info caps\n"); + TDM_SNPRINTF(reply, len, "-----------------------------------------------------\n"); + for (j = 0; j < cnt; j++) { + tdm_layer *layer; + tbm_surface_h buf; + tdm_layer_capability layer_caps; + int k, c, zpos; + tdm_info_layer info; + + memset(&info, 0, sizeof info); + + layer = tdm_output_get_layer(output, j, &ret); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + ret = tdm_layer_get_capabilities(layer, &layer_caps); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + ret = tdm_layer_get_zpos(layer, &zpos); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + + ret = tdm_layer_get_info(layer, &info); + buf = tdm_layer_get_displaying_buffer(layer, &ret); + + if (info.src_config.format) + TDM_SNPRINTF(reply, len, "%d %d %d %p %c%c%c%c %dx%d (%d,%d %dx%d) (%d,%d %dx%d) trans(%d) ", + j, i, zpos, buf, + FOURCC_STR(info.src_config.format), info.src_config.size.h, info.src_config.size.v, + info.src_config.pos.x, info.src_config.pos.y, info.src_config.pos.w, info.src_config.pos.h, + info.dst_pos.x, info.dst_pos.y, info.dst_pos.w, info.dst_pos.h, + info.transform); + else + TDM_SNPRINTF(reply, len, "%d %d %d %p %c%c%c%c %dx%d (%d,%d %dx%d) (%d,%d %dx%d) trans(%d) ", + j, i, zpos, buf, + 'N', 'O', 'N', 'E', info.src_config.size.h, info.src_config.size.v, + info.src_config.pos.x, info.src_config.pos.y, info.src_config.pos.w, info.src_config.pos.h, + info.dst_pos.x, info.dst_pos.y, info.dst_pos.w, info.dst_pos.h, + info.transform); + tdm_layer_caps_str(layer_caps, &reply, len); + TDM_SNPRINTF(reply, len, "\n"); + + ret = tdm_layer_get_available_properties(layer, &props, &c); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + TDM_SNPRINTF(reply, len, "\t%d properties:\n", c); + if (c > 0) { + TDM_SNPRINTF(reply, len, "\t\tname idx value\n"); + for (k = 0; k < c; k++) { + tdm_value value; + ret = tdm_layer_get_property(layer, props[k].id, &value); + TDM_SNPRINTF(reply, len, "\t\t%s %d %d\n", + props[k].name, + props[k].id, + value.u32); + } + } + + ret = tdm_layer_get_available_formats(layer, &formats, &c); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + TDM_SNPRINTF(reply, len, "\tformats:"); + for (k = 0; k < c; k++) + TDM_SNPRINTF(reply, len, " %c%c%c%c", FOURCC_STR(formats[k])); + TDM_SNPRINTF(reply, len, "\n"); + } + } + } + + TDM_SNPRINTF(reply, len, "\n"); + + ret = tdm_display_get_capabilities(dpy, &display_caps); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + + if (display_caps & TDM_DISPLAY_CAPABILITY_PP) { + tdm_pp_capability pp_caps; + + ret = tdm_display_get_pp_capabilities(dpy, &pp_caps); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + ret = tdm_display_get_pp_available_formats(dpy, &formats, &count); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + ret = tdm_display_get_pp_available_size(dpy, &min_w, &min_h, &max_w, &max_h, &preferred_align); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + + TDM_SNPRINTF(reply, len, "[PP information]\n"); + TDM_SNPRINTF(reply, len, "caps: "); + tdm_pp_caps_str(pp_caps, &reply, len); + TDM_SNPRINTF(reply, len, "\n"); + TDM_SNPRINTF(reply, len, "formats: "); + for (i = 0; i < count; i++) + TDM_SNPRINTF(reply, len, " %c%c%c%c", FOURCC_STR(formats[i])); + TDM_SNPRINTF(reply, len, "\n"); + TDM_SNPRINTF(reply, len, "size: min(%dx%d) max(%dx%d) preferred(%d)\n", + min_w, min_h, max_w, max_h, preferred_align); + } + + if (display_caps & TDM_DISPLAY_CAPABILITY_CAPTURE) { + tdm_capture_capability capture_caps; + + ret = tdm_display_get_capture_capabilities(dpy, &capture_caps); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + ret = tdm_display_get_catpure_available_formats(dpy, &formats, &count); + TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + + TDM_SNPRINTF(reply, len, "[Capture information]\n"); + TDM_SNPRINTF(reply, len, "caps: "); + tdm_capture_caps_str(capture_caps, &reply, len); + TDM_SNPRINTF(reply, len, "\n"); + TDM_SNPRINTF(reply, len, "formats: "); + for (i = 0; i < count; i++) + TDM_SNPRINTF(reply, len, " %c%c%c%c", FOURCC_STR(formats[i])); + TDM_SNPRINTF(reply, len, "\n"); + } +} diff --git a/src/tdm_macro.h b/src/tdm_macro.h index 3983c68..d56267d 100644 --- a/src/tdm_macro.h +++ b/src/tdm_macro.h @@ -42,11 +42,14 @@ #include #include +#include #ifdef __cplusplus extern "C" { #endif +#define TDM_SERVER_REPLY_MSG_LEN 8192 + #undef EXTERN #undef DEPRECATED #undef INTERN @@ -101,6 +104,12 @@ extern "C" { goto dst; \ } \ } +#define TDM_EXIT_IF_FAIL(cond) { \ + if (!(cond)) { \ + TDM_ERR("'%s' failed", #cond); \ + exit(0); \ + } \ +} #define TDM_NEVER_GET_HERE() TDM_WRN("** NEVER GET HERE **") @@ -113,12 +122,23 @@ extern "C" { } \ } while (0) +#define TDM_DBG_RETURN_IF_FAIL(cond) { \ + if (!(cond)) { \ + TDM_SNPRINTF(reply, len, "[%s %d] '%s' failed\n", __func__, __LINE__, #cond); \ + return; \ + } \ +} + #define C(b, m) (((b) >> (m)) & 0xFF) #define B(c, s) ((((unsigned int)(c)) & 0xff) << (s)) #define FOURCC(a, b, c, d) (B(d, 24) | B(c, 16) | B(b, 8) | B(a, 0)) #define FOURCC_STR(id) C(id, 0), C(id, 8), C(id, 16), C(id, 24) #define FOURCC_ID(str) FOURCC(((char*)str)[0], ((char*)str)[1], ((char*)str)[2], ((char*)str)[3]) +#define IS_RGB(f) ((f) == TBM_FORMAT_XRGB8888 || (f) == TBM_FORMAT_ARGB8888) +/* don't using !,$,# */ +#define TDM_DELIM "@^&*+-|,:~" +#define TDM_ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1)) #define TDM_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) @@ -144,7 +164,6 @@ static struct tdm_type_name tdm_dpms_names[] = { { TDM_OUTPUT_DPMS_SUSPEND, "suspend" }, { TDM_OUTPUT_DPMS_OFF, "off" }, }; - TDM_TYPE_NAME_FN(dpms) static struct tdm_type_name tdm_status_names[] = { @@ -152,9 +171,121 @@ static struct tdm_type_name tdm_status_names[] = { { TDM_OUTPUT_CONN_STATUS_CONNECTED, "connected" }, { TDM_OUTPUT_CONN_STATUS_MODE_SETTED, "mode_setted" }, }; - TDM_TYPE_NAME_FN(status) +static struct tdm_type_name tdm_conn_names[] = { + { TDM_OUTPUT_TYPE_Unknown, "Unknown" }, + { TDM_OUTPUT_TYPE_VGA, "VGA" }, + { TDM_OUTPUT_TYPE_DVII, "DVII" }, + { TDM_OUTPUT_TYPE_DVID, "DVID" }, + { TDM_OUTPUT_TYPE_DVIA, "DVIA" }, + { TDM_OUTPUT_TYPE_Composite, "Composite" }, + { TDM_OUTPUT_TYPE_SVIDEO, "SVIDEO" }, + { TDM_OUTPUT_TYPE_LVDS, "LVDS" }, + { TDM_OUTPUT_TYPE_Component, "Component" }, + { TDM_OUTPUT_TYPE_9PinDIN, "9PinDIN" }, + { TDM_OUTPUT_TYPE_DisplayPort, "DisplayPort" }, + { TDM_OUTPUT_TYPE_HDMIA, "HDMIA" }, + { TDM_OUTPUT_TYPE_HDMIB, "HDMIB" }, + { TDM_OUTPUT_TYPE_TV, "TV" }, + { TDM_OUTPUT_TYPE_eDP, "eDP" }, + { TDM_OUTPUT_TYPE_VIRTUAL, "VIRTUAL" }, + { TDM_OUTPUT_TYPE_DSI, "DSI" }, +}; +TDM_TYPE_NAME_FN(conn) + + +#define TDM_BIT_NAME_FB(res) \ +static inline const char * tdm_##res##_str(int type, char **reply, int *len) \ +{ \ + unsigned int i; \ + const char *sep = ""; \ + for (i = 0; i < TDM_ARRAY_SIZE(tdm_##res##_names); i++) { \ + if (type & (1 << i)) { \ + TDM_SNPRINTF(*reply, len, "%s%s", sep, tdm_##res##_names[i]); \ + sep = ","; \ + } \ + } \ + return NULL; \ +} + +static const char *tdm_mode_type_names[] = { + "builtin", + "clock_c", + "crtc_c", + "preferred", + "default", + "userdef", + "driver", +}; +TDM_BIT_NAME_FB(mode_type) + +static const char *tdm_mode_flag_names[] = { + "phsync", + "nhsync", + "pvsync", + "nvsync", + "interlace", + "dblscan", + "csync", + "pcsync", + "ncsync", + "hskew", + "bcast", + "pixmux", + "dblclk", + "clkdiv2" +}; +TDM_BIT_NAME_FB(mode_flag) + +static const char *tdm_layer_caps_names[] = { + "cursor", + "primary", + "overlay", + "", + "graphic", + "video", + "", + "", + "scale", + "transform", + "scanout", + "reserved", + "no_crop", +}; +TDM_BIT_NAME_FB(layer_caps) + +static const char *tdm_pp_caps_names[] = { + "sync", + "async", + "scale", + "transform", +}; +TDM_BIT_NAME_FB(pp_caps) + +static const char *tdm_capture_caps_names[] = { + "output", + "layer", + "scale", + "transform", +}; +TDM_BIT_NAME_FB(capture_caps) + +static inline char* +strtostr(char *buf, int len, char *str, char *delim) +{ + char *end; + end = strpbrk(str, delim); + if (end) + len = ((end - str + 1) < len) ? (end - str + 1) : len; + else { + int l = strlen(str); + len = ((l + 1) < len) ? (l + 1) : len; + } + snprintf(buf, len, "%s", str); + return str + len - 1; +} + #ifdef __cplusplus } #endif diff --git a/src/tdm_pp.c b/src/tdm_pp.c index 17ea9df..91a1239 100644 --- a/src/tdm_pp.c +++ b/src/tdm_pp.c @@ -40,6 +40,7 @@ #include "tdm.h" #include "tdm_backend.h" #include "tdm_private.h" +#include "tdm_helper.h" typedef struct _tdm_pp_private_buffer { tbm_surface_h src; @@ -136,6 +137,13 @@ tdm_pp_cb_done(tdm_pp *pp_backend, tbm_surface_h src, tbm_surface_h dst, if (private_pp->owner_tid != syscall(SYS_gettid)) TDM_NEVER_GET_HERE(); + if (tdm_debug_dump & TDM_DUMP_FLAG_PP) { + char str[TDM_PATH_LEN]; + static int i; + snprintf(str, TDM_PATH_LEN, "pp_dst_%03d", i++); + tdm_helper_dump_buffer_str(dst, str); + } + if (tdm_debug_buffer) TDM_INFO("pp(%p) done: src(%p) dst(%p)", private_pp, src, dst); @@ -363,6 +371,13 @@ tdm_pp_attach(tdm_pp *pp, tbm_surface_h src, tbm_surface_h dst) } } + if (tdm_debug_dump & TDM_DUMP_FLAG_PP) { + char str[TDM_PATH_LEN]; + static int i; + snprintf(str, TDM_PATH_LEN, "pp_src_%03d", i++); + tdm_helper_dump_buffer_str(src, str); + } + pp_buffer = calloc(1, sizeof *pp_buffer); if (!pp_buffer) { _pthread_mutex_unlock(&private_display->lock); diff --git a/src/tdm_private.h b/src/tdm_private.h index a9ad25e..b2b61e6 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -76,6 +76,7 @@ extern "C" { extern int tdm_debug_buffer; extern int tdm_debug_mutex; extern int tdm_debug_thread; +extern int tdm_debug_dump; #ifdef HAVE_TTRACE #include @@ -91,6 +92,12 @@ typedef enum { TDM_CAPTURE_TARGET_LAYER, } tdm_capture_target; +enum { + TDM_DUMP_FLAG_LAYER = (1 << 0), + TDM_DUMP_FLAG_PP = (1 << 1), + TDM_DUMP_FLAG_CAPTURE = (1 << 2), +}; + typedef struct _tdm_private_display tdm_private_display; typedef struct _tdm_private_output tdm_private_output; typedef struct _tdm_private_layer tdm_private_layer; @@ -446,6 +453,8 @@ tdm_server_init(tdm_private_loop *private_loop); void tdm_server_deinit(tdm_private_loop *private_loop); +void +tdm_helper_dump_buffer_str(tbm_surface_h buffer, const char *str); unsigned long tdm_helper_get_time_in_millis(void); unsigned long @@ -513,7 +522,10 @@ static inline int TDM_MUTEX_IS_LOCKED(void) tdm_error tdm_display_update_output(tdm_private_display *private_display, tdm_output *output_backend, int pipe); - +void +tdm_display_enable_debug(char *debug, int enable); +void +tdm_display_enable_dump(const char *dump_str); /** * @brief The tdm vblank object @@ -536,6 +548,9 @@ tdm_vblank_set_enable_fake(tdm_vblank *vblank, unsigned int enable_fake); tdm_error tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec, unsigned int interval, tdm_vblank_handler func, void *user_data); +void +tdm_dbg_server_command(tdm_display *dpy, const char *options, char *reply, int *len); + #ifdef __cplusplus } #endif diff --git a/src/tdm_server.c b/src/tdm_server.c index 7c18228..a20984f 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -413,8 +413,22 @@ _tdm_server_cb_create_output(struct wl_client *client, struct wl_resource *resou wl_tdm_output_send_connection(output_resource, status); } +static void +_tdm_server_cb_debug(struct wl_client *client, struct wl_resource *resource, const char *options) +{ + tdm_private_server *private_server = wl_resource_get_user_data(resource); + tdm_private_loop *private_loop = private_server->private_loop; + + char message[TDM_SERVER_REPLY_MSG_LEN]; + int size = sizeof(message); + + tdm_dbg_server_command(private_loop->dpy, options, message, &size); + wl_tdm_send_debug_done(resource, message); +} + static const struct wl_tdm_interface tdm_implementation = { _tdm_server_cb_create_output, + _tdm_server_cb_debug, }; static void diff --git a/tools/Makefile.am b/tools/Makefile.am index 05edfb5..2057e4a 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,9 +1,23 @@ bin_PROGRAMS = \ + tdm-test-server \ tdm-test-client +#tdm-test-server +tdm_test_server_SOURCES = \ + buffers.c \ + tdm_test_server.c +tdm_test_server_LDFLAGS = ${LDFLAGS} +tdm_test_server_CFLAGS = \ + $(TDM_CFLAGS) \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/src +tdm_test_server_LDADD = \ + $(TDM_LIBS) \ + $(top_builddir)/src/libtdm.la + +#tdm-test-cliet tdm_test_client_SOURCES = \ tdm_test_client.c - tdm_test_client_LDFLAGS = ${LDFLAGS} tdm_test_client_CFLAGS = \ $(TDM_CFLAGS) \ diff --git a/tools/buffers.c b/tools/buffers.c new file mode 100644 index 0000000..53ed615 --- /dev/null +++ b/tools/buffers.c @@ -0,0 +1,950 @@ +/* + * DRM based mode setting test program + * Copyright 2008 Tungsten Graphics + * Jakob Bornecrantz + * Copyright 2008 Intel Corporation + * Jesse Barnes + * + * 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, sublicense, + * 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 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 NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include "tdm_macro.h" +#include "buffers.h" + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +/* ----------------------------------------------------------------------------- + * Formats + */ + +struct color_component { + unsigned int length; + unsigned int offset; +}; + +struct rgb_info { + struct color_component red; + struct color_component green; + struct color_component blue; + struct color_component alpha; +}; + +enum yuv_order { + YUV_YCbCr = 1, + YUV_YCrCb = 2, + YUV_YC = 4, + YUV_CY = 8, +}; + +struct yuv_info { + enum yuv_order order; + unsigned int xsub; + unsigned int ysub; + unsigned int chroma_stride; +}; + +struct format_info { + unsigned int format; + const char *name; + const struct rgb_info rgb; + const struct yuv_info yuv; +}; + +#define MAKE_RGB_INFO(rl, ro, bl, bo, gl, go, al, ao) \ + .rgb = { { (rl), (ro) }, { (bl), (bo) }, { (gl), (go) }, { (al), (ao) } } + +#define MAKE_YUV_INFO(order, xsub, ysub, chroma_stride) \ + .yuv = { (order), (xsub), (ysub), (chroma_stride) } + +static const struct format_info format_info[] = { + /* YUV packed */ + { TBM_FORMAT_UYVY, "UYVY", MAKE_YUV_INFO(YUV_YCbCr | YUV_CY, 2, 2, 2) }, + { TBM_FORMAT_VYUY, "VYUY", MAKE_YUV_INFO(YUV_YCrCb | YUV_CY, 2, 2, 2) }, + { TBM_FORMAT_YUYV, "YUYV", MAKE_YUV_INFO(YUV_YCbCr | YUV_YC, 2, 2, 2) }, + { TBM_FORMAT_YVYU, "YVYU", MAKE_YUV_INFO(YUV_YCrCb | YUV_YC, 2, 2, 2) }, + /* YUV semi-planar */ + { TBM_FORMAT_NV12, "NV12", MAKE_YUV_INFO(YUV_YCbCr, 2, 2, 2) }, + { TBM_FORMAT_NV21, "NV21", MAKE_YUV_INFO(YUV_YCrCb, 2, 2, 2) }, + { TBM_FORMAT_NV16, "NV16", MAKE_YUV_INFO(YUV_YCbCr, 2, 1, 2) }, + { TBM_FORMAT_NV61, "NV61", MAKE_YUV_INFO(YUV_YCrCb, 2, 1, 2) }, + /* YUV planar */ + { TBM_FORMAT_YUV420, "YU12", MAKE_YUV_INFO(YUV_YCbCr, 2, 2, 1) }, + { TBM_FORMAT_YVU420, "YV12", MAKE_YUV_INFO(YUV_YCrCb, 2, 2, 1) }, + /* RGB16 */ + { TBM_FORMAT_ARGB4444, "AR12", MAKE_RGB_INFO(4, 8, 4, 4, 4, 0, 4, 12) }, + { TBM_FORMAT_XRGB4444, "XR12", MAKE_RGB_INFO(4, 8, 4, 4, 4, 0, 0, 0) }, + { TBM_FORMAT_ABGR4444, "AB12", MAKE_RGB_INFO(4, 0, 4, 4, 4, 8, 4, 12) }, + { TBM_FORMAT_XBGR4444, "XB12", MAKE_RGB_INFO(4, 0, 4, 4, 4, 8, 0, 0) }, + { TBM_FORMAT_RGBA4444, "RA12", MAKE_RGB_INFO(4, 12, 4, 8, 4, 4, 4, 0) }, + { TBM_FORMAT_RGBX4444, "RX12", MAKE_RGB_INFO(4, 12, 4, 8, 4, 4, 0, 0) }, + { TBM_FORMAT_BGRA4444, "BA12", MAKE_RGB_INFO(4, 4, 4, 8, 4, 12, 4, 0) }, + { TBM_FORMAT_BGRX4444, "BX12", MAKE_RGB_INFO(4, 4, 4, 8, 4, 12, 0, 0) }, + { TBM_FORMAT_ARGB1555, "AR15", MAKE_RGB_INFO(5, 10, 5, 5, 5, 0, 1, 15) }, + { TBM_FORMAT_XRGB1555, "XR15", MAKE_RGB_INFO(5, 10, 5, 5, 5, 0, 0, 0) }, + { TBM_FORMAT_ABGR1555, "AB15", MAKE_RGB_INFO(5, 0, 5, 5, 5, 10, 1, 15) }, + { TBM_FORMAT_XBGR1555, "XB15", MAKE_RGB_INFO(5, 0, 5, 5, 5, 10, 0, 0) }, + { TBM_FORMAT_RGBA5551, "RA15", MAKE_RGB_INFO(5, 11, 5, 6, 5, 1, 1, 0) }, + { TBM_FORMAT_RGBX5551, "RX15", MAKE_RGB_INFO(5, 11, 5, 6, 5, 1, 0, 0) }, + { TBM_FORMAT_BGRA5551, "BA15", MAKE_RGB_INFO(5, 1, 5, 6, 5, 11, 1, 0) }, + { TBM_FORMAT_BGRX5551, "BX15", MAKE_RGB_INFO(5, 1, 5, 6, 5, 11, 0, 0) }, + { TBM_FORMAT_RGB565, "RG16", MAKE_RGB_INFO(5, 11, 6, 5, 5, 0, 0, 0) }, + { TBM_FORMAT_BGR565, "BG16", MAKE_RGB_INFO(5, 0, 6, 5, 5, 11, 0, 0) }, + /* RGB24 */ + { TBM_FORMAT_BGR888, "BG24", MAKE_RGB_INFO(8, 0, 8, 8, 8, 16, 0, 0) }, + { TBM_FORMAT_RGB888, "RG24", MAKE_RGB_INFO(8, 16, 8, 8, 8, 0, 0, 0) }, + /* RGB32 */ + { TBM_FORMAT_ARGB8888, "AR24", MAKE_RGB_INFO(8, 16, 8, 8, 8, 0, 8, 24) }, + { TBM_FORMAT_XRGB8888, "XR24", MAKE_RGB_INFO(8, 16, 8, 8, 8, 0, 0, 0) }, + { TBM_FORMAT_ABGR8888, "AB24", MAKE_RGB_INFO(8, 0, 8, 8, 8, 16, 8, 24) }, + { TBM_FORMAT_XBGR8888, "XB24", MAKE_RGB_INFO(8, 0, 8, 8, 8, 16, 0, 0) }, + { TBM_FORMAT_RGBA8888, "RA24", MAKE_RGB_INFO(8, 24, 8, 16, 8, 8, 8, 0) }, + { TBM_FORMAT_RGBX8888, "RX24", MAKE_RGB_INFO(8, 24, 8, 16, 8, 8, 0, 0) }, + { TBM_FORMAT_BGRA8888, "BA24", MAKE_RGB_INFO(8, 8, 8, 16, 8, 24, 8, 0) }, + { TBM_FORMAT_BGRX8888, "BX24", MAKE_RGB_INFO(8, 8, 8, 16, 8, 24, 0, 0) }, + { TBM_FORMAT_ARGB2101010, "AR30", MAKE_RGB_INFO(10, 20, 10, 10, 10, 0, 2, 30) }, + { TBM_FORMAT_XRGB2101010, "XR30", MAKE_RGB_INFO(10, 20, 10, 10, 10, 0, 0, 0) }, + { TBM_FORMAT_ABGR2101010, "AB30", MAKE_RGB_INFO(10, 0, 10, 10, 10, 20, 2, 30) }, + { TBM_FORMAT_XBGR2101010, "XB30", MAKE_RGB_INFO(10, 0, 10, 10, 10, 20, 0, 0) }, + { TBM_FORMAT_RGBA1010102, "RA30", MAKE_RGB_INFO(10, 22, 10, 12, 10, 2, 2, 0) }, + { TBM_FORMAT_RGBX1010102, "RX30", MAKE_RGB_INFO(10, 22, 10, 12, 10, 2, 0, 0) }, + { TBM_FORMAT_BGRA1010102, "BA30", MAKE_RGB_INFO(10, 2, 10, 12, 10, 22, 2, 0) }, + { TBM_FORMAT_BGRX1010102, "BX30", MAKE_RGB_INFO(10, 2, 10, 12, 10, 22, 0, 0) }, +}; + +unsigned int format_fourcc(const char *name) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(format_info); i++) { + if (!strcmp(format_info[i].name, name)) + return format_info[i].format; + } + return 0; +} + +/* ----------------------------------------------------------------------------- + * Test patterns + */ + +struct color_rgb24 { + unsigned int value: 24; +} __attribute__((__packed__)); + +struct color_yuv { + unsigned char y; + unsigned char u; + unsigned char v; +}; + +#define MAKE_YUV_601_Y(r, g, b) \ + ((( 66 * (r) + 129 * (g) + 25 * (b) + 128) >> 8) + 16) +#define MAKE_YUV_601_U(r, g, b) \ + (((-38 * (r) - 74 * (g) + 112 * (b) + 128) >> 8) + 128) +#define MAKE_YUV_601_V(r, g, b) \ + (((112 * (r) - 94 * (g) - 18 * (b) + 128) >> 8) + 128) + +#define MAKE_YUV_601(r, g, b) \ + { .y = MAKE_YUV_601_Y(r, g, b), \ + .u = MAKE_YUV_601_U(r, g, b), \ + .v = MAKE_YUV_601_V(r, g, b) } + +#define MAKE_RGBA(rgb, r, g, b, a) \ + ((((r) >> (8 - (rgb)->red.length)) << (rgb)->red.offset) | \ + (((g) >> (8 - (rgb)->green.length)) << (rgb)->green.offset) | \ + (((b) >> (8 - (rgb)->blue.length)) << (rgb)->blue.offset) | \ + (((a) >> (8 - (rgb)->alpha.length)) << (rgb)->alpha.offset)) + +#define MAKE_RGB24(rgb, r, g, b) \ + { .value = MAKE_RGBA(rgb, r, g, b, 0) } + +static void +fill_smpte_yuv_planar(const struct yuv_info *yuv, + unsigned char *y_mem, unsigned char *u_mem, + unsigned char *v_mem, unsigned int width, + unsigned int height, unsigned int stride) +{ + const struct color_yuv colors_top[] = { + MAKE_YUV_601(191, 192, 192), /* grey */ + MAKE_YUV_601(192, 192, 0), /* yellow */ + MAKE_YUV_601(0, 192, 192), /* cyan */ + MAKE_YUV_601(0, 192, 0), /* green */ + MAKE_YUV_601(192, 0, 192), /* magenta */ + MAKE_YUV_601(192, 0, 0), /* red */ + MAKE_YUV_601(0, 0, 192), /* blue */ + }; + const struct color_yuv colors_middle[] = { + MAKE_YUV_601(0, 0, 192), /* blue */ + MAKE_YUV_601(19, 19, 19), /* black */ + MAKE_YUV_601(192, 0, 192), /* magenta */ + MAKE_YUV_601(19, 19, 19), /* black */ + MAKE_YUV_601(0, 192, 192), /* cyan */ + MAKE_YUV_601(19, 19, 19), /* black */ + MAKE_YUV_601(192, 192, 192), /* grey */ + }; + const struct color_yuv colors_bottom[] = { + MAKE_YUV_601(0, 33, 76), /* in-phase */ + MAKE_YUV_601(255, 255, 255), /* super white */ + MAKE_YUV_601(50, 0, 106), /* quadrature */ + MAKE_YUV_601(19, 19, 19), /* black */ + MAKE_YUV_601(9, 9, 9), /* 3.5% */ + MAKE_YUV_601(19, 19, 19), /* 7.5% */ + MAKE_YUV_601(29, 29, 29), /* 11.5% */ + MAKE_YUV_601(19, 19, 19), /* black */ + }; + unsigned int cs = yuv->chroma_stride; + unsigned int xsub = yuv->xsub; + unsigned int ysub = yuv->ysub; + unsigned int x; + unsigned int y; + + /* Luma */ + for (y = 0; y < height * 6 / 9; ++y) { + for (x = 0; x < width; ++x) + y_mem[x] = colors_top[x * 7 / width].y; + y_mem += stride; + } + + for (; y < height * 7 / 9; ++y) { + for (x = 0; x < width; ++x) + y_mem[x] = colors_middle[x * 7 / width].y; + y_mem += stride; + } + + for (; y < height; ++y) { + for (x = 0; x < width * 5 / 7; ++x) + y_mem[x] = colors_bottom[x * 4 / (width * 5 / 7)].y; + for (; x < width * 6 / 7; ++x) + y_mem[x] = colors_bottom[(x - width * 5 / 7) * 3 + / (width / 7) + 4].y; + for (; x < width; ++x) + y_mem[x] = colors_bottom[7].y; + y_mem += stride; + } + + /* Chroma */ + for (y = 0; y < height / ysub * 6 / 9; ++y) { + for (x = 0; x < width; x += xsub) { + u_mem[x * cs / xsub] = colors_top[x * 7 / width].u; + v_mem[x * cs / xsub] = colors_top[x * 7 / width].v; + } + u_mem += stride * cs / xsub; + v_mem += stride * cs / xsub; + } + + for (; y < height / ysub * 7 / 9; ++y) { + for (x = 0; x < width; x += xsub) { + u_mem[x * cs / xsub] = colors_middle[x * 7 / width].u; + v_mem[x * cs / xsub] = colors_middle[x * 7 / width].v; + } + u_mem += stride * cs / xsub; + v_mem += stride * cs / xsub; + } + + for (; y < height / ysub; ++y) { + for (x = 0; x < width * 5 / 7; x += xsub) { + u_mem[x * cs / xsub] = + colors_bottom[x * 4 / (width * 5 / 7)].u; + v_mem[x * cs / xsub] = + colors_bottom[x * 4 / (width * 5 / 7)].v; + } + for (; x < width * 6 / 7; x += xsub) { + u_mem[x * cs / xsub] = colors_bottom[(x - width * 5 / 7) * + 3 / (width / 7) + 4].u; + v_mem[x * cs / xsub] = colors_bottom[(x - width * 5 / 7) * + 3 / (width / 7) + 4].v; + } + for (; x < width; x += xsub) { + u_mem[x * cs / xsub] = colors_bottom[7].u; + v_mem[x * cs / xsub] = colors_bottom[7].v; + } + u_mem += stride * cs / xsub; + v_mem += stride * cs / xsub; + } +} + +static void +fill_smpte_yuv_packed(const struct yuv_info *yuv, unsigned char *mem, + unsigned int width, unsigned int height, + unsigned int stride) +{ + const struct color_yuv colors_top[] = { + MAKE_YUV_601(191, 192, 192), /* grey */ + MAKE_YUV_601(192, 192, 0), /* yellow */ + MAKE_YUV_601(0, 192, 192), /* cyan */ + MAKE_YUV_601(0, 192, 0), /* green */ + MAKE_YUV_601(192, 0, 192), /* magenta */ + MAKE_YUV_601(192, 0, 0), /* red */ + MAKE_YUV_601(0, 0, 192), /* blue */ + }; + const struct color_yuv colors_middle[] = { + MAKE_YUV_601(0, 0, 192), /* blue */ + MAKE_YUV_601(19, 19, 19), /* black */ + MAKE_YUV_601(192, 0, 192), /* magenta */ + MAKE_YUV_601(19, 19, 19), /* black */ + MAKE_YUV_601(0, 192, 192), /* cyan */ + MAKE_YUV_601(19, 19, 19), /* black */ + MAKE_YUV_601(192, 192, 192), /* grey */ + }; + const struct color_yuv colors_bottom[] = { + MAKE_YUV_601(0, 33, 76), /* in-phase */ + MAKE_YUV_601(255, 255, 255), /* super white */ + MAKE_YUV_601(50, 0, 106), /* quadrature */ + MAKE_YUV_601(19, 19, 19), /* black */ + MAKE_YUV_601(9, 9, 9), /* 3.5% */ + MAKE_YUV_601(19, 19, 19), /* 7.5% */ + MAKE_YUV_601(29, 29, 29), /* 11.5% */ + MAKE_YUV_601(19, 19, 19), /* black */ + }; + unsigned char *y_mem = (yuv->order & YUV_YC) ? mem : mem + 1; + unsigned char *c_mem = (yuv->order & YUV_CY) ? mem : mem + 1; + unsigned int u = (yuv->order & YUV_YCrCb) ? 2 : 0; + unsigned int v = (yuv->order & YUV_YCbCr) ? 2 : 0; + unsigned int x; + unsigned int y; + + if (width < 8) + return; + + /* Luma */ + for (y = 0; y < height * 6 / 9; ++y) { + for (x = 0; x < width; ++x) + y_mem[2 * x] = colors_top[x * 7 / width].y; + y_mem += stride; + } + + for (; y < height * 7 / 9; ++y) { + for (x = 0; x < width; ++x) + y_mem[2 * x] = colors_middle[x * 7 / width].y; + y_mem += stride; + } + + for (; y < height; ++y) { + for (x = 0; x < width * 5 / 7; ++x) + y_mem[2 * x] = colors_bottom[x * 4 / (width * 5 / 7)].y; + for (; x < width * 6 / 7; ++x) + y_mem[2 * x] = colors_bottom[(x - width * 5 / 7) * 3 + / (width / 7) + 4].y; + for (; x < width; ++x) + y_mem[2 * x] = colors_bottom[7].y; + y_mem += stride; + } + + /* Chroma */ + for (y = 0; y < height * 6 / 9; ++y) { + for (x = 0; x < width; x += 2) { + c_mem[2 * x + u] = colors_top[x * 7 / width].u; + c_mem[2 * x + v] = colors_top[x * 7 / width].v; + } + c_mem += stride; + } + + for (; y < height * 7 / 9; ++y) { + for (x = 0; x < width; x += 2) { + c_mem[2 * x + u] = colors_middle[x * 7 / width].u; + c_mem[2 * x + v] = colors_middle[x * 7 / width].v; + } + c_mem += stride; + } + + for (; y < height; ++y) { + for (x = 0; x < width * 5 / 7; x += 2) { + c_mem[2 * x + u] = colors_bottom[x * 4 / (width * 5 / 7)].u; + c_mem[2 * x + v] = colors_bottom[x * 4 / (width * 5 / 7)].v; + } + for (; x < width * 6 / 7; x += 2) { + c_mem[2 * x + u] = colors_bottom[(x - width * 5 / 7) * + 3 / (width / 7) + 4].u; + c_mem[2 * x + v] = colors_bottom[(x - width * 5 / 7) * + 3 / (width / 7) + 4].v; + } + for (; x < width; x += 2) { + c_mem[2 * x + u] = colors_bottom[7].u; + c_mem[2 * x + v] = colors_bottom[7].v; + } + c_mem += stride; + } +} + +static void +fill_smpte_rgb16(const struct rgb_info *rgb, unsigned char *mem, + unsigned int width, unsigned int height, unsigned int stride) +{ + const uint16_t colors_top[] = { + MAKE_RGBA(rgb, 192, 192, 192, 255), /* grey */ + MAKE_RGBA(rgb, 192, 192, 0, 255), /* yellow */ + MAKE_RGBA(rgb, 0, 192, 192, 255), /* cyan */ + MAKE_RGBA(rgb, 0, 192, 0, 255), /* green */ + MAKE_RGBA(rgb, 192, 0, 192, 255), /* magenta */ + MAKE_RGBA(rgb, 192, 0, 0, 255), /* red */ + MAKE_RGBA(rgb, 0, 0, 192, 255), /* blue */ + }; + const uint16_t colors_middle[] = { + MAKE_RGBA(rgb, 0, 0, 192, 255), /* blue */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* black */ + MAKE_RGBA(rgb, 192, 0, 192, 255), /* magenta */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* black */ + MAKE_RGBA(rgb, 0, 192, 192, 255), /* cyan */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* black */ + MAKE_RGBA(rgb, 192, 192, 192, 255), /* grey */ + }; + const uint16_t colors_bottom[] = { + MAKE_RGBA(rgb, 0, 33, 76, 255), /* in-phase */ + MAKE_RGBA(rgb, 255, 255, 255, 255), /* super white */ + MAKE_RGBA(rgb, 50, 0, 106, 255), /* quadrature */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* black */ + MAKE_RGBA(rgb, 9, 9, 9, 255), /* 3.5% */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* 7.5% */ + MAKE_RGBA(rgb, 29, 29, 29, 255), /* 11.5% */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* black */ + }; + unsigned int x; + unsigned int y; + + if (width < 8) + return; + + for (y = 0; y < height * 6 / 9; ++y) { + for (x = 0; x < width; ++x) + ((uint16_t *)mem)[x] = colors_top[x * 7 / width]; + mem += stride; + } + + for (; y < height * 7 / 9; ++y) { + for (x = 0; x < width; ++x) + ((uint16_t *)mem)[x] = colors_middle[x * 7 / width]; + mem += stride; + } + + for (; y < height; ++y) { + for (x = 0; x < width * 5 / 7; ++x) + ((uint16_t *)mem)[x] = + colors_bottom[x * 4 / (width * 5 / 7)]; + for (; x < width * 6 / 7; ++x) + ((uint16_t *)mem)[x] = + colors_bottom[(x - width * 5 / 7) * 3 + / (width / 7) + 4]; + for (; x < width; ++x) + ((uint16_t *)mem)[x] = colors_bottom[7]; + mem += stride; + } +} + +static void +fill_smpte_rgb24(const struct rgb_info *rgb, void *mem, + unsigned int width, unsigned int height, unsigned int stride) +{ + const struct color_rgb24 colors_top[] = { + MAKE_RGB24(rgb, 192, 192, 192), /* grey */ + MAKE_RGB24(rgb, 192, 192, 0), /* yellow */ + MAKE_RGB24(rgb, 0, 192, 192), /* cyan */ + MAKE_RGB24(rgb, 0, 192, 0), /* green */ + MAKE_RGB24(rgb, 192, 0, 192), /* magenta */ + MAKE_RGB24(rgb, 192, 0, 0), /* red */ + MAKE_RGB24(rgb, 0, 0, 192), /* blue */ + }; + const struct color_rgb24 colors_middle[] = { + MAKE_RGB24(rgb, 0, 0, 192), /* blue */ + MAKE_RGB24(rgb, 19, 19, 19), /* black */ + MAKE_RGB24(rgb, 192, 0, 192), /* magenta */ + MAKE_RGB24(rgb, 19, 19, 19), /* black */ + MAKE_RGB24(rgb, 0, 192, 192), /* cyan */ + MAKE_RGB24(rgb, 19, 19, 19), /* black */ + MAKE_RGB24(rgb, 192, 192, 192), /* grey */ + }; + const struct color_rgb24 colors_bottom[] = { + MAKE_RGB24(rgb, 0, 33, 76), /* in-phase */ + MAKE_RGB24(rgb, 255, 255, 255), /* super white */ + MAKE_RGB24(rgb, 50, 0, 106), /* quadrature */ + MAKE_RGB24(rgb, 19, 19, 19), /* black */ + MAKE_RGB24(rgb, 9, 9, 9), /* 3.5% */ + MAKE_RGB24(rgb, 19, 19, 19), /* 7.5% */ + MAKE_RGB24(rgb, 29, 29, 29), /* 11.5% */ + MAKE_RGB24(rgb, 19, 19, 19), /* black */ + }; + unsigned int x; + unsigned int y; + + if (width < 8) + return; + + for (y = 0; y < height * 6 / 9; ++y) { + for (x = 0; x < width; ++x) + ((struct color_rgb24 *)mem)[x] = + colors_top[x * 7 / width]; + mem += stride; + } + + for (; y < height * 7 / 9; ++y) { + for (x = 0; x < width; ++x) + ((struct color_rgb24 *)mem)[x] = + colors_middle[x * 7 / width]; + mem += stride; + } + + for (; y < height; ++y) { + for (x = 0; x < width * 5 / 7; ++x) + ((struct color_rgb24 *)mem)[x] = + colors_bottom[x * 4 / (width * 5 / 7)]; + for (; x < width * 6 / 7; ++x) + ((struct color_rgb24 *)mem)[x] = + colors_bottom[(x - width * 5 / 7) * 3 + / (width / 7) + 4]; + for (; x < width; ++x) + ((struct color_rgb24 *)mem)[x] = colors_bottom[7]; + mem += stride; + } +} + +static void +fill_smpte_rgb32(const struct rgb_info *rgb, unsigned char *mem, + unsigned int width, unsigned int height, unsigned int stride) +{ + const uint32_t colors_top[] = { + MAKE_RGBA(rgb, 192, 192, 192, 255), /* grey */ + MAKE_RGBA(rgb, 192, 192, 0, 255), /* yellow */ + MAKE_RGBA(rgb, 0, 192, 192, 255), /* cyan */ + MAKE_RGBA(rgb, 0, 192, 0, 255), /* green */ + MAKE_RGBA(rgb, 192, 0, 192, 255), /* magenta */ + MAKE_RGBA(rgb, 192, 0, 0, 255), /* red */ + MAKE_RGBA(rgb, 0, 0, 192, 255), /* blue */ + }; + const uint32_t colors_middle[] = { + MAKE_RGBA(rgb, 0, 0, 192, 255), /* blue */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* black */ + MAKE_RGBA(rgb, 192, 0, 192, 255), /* magenta */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* black */ + MAKE_RGBA(rgb, 0, 192, 192, 255), /* cyan */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* black */ + MAKE_RGBA(rgb, 192, 192, 192, 255), /* grey */ + }; + const uint32_t colors_bottom[] = { + MAKE_RGBA(rgb, 0, 33, 76, 255), /* in-phase */ + MAKE_RGBA(rgb, 255, 255, 255, 255), /* super white */ + MAKE_RGBA(rgb, 50, 0, 106, 255), /* quadrature */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* black */ + MAKE_RGBA(rgb, 9, 9, 9, 255), /* 3.5% */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* 7.5% */ + MAKE_RGBA(rgb, 29, 29, 29, 255), /* 11.5% */ + MAKE_RGBA(rgb, 19, 19, 19, 255), /* black */ + }; + unsigned int x; + unsigned int y; + unsigned int seed = time(NULL);; + + if (width < 8) + return; + + for (y = 0; y < height * 6 / 9; ++y) { + for (x = 0; x < width; ++x) + ((uint32_t *)mem)[x] = colors_top[x * 7 / width]; + mem += stride; + } + + for (; y < height * 7 / 9; ++y) { + for (x = 0; x < width; ++x) + ((uint32_t *)mem)[x] = colors_middle[x * 7 / width]; + mem += stride; + } + + for (; y < height; ++y) { + for (x = 0; x < width * 5 / 7; ++x) + ((uint32_t *)mem)[x] = + colors_bottom[x * 4 / (width * 5 / 7)]; + for (; x < width * 6 / 7; ++x) + ((uint32_t *)mem)[x] = + colors_bottom[(x - width * 5 / 7) * 3 + / (width / 7) + 4]; + for (; x < width; ++x) { + ((uint32_t *)mem)[x] = (rand_r(&seed) % 2) ? MAKE_RGBA(rgb, 255, 255, 255, 255) : MAKE_RGBA(rgb, 0, 0, 0, 255); + } + mem += stride; + } +} + +static void +fill_smpte(const struct format_info *info, void *planes[3], unsigned int width, + unsigned int height, unsigned int stride) +{ + unsigned char *u, *v; + + switch (info->format) { + case TBM_FORMAT_UYVY: + case TBM_FORMAT_VYUY: + case TBM_FORMAT_YUYV: + case TBM_FORMAT_YVYU: + return fill_smpte_yuv_packed(&info->yuv, planes[0], width, + height, stride); + + case TBM_FORMAT_NV12: + case TBM_FORMAT_NV21: + case TBM_FORMAT_NV16: + case TBM_FORMAT_NV61: + u = info->yuv.order & YUV_YCbCr ? planes[1] : planes[1] + 1; + v = info->yuv.order & YUV_YCrCb ? planes[1] : planes[1] + 1; + return fill_smpte_yuv_planar(&info->yuv, planes[0], u, v, + width, height, stride); + + case TBM_FORMAT_YUV420: + return fill_smpte_yuv_planar(&info->yuv, planes[0], planes[1], + planes[2], width, height, stride); + + case TBM_FORMAT_YVU420: + return fill_smpte_yuv_planar(&info->yuv, planes[0], planes[2], + planes[1], width, height, stride); + + case TBM_FORMAT_ARGB4444: + case TBM_FORMAT_XRGB4444: + case TBM_FORMAT_ABGR4444: + case TBM_FORMAT_XBGR4444: + case TBM_FORMAT_RGBA4444: + case TBM_FORMAT_RGBX4444: + case TBM_FORMAT_BGRA4444: + case TBM_FORMAT_BGRX4444: + case TBM_FORMAT_RGB565: + case TBM_FORMAT_BGR565: + case TBM_FORMAT_ARGB1555: + case TBM_FORMAT_XRGB1555: + case TBM_FORMAT_ABGR1555: + case TBM_FORMAT_XBGR1555: + case TBM_FORMAT_RGBA5551: + case TBM_FORMAT_RGBX5551: + case TBM_FORMAT_BGRA5551: + case TBM_FORMAT_BGRX5551: + return fill_smpte_rgb16(&info->rgb, planes[0], + width, height, stride); + + case TBM_FORMAT_BGR888: + case TBM_FORMAT_RGB888: + return fill_smpte_rgb24(&info->rgb, planes[0], + width, height, stride); + case TBM_FORMAT_ARGB8888: + case TBM_FORMAT_XRGB8888: + case TBM_FORMAT_ABGR8888: + case TBM_FORMAT_XBGR8888: + case TBM_FORMAT_RGBA8888: + case TBM_FORMAT_RGBX8888: + case TBM_FORMAT_BGRA8888: + case TBM_FORMAT_BGRX8888: + case TBM_FORMAT_ARGB2101010: + case TBM_FORMAT_XRGB2101010: + case TBM_FORMAT_ABGR2101010: + case TBM_FORMAT_XBGR2101010: + case TBM_FORMAT_RGBA1010102: + case TBM_FORMAT_RGBX1010102: + case TBM_FORMAT_BGRA1010102: + case TBM_FORMAT_BGRX1010102: + return fill_smpte_rgb32(&info->rgb, planes[0], + width, height, stride); + } +} + +static void +fill_tiles_yuv_planar(const struct format_info *info, + unsigned char *y_mem, unsigned char *u_mem, + unsigned char *v_mem, unsigned int width, + unsigned int height, unsigned int stride) +{ + const struct yuv_info *yuv = &info->yuv; + unsigned int cs = yuv->chroma_stride; + unsigned int xsub = yuv->xsub; + unsigned int ysub = yuv->ysub; + unsigned int x; + unsigned int y; + + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + div_t d = div(x + y, width); + uint32_t rgb32 = 0x00130502 * (d.quot >> 6) + + 0x000a1120 * (d.rem >> 6); + struct color_yuv color = + MAKE_YUV_601((rgb32 >> 16) & 0xff, + (rgb32 >> 8) & 0xff, rgb32 & 0xff); + + y_mem[x] = color.y; + u_mem[x / xsub * cs] = color.u; + v_mem[x / xsub * cs] = color.v; + } + + y_mem += stride; + if ((y + 1) % ysub == 0) { + u_mem += stride * cs / xsub; + v_mem += stride * cs / xsub; + } + } +} + +static void +fill_tiles_yuv_packed(const struct format_info *info, unsigned char *mem, + unsigned int width, unsigned int height, + unsigned int stride) +{ + const struct yuv_info *yuv = &info->yuv; + unsigned char *y_mem = (yuv->order & YUV_YC) ? mem : mem + 1; + unsigned char *c_mem = (yuv->order & YUV_CY) ? mem : mem + 1; + unsigned int u = (yuv->order & YUV_YCrCb) ? 2 : 0; + unsigned int v = (yuv->order & YUV_YCbCr) ? 2 : 0; + unsigned int x; + unsigned int y; + + for (y = 0; y < height; ++y) { + for (x = 0; x < width; x += 2) { + div_t d = div(x + y, width); + uint32_t rgb32 = 0x00130502 * (d.quot >> 6) + + 0x000a1120 * (d.rem >> 6); + struct color_yuv color = + MAKE_YUV_601((rgb32 >> 16) & 0xff, + (rgb32 >> 8) & 0xff, rgb32 & 0xff); + + y_mem[2 * x] = color.y; + c_mem[2 * x + u] = color.u; + y_mem[2 * x + 2] = color.y; + c_mem[2 * x + v] = color.v; + } + + y_mem += stride; + c_mem += stride; + } +} + +static void +fill_tiles_rgb16(const struct format_info *info, unsigned char *mem, + unsigned int width, unsigned int height, unsigned int stride) +{ + const struct rgb_info *rgb = &info->rgb; + unsigned int x, y; + + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + div_t d = div(x + y, width); + uint32_t rgb32 = 0x00130502 * (d.quot >> 6) + + 0x000a1120 * (d.rem >> 6); + uint16_t color = + MAKE_RGBA(rgb, (rgb32 >> 16) & 0xff, + (rgb32 >> 8) & 0xff, rgb32 & 0xff, + 255); + + ((uint16_t *)mem)[x] = color; + } + mem += stride; + } +} + +static void +fill_tiles_rgb24(const struct format_info *info, unsigned char *mem, + unsigned int width, unsigned int height, unsigned int stride) +{ + const struct rgb_info *rgb = &info->rgb; + unsigned int x, y; + + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + div_t d = div(x + y, width); + uint32_t rgb32 = 0x00130502 * (d.quot >> 6) + + 0x000a1120 * (d.rem >> 6); + struct color_rgb24 color = + MAKE_RGB24(rgb, (rgb32 >> 16) & 0xff, + (rgb32 >> 8) & 0xff, rgb32 & 0xff); + + ((struct color_rgb24 *)mem)[x] = color; + } + mem += stride; + } +} + +static void +fill_tiles_rgb32(const struct format_info *info, unsigned char *mem, + unsigned int width, unsigned int height, unsigned int stride) +{ + const struct rgb_info *rgb = &info->rgb; + unsigned int x, y; + + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + div_t d = div(x + y, width); + uint32_t rgb32 = 0x00130502 * (d.quot >> 6) + + 0x000a1120 * (d.rem >> 6); + uint32_t color = + MAKE_RGBA(rgb, (rgb32 >> 16) & 0xff, + (rgb32 >> 8) & 0xff, rgb32 & 0xff, + 255); + + ((uint32_t *)mem)[x] = color; + } + mem += stride; + } +} + +static void +fill_tiles(const struct format_info *info, void *planes[3], unsigned int width, + unsigned int height, unsigned int stride) +{ + unsigned char *u, *v; + + switch (info->format) { + case TBM_FORMAT_UYVY: + case TBM_FORMAT_VYUY: + case TBM_FORMAT_YUYV: + case TBM_FORMAT_YVYU: + return fill_tiles_yuv_packed(info, planes[0], + width, height, stride); + + case TBM_FORMAT_NV12: + case TBM_FORMAT_NV21: + case TBM_FORMAT_NV16: + case TBM_FORMAT_NV61: + u = info->yuv.order & YUV_YCbCr ? planes[1] : planes[1] + 1; + v = info->yuv.order & YUV_YCrCb ? planes[1] : planes[1] + 1; + return fill_tiles_yuv_planar(info, planes[0], u, v, + width, height, stride); + + case TBM_FORMAT_YUV420: + return fill_tiles_yuv_planar(info, planes[0], planes[1], + planes[2], width, height, stride); + + case TBM_FORMAT_YVU420: + return fill_tiles_yuv_planar(info, planes[0], planes[2], + planes[1], width, height, stride); + + case TBM_FORMAT_ARGB4444: + case TBM_FORMAT_XRGB4444: + case TBM_FORMAT_ABGR4444: + case TBM_FORMAT_XBGR4444: + case TBM_FORMAT_RGBA4444: + case TBM_FORMAT_RGBX4444: + case TBM_FORMAT_BGRA4444: + case TBM_FORMAT_BGRX4444: + case TBM_FORMAT_RGB565: + case TBM_FORMAT_BGR565: + case TBM_FORMAT_ARGB1555: + case TBM_FORMAT_XRGB1555: + case TBM_FORMAT_ABGR1555: + case TBM_FORMAT_XBGR1555: + case TBM_FORMAT_RGBA5551: + case TBM_FORMAT_RGBX5551: + case TBM_FORMAT_BGRA5551: + case TBM_FORMAT_BGRX5551: + return fill_tiles_rgb16(info, planes[0], + width, height, stride); + + case TBM_FORMAT_BGR888: + case TBM_FORMAT_RGB888: + return fill_tiles_rgb24(info, planes[0], + width, height, stride); + case TBM_FORMAT_ARGB8888: + case TBM_FORMAT_XRGB8888: + case TBM_FORMAT_ABGR8888: + case TBM_FORMAT_XBGR8888: + case TBM_FORMAT_RGBA8888: + case TBM_FORMAT_RGBX8888: + case TBM_FORMAT_BGRA8888: + case TBM_FORMAT_BGRX8888: + case TBM_FORMAT_ARGB2101010: + case TBM_FORMAT_XRGB2101010: + case TBM_FORMAT_ABGR2101010: + case TBM_FORMAT_XBGR2101010: + case TBM_FORMAT_RGBA1010102: + case TBM_FORMAT_RGBX1010102: + case TBM_FORMAT_BGRA1010102: + case TBM_FORMAT_BGRX1010102: + return fill_tiles_rgb32(info, planes[0], + width, height, stride); + } +} + +static void +fill_plain(const struct format_info *info, void *planes[3], unsigned int width, + unsigned int height, unsigned int stride) +{ + memset(planes[0], 0x77, stride * height); +} + +/* + * fill_pattern - Fill a buffer with a test pattern + * @format: Pixel format + * @pattern: Test pattern + * @buffer: Buffer memory + * @width: Width in pixels + * @height: Height in pixels + * @stride: Line stride (pitch) in bytes + * + * Fill the buffer with the test pattern specified by the pattern parameter. + * Supported formats vary depending on the selected pattern. + */ +static void +fill_pattern(unsigned int format, enum fill_pattern pattern, void *planes[3], + unsigned int width, unsigned int height, unsigned int stride) +{ + const struct format_info *info = NULL; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format_info); ++i) { + if (format_info[i].format == format) { + info = &format_info[i]; + break; + } + } + + if (info == NULL) + return; + + switch (pattern) { + case PATTERN_TILES: + return fill_tiles(info, planes, width, height, stride); + + case PATTERN_SMPTE: + return fill_smpte(info, planes, width, height, stride); + + case PATTERN_PLAIN: + return fill_plain(info, planes, width, height, stride); + + default: + printf("Error: unsupported test pattern %u.\n", pattern); + break; + } +} + +void +tdm_test_buffer_fill(tbm_surface_h buffer, int pattern) +{ + tbm_surface_info_s info; + void *plane[3]; + int ret; + + ret = tbm_surface_map(buffer, TBM_OPTION_WRITE, &info); + TDM_EXIT_IF_FAIL(ret == 0); + + plane[0] = info.planes[0].ptr; + plane[1] = info.planes[1].ptr; + plane[2] = info.planes[2].ptr; + fill_pattern(info.format, pattern, plane, info.width, info.height, info.planes[0].stride); + tbm_surface_unmap(buffer); +} diff --git a/tools/buffers.h b/tools/buffers.h new file mode 100644 index 0000000..55afd3d --- /dev/null +++ b/tools/buffers.h @@ -0,0 +1,39 @@ +/* + * DRM based mode setting test program + * Copyright 2008 Tungsten Graphics + * Jakob Bornecrantz + * Copyright 2008 Intel Corporation + * Jesse Barnes + * + * 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, sublicense, + * 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 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 NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS 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. + */ + +#ifndef __BUFFERS_H__ +#define __BUFFERS_H__ + +enum fill_pattern { + PATTERN_TILES = 0, + PATTERN_PLAIN = 1, + PATTERN_SMPTE = 2, +}; + +void +tdm_test_buffer_fill(tbm_surface_h buffer, int pattern); + +#endif diff --git a/tools/tdm_test_client.c b/tools/tdm_test_client.c index 4a8e583..ab322aa 100644 --- a/tools/tdm_test_client.c +++ b/tools/tdm_test_client.c @@ -65,15 +65,15 @@ typedef struct _tdm_test_client { struct typestrings { int type; - char string[512]; + const char *string; }; struct optstrings { int type; - char opt[512]; - char desc[512]; - char arg[512]; - char ex[512]; + const char *opt; + const char *desc; + const char *arg; + const char *ex; }; enum { @@ -90,26 +90,9 @@ static struct typestrings typestrs[] = { static struct optstrings optstrs[] = { {OPT_QRY, "qo", "output objects info", "", "primary"}, - {OPT_TST, "v", "vblank test", "[,][@][#][+][*fake]", "primary,0@60#1+0*1"}, + {OPT_TST, "v", "vblank test", "[,][@][~][+][*fake]", "primary,0@60~1+0*1"}, }; -#define DELIM "!@#^&*+-|," - -static char* -strtostr(char *buf, int len, char *str, char *delim) -{ - char *end; - end = strpbrk(str, delim); - if (end) - len = ((end - str + 1) < len) ? (end - str + 1) : len; - else { - int l = strlen(str); - len = ((l + 1) < len) ? (l + 1) : len; - } - snprintf(buf, len, "%s", str); - return str + len - 1; -} - static void usage(char *app_name) { @@ -127,10 +110,13 @@ usage(char *app_name) if (f == 1) printf(" %s options:\n\n", typestrs[t].string); printf("\t-%s\t%s\n", optstrs[o].opt, optstrs[o].desc); - printf("\t\t%s\n", optstrs[o].arg); - printf("\t\tex) %s\n", optstrs[o].ex); + if (optstrs[o].arg) + printf("\t\t %s\n", optstrs[o].arg); + if (optstrs[o].ex) + printf("\t\t ex) %s\n", optstrs[o].ex); f = 0; } + printf("\n"); } exit(0); @@ -138,73 +124,68 @@ usage(char *app_name) //"" static void -parse_arg_qo(tdm_test_client *data, char *p) +parse_arg_qo(tdm_test_client *data, char *arg) { - strtostr(data->args.output_name, 512, p, DELIM); + strtostr(data->args.output_name, 512, arg, TDM_DELIM); } -//"[,][@][#][+][*fake]" +//"[,][@][~][+][*fake]" static void -parse_arg_v(tdm_test_client *data, char *p) +parse_arg_v(tdm_test_client *data, char *arg) { - char *end = p; + char *end = arg; - end = strtostr(data->args.output_name, 512, p, DELIM); + end = strtostr(data->args.output_name, 512, arg, TDM_DELIM); if (*end == ',') { - p = end + 1; - data->args.sync = strtol(p, &end, 10); + arg = end + 1; + data->args.sync = strtol(arg, &end, 10); } if (*end == '@') { - p = end + 1; - data->args.fps = strtol(p, &end, 10); + arg = end + 1; + data->args.fps = strtol(arg, &end, 10); } - if (*end == '#') { - p = end + 1; - data->args.interval = strtol(p, &end, 10); + if (*end == '~') { + arg = end + 1; + data->args.interval = strtol(arg, &end, 10); } if (*end == '+' || *end == '-') { - p = end; - data->args.offset = strtol(p, &end, 10); + arg = end; + data->args.offset = strtol(arg, &end, 10); } if (*end == '*') { - p = end + 1; - data->args.enable_fake= strtol(p, &end, 10); + arg = end + 1; + data->args.enable_fake= strtol(arg, &end, 10); } } static void parse_args(tdm_test_client *data, int argc, char *argv[]) { - int size = sizeof(optstrs) / sizeof(struct optstrings); - int i, j = 0; + int i; - if (argc < 2) { + if (argc < 3) { usage(argv[0]); - exit(1); + exit(0); } memset(data, 0, sizeof *data); data->args.interval = 1; for (i = 1; i < argc; i++) { - for (j = 0; j < size; j++) { - if (!strncmp(argv[i]+1, "qo", 512)) { - data->do_query = 1; - parse_arg_qo(data, argv[++i]); - break; - } else if (!strncmp(argv[i]+1, "v", 512)) { - data->do_vblank = 1; - parse_arg_v(data, argv[++i]); - break; - } else { - usage(argv[0]); - exit(1); - } + if (!strncmp(argv[i]+1, "qo", 2)) { + data->do_query = 1; + parse_arg_qo(data, argv[++i]); + } else if (!strncmp(argv[i]+1, "v", 1)) { + data->do_vblank = 1; + parse_arg_v(data, argv[++i]); + } else { + usage(argv[0]); + exit(0); } } } @@ -232,12 +213,12 @@ _client_vblank_handler(tdm_client_vblank *vblank, tdm_error error, unsigned int if (error == TDM_ERROR_DPMS_OFF) { printf("exit: dpms off\n"); - exit(1); + exit(0); } if (error != TDM_ERROR_NONE) { printf("exit: error(%d)\n", error); - exit(1); + exit(0); } cur = get_time_in_micros(); @@ -377,6 +358,17 @@ main(int argc, char *argv[]) tdm_test_client *data = &ttc_data; tdm_error error; +#if 1 /* for testing */ + const char *xdg = (const char*)getenv("XDG_RUNTIME_DIR"); + if (!xdg) { + char buf[32]; + snprintf(buf, sizeof(buf), "/run"); + int ret = setenv("XDG_RUNTIME_DIR", (const char*)buf, 1); + if (ret != 0) + exit (0); + } +#endif + parse_args(data, argc, argv); printf("sync(%d) fps(%d) interval(%d) offset(%d) enable_fake(%d)\n", diff --git a/tools/tdm_test_server.c b/tools/tdm_test_server.c new file mode 100644 index 0000000..932e20d --- /dev/null +++ b/tools/tdm_test_server.c @@ -0,0 +1,1345 @@ +/************************************************************************** + * + * libtdm + * + * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Contact: Eunchul Kim , + * JinYoung Jeon , + * Taeheon Kim , + * YoungJun Cho , + * SooChan Lim , + * Boram Park + * + * 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. + * +**************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "tdm_macro.h" +#include "tdm_private.h" +#include "buffers.h" + +//////////////////////////////////////////////////////////////////////////////// +struct typestrings { + int type; + const char *string; + const char *desc; +}; + +struct optstrings { + int type; + const char *opt; + const char *desc; + const char *arg; + const char *ex; +}; + +enum { + OPT_QRY, + OPT_TST, + OPT_GEN, +}; + +static struct typestrings typestrs[] = { + {OPT_QRY, "Query", NULL}, + {OPT_TST, "Test", NULL}, + {OPT_GEN, "General", NULL}, +}; + +static struct optstrings optstrs[] = { + { + OPT_QRY, "q", "show tdm output, layer information", + NULL, NULL + }, + { + OPT_TST, "a", "set all layer objects for all connected outputs", + NULL, NULL + }, + { + OPT_TST, "o", "set a mode for a output object", + "@[&]", "0@1920x1080" + }, + { + OPT_TST, "l", "set a layer object", + "[:x[++][,x][@]]~x[++][*]", NULL + }, + { + OPT_TST, "p", "set a PP object.\n\t\t'-l' is used to show the result on screen.", + "x[++][,x][@]~x[++][,x][@][*][&]", NULL + }, + { + OPT_TST, "c", "catpure a output object or a layer object.\n\t\t'-l' is used to show the result on screen.", + "[,]~x[++][,x][@][*]", NULL + }, + { + OPT_GEN, "w", "set the property of a object", + ":", NULL + }, + { + OPT_GEN, "b", "set the fill(smtpe,tiles,plane) and framebuffer type(scanout,noncachable,wc)", + "[:[,[,...]]]", NULL + }, + { + OPT_GEN, "v", "update layers every vblank", + NULL, NULL + }, +}; + +static void +usage(char *app_name) +{ + int type_size = sizeof(typestrs) / sizeof(struct typestrings); + int opt_size = sizeof(optstrs) / sizeof(struct optstrings); + int t; + + printf("usage: %s \n\n", app_name); + + for (t = 0; t < type_size; t++) { + int o, f = 1; + + for (o = 0; o < opt_size; o++) + if (optstrs[o].type == typestrs[t].type) { + if (f == 1) + printf(" %s options: %s\n\n", typestrs[t].string, (typestrs[t].desc) ? : ""); + printf("\t-%s\t%s\n", optstrs[o].opt, optstrs[o].desc); + if (optstrs[o].arg) + printf("\t\t %s\n", optstrs[o].arg); + if (optstrs[o].ex) + printf("\t\t ex) %s\n", optstrs[o].ex); + f = 0; + } + printf("\n"); + } + + exit(0); +} + +//////////////////////////////////////////////////////////////////////////////// + +static const char *tdm_buf_flag_names[] = { + "scanout", + "noncachable", + "wc", +}; +TDM_BIT_NAME_FB(buf_flag) + +#define print_size(s) \ + printf("%dx%d", (s)->h, (s)->v) +#define print_pos(p) \ + printf("%dx%d+%d+%d", (p)->w, (p)->h, (p)->x, (p)->y) +#define print_format(f) \ + if (f) printf("%c%c%c%c", FOURCC_STR(f)); \ + else printf("NONE") +#define print_config(c) \ + do { \ + print_size(&(c)->size); \ + printf(" "); \ + print_pos(&(c)->pos); \ + printf(" "); \ + print_format((c)->format); \ + } while (0) +#define print_prop(w) \ + printf("%s(%d)", (w)->name, ((w)->value).u32) + +typedef struct _tdm_test_server tdm_test_server; +typedef struct _tdm_test_server_layer tdm_test_server_layer; +typedef struct _tdm_test_server_capture tdm_test_server_capture; + +typedef struct _tdm_test_server_prop { + /* args */ + char name[TDM_NAME_LEN]; + tdm_value value; + + /* variables for test */ + struct list_head link; +} tdm_test_server_prop; + +typedef struct _tdm_test_server_buffer { + /* variables for test */ + tbm_surface_h buffer; + int in_use; + tdm_test_server_layer *l; + + tdm_test_server_capture *c; + tdm_buffer_release_handler done; +} tdm_test_server_buffer; + +typedef struct _tdm_test_server_output { + /* args */ + int idx; + char mode[TDM_NAME_LEN]; + int refresh; + + /* variables for test */ + struct list_head link; + struct list_head prop_list; + struct list_head layer_list; + tdm_test_server *data; + tdm_output *output; +} tdm_test_server_output; + +typedef struct _tdm_test_server_pp { + /* args */ + tdm_info_pp info; + int fps; + + /* variables for test */ + struct list_head link; + tdm_test_server *data; + tdm_test_server_layer *l; + tdm_pp *pp; + tdm_test_server_buffer *bufs[6]; + int buf_idx; + + tdm_event_loop_source *timer_source; +} tdm_test_server_pp; + +struct _tdm_test_server_capture { + /* args */ + int output_idx; + int layer_idx; + tdm_info_capture info; + + /* variables for test */ + struct list_head link; + tdm_test_server *data; + tdm_test_server_layer *l; + tdm_capture *capture; +}; + +struct _tdm_test_server_layer { + /* args */ + int idx; + tdm_info_layer info; + + /* variables for test */ + struct list_head link; + struct list_head prop_list; + tdm_test_server *data; + tdm_test_server_output *o; + tdm_layer *layer; + int is_primary; + tdm_test_server_pp *owner_p; + tdm_test_server_capture *owner_c; + tdm_test_server_buffer *bufs[3]; + int buf_idx; +}; + +struct _tdm_test_server { + /* args */ + int do_query; + int do_all; + int do_vblank; + int bflags; + int b_fill; + + /* variables for test */ + struct list_head output_list; + struct list_head pp_list; + struct list_head capture_list; + tdm_display *display; +}; + +static void run_test(tdm_test_server *data); +static void output_setup(tdm_test_server_output *o); +static void layer_show_buffer(tdm_test_server_layer *l, tdm_test_server_buffer *b); +static void capture_attach(tdm_test_server_capture *c, tdm_test_server_buffer *b); + +static char* +parse_size(tdm_size *size, char *arg) +{ + char *end; + size->h = strtol(arg, &end, 10); + TDM_EXIT_IF_FAIL(*end == 'x'); + arg = end + 1; + size->v = strtol(arg, &end, 10); + return end; +} + +static char* +parse_pos(tdm_pos *pos, char *arg) +{ + char *end; + pos->w = strtol(arg, &end, 10); + TDM_EXIT_IF_FAIL(*end == 'x'); + arg = end + 1; + pos->h = strtol(arg, &end, 10); + if (*end == '+') { + arg = end + 1; + pos->x = strtol(arg, &end, 10); + TDM_EXIT_IF_FAIL(*end == '+'); + arg = end + 1; + pos->y = strtol(arg, &end, 10); + } + return end; +} + +static char* +parse_config(tdm_info_config *config, char *arg) +{ + char *end; + end = parse_pos(&config->pos, arg); + if (*end == ',') { + arg = end + 1; + end = parse_size(&config->size, arg); + } + if (*end == '@') { + char temp[32]; + arg = end + 1; + end = strtostr(temp, 32, arg, TDM_DELIM); + config->format = FOURCC_ID(temp); + } + return end; +} + +static void +parse_arg_o(tdm_test_server_output *o, char *arg) +{ + char *end; + TDM_EXIT_IF_FAIL(arg != NULL); + o->idx = strtol(arg, &end, 10); + TDM_EXIT_IF_FAIL(*end == '@'); + arg = end + 1; + end = strtostr(o->mode, TDM_NAME_LEN, arg, TDM_DELIM); + if (*end == ',') { + arg = end + 1; + o->refresh = strtol(arg, &end, 10); + } +} + +static void +parse_arg_p(tdm_test_server_pp *p, char *arg) +{ + tdm_info_pp *pp_info = &p->info; + char *end; + TDM_EXIT_IF_FAIL(arg != NULL); + end = parse_config(&pp_info->src_config, arg); + TDM_EXIT_IF_FAIL(*end == '~'); + arg = end + 1; + end = parse_config(&pp_info->dst_config, arg); + if (*end == '*') { + arg = end + 1; + pp_info->transform = strtol(arg, &end, 10); + } + if (*end == '&') { + arg = end + 1; + p->fps = strtol(arg, &end, 10); + } +} + +static void +parse_arg_c(tdm_test_server_capture *c, char *arg) +{ + tdm_info_capture *capture_info = &c->info; + char *end; + TDM_EXIT_IF_FAIL(arg != NULL); + c->output_idx = strtol(arg, &end, 10); + if (*end == ',') { + arg = end + 1; + c->layer_idx = strtol(arg, &end, 10); + } + TDM_EXIT_IF_FAIL(*end == '~'); + arg = end + 1; + end = parse_config(&capture_info->dst_config, arg); + if (*end == '*') { + arg = end + 1; + capture_info->transform = strtol(arg, &end, 10); + } +} + +static void +parse_arg_l(tdm_test_server_layer *l, char *arg) +{ + tdm_info_layer *layer_info = &l->info; + char *end; + TDM_EXIT_IF_FAIL(arg != NULL); + l->idx = strtol(arg, &end, 10); + if (*end == ':') { + arg = end + 1; + end = parse_config(&layer_info->src_config, arg); + } + TDM_EXIT_IF_FAIL(*end == '~'); + arg = end + 1; + end = parse_pos(&layer_info->dst_pos, arg); + if (*end == '*') { + arg = end + 1; + layer_info->transform = strtol(arg, &end, 10); + } +} + +static void +parse_arg_w(tdm_test_server_prop *w, char *arg) +{ + char *end; + TDM_EXIT_IF_FAIL(arg != NULL); + end = strtostr(w->name, TDM_PATH_LEN, arg, TDM_DELIM); + TDM_EXIT_IF_FAIL(*end == ':'); + arg = end + 1; + w->value.u32 = strtol(arg, &end, 10); +} + +static void +parse_arg_b(tdm_test_server *data, char *arg) +{ + char *end = arg; + char temp[TDM_NAME_LEN] = {0,}; + TDM_EXIT_IF_FAIL(arg != NULL); + + end = strtostr(temp, 32, arg, TDM_DELIM); + if (!strncmp(temp, "smpte", 5)) + data->b_fill = PATTERN_SMPTE; + else if (!strncmp(temp, "tiles", 5)) + data->b_fill = PATTERN_TILES; + else if (!strncmp(temp, "plain", 5)) + data->b_fill = PATTERN_PLAIN; + else { + printf("'%s': unknown flag\n", temp); + exit(0); + } + + if (*arg == ':') { + data->bflags = 0; + arg = end + 1; + snprintf(temp, TDM_NAME_LEN, "%s", arg); + arg = strtok_r(temp, ",", &end); + while (arg) { + if (!strncmp(arg, "default", 7)) + printf("Ignore '%s' flag\n", arg); + else if (!strncmp(arg, "scanout", 7)) + data->bflags |= TBM_BO_SCANOUT; + else if (!strncmp(arg, "noncachable", 11)) + data->bflags |= TBM_BO_NONCACHABLE; + else if (!strncmp(arg, "wc", 2)) + data->bflags |= TBM_BO_WC; + else { + printf("'%s': unknown flag\n", arg); + exit(0); + } + arg = strtok_r(NULL, ",", &end); + } + } +} + +static void +parse_args(tdm_test_server *data, int argc, char *argv[]) +{ + tdm_test_server_output *o = NULL; + tdm_test_server_layer *l = NULL; + tdm_test_server_pp *p = NULL; + tdm_test_server_capture *c = NULL; + tdm_test_server_prop *w = NULL; + void *last_option = NULL; + void *last_object = NULL; + int i; + + if (argc < 2) { + usage(argv[0]); + exit(0); + } + + for (i = 1; i < argc; i++) { + if (!strncmp(argv[i] + 1, "q", 1)) { + data->do_query = 1; + return; + } else if (!strncmp(argv[i] + 1, "a", 1)) { + data->do_all = 1; + } else if (!strncmp(argv[i] + 1, "o", 1)) { + TDM_GOTO_IF_FAIL(data->do_all == 0, all); + o = calloc(1, sizeof * o); + TDM_EXIT_IF_FAIL(o != NULL); + o->data = data; + LIST_INITHEAD(&o->layer_list); + LIST_INITHEAD(&o->prop_list); + LIST_ADDTAIL(&o->link, &data->output_list); + parse_arg_o(o, argv[++i]); + last_option = o; + last_object = o; + } else if (!strncmp(argv[i] + 1, "p", 1)) { + TDM_GOTO_IF_FAIL(data->do_all == 0, all); + p = calloc(1, sizeof * p); + TDM_EXIT_IF_FAIL(p != NULL); + p->data = data; + p->fps = 30; + LIST_ADDTAIL(&p->link, &data->pp_list); + parse_arg_p(p, argv[++i]); + last_option = p; + last_object = o; + } else if (!strncmp(argv[i] + 1, "c", 1)) { + TDM_GOTO_IF_FAIL(data->do_all == 0, all); + c = calloc(1, sizeof * c); + TDM_EXIT_IF_FAIL(c != NULL); + c->data = data; + c->output_idx = -1; + c->layer_idx = -1; + LIST_ADDTAIL(&c->link, &data->capture_list); + parse_arg_c(c, argv[++i]); + last_option = c; + last_object = o; + } else if (!strncmp(argv[i] + 1, "l", 1)) { + TDM_GOTO_IF_FAIL(data->do_all == 0, all); + if (!o) + goto no_output; + l = calloc(1, sizeof * l); + TDM_EXIT_IF_FAIL(l != NULL); + LIST_INITHEAD(&l->prop_list); + LIST_ADDTAIL(&l->link, &o->layer_list); + l->data = data; + l->o = o; + parse_arg_l(l, argv[++i]); + if (p && last_option == p) { + p->l = l; + l->owner_p = p; + } + else if (c && last_option == c) { + c->l = l; + l->owner_c = c; + } + last_object = o; + } else if (!strncmp(argv[i] + 1, "w", 1)) { + TDM_GOTO_IF_FAIL(data->do_all == 0, all); + if (!last_object) + goto no_object; + w = calloc(1, sizeof * w); + TDM_EXIT_IF_FAIL(w != NULL); + if (o && last_object == o) + LIST_ADDTAIL(&w->link, &o->prop_list); + else if (l && last_object == l) + LIST_ADDTAIL(&w->link, &l->prop_list); + parse_arg_w(w, argv[++i]); + } else if (!strncmp(argv[i] + 1, "b", 1)) { + parse_arg_b(data, argv[++i]); + } else if (!strncmp(argv[i] + 1, "v", 1)) { + data->do_vblank = 1; + } else { + usage(argv[0]); + exit(0); + } + } + return; +no_output: + printf("Use '-o' to set a output first.\n"); + exit(0); +no_object: + printf("Use '-o' or '-l' or '-p' or '-c' to set a object first.\n"); + exit(0); +all: + printf("Can't use '-%s' with '-a'.\n", argv[i] + 1); + exit(0); +} + +static void +interpret_args(tdm_test_server *data) +{ + tdm_test_server_output *o = NULL; + tdm_test_server_layer *l = NULL; + tdm_error ret; + + if (data->do_all) { + int i, output_count; + + ret = tdm_display_get_output_count(data->display, &output_count); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + for (i = 0; i < output_count; i++) { + tdm_output *output; + int j, layer_count; + + o = calloc(1, sizeof * o); + TDM_EXIT_IF_FAIL(o != NULL); + o->data = data; + LIST_INITHEAD(&o->layer_list); + LIST_INITHEAD(&o->prop_list); + LIST_ADDTAIL(&o->link, &data->output_list); + o->idx = i; + + output = tdm_display_get_output(data->display, i, &ret); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + ret = tdm_output_get_layer_count(output, &layer_count); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + for (j = 0; j < layer_count; j++) { + tdm_layer *layer; + tdm_layer_capability capabilities; + + l = calloc(1, sizeof * l); + TDM_EXIT_IF_FAIL(l != NULL); + LIST_INITHEAD(&l->prop_list); + LIST_ADDTAIL(&l->link, &o->layer_list); + l->o = o; + l->idx = j; + + layer = tdm_output_get_layer(output, j, &ret); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + ret = tdm_layer_get_capabilities(layer, &capabilities); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + if (capabilities & TDM_LAYER_CAPABILITY_PRIMARY) + l->is_primary = 1; + } + } + } + + /* fill layer information */ + LIST_FOR_EACH_ENTRY(o, &data->output_list, link) { + tdm_output *output; + const tdm_output_mode *mode; + int minw, minh, maxw, maxh; + int layer_count, i = 1; + + output_setup(o); + + output = tdm_display_get_output(data->display, o->idx, &ret); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + ret = tdm_output_get_mode(output, &mode); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + ret = tdm_output_get_layer_count(output, &layer_count); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + ret = tdm_output_get_available_size(output, &minw, &minh, &maxw, &maxh, NULL); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + if (LIST_IS_EMPTY(&o->layer_list)) { + ret = tdm_output_get_layer_count(output, &layer_count); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + for (i = 0; i < layer_count; i++) { + tdm_layer *layer; + tdm_layer_capability capabilities; + + layer = tdm_output_get_layer(output, i, &ret); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + ret = tdm_layer_get_capabilities(layer, &capabilities); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + if (!(capabilities & TDM_LAYER_CAPABILITY_PRIMARY)) + continue; + + l = calloc(1, sizeof * l); + TDM_EXIT_IF_FAIL(l != NULL); + LIST_INITHEAD(&l->prop_list); + LIST_ADDTAIL(&l->link, &o->layer_list); + l->o = o; + l->idx = i; + l->is_primary = 1; + l->info.src_config.pos.w = l->info.src_config.size.h = mode->hdisplay; + l->info.src_config.pos.h = l->info.src_config.size.v = mode->vdisplay; + l->info.dst_pos = l->info.src_config.pos; + } + } else { + LIST_FOR_EACH_ENTRY(l, &o->layer_list, link) { + if (l->info.dst_pos.w == 0) { + TDM_EXIT_IF_FAIL(!l->owner_p && !l->owner_c); + if (l->is_primary) { + l->info.dst_pos.w = mode->hdisplay; + l->info.dst_pos.h = mode->vdisplay; + } else { + l->info.dst_pos.w = TDM_ALIGN(mode->hdisplay / 3, 2); + l->info.dst_pos.h = TDM_ALIGN(mode->vdisplay / 3, 2); + l->info.dst_pos.x = TDM_ALIGN(((mode->hdisplay / 3) / layer_count) * i, 2); + l->info.dst_pos.y = TDM_ALIGN(((mode->vdisplay / 3) / layer_count) * i, 2); + i++; + } + } + if (minw > 0 && minh > 0) { + TDM_EXIT_IF_FAIL(l->info.dst_pos.w >= minw); + TDM_EXIT_IF_FAIL(l->info.dst_pos.h >= minh); + } + if (maxw > 0 && maxh > 0) { + TDM_EXIT_IF_FAIL(l->info.dst_pos.w <= maxw); + TDM_EXIT_IF_FAIL(l->info.dst_pos.h <= maxh); + } + if (l->owner_p) { + l->info.src_config = l->owner_p->info.dst_config; + } else if (l->owner_c) { + l->info.src_config = l->owner_c->info.dst_config; + } else { + if (l->info.src_config.pos.w == 0) { + l->info.src_config.pos.w = l->info.dst_pos.w; + l->info.src_config.pos.h = l->info.dst_pos.h; + } + } + } + } + } +} + +static void +print_args(tdm_test_server *data) +{ + tdm_test_server_output *o = NULL; + tdm_test_server_layer *l = NULL; + tdm_test_server_pp *p = NULL; + tdm_test_server_capture *c = NULL; + tdm_test_server_prop *w = NULL; + + if (data->do_query) { + printf("query\n"); + return; + } + LIST_FOR_EACH_ENTRY(o, &data->output_list, link) { + printf("output %d: %s", o->idx, o->mode); + if (o->refresh > 0) + printf(" %d\n", o->refresh); + else + printf("\n"); + if (!LIST_IS_EMPTY(&o->prop_list)) { + printf("\tprops: "); + LIST_FOR_EACH_ENTRY(w, &o->prop_list, link) { + print_prop(w); + printf(" "); + } + printf("\n"); + } + LIST_FOR_EACH_ENTRY(l, &o->layer_list, link) { + printf("\t"); + printf("layer %d: ", l->idx); + print_config(&l->info.src_config); + printf(" ! "); + print_pos(&l->info.dst_pos); + printf(" trans(%d)\n", l->info.transform); + if (!LIST_IS_EMPTY(&l->prop_list)) { + printf("\t\tprops: "); + LIST_FOR_EACH_ENTRY(w, &l->prop_list, link) { + print_prop(w); + printf(" "); + } + printf("\n"); + } + } + } + LIST_FOR_EACH_ENTRY(p, &data->pp_list, link) { + printf("pp: "); + print_config(&p->info.src_config); + printf(" ! "); + print_config(&p->info.dst_config); + printf(" fps(%d) trans(%d)\n", p->fps, p->info.transform); + if (p->l) + printf("\toutput_idx(%d) layer_idx(%d)\n", p->l->o->idx, p->l->idx); + } + LIST_FOR_EACH_ENTRY(c, &data->capture_list, link) { + printf("capture: o(%d) l(%d) ", c->output_idx, c->layer_idx); + print_config(&c->info.dst_config); + printf(" trans(%d)\n", c->info.transform); + if (c->l) + printf("\toutput_idx(%d) layer_idx(%d)\n", c->l->o->idx, c->l->idx); + } + if (data->bflags != 0) { + printf("buffer: "); + char temp[256]; + char *p = temp; + int len = sizeof temp; + tdm_buf_flag_str(data->bflags, &p, &len); + printf(" (%s)\n", temp); + } +} + +static tdm_test_server tts_data; + +static void +exit_test(int sig) +{ + tdm_test_server *data = &tts_data; + tdm_test_server_output *o = NULL, *oo = NULL; + tdm_test_server_layer *l = NULL, *ll = NULL; + tdm_test_server_pp *p = NULL, *pp = NULL; + tdm_test_server_capture *c = NULL, *cc = NULL; + tdm_test_server_prop *w = NULL, *ww = NULL; + int i; + + printf("got signal: %d\n", sig); + + LIST_FOR_EACH_ENTRY_SAFE(o, oo, &data->output_list, link) { + LIST_DEL(&o->link); + + LIST_FOR_EACH_ENTRY_SAFE(l, ll, &o->layer_list, link) { + LIST_DEL(&l->link); + + tdm_layer_unset_buffer(l->layer); + + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &l->prop_list, link) { + LIST_DEL(&w->link); + free(w); + } + for (i = 0; i < TDM_ARRAY_SIZE(l->bufs); i++) { + tbm_surface_destroy(l->bufs[i]->buffer); + free(l->bufs[i]); + } + free(l); + } + + tdm_output_commit(o->output, 0, NULL, NULL); + + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &o->prop_list, link) { + LIST_DEL(&w->link); + free(w); + } + + free(o); + } + + + LIST_FOR_EACH_ENTRY_SAFE(p, pp, &data->pp_list, link) { + LIST_DEL(&p->link); + + tdm_display_lock(data->display); + tdm_event_loop_source_remove(p->timer_source); + tdm_display_unlock(data->display); + + tdm_pp_destroy(p->pp); + for (i = 0; i < TDM_ARRAY_SIZE(p->bufs); i++) { + tbm_surface_destroy(p->bufs[i]->buffer); + free(p->bufs[i]); + } + free(p); + } + + LIST_FOR_EACH_ENTRY_SAFE(c, cc, &data->capture_list, link) { + LIST_DEL(&c->link); + tdm_capture_destroy(c->capture); + free(c); + } + + if (data->display) + tdm_display_deinit(data->display); + + exit(0); +} + +int +main(int argc, char *argv[]) +{ + tdm_test_server *data = &tts_data; + char temp[TDM_SERVER_REPLY_MSG_LEN]; + int len = sizeof temp; + tdm_error ret; + + signal(SIGINT, exit_test); /* 2 */ + signal(SIGSEGV, exit_test); /* 11 */ + signal(SIGTERM, exit_test); /* 15 */ + + memset(data, 0, sizeof * data); + LIST_INITHEAD(&data->output_list); + LIST_INITHEAD(&data->pp_list); + LIST_INITHEAD(&data->capture_list); + + /* init value */ + data->bflags = TBM_BO_SCANOUT; + data->b_fill = PATTERN_SMPTE; + + data->display = tdm_display_init(&ret); + TDM_EXIT_IF_FAIL(data->display != NULL); + + parse_args(data, argc, argv); + interpret_args(data); + print_args(data); + + if (data->do_query) { + tdm_helper_get_display_information(data->display, temp, &len); + printf("%s", temp); + goto done; + } + + run_test(data); + +done: + tdm_display_deinit(data->display); + + return 0; +} + +static void +output_setup(tdm_test_server_output *o) +{ + tdm_test_server *data = o->data; + const tdm_output_mode *modes, *found = NULL, *best = NULL, *prefer = NULL; + tdm_test_server_prop *w = NULL; + const tdm_prop *props; + int i, count; + tdm_error ret; + + o->output = tdm_display_get_output(data->display, o->idx, &ret); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + ret = tdm_output_get_available_modes(o->output, &modes, &count); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + for (i = 0; i < count; i++) { + if (!strncmp(o->mode, modes[i].name, TDM_NAME_LEN) && o->refresh == modes[i].vrefresh) { + found = &modes[i]; + printf("found mode: %dx%d %d\n", found->hdisplay, found->vdisplay, found->vrefresh); + break; + } + if (!best) + best = &modes[i]; + if (modes[i].flags & TDM_OUTPUT_MODE_TYPE_PREFERRED) + prefer = &modes[i]; + } + if (!found && prefer) { + found = prefer; + printf("found prefer mode: %dx%d %d\n", found->hdisplay, found->vdisplay, found->vrefresh); + } + if (!found && best) { + found = best; + printf("found best mode: %dx%d %d\n", found->hdisplay, found->vdisplay, found->vrefresh); + } + + if (!found) { + printf("couldn't find any mode\n"); + exit(0); + } + + ret = tdm_output_set_mode(o->output, found); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + ret = tdm_output_get_available_properties(o->output, &props, &count); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + LIST_FOR_EACH_ENTRY(w, &o->prop_list, link) { + for (i = 0; i < count; i++) { + if (strncmp(w->name, props[i].name, TDM_NAME_LEN)) + continue; + ret = tdm_output_set_property(o->output, props[i].id, w->value); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + break; + } + } + + /* DPMS on forcely at the first time. */ + ret = tdm_output_set_dpms(o->output, TDM_OUTPUT_DPMS_ON); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); +} + +static tdm_test_server_buffer* +layer_get_buffer(tdm_test_server_layer *l) +{ + int i, size = TDM_ARRAY_SIZE(l->bufs); + if (!l->bufs[0]) { + for (i = 0; i < size; i++) { + int width = (l->info.src_config.size.h)?:l->info.src_config.pos.w; + int height = (l->info.src_config.size.v)?:l->info.src_config.pos.h; + unsigned int format = (l->info.src_config.format)?:TBM_FORMAT_ARGB8888; + int flags = l->o->data->bflags; + tdm_test_server_buffer *b = calloc(1, sizeof *b); + TDM_EXIT_IF_FAIL(b != NULL); + b->buffer = tbm_surface_internal_create_with_flags(width, height, format, flags); + TDM_EXIT_IF_FAIL(b->buffer != NULL); + l->bufs[i] = b; + } + } + for (i = 0; i < size; i++) { + if (!l->bufs[i]->in_use) { + tdm_test_buffer_fill(l->bufs[i]->buffer, l->data->b_fill); + return l->bufs[i]; + } + } + printf("no available layer buffer.\n"); + exit(0); +} + +static void +layer_cb_commit(tdm_output *output, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, + void *user_data) +{ + tdm_test_server_layer *l = user_data; + tdm_test_server_buffer *b = layer_get_buffer(l); + + TDM_EXIT_IF_FAIL(b != NULL); + layer_show_buffer(l, b); +} + +static void +layer_cb_buffer_release(tbm_surface_h buffer, void *user_data) +{ + tdm_test_server_buffer *b = user_data; + b->in_use = 0; + tdm_buffer_remove_release_handler(b->buffer, layer_cb_buffer_release, b); + if (b->done) + b->done(buffer, user_data); +} + +static void +layer_show_buffer(tdm_test_server_layer *l, tdm_test_server_buffer *b) +{ + tdm_test_server *data = l->o->data; + tdm_error ret; + + ret = tdm_layer_set_buffer(l->layer, b->buffer); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + b->in_use = 1; + tdm_buffer_add_release_handler(b->buffer, layer_cb_buffer_release, b); + + if (data->do_vblank) + ret = tdm_output_commit(l->o->output, 0, layer_cb_commit, l); + else + ret = tdm_output_commit(l->o->output, 0, NULL, NULL); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + printf("show:\tl(%p) b(%p)\n", l, b); +} + +static void +layer_setup(tdm_test_server_layer *l, tdm_test_server_buffer *b) +{ + tdm_test_server_prop *w = NULL; + const tdm_prop *props; + int i, count; + tdm_error ret; + tbm_surface_info_s info; + + l->layer = tdm_output_get_layer(l->o->output, l->idx, &ret); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + /* The size and format information should be same with buffer's */ + tbm_surface_get_info(b->buffer, &info); + if (IS_RGB(info.format)) { + l->info.src_config.size.h = info.planes[0].stride >> 2; + l->info.src_config.size.v = info.height; + } else { + l->info.src_config.size.h = info.planes[0].stride; + l->info.src_config.size.v = info.height; + } + l->info.src_config.format = info.format; + + ret = tdm_layer_set_info(l->layer, &l->info); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + ret = tdm_layer_get_available_properties(l->layer, &props, &count); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + LIST_FOR_EACH_ENTRY(w, &l->prop_list, link) { + for (i = 0; i < count; i++) { + if (strncmp(w->name, props[i].name, TDM_NAME_LEN)) + continue; + ret = tdm_layer_set_property(l->layer, props[i].id, w->value); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + break; + } + } +} + +static tdm_test_server_buffer* +pp_get_buffer(tdm_test_server_pp *p) +{ + int i, size = TDM_ARRAY_SIZE(p->bufs); + if (!p->bufs[0]) { + for (i = 0; i < size; i++) { + int width = (p->info.src_config.size.h)?:p->info.src_config.pos.w; + int height = (p->info.src_config.size.v)?:p->info.src_config.pos.h; + unsigned int format = (p->info.src_config.format)?:TBM_FORMAT_ARGB8888; + int flags = p->l->o->data->bflags; + tdm_test_server_buffer *b = calloc(1, sizeof *b); + TDM_EXIT_IF_FAIL(b != NULL); + b->buffer = tbm_surface_internal_create_with_flags(width, height, format, flags); + TDM_EXIT_IF_FAIL(b->buffer != NULL); + p->bufs[i] = b; + } + } + for (i = 0; i < size; i++) { + if (!p->bufs[i]->in_use) { + tdm_test_buffer_fill(p->bufs[i]->buffer, p->data->b_fill); + return p->bufs[i]; + } + } + printf("no available pp buffer.\n"); + exit(0); +} + +static void +pp_cb_sb_release(tbm_surface_h buffer, void *user_data) +{ + tdm_test_server_buffer *b = user_data; + b->in_use = 0; + tdm_buffer_remove_release_handler(b->buffer, pp_cb_sb_release, b); +} + +static void +pp_cb_db_release(tbm_surface_h buffer, void *user_data) +{ + tdm_test_server_buffer *b = user_data; + b->in_use = 0; + tdm_buffer_remove_release_handler(b->buffer, pp_cb_db_release, b); + layer_show_buffer(b->l, b); +} + +static void +pp_convert_buffer(tdm_test_server_pp *p, tdm_test_server_buffer *sb, tdm_test_server_buffer *db) +{ + tdm_error ret; + + ret = tdm_pp_attach(p->pp, sb->buffer, db->buffer); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + sb->in_use = db->in_use = 1; + db->l = p->l; + tdm_buffer_add_release_handler(sb->buffer, pp_cb_sb_release, sb); + tdm_buffer_add_release_handler(db->buffer, pp_cb_db_release, db); + + ret = tdm_pp_commit(p->pp); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + printf("convt:\tp(%p) sb(%p) db(%p)\n", p, sb, db); +} + +/* tdm_event_loop_xxx() function is not for the display server. It's for TDM + * backend module. I use them only for simulating the video play. When we call + * tdm_event_loop_xxx() outside of TDM backend module, we have to lock/unlock + * the TDM display. And when the callback function of tdm_event_loop_xxx() is + * called, the display has been already locked. So in this test application, + * we have to unlock/lock the display for testing. + */ +static tdm_error +pp_cb_timeout(void *user_data) +{ + tdm_test_server_pp *p = user_data; + tdm_test_server *data = p->l->o->data; + tdm_test_server_buffer *sb, *db; + tdm_error ret; + + tdm_display_unlock(data->display); + + sb = pp_get_buffer(p); + TDM_EXIT_IF_FAIL(sb != NULL); + db = layer_get_buffer(p->l); + TDM_EXIT_IF_FAIL(db != NULL); + + pp_convert_buffer(p, sb, db); + + tdm_display_lock(data->display); + ret = tdm_event_loop_source_timer_update(p->timer_source, 1000 / p->fps); + tdm_display_unlock(data->display); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + tdm_display_lock(data->display); + + return TDM_ERROR_NONE; +} + +static void +pp_setup(tdm_test_server_pp *p, tdm_test_server_buffer *sb, tdm_test_server_buffer *db) +{ + tdm_test_server *data = p->l->o->data; + tbm_surface_info_s info; + tdm_error ret; + + p->pp = tdm_display_create_pp(data->display, &ret); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + /* The size and format information should be same with buffer's */ + tbm_surface_get_info(sb->buffer, &info); + if (IS_RGB(info.format)) { + p->info.src_config.size.h = info.planes[0].stride >> 2; + p->info.src_config.size.v = info.height; + } else { + p->info.src_config.size.h = info.planes[0].stride; + p->info.src_config.size.v = info.height; + } + p->info.src_config.format = info.format; + + /* The size and format information should be same with buffer's */ + tbm_surface_get_info(db->buffer, &info); + if (IS_RGB(info.format)) { + p->info.dst_config.size.h = info.planes[0].stride >> 2; + p->info.dst_config.size.v = info.height; + } else { + p->info.dst_config.size.h = info.planes[0].stride; + p->info.dst_config.size.v = info.height; + } + p->info.dst_config.format = info.format; + + ret = tdm_pp_set_info(p->pp, &p->info); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + layer_setup(p->l, db); + + /* tdm_event_loop_xxx() function is not for the display server. It's for TDM + * backend module. I use them only for simulating the video play. When we call + * tdm_event_loop_xxx() outside of TDM backend module, we have to lock/unlock + * the TDM display. And when the callback function of tdm_event_loop_xxx() is + * called, the display has been already locked. So in this test application, + * we have to unlock/lock the display for testing. + */ + tdm_display_lock(data->display); + p->timer_source = tdm_event_loop_add_timer_handler(data->display, pp_cb_timeout, p, &ret); + tdm_display_unlock(data->display); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + tdm_display_lock(data->display); + ret = tdm_event_loop_source_timer_update(p->timer_source, 1000 / p->fps); + tdm_display_unlock(data->display); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); +} + +static void +capture_cb_buffer_done(tbm_surface_h buffer, void *user_data) +{ + tdm_test_server_buffer *b = user_data; + capture_attach(b->c, b); +} + +static void +capture_cb_buffer_release(tbm_surface_h buffer, void *user_data) +{ + tdm_test_server_buffer *b = user_data; + b->in_use = 0; + tdm_buffer_remove_release_handler(b->buffer, capture_cb_buffer_release, b); + b->done = capture_cb_buffer_done; + layer_show_buffer(b->l, b); +} + +static void +capture_attach(tdm_test_server_capture *c, tdm_test_server_buffer *b) +{ + tdm_error ret; + + ret = tdm_capture_attach(c->capture, b->buffer); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + b->in_use = 1; + b->l = c->l; + b->c = c; + tdm_buffer_add_release_handler(b->buffer, capture_cb_buffer_release, b); + printf("capture:\tc(%p) b(%p)\n", c, b); + + ret = tdm_capture_commit(c->capture); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); +} + +static void +capture_setup(tdm_test_server_capture *c, tdm_test_server_buffer *b) +{ + tdm_test_server *data = c->l->o->data; + tdm_output *output; + tdm_layer *layer; + tbm_surface_info_s info; + tdm_error ret; + + if (c->output_idx != -1 && c->layer_idx == -1) { + output = tdm_display_get_output(data->display, c->output_idx, &ret); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + c->capture = tdm_output_create_capture(output, &ret); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + } else if (c->output_idx != -1 && c->layer_idx != -1) { + output = tdm_display_get_output(data->display, c->output_idx, &ret); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + layer = tdm_output_get_layer(output, c->layer_idx, &ret); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + c->capture = tdm_layer_create_capture(layer, &ret); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + } + + TDM_EXIT_IF_FAIL(c->capture != NULL); + + /* The size and format information should be same with buffer's */ + tbm_surface_get_info(b->buffer, &info); + if (IS_RGB(info.format)) { + c->info.dst_config.size.h = info.planes[0].stride >> 2; + c->info.dst_config.size.v = info.height; + } else { + c->info.dst_config.size.h = info.planes[0].stride; + c->info.dst_config.size.v = info.height; + } + c->info.dst_config.format = info.format; + + ret = tdm_capture_set_info(c->capture, &c->info); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + layer_setup(c->l, b); +} + +static void +run_test(tdm_test_server *data) +{ + tdm_test_server_output *o = NULL; + tdm_test_server_layer *l = NULL; + tdm_test_server_pp *p = NULL; + tdm_test_server_capture *c = NULL; + tdm_display_capability caps; + tdm_error ret; + + ret = tdm_display_get_capabilities(data->display, &caps); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + LIST_FOR_EACH_ENTRY(o, &data->output_list, link) { + LIST_FOR_EACH_ENTRY(l, &o->layer_list, link) { + if (!l->owner_p && !l->owner_c) { + tdm_test_server_buffer *b; + b = layer_get_buffer(l); + layer_setup(l, b); + layer_show_buffer(l, b); + } + } + } + + LIST_FOR_EACH_ENTRY(p, &data->pp_list, link) { + tdm_test_server_buffer *sb, *db; + TDM_GOTO_IF_FAIL(caps & TDM_DISPLAY_CAPABILITY_PP, no_pp); + sb = pp_get_buffer(p); + TDM_EXIT_IF_FAIL(sb != NULL); + db = layer_get_buffer(p->l); + TDM_EXIT_IF_FAIL(db != NULL); + pp_setup(p, sb, db); + pp_convert_buffer(p, sb, db); + } + + LIST_FOR_EACH_ENTRY(c, &data->capture_list, link) { + TDM_GOTO_IF_FAIL(caps & TDM_DISPLAY_CAPABILITY_CAPTURE, no_capture); + tdm_test_server_buffer *b; + b = layer_get_buffer(c->l); + capture_setup(c, b); + capture_attach(c, b); + b = layer_get_buffer(c->l); + capture_attach(c, b); + b = layer_get_buffer(c->l); + capture_attach(c, b); + } + + printf("enter test loop\n"); + + while (1) + tdm_display_handle_events(data->display); + + return; +no_pp: + printf("no PP capability\n"); + exit(0); +no_capture: + printf("no Capture capability\n"); + exit(0); +} -- 2.7.4 From 8bf130d7b0a92368b7869301b93b115da6014287 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Mon, 11 Jul 2016 11:21:00 +0900 Subject: [PATCH 15/16] correct usage of rand_r() Change-Id: Iebaf7cdaafcedd6b08e1bf34f6c2495393b83f0f --- tools/buffers.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/buffers.c b/tools/buffers.c index 53ed615..c4de575 100644 --- a/tools/buffers.c +++ b/tools/buffers.c @@ -141,6 +141,8 @@ static const struct format_info format_info[] = { { TBM_FORMAT_BGRX1010102, "BX30", MAKE_RGB_INFO(10, 2, 10, 12, 10, 22, 0, 0) }, }; +static unsigned int rand_seed; + unsigned int format_fourcc(const char *name) { unsigned int i; @@ -558,7 +560,6 @@ fill_smpte_rgb32(const struct rgb_info *rgb, unsigned char *mem, }; unsigned int x; unsigned int y; - unsigned int seed = time(NULL);; if (width < 8) return; @@ -584,7 +585,7 @@ fill_smpte_rgb32(const struct rgb_info *rgb, unsigned char *mem, colors_bottom[(x - width * 5 / 7) * 3 / (width / 7) + 4]; for (; x < width; ++x) { - ((uint32_t *)mem)[x] = (rand_r(&seed) % 2) ? MAKE_RGBA(rgb, 255, 255, 255, 255) : MAKE_RGBA(rgb, 0, 0, 0, 255); + ((uint32_t *)mem)[x] = (rand_r(&rand_seed) % 2) ? MAKE_RGBA(rgb, 255, 255, 255, 255) : MAKE_RGBA(rgb, 0, 0, 0, 255); } mem += stride; } @@ -939,6 +940,9 @@ tdm_test_buffer_fill(tbm_surface_h buffer, int pattern) void *plane[3]; int ret; + if (rand_seed == 0) + rand_seed = time(NULL); + ret = tbm_surface_map(buffer, TBM_OPTION_WRITE, &info); TDM_EXIT_IF_FAIL(ret == 0); -- 2.7.4 From 02d2100670785551b95cde50a3cf0f0ee395031a Mon Sep 17 00:00:00 2001 From: Boram Park Date: Mon, 11 Jul 2016 16:24:56 +0900 Subject: [PATCH 16/16] fix the too many logs when enabled Change-Id: I155b5fc09cc38d0786b5e66867b7565de51cf53e --- src/tdm.c | 93 ++++++++++++++++++++++++++++++++-------------------- src/tdm_buffer.c | 6 ++-- src/tdm_capture.c | 4 +-- src/tdm_dbg_server.c | 34 +++++++------------ src/tdm_display.c | 20 +++++------ src/tdm_event_loop.c | 4 +-- src/tdm_pp.c | 4 +-- src/tdm_private.h | 25 +++++++++----- src/tdm_server.c | 8 ++--- src/tdm_thread.c | 14 ++++---- src/tdm_vblank.c | 49 +++++++++++++++++---------- 11 files changed, 146 insertions(+), 115 deletions(-) diff --git a/src/tdm.c b/src/tdm.c index 0bbd7a7..d388f19 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -668,9 +668,7 @@ tdm_display_update(tdm_display *dpy) #define SUFFIX_MODULE ".so" #define DEFAULT_MODULE "libtdm-default"SUFFIX_MODULE -int tdm_debug_buffer; -int tdm_debug_thread; -int tdm_debug_mutex; +int tdm_debug_module; int tdm_debug_dump; static tdm_private_display *g_private_display; @@ -901,22 +899,14 @@ tdm_display_init(tdm_error *error) return g_private_display; } - debug = getenv("TDM_DEBUG_BUFFER"); - if (debug && (strstr(debug, "1"))) - tdm_debug_buffer = 1; + debug = getenv("TDM_DEBUG_MODULE"); + if (debug) + tdm_display_enable_debug_module(debug); debug = getenv("TDM_DEBUG_DUMP"); if (debug) tdm_display_enable_dump(debug); - debug = getenv("TDM_DEBUG_THREAD"); - if (debug && (strstr(debug, "1"))) - tdm_debug_thread = 1; - - debug = getenv("TDM_DEBUG_MUTEX"); - if (debug && (strstr(debug, "1"))) - tdm_debug_mutex = 1; - private_display = calloc(1, sizeof(tdm_private_display)); if (!private_display) { ret = TDM_ERROR_OUT_OF_MEMORY; @@ -984,7 +974,6 @@ failed_event: failed_mutex_init: free(private_display); failed_alloc: - tdm_debug_buffer = 0; if (error) *error = ret; _pthread_mutex_unlock(&gLock); @@ -1028,7 +1017,6 @@ tdm_display_deinit(tdm_display *dpy) pthread_mutex_destroy(&private_display->lock); free(private_display); g_private_display = NULL; - tdm_debug_buffer = 0; _pthread_mutex_unlock(&gLock); @@ -1049,42 +1037,77 @@ tdm_display_check_module_abi(tdm_private_display *private_display, int abimaj, i return 1; } -INTERN void -tdm_display_enable_debug(char *debug, int enable) +INTERN tdm_error +tdm_display_enable_debug_module(const char*modules) { - if (!strncmp(debug, "TDM_DEBUG_BUFFER", 16)) - tdm_debug_buffer = enable; - else if (!strncmp(debug, "TDM_DEBUG_THREAD", 16)) - tdm_debug_thread = enable; - else if (!strncmp(debug, "TDM_DEBUG_MUTEX", 15)) - tdm_debug_mutex = enable; + char temp[TDM_PATH_LEN]; + char *arg; + char *end; + + snprintf(temp, TDM_PATH_LEN, "%s", modules); + + tdm_debug_module = 0; + + arg = strtok_r(temp, TDM_DELIM, &end); + while (arg) { + if (!strncmp(arg, "none", 4)) { + tdm_debug_module = 0; + return TDM_ERROR_NONE; + } + if (!strncmp(arg, "all", 3)) { + tdm_debug_module = 0xFFFFFFFF; + return TDM_ERROR_NONE; + } + if (!strncmp(arg, "buffer", 6)) + tdm_debug_module |= TDM_DEBUG_BUFFER; + else if (!strncmp(arg, "thread", 6)) + tdm_debug_module |= TDM_DEBUG_THREAD; + else if (!strncmp(arg, "mutex", 5)) + tdm_debug_module |= TDM_DEBUG_MUTEX; + else + return TDM_ERROR_BAD_REQUEST; + + arg = strtok_r(NULL, TDM_DELIM, &end); + } + + TDM_INFO("module debugging... '%s'", modules); + + return TDM_ERROR_NONE; } -INTERN void +INTERN tdm_error tdm_display_enable_dump(const char *dump_str) { char temp[1024]; char *arg; char *end; - int flags = 0; + + tdm_debug_dump = 0; snprintf(temp, sizeof(temp), "%s", dump_str); arg = strtok_r(temp, ",", &end); while (arg) { + if (!strncmp(arg, "none", 4)) { + tdm_debug_dump = 0; + return TDM_ERROR_NONE; + } if (!strncmp(arg, "all", 3)) { - flags = TDM_DUMP_FLAG_LAYER|TDM_DUMP_FLAG_PP|TDM_DUMP_FLAG_CAPTURE; - break; + tdm_debug_dump = 0xFFFFFFFF; + return TDM_ERROR_NONE; } - else if (!strncmp(arg, "layer", 5)) - flags |= TDM_DUMP_FLAG_LAYER; + if (!strncmp(arg, "layer", 5)) + tdm_debug_dump |= TDM_DUMP_FLAG_LAYER; else if (!strncmp(arg, "pp", 2)) - flags |= TDM_DUMP_FLAG_PP; + tdm_debug_dump |= TDM_DUMP_FLAG_PP; else if (!strncmp(arg, "capture", 7)) - flags |= TDM_DUMP_FLAG_CAPTURE; - else if (!strncmp(arg, "none", 4)) - flags = 0; + tdm_debug_dump |= TDM_DUMP_FLAG_CAPTURE; + else + return TDM_ERROR_BAD_REQUEST; + arg = strtok_r(NULL, ",", &end); } - tdm_debug_dump = flags; + TDM_INFO("dump... '%s'", dump_str); + + return TDM_ERROR_NONE; } diff --git a/src/tdm_buffer.c b/src/tdm_buffer.c index 93bbd61..9b4bdb8 100644 --- a/src/tdm_buffer.c +++ b/src/tdm_buffer.c @@ -60,7 +60,7 @@ _tdm_buffer_destroy_info(void *user_data) if (buf_info->backend_ref_count > 0) { TDM_NEVER_GET_HERE(); - if (tdm_debug_buffer) + if (tdm_debug_module & TDM_DEBUG_BUFFER) TDM_INFO("%p", buf_info->buffer); } @@ -77,7 +77,7 @@ _tdm_buffer_destroy_info(void *user_data) free(func_info); } - if (tdm_debug_buffer) + if (tdm_debug_module & TDM_DEBUG_BUFFER) TDM_INFO("%p destroyed", buf_info->buffer); free(buf_info); @@ -107,7 +107,7 @@ tdm_buffer_get_info(tbm_surface_h buffer) return NULL; } - if (tdm_debug_buffer) + if (tdm_debug_module & TDM_DEBUG_BUFFER) TDM_INFO("%p created", buf_info->buffer); } diff --git a/src/tdm_capture.c b/src/tdm_capture.c index cc79f62..f5bcfc2 100644 --- a/src/tdm_capture.c +++ b/src/tdm_capture.c @@ -112,7 +112,7 @@ tdm_capture_cb_done(tdm_capture *capture_backend, tbm_surface_h buffer, tdm_helper_dump_buffer_str(buffer, str); } - if (tdm_debug_buffer) + if (tdm_debug_module & TDM_DEBUG_BUFFER) TDM_INFO("capture(%p) done: %p", private_capture, buffer); first_entry = tdm_buffer_list_get_first_entry(&private_capture->buffer_list); @@ -387,7 +387,7 @@ tdm_capture_attach(tdm_capture *capture, tbm_surface_h buffer) if ((buf_info = tdm_buffer_get_info(buffer))) LIST_ADDTAIL(&buf_info->link, &private_capture->pending_buffer_list); - if (tdm_debug_buffer) { + if (tdm_debug_module & TDM_DEBUG_BUFFER) { TDM_INFO("capture(%p) attached:", private_capture); tdm_buffer_list_dump(&private_capture->buffer_list); } diff --git a/src/tdm_dbg_server.c b/src/tdm_dbg_server.c index 76f895f..c5db70a 100644 --- a/src/tdm_dbg_server.c +++ b/src/tdm_dbg_server.c @@ -89,7 +89,7 @@ _tdm_dbg_server_dpms(unsigned int pid, char *cwd, int argc, char *argv[], char * static void _tdm_dbg_server_debug(unsigned int pid, char *cwd, int argc, char *argv[], char *reply, int *len, tdm_display *dpy) { - int value; + int level; char *arg; char *end; @@ -99,29 +99,17 @@ _tdm_dbg_server_debug(unsigned int pid, char *cwd, int argc, char *argv[], char } arg = argv[2]; - value = strtol(arg, &end, 10); + level = strtol(arg, &end, 10); - tdm_log_set_debug_level(value); + tdm_log_set_debug_level(level); + TDM_SNPRINTF(reply, len, "debug level: %d\n", level); - value = !!value; - tdm_log_enable_debug(value); + if (*end == '@') { + char *arg = end + 1; - TDM_SNPRINTF(reply, len, "debug '%s'\n", (value) ? "on" : "off"); + tdm_display_enable_debug_module((const char *)arg); - if (argc > 3) { - char temp[TDM_PATH_LEN]; - char *arg; - char *end; - - snprintf(temp, TDM_PATH_LEN, "%s", argv[3]); - - arg = strtok_r(temp, TDM_DELIM, &end); - while (arg) { - tdm_display_enable_debug(arg, value); - if (value) - TDM_SNPRINTF(reply, len, "debuging '%s'...\n", arg); - arg = strtok_r(NULL, TDM_DELIM, &end); - } + TDM_SNPRINTF(reply, len, "debugging... '%s'\n", arg); } } @@ -296,9 +284,9 @@ static struct { }, { "debug", _tdm_dbg_server_debug, - "enable the debug level log", - "", - "0 or 1" + "set the debug level and modules(none,mutex,buffer,thread,vblank)", + "[@[,]]", + NULL }, { "log_path", _tdm_dbg_server_log_path, diff --git a/src/tdm_display.c b/src/tdm_display.c index fd4dff6..8dcf66f 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -332,7 +332,7 @@ tdm_display_handle_events(tdm_display *dpy) fds.fd = fd; fds.revents = 0; - if (tdm_debug_thread) + if (tdm_debug_module & TDM_DEBUG_THREAD) TDM_INFO("fd(%d) polling in", fd); while (poll(&fds, 1, -1) < 0) { @@ -344,7 +344,7 @@ tdm_display_handle_events(tdm_display *dpy) } } - if (tdm_debug_thread) + if (tdm_debug_module & TDM_DEBUG_THREAD) TDM_INFO("fd(%d) polling out", fd); if (tdm_thread_is_running()) @@ -898,7 +898,7 @@ tdm_output_cb_commit(tdm_output *output_backend, unsigned int sequence, private_layer->showing_buffer = private_layer->waiting_buffer; private_layer->waiting_buffer = NULL; - if (tdm_debug_buffer) + if (tdm_debug_module & TDM_DEBUG_BUFFER) TDM_INFO("layer(%p) waiting_buffer(%p) showing_buffer(%p)", private_layer, private_layer->waiting_buffer, private_layer->showing_buffer); @@ -1496,7 +1496,7 @@ tdm_layer_set_buffer(tdm_layer *layer, tbm_surface_h buffer) } private_layer->waiting_buffer = tdm_buffer_ref_backend(buffer); - if (tdm_debug_buffer) + if (tdm_debug_module & TDM_DEBUG_BUFFER) TDM_INFO("layer(%p) waiting_buffer(%p)", private_layer, private_layer->waiting_buffer); } @@ -1522,7 +1522,7 @@ tdm_layer_unset_buffer(tdm_layer *layer) _pthread_mutex_lock(&private_display->lock); private_layer->waiting_buffer = NULL; - if (tdm_debug_buffer) + if (tdm_debug_module & TDM_DEBUG_BUFFER) TDM_INFO("layer(%p) waiting_buffer(%p)", private_layer, private_layer->waiting_buffer); } @@ -1533,7 +1533,7 @@ tdm_layer_unset_buffer(tdm_layer *layer) _pthread_mutex_lock(&private_display->lock); private_layer->showing_buffer = NULL; - if (tdm_debug_buffer) + if (tdm_debug_module & TDM_DEBUG_BUFFER) TDM_INFO("layer(%p) showing_buffer(%p)", private_layer, private_layer->showing_buffer); } @@ -1622,7 +1622,7 @@ _tbm_layer_queue_acquirable_cb(tbm_surface_queue_h surface_queue, void *data) private_layer->waiting_buffer = tdm_buffer_ref_backend(surface); - if (tdm_debug_buffer) + if (tdm_debug_module & TDM_DEBUG_BUFFER) TDM_INFO("layer(%p) waiting_buffer(%p)", private_layer, private_layer->waiting_buffer); @@ -1693,7 +1693,7 @@ tdm_layer_set_buffer_queue(tdm_layer *layer, tbm_surface_queue_h buffer_queue) private_layer->waiting_buffer = NULL; _pthread_mutex_lock(&private_display->lock); - if (tdm_debug_buffer) + if (tdm_debug_module & TDM_DEBUG_BUFFER) TDM_INFO("layer(%p) waiting_buffer(%p)", private_layer, private_layer->waiting_buffer); } @@ -1728,7 +1728,7 @@ tdm_layer_unset_buffer_queue(tdm_layer *layer) private_layer->waiting_buffer = NULL; _pthread_mutex_lock(&private_display->lock); - if (tdm_debug_buffer) + if (tdm_debug_module & TDM_DEBUG_BUFFER) TDM_INFO("layer(%p) waiting_buffer(%p)", private_layer, private_layer->waiting_buffer); } @@ -1741,7 +1741,7 @@ tdm_layer_unset_buffer_queue(tdm_layer *layer) _pthread_mutex_lock(&private_display->lock); private_layer->showing_buffer = NULL; - if (tdm_debug_buffer) + if (tdm_debug_module & TDM_DEBUG_BUFFER) TDM_INFO("layer(%p) showing_buffer(%p)", private_layer, private_layer->showing_buffer); } diff --git a/src/tdm_event_loop.c b/src/tdm_event_loop.c index 4496806..29a9763 100644 --- a/src/tdm_event_loop.c +++ b/src/tdm_event_loop.c @@ -75,7 +75,7 @@ _tdm_event_loop_main_fd_handler(int fd, tdm_event_loop_mask mask, void *user_dat private_loop = private_display->private_loop; - if (tdm_debug_thread) + if (tdm_debug_module & TDM_DEBUG_THREAD) TDM_INFO("backend fd(%d) event happens", private_loop->backend_fd); func_display = &private_display->func_display; @@ -225,7 +225,7 @@ tdm_event_loop_dispatch(tdm_private_display *private_display) TDM_RETURN_VAL_IF_FAIL(private_loop->wl_loop != NULL, TDM_ERROR_OPERATION_FAILED); - if (tdm_debug_thread) + if (tdm_debug_module & TDM_DEBUG_THREAD) TDM_INFO("dispatch"); if (tdm_thread_is_running() && diff --git a/src/tdm_pp.c b/src/tdm_pp.c index 91a1239..a43d257 100644 --- a/src/tdm_pp.c +++ b/src/tdm_pp.c @@ -144,7 +144,7 @@ tdm_pp_cb_done(tdm_pp *pp_backend, tbm_surface_h src, tbm_surface_h dst, tdm_helper_dump_buffer_str(dst, str); } - if (tdm_debug_buffer) + if (tdm_debug_module & TDM_DEBUG_BUFFER) TDM_INFO("pp(%p) done: src(%p) dst(%p)", private_pp, src, dst); if (!LIST_IS_EMPTY(&private_pp->buffer_list)) { @@ -399,7 +399,7 @@ tdm_pp_attach(tdm_pp *pp, tbm_surface_h src, tbm_surface_h dst) pp_buffer->src = tdm_buffer_ref_backend(src); pp_buffer->dst = tdm_buffer_ref_backend(dst); - if (tdm_debug_buffer) { + if (tdm_debug_module & TDM_DEBUG_BUFFER) { TDM_INFO("pp(%p) attached:", private_pp); _tdm_pp_print_list(&private_pp->pending_buffer_list); } diff --git a/src/tdm_private.h b/src/tdm_private.h index b2b61e6..0e792c8 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -73,9 +73,16 @@ extern "C" { * @brief The private header file for a frontend library */ -extern int tdm_debug_buffer; -extern int tdm_debug_mutex; -extern int tdm_debug_thread; +enum { + TDM_DEBUG_NONE, + TDM_DEBUG_BUFFER = (1 << 0), + TDM_DEBUG_MUTEX = (1 << 1), + TDM_DEBUG_THREAD = (1 << 2), + TDM_DEBUG_SERVER = (1 << 3), + TDM_DEBUG_VBLANK = (1 << 4), +}; + +extern int tdm_debug_module; extern int tdm_debug_dump; #ifdef HAVE_TTRACE @@ -466,7 +473,7 @@ extern int tdm_dump_enable; #define _pthread_mutex_unlock(l) \ do { \ - if (tdm_debug_mutex) \ + if (tdm_debug_module & TDM_DEBUG_MUTEX) \ TDM_INFO("mutex unlock"); \ pthread_mutex_lock(&tdm_mutex_check_lock); \ tdm_mutex_locked = 0; \ @@ -477,7 +484,7 @@ extern int tdm_dump_enable; #define MUTEX_TIMEOUT_SEC 5 #define _pthread_mutex_lock(l) \ do { \ - if (tdm_debug_mutex) \ + if (tdm_debug_module & TDM_DEBUG_MUTEX) \ TDM_INFO("mutex lock"); \ struct timespec rtime; \ clock_gettime(CLOCK_REALTIME, &rtime); \ @@ -495,7 +502,7 @@ extern int tdm_dump_enable; #else //TDM_CONFIG_MUTEX_TIMEOUT #define _pthread_mutex_lock(l) \ do { \ - if (tdm_debug_mutex) \ + if (tdm_debug_module & TDM_DEBUG_MUTEX) \ TDM_INFO("mutex lock"); \ pthread_mutex_lock(l); \ pthread_mutex_lock(&tdm_mutex_check_lock); \ @@ -522,9 +529,9 @@ static inline int TDM_MUTEX_IS_LOCKED(void) tdm_error tdm_display_update_output(tdm_private_display *private_display, tdm_output *output_backend, int pipe); -void -tdm_display_enable_debug(char *debug, int enable); -void +tdm_error +tdm_display_enable_debug_module(const char*modules); +tdm_error tdm_display_enable_dump(const char *dump_str); /** diff --git a/src/tdm_server.c b/src/tdm_server.c index a20984f..ab6467e 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -140,7 +140,8 @@ _tdm_server_send_done(tdm_server_wait_info *wait_info, tdm_error error, return; } - TDM_DBG("req_id(%d) done", wait_info->req_id); + if (tdm_debug_module & TDM_DEBUG_VBLANK) + TDM_INFO("req_id(%d) done", wait_info->req_id); vblank_info = wait_info->vblank_info; wl_tdm_vblank_send_done(vblank_info->resource, wait_info->req_id, @@ -254,7 +255,8 @@ _tdm_server_vblank_cb_wait_vblank(struct wl_client *client, struct wl_resource * wait_info->vblank_info = vblank_info; wait_info->req_id = req_id; - TDM_DBG("req_id(%d) wait", req_id); + if (tdm_debug_module & TDM_DEBUG_VBLANK) + TDM_INFO("req_id(%d) wait", req_id); ret = tdm_vblank_wait(vblank_info->vblank, req_sec, req_usec, interval, _tdm_server_cb_vblank, wait_info); TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, wait_failed); @@ -443,8 +445,6 @@ _tdm_server_bind(struct wl_client *client, void *data, return; } - TDM_DBG("tdm server binding"); - wl_resource_set_implementation(resource, &tdm_implementation, data, NULL); } diff --git a/src/tdm_thread.c b/src/tdm_thread.c index b9490a5..03ce03a 100644 --- a/src/tdm_thread.c +++ b/src/tdm_thread.c @@ -86,16 +86,16 @@ _tdm_thread_main(void *data) fds.revents = 0; while (1) { - if (tdm_debug_thread) + if (tdm_debug_module & TDM_DEBUG_THREAD) TDM_INFO("server flush"); tdm_event_loop_flush(private_loop->dpy); - if (tdm_debug_thread) + if (tdm_debug_module & TDM_DEBUG_THREAD) TDM_INFO("fd(%d) polling in", fd); ret = poll(&fds, 1, -1); - if (tdm_debug_thread) + if (tdm_debug_module & TDM_DEBUG_THREAD) TDM_INFO("fd(%d) polling out", fd); if (ret < 0) { @@ -107,7 +107,7 @@ _tdm_thread_main(void *data) } } - if (tdm_debug_thread) + if (tdm_debug_module & TDM_DEBUG_THREAD) TDM_INFO("thread got events"); if (tdm_event_loop_dispatch(private_loop->dpy) < 0) @@ -229,7 +229,7 @@ tdm_thread_send_cb(tdm_private_loop *private_loop, tdm_thread_cb_base *base) private_thread = private_loop->private_thread; - if (tdm_debug_thread) + if (tdm_debug_module & TDM_DEBUG_THREAD) TDM_INFO("fd(%d) type(%d), length(%d)", private_thread->pipe[1], base->type, base->length); @@ -261,7 +261,7 @@ tdm_thread_handle_cb(tdm_private_loop *private_loop) len = read(private_thread->pipe[0], buffer, sizeof buffer); - if (tdm_debug_thread) + if (tdm_debug_module & TDM_DEBUG_THREAD) TDM_INFO("fd(%d) read length(%d)", private_thread->pipe[0], len); if (len == 0) @@ -277,7 +277,7 @@ tdm_thread_handle_cb(tdm_private_loop *private_loop) i = 0; while (i < len) { base = (tdm_thread_cb_base*)&buffer[i]; - if (tdm_debug_thread) + if (tdm_debug_module & TDM_DEBUG_THREAD) TDM_INFO("type(%d), length(%d)", base->type, base->length); switch (base->type) { case TDM_THREAD_CB_OUTPUT_COMMIT: { diff --git a/src/tdm_vblank.c b/src/tdm_vblank.c index 30ed7df..0f278c2 100644 --- a/src/tdm_vblank.c +++ b/src/tdm_vblank.c @@ -335,8 +335,9 @@ tdm_vblank_create(tdm_display *dpy, tdm_output *output, tdm_error *error) LIST_ADD(&private_vblank->link, &vblank_list); - VDB("created. vrefresh(%d) dpms(%d)", - private_vblank->vrefresh, private_vblank->dpms); + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("created. vrefresh(%d) dpms(%d)", + private_vblank->vrefresh, private_vblank->dpms); return (tdm_vblank*)private_vblank; } @@ -382,7 +383,8 @@ tdm_vblank_destroy(tdm_vblank *vblank) free(w); } - VIN("destroyed"); + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("destroyed"); free(private_vblank); } @@ -401,7 +403,8 @@ tdm_vblank_set_fps(tdm_vblank *vblank, unsigned int fps) private_vblank->fps = fps; private_vblank->check_HW_or_SW = 1; - VDB("fps(%d)", private_vblank->fps); + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("fps(%d)", private_vblank->fps); return TDM_ERROR_NONE; } @@ -419,7 +422,8 @@ tdm_vblank_set_offset(tdm_vblank *vblank, int offset) private_vblank->offset = offset; private_vblank->check_HW_or_SW = 1; - VDB("offset(%d)", private_vblank->offset); + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("offset(%d)", private_vblank->offset); return TDM_ERROR_NONE; } @@ -436,7 +440,8 @@ tdm_vblank_set_enable_fake(tdm_vblank *vblank, unsigned int enable_fake) private_vblank->enable_fake = enable_fake; - VDB("enable_fake(%d)", private_vblank->enable_fake); + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("enable_fake(%d)", private_vblank->enable_fake); return TDM_ERROR_NONE; } @@ -473,7 +478,8 @@ _tdm_vblank_cb_vblank_HW(tdm_output *output, unsigned int sequence, wait_info->func(private_vblank, TDM_ERROR_NONE, private_vblank->last_seq, tv_sec, tv_usec, wait_info->user_data); - VDB("wait(%p) done", wait_info); + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("wait(%p) done", wait_info); free(wait_info); } @@ -497,7 +503,8 @@ _tdm_vblank_wait_HW(tdm_vblank_wait_info *wait_info) return ret; } - VDB("wait(%p) waiting", wait_info); + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("wait(%p) waiting", wait_info); return TDM_ERROR_NONE; } @@ -518,7 +525,8 @@ _tdm_vblank_cb_vblank_SW(void *user_data) first_wait_info = container_of(private_vblank->SW_wait_list.next, first_wait_info, link); TDM_RETURN_VAL_IF_FAIL(first_wait_info != NULL, TDM_ERROR_OPERATION_FAILED); - VDB("wait(%p) done", first_wait_info); + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("wait(%p) done", first_wait_info); private_vblank->last_seq = first_wait_info->target_seq; private_vblank->last_tv_sec = first_wait_info->target_sec; @@ -568,7 +576,8 @@ _tdm_vblank_cb_vblank_SW_first(tdm_output *output, unsigned int sequence, w = container_of((&private_vblank->SW_pending_wait_list)->next, w, link); TDM_RETURN_IF_FAIL(w != NULL); - VDB("wait(%p) done", w); + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("wait(%p) done", w); min_interval = w->interval; @@ -621,8 +630,9 @@ _tdm_vblank_sw_timer_update(tdm_private_vblank *private_vblank) if (ms_delay < 1) ms_delay = 1; - VDB("wait(%p) curr(%4lu) target(%4lu) ms_delay(%d)", - first_wait_info, curr, target, ms_delay); + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("wait(%p) curr(%4lu) target(%4lu) ms_delay(%d)", + first_wait_info, curr, target, ms_delay); tdm_display_lock(private_vblank->dpy); @@ -749,7 +759,8 @@ _tdm_vblank_wait_SW(tdm_vblank_wait_info *wait_info) LIST_DEL(&wait_info->link); return ret; } - VDB("wait(%p) waiting", wait_info); + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("wait(%p) waiting", wait_info); } return TDM_ERROR_NONE; } @@ -823,8 +834,9 @@ _tdm_vblank_calculate_target(tdm_vblank_wait_info *wait_info) } } - VDB("target_seq(%d) last_seq(%d) skip(%d)", - wait_info->target_seq, private_vblank->last_seq, skip); + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("target_seq(%d) last_seq(%d) skip(%d)", + wait_info->target_seq, private_vblank->last_seq, skip); #if 0 target -= (private_vblank->SW_align_offset * skip * private_vblank->HW_quotient); @@ -834,9 +846,10 @@ _tdm_vblank_calculate_target(tdm_vblank_wait_info *wait_info) wait_info->target_sec = target / 1000000; wait_info->target_usec = target % 1000000; - VDB("wait(%p) last(%4lu) req(%4lu) prev(%4lu) curr(%4lu) skip(%d) hw_interval(%d) target(%4lu,%4lu)", - wait_info, last, req - last, prev - last, curr - last, - skip, wait_info->target_hw_interval, target, target - last); + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("wait(%p) last(%4lu) req(%4lu) prev(%4lu) curr(%4lu) skip(%d) hw_interval(%d) target(%4lu,%4lu)", + wait_info, last, req - last, prev - last, curr - last, + skip, wait_info->target_hw_interval, target, target - last); } tdm_error -- 2.7.4