From 5ecd7fb59c26446e6f7a83cd2278b0cecd5393bf Mon Sep 17 00:00:00 2001 From: Boram Park Date: Mon, 4 Jul 2016 13:35:19 +0900 Subject: [PATCH] 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