From 9b901bab29b2fe53d666b5939dafe87e591b939b Mon Sep 17 00:00:00 2001 From: Roman Marchenko Date: Fri, 27 May 2016 16:12:07 +0300 Subject: [PATCH 01/16] move a registration of commit_cb before calling output_commit If TDM backend works without events (for example fbdev backend) then it should be able to call the commit handler function right after doing commit. Change-Id: I981a02bf257fda116861554db47e0bdf822bfa1f Signed-off-by: Roman Marchenko --- src/tdm_display.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tdm_display.c b/src/tdm_display.c index 1790229..969b227 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -1058,16 +1058,16 @@ _tdm_output_commit(tdm_output *output, int sync, tdm_output_commit_handler func, commit_handler->user_data = user_data; commit_handler->owner_tid = syscall(SYS_gettid); - ret = func_output->output_commit(private_output->output_backend, sync, - commit_handler); - TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret); - if (!private_output->regist_commit_cb) { private_output->regist_commit_cb = 1; ret = func_output->output_set_commit_handler(private_output->output_backend, tdm_output_cb_commit); } + ret = func_output->output_commit(private_output->output_backend, sync, + commit_handler); + TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret); + return ret; } -- 2.7.4 From ad3d294b782be0e2b55887399e81084253886bf3 Mon Sep 17 00:00:00 2001 From: Junkyeong Kim Date: Wed, 1 Jun 2016 16:14:55 +0900 Subject: [PATCH 02/16] erase tdm log macros semicolon Change-Id: Ic012277818e726da3bc9f3e92758e59a038d7c7c Signed-off-by: Junkyeong Kim --- include/tdm_log.h | 40 +++++++++++++++++++++------------------- src/tdm_display.c | 7 +++---- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/include/tdm_log.h b/include/tdm_log.h index 02943c0..c55acf6 100644 --- a/include/tdm_log.h +++ b/include/tdm_log.h @@ -79,14 +79,15 @@ extern int tdm_debug; #define LOG_TAG "TDM" #define TDM_DBG(fmt, args...) \ - if (tdm_debug) \ do { \ - struct timespec ts; \ - clock_gettime(CLOCK_MONOTONIC, &ts); \ - LOGD("[%d.%06d] "fmt"\n", (int)ts.tv_sec, (int)ts.tv_nsec / 1000, ##args); \ - printf("[TDM_DBG][%d.%06d][%d][%s %d] "fmt"\n", (int)ts.tv_sec, \ - (int)ts.tv_nsec / 1000, (int)syscall(SYS_gettid), __func__, __LINE__, ##args); \ - } while (0); + if (tdm_debug) { \ + struct timespec ts; \ + clock_gettime(CLOCK_MONOTONIC, &ts); \ + LOGD("[%d.%06d] "fmt"\n", (int)ts.tv_sec, (int)ts.tv_nsec / 1000, ##args); \ + printf("[TDM_DBG][%d.%06d][%d][%s %d] "fmt"\n", (int)ts.tv_sec, \ + (int)ts.tv_nsec / 1000, (int)syscall(SYS_gettid), __func__, __LINE__, ##args); \ + } \ + } while (0) #define TDM_INFO(fmt, args...) \ do { \ @@ -95,7 +96,7 @@ extern int tdm_debug; LOGI("[%d.%06d] "fmt"\n", (int)ts.tv_sec, (int)ts.tv_nsec / 1000, ##args); \ printf("[TDM_INF][%d.%06d][%d][%s %d] "fmt"\n", (int)ts.tv_sec, \ (int)ts.tv_nsec / 1000, (int)syscall(SYS_gettid), __func__, __LINE__, ##args); \ - } while (0); + } while (0) #define TDM_WRN(fmt, args...) \ do { \ @@ -104,7 +105,7 @@ extern int tdm_debug; LOGI("[%d.%06d] "fmt"\n", (int)ts.tv_sec, (int)ts.tv_nsec / 1000, ##args); \ printf("[TDM_WRN][%d.%06d][%d][%s %d] "fmt"\n", (int)ts.tv_sec, \ (int)ts.tv_nsec / 1000, (int)syscall(SYS_gettid), __func__, __LINE__, ##args); \ - } while (0); + } while (0) #define TDM_ERR(fmt, args...) \ do { \ @@ -113,7 +114,7 @@ extern int tdm_debug; LOGE("[%d.%06d] "fmt"\n", (int)ts.tv_sec, (int)ts.tv_nsec / 1000, ##args); \ printf("[TDM_ERR][%d.%06d][%d][%s %d] "fmt"\n", (int)ts.tv_sec, \ (int)ts.tv_nsec / 1000, (int)syscall(SYS_gettid), __func__, __LINE__, ##args); \ - } while (0); + } while (0) #else /* TDM_CONFIG_DLOG */ @@ -125,13 +126,14 @@ extern int tdm_debug; #define COLOR_RESET "\x1b[0m" #define TDM_DBG(fmt, args...) \ - if (tdm_debug) \ do { \ - struct timespec ts; \ - clock_gettime(CLOCK_MONOTONIC, &ts); \ - printf("[TDM_DBG][%d.%06d][%d][%s %d] "fmt"\n", (int)ts.tv_sec, \ - (int)ts.tv_nsec / 1000, (int)syscall(SYS_gettid), __func__, __LINE__, ##args); \ - } while (0); + if (tdm_debug) { \ + struct timespec ts; \ + clock_gettime(CLOCK_MONOTONIC, &ts); \ + printf("[TDM_DBG][%d.%06d][%d][%s %d] "fmt"\n", (int)ts.tv_sec, \ + (int)ts.tv_nsec / 1000, (int)syscall(SYS_gettid), __func__, __LINE__, ##args); \ + } \ + } while (0) #define TDM_INFO(fmt, args...) \ do { \ @@ -139,7 +141,7 @@ extern int tdm_debug; clock_gettime(CLOCK_MONOTONIC, &ts); \ printf(COLOR_GREEN"[TDM_INF]"COLOR_RESET"[%d.%06d][%d][%s %d] "fmt"\n", (int)ts.tv_sec, \ (int)ts.tv_nsec / 1000, (int)syscall(SYS_gettid), __func__, __LINE__, ##args); \ - } while (0); + } while (0) #define TDM_WRN(fmt, args...) \ do { \ @@ -148,7 +150,7 @@ extern int tdm_debug; printf(COLOR_YELLOW"[TDM_WRN]"COLOR_RESET"[%d.%06d][%d][%s %d] "fmt"\n", (int)ts.tv_sec, \ (int)ts.tv_nsec / 1000, (int)syscall(SYS_gettid), __func__, __LINE__, ##args); \ TDM_ASSERT(0); \ - } while (0); + } while (0) #define TDM_ERR(fmt, args...) \ do { \ @@ -157,7 +159,7 @@ extern int tdm_debug; printf(COLOR_RED"[TDM_ERR]"COLOR_RESET"[%d.%06d][%d][%s %d] "fmt"\n", (int)ts.tv_sec, \ (int)ts.tv_nsec / 1000, (int)syscall(SYS_gettid), __func__, __LINE__, ##args); \ TDM_ASSERT(0); \ - } while (0); + } while (0) #endif /* TDM_CONFIG_DLOG */ diff --git a/src/tdm_display.c b/src/tdm_display.c index 969b227..2fdc486 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -154,11 +154,10 @@ _tdm_display_lock(tdm_display *dpy, const char *func) ret = pthread_mutex_trylock(&private_display->lock); if (ret < 0) { - if (ret == EBUSY) { + if (ret == EBUSY) TDM_ERR("mutex lock busy: %s", func); - } else { + else TDM_ERR("mutex lock failed: %s(%m)", func); - } return TDM_ERROR_OPERATION_FAILED; } @@ -1578,7 +1577,7 @@ tdm_layer_get_displaying_buffer(tdm_layer *layer, tdm_error *error) if (error) *error = TDM_ERROR_OPERATION_FAILED; _pthread_mutex_unlock(&private_display->lock); - TDM_ERR("layer(%p) showing_buffer is null", private_layer); + TDM_DBG("layer(%p) showing_buffer is null", private_layer); return NULL; } _pthread_mutex_unlock(&private_display->lock); -- 2.7.4 From bed1f05b96cd51546c1f6101832699008b72a2e8 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Tue, 7 Jun 2016 22:47:59 +0900 Subject: [PATCH 03/16] enhance log Change-Id: I0a8286733d527ba4c41f9b4301f701041ab751c5 --- src/tdm_display.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/tdm_display.c b/src/tdm_display.c index 2fdc486..17d951e 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -1393,6 +1393,9 @@ tdm_layer_set_info(tdm_layer *layer, tdm_info_layer *info) func_layer = &private_display->func_layer; + if (private_layer->usable) + TDM_INFO("layer(%p) not usable", private_layer); + private_layer->usable = 0; if (!func_layer->layer_set_info) { @@ -1476,6 +1479,9 @@ tdm_layer_set_buffer(tdm_layer *layer, tbm_surface_h buffer) func_layer = &private_display->func_layer; + if (private_layer->usable) + TDM_INFO("layer(%p) not usable", private_layer); + private_layer->usable = 0; if (!func_layer->layer_set_buffer) { @@ -1546,6 +1552,9 @@ tdm_layer_unset_buffer(tdm_layer *layer) private_layer->usable = 1; + if (private_layer->usable) + TDM_INFO("layer(%p) now usable", private_layer); + if (!func_layer->layer_unset_buffer) { _pthread_mutex_unlock(&private_display->lock); TDM_ERR("not implemented!!"); @@ -1671,6 +1680,9 @@ tdm_layer_set_buffer_queue(tdm_layer *layer, tbm_surface_queue_h buffer_queue) func_layer = &private_display->func_layer; + if (private_layer->usable) + TDM_INFO("layer(%p) not usable", private_layer); + private_layer->usable = 0; if (!func_layer->layer_set_buffer) { @@ -1750,6 +1762,9 @@ tdm_layer_unset_buffer_queue(tdm_layer *layer) private_layer->buffer_queue = NULL; private_layer->usable = 1; + if (private_layer->usable) + TDM_INFO("layer(%p) now usable", private_layer); + if (!func_layer->layer_unset_buffer) { _pthread_mutex_unlock(&private_display->lock); TDM_ERR("not implemented!!"); -- 2.7.4 From d7b3a6fbc139908131ef977026a45b8a685596df Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 8 Jun 2016 21:20:57 +0900 Subject: [PATCH 04/16] add description for tdm protocol Change-Id: Idbb753a959e4237676806f43a701e4366843fa59 --- protocol/tdm.xml | 54 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/protocol/tdm.xml b/protocol/tdm.xml index 6fa7f3d..7564670 100644 --- a/protocol/tdm.xml +++ b/protocol/tdm.xml @@ -2,41 +2,59 @@ - + - - - - - + + TDM uses the wayland protocol to communicate between tdm client and tdm server. + + + + + + + TDM uses the wayland protocol to communicate between tdm client and tdm server. + + - - - + + + + + + When wl_tdm_client.wait_vblank is called, wl_tdm_vblank resource is created. + And when TDM server gets the HW vblank, TDM server will send the 'done' + event of wl_tdm_vblank interface to let the TDM client get the HW vblank. + If 'sw_timer' param is 1, TDM server will use the SW timer in case of DPMS off. + And TDM server will create the fake vblank event and send it to TDM client. + Otherwise, TDM server will return 'dpms_off' error. + + - - - - - + + + + + - + - - - + + + + + -- 2.7.4 From a3ae245fc52ea4e43684f77981b5cdc730c9e1ed Mon Sep 17 00:00:00 2001 From: Junkyeong Kim Date: Wed, 1 Jun 2016 16:20:27 +0900 Subject: [PATCH 05/16] add tdm_helper_capture_output API It makes composite image with output's all layers showing buffer. After composing, call tdm_helper_capture_handler function. Change-Id: I1ea7b939d77aeaf4f6d2c5347a3443ddac2b6d1d Signed-off-by: Junkyeong Kim --- configure.ac | 2 +- include/tdm_helper.h | 26 +++++++++ packaging/libtdm.spec | 1 + src/tdm_helper.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 184 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index fa99a81..ff06dd9 100644 --- a/configure.ac +++ b/configure.ac @@ -37,7 +37,7 @@ fi PKG_CHECK_MODULES(WAYLAND_SCANNER, wayland-scanner >= 1.7.0) -PKG_CHECK_MODULES(TDM, dlog libtbm pthread-stubs libpng wayland-server) +PKG_CHECK_MODULES(TDM, dlog libtbm pthread-stubs libpng pixman-1 wayland-server) PKG_CHECK_MODULES(TTRACE, [ttrace], diff --git a/include/tdm_helper.h b/include/tdm_helper.h index 0bde0b9..e98ad9f 100644 --- a/include/tdm_helper.h +++ b/include/tdm_helper.h @@ -118,6 +118,32 @@ tdm_helper_dump_start(char *dumppath, int *count); void tdm_helper_dump_stop(void); +/** + * @brief The tdm helper capture handler + * @details + * This handler will be called when composit image produced. + * @see #tdm_helper_capture_output() function + */ +typedef void (*tdm_helper_capture_handler)(tbm_surface_h buffer, void *user_data); + +/** + * @brief Make an output's image surface. + * @details Composit specific output's all layer's buffer to dst_buffer surface. + * After composing, tdm_helper_capture_handler func will be called. + * @param[in] output A output object + * @param[in] dst_buffer A surface composite image saved + * @param[in] x A horizontal position of composite image on dst_buffer + * @param[in] y A vertical position of composite image on dst_buffer + * @param[in] w A composite image width + * @param[in] h A composite image height + * @param[in] func A composing done handler + * @param[in] user_data The user data + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +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); #ifdef __cplusplus } #endif diff --git a/packaging/libtdm.spec b/packaging/libtdm.spec index 881db02..077db23 100644 --- a/packaging/libtdm.spec +++ b/packaging/libtdm.spec @@ -11,6 +11,7 @@ BuildRequires: pkgconfig(libtbm) BuildRequires: pkgconfig(libpng) BuildRequires: pkgconfig(ttrace) BuildRequires: pkgconfig(wayland-server) +BuildRequires: pkgconfig(pixman-1) %description Common user library of Tizen Display Manager : libtdm front-end library diff --git a/src/tdm_helper.c b/src/tdm_helper.c index a57a732..354e2e8 100644 --- a/src/tdm_helper.c +++ b/src/tdm_helper.c @@ -43,9 +43,11 @@ #include #include #include +#include #include "tdm.h" #include "tdm_private.h" +#include "tdm_helper.h" #define PNG_DEPTH 8 @@ -308,3 +310,157 @@ tdm_helper_dump_stop(void) TDM_DBG("tdm_helper_dump stop."); } +static pixman_format_code_t +_tdm_helper_pixman_format_get(tbm_format format) +{ + switch (format) { + case TBM_FORMAT_ARGB8888: + return PIXMAN_a8r8g8b8; + case TBM_FORMAT_XRGB8888: + return PIXMAN_x8r8g8b8; + default: + return 0; + } + + return 0; +} + +static tdm_error +_tdm_helper_buffer_convert(tbm_surface_h srcbuf, tbm_surface_h dstbuf, + int dx, int dy, int dw, int dh, int count) +{ + pixman_image_t *src_img = NULL, *dst_img = NULL; + pixman_format_code_t src_format, dst_format; + pixman_transform_t t; + struct pixman_f_transform ft; + pixman_op_t op; + tbm_surface_info_s src_info = {0, }; + tbm_surface_info_s dst_info = {0, }; + int stride, width; + double scale_x, scale_y; + + TDM_RETURN_VAL_IF_FAIL(srcbuf != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(dstbuf != NULL, TDM_ERROR_INVALID_PARAMETER); + + if (tbm_surface_map(srcbuf, TBM_SURF_OPTION_READ, &src_info) + != TBM_SURFACE_ERROR_NONE) { + TDM_ERR("cannot mmap srcbuf\n"); + return TDM_ERROR_OPERATION_FAILED; + } + + if (tbm_surface_map(dstbuf, TBM_SURF_OPTION_WRITE, &dst_info) + != TBM_SURFACE_ERROR_NONE) { + TDM_ERR("cannot mmap dstbuf\n"); + tbm_surface_unmap(srcbuf); + return TDM_ERROR_OPERATION_FAILED; + } + TDM_GOTO_IF_FAIL(src_info.num_planes == 1, cant_convert); + TDM_GOTO_IF_FAIL(dst_info.num_planes == 1, cant_convert); + + /* src */ + src_format = _tdm_helper_pixman_format_get(src_info.format); + TDM_GOTO_IF_FAIL(src_format > 0, cant_convert); + + width = src_info.planes[0].stride / 4; + stride = src_info.planes[0].stride; + src_img = pixman_image_create_bits(src_format, width, src_info.height, + (uint32_t*)src_info.planes[0].ptr, stride); + TDM_GOTO_IF_FAIL(src_img != NULL, cant_convert); + + /* dst */ + dst_format = _tdm_helper_pixman_format_get(dst_info.format); + TDM_GOTO_IF_FAIL(dst_format > 0, cant_convert); + + width = dst_info.planes[0].stride / 4; + stride = dst_info.planes[0].stride; + dst_img = pixman_image_create_bits(dst_format, width, dst_info.height, + (uint32_t*)dst_info.planes[0].ptr, stride); + TDM_GOTO_IF_FAIL(dst_img != NULL, cant_convert); + + pixman_f_transform_init_identity(&ft); + + scale_x = (double)src_info.width / dw; + scale_y = (double)src_info.height / dh; + + pixman_f_transform_scale(&ft, NULL, scale_x, scale_y); + pixman_f_transform_translate(&ft, NULL, 0, 0); + pixman_transform_from_pixman_f_transform(&t, &ft); + pixman_image_set_transform(src_img, &t); + + if (count == 0) + op = PIXMAN_OP_SRC; + else + op = PIXMAN_OP_OVER; + + pixman_image_composite(op, src_img, NULL, dst_img, + 0, 0, 0, 0, dx, dy, dw, dh); + + if (src_img) + pixman_image_unref(src_img); + if (dst_img) + pixman_image_unref(dst_img); + + tbm_surface_unmap(srcbuf); + tbm_surface_unmap(dstbuf); + + return TDM_ERROR_NONE; + +cant_convert: + if (src_img) + pixman_image_unref(src_img); + if (dst_img) + pixman_image_unref(dst_img); + + tbm_surface_unmap(srcbuf); + tbm_surface_unmap(dstbuf); + + return TDM_ERROR_OPERATION_FAILED; +} + +EXTERN 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) +{ + tbm_surface_h surface; + tdm_error err; + int i, count, first = 0; + + TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(dst_buffer != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(x >= 0, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(y >= 0, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(w >= 0, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(h >= 0, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(data != NULL, TDM_ERROR_INVALID_PARAMETER); + + err = tdm_output_get_layer_count(output, &count); + if (err != TDM_ERROR_NONE) { + TDM_ERR("tdm_output_get_layer_count fail(%d)\n", err); + return TDM_ERROR_OPERATION_FAILED; + } + if (count <= 0) { + TDM_ERR("tdm_output_get_layer_count err(%d, %d)\n", err, count); + return TDM_ERROR_BAD_MODULE; + } + + for (i = count - 1; i >= 0; i--) { + tdm_layer *layer = tdm_output_get_layer(output, i, NULL); + + surface = tdm_layer_get_displaying_buffer(layer, &err); + if (err != TDM_ERROR_NONE) + continue; + + err = _tdm_helper_buffer_convert(surface, dst_buffer, x, y, w, h, first++); + if (err != TDM_ERROR_NONE) + TDM_DBG("convert fail %d-layer buffer\n", i); + else + TDM_DBG("convert success %d-layer buffer\n", i); + } + + func(dst_buffer, data); + + return TDM_ERROR_NONE; +} + -- 2.7.4 From 86601c31209e1a3fbc9c2457d105227a57cdfbaa Mon Sep 17 00:00:00 2001 From: Boram Park Date: Thu, 9 Jun 2016 18:58:46 +0900 Subject: [PATCH 06/16] fix syntax error Change-Id: I4fd24640ca850fef42a7f804282e2a885e695db3 --- include/tdm.h | 2 +- src/tdm_display.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/tdm.h b/include/tdm.h index 488a549..4af34d7 100644 --- a/include/tdm.h +++ b/include/tdm.h @@ -519,7 +519,7 @@ tdm_layer_get_available_properties(tdm_layer *layer, const tdm_prop **props, * @see tdm_layer_set_video_pos, tdm_layer_capability */ tdm_error -tdm_layer_get_zpos(tdm_layer *layer, unsigned int *zpos); +tdm_layer_get_zpos(tdm_layer *layer, int *zpos); /** * @brief Set the property which has a given id. diff --git a/src/tdm_display.c b/src/tdm_display.c index 17d951e..ef0b1ca 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -1318,7 +1318,7 @@ tdm_layer_get_available_properties(tdm_layer *layer, const tdm_prop **props, int } EXTERN tdm_error -tdm_layer_get_zpos(tdm_layer *layer, unsigned int *zpos) +tdm_layer_get_zpos(tdm_layer *layer, int *zpos) { LAYER_FUNC_ENTRY(); -- 2.7.4 From 6a9387efb3425d378d316bb90b153baa368be5a9 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Thu, 9 Jun 2016 19:26:02 +0900 Subject: [PATCH 07/16] pp: allow twice attach Change-Id: I4f9d754f8e83f0c45604a821e06d1b52344b0713 --- src/tdm_pp.c | 221 ++++++++++++++++++++++++++---------------------------- src/tdm_private.h | 6 +- 2 files changed, 108 insertions(+), 119 deletions(-) diff --git a/src/tdm_pp.c b/src/tdm_pp.c index 2db7af4..a440c41 100644 --- a/src/tdm_pp.c +++ b/src/tdm_pp.c @@ -41,6 +41,13 @@ #include "tdm_backend.h" #include "tdm_private.h" +typedef struct _tdm_pp_private_buffer { + tbm_surface_h src; + tbm_surface_h dst; + struct list_head link; + struct list_head commit_link; +} tdm_pp_private_buffer; + #define PP_FUNC_ENTRY() \ tdm_func_pp *func_pp; \ tdm_private_display *private_display; \ @@ -51,41 +58,52 @@ private_display = private_pp->private_display; \ func_pp = &private_display->func_pp -static tdm_error -_tdm_pp_check_if_exist(tdm_private_pp *private_pp, - tbm_surface_h src, tbm_surface_h dst) +static void +_tdm_pp_print_list(struct list_head *list) { - tdm_buffer_info *buf_info = NULL; - - LIST_FOR_EACH_ENTRY(buf_info, &private_pp->src_buffer_list, link) { - if (buf_info->buffer == src) { - TDM_ERR("%p attached twice", src); - return TDM_ERROR_BAD_REQUEST; - } + tdm_pp_private_buffer *b = NULL; + char str[512], *p; + int len = sizeof(str); + + TDM_RETURN_IF_FAIL(list != NULL); + + p = str; + LIST_FOR_EACH_ENTRY(b, list, link) { + if (len > 0) { + int l = snprintf(p, len, " (%p,%p)", b->src, b->dst); + p += l; + len -= l; + } else + break; } - LIST_FOR_EACH_ENTRY(buf_info, &private_pp->src_pending_buffer_list, link) { - if (buf_info->buffer == src) { - TDM_ERR("%p attached twice", src); - return TDM_ERROR_BAD_REQUEST; - } - } + TDM_INFO("\t %s", str); +} - LIST_FOR_EACH_ENTRY(buf_info, &private_pp->dst_buffer_list, link) { - if (buf_info->buffer == dst) { - TDM_ERR("%p attached twice", dst); - return TDM_ERROR_BAD_REQUEST; - } +static tdm_pp_private_buffer * +_tdm_pp_find_tbm_buffers(struct list_head *list, tbm_surface_h src, tbm_surface_h dst) +{ + tdm_pp_private_buffer *b = NULL, *bb = NULL; + + LIST_FOR_EACH_ENTRY_SAFE(b, bb, list, link) { + if (b->src == src && b->dst == dst) + return b; } - LIST_FOR_EACH_ENTRY(buf_info, &private_pp->dst_pending_buffer_list, link) { - if (buf_info->buffer == dst) { - TDM_ERR("%p attached twice", dst); - return TDM_ERROR_BAD_REQUEST; - } + return NULL; +} + +static tdm_pp_private_buffer * +_tdm_pp_find_buffer(struct list_head *list, tdm_pp_private_buffer *pp_buffer) +{ + tdm_pp_private_buffer *b = NULL, *bb = NULL; + + LIST_FOR_EACH_ENTRY_SAFE(b, bb, list, link) { + if (b == pp_buffer) + return b; } - return TDM_ERROR_NONE; + return NULL; } INTERN void @@ -94,8 +112,7 @@ tdm_pp_cb_done(tdm_pp *pp_backend, tbm_surface_h src, tbm_surface_h dst, { tdm_private_pp *private_pp = user_data; tdm_private_display *private_display = private_pp->private_display; - tdm_buffer_info *buf_info; - tbm_surface_h first_entry; + tdm_pp_private_buffer *pp_buffer, *first_entry; TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED()); @@ -122,24 +139,20 @@ tdm_pp_cb_done(tdm_pp *pp_backend, tbm_surface_h src, tbm_surface_h dst, if (tdm_debug_buffer) TDM_INFO("pp(%p) done: src(%p) dst(%p)", private_pp, src, dst); - first_entry = tdm_buffer_list_get_first_entry(&private_pp->src_buffer_list); - if (first_entry != src) - TDM_ERR("src(%p) is skipped", first_entry); - - first_entry = tdm_buffer_list_get_first_entry(&private_pp->dst_buffer_list); - if (first_entry != dst) - TDM_ERR("dst(%p) is skipped", first_entry); + first_entry = container_of((&private_pp->buffer_list)->next, pp_buffer, link); + if (first_entry->src != src || first_entry->dst != dst) + TDM_ERR("buffer(%p,%p) is skipped", first_entry->src, first_entry->dst); - if ((buf_info = tdm_buffer_get_info(src))) - LIST_DEL(&buf_info->link); + if ((pp_buffer = _tdm_pp_find_tbm_buffers(&private_pp->buffer_list, src, dst))) { + LIST_DEL(&pp_buffer->link); - if ((buf_info = tdm_buffer_get_info(dst))) - LIST_DEL(&buf_info->link); + _pthread_mutex_unlock(&private_display->lock); + tdm_buffer_unref_backend(src); + tdm_buffer_unref_backend(dst); + _pthread_mutex_lock(&private_display->lock); - _pthread_mutex_unlock(&private_display->lock); - tdm_buffer_unref_backend(src); - tdm_buffer_unref_backend(dst); - _pthread_mutex_lock(&private_display->lock); + free(pp_buffer); + } } INTERN tdm_private_pp * @@ -212,10 +225,8 @@ tdm_pp_create_internal(tdm_private_display *private_display, tdm_error *error) private_pp->pp_backend = pp_backend; private_pp->owner_tid = syscall(SYS_gettid); - LIST_INITHEAD(&private_pp->src_pending_buffer_list); - LIST_INITHEAD(&private_pp->dst_pending_buffer_list); - LIST_INITHEAD(&private_pp->src_buffer_list); - LIST_INITHEAD(&private_pp->dst_buffer_list); + LIST_INITHEAD(&private_pp->pending_buffer_list); + LIST_INITHEAD(&private_pp->buffer_list); if (error) *error = TDM_ERROR_NONE; @@ -228,7 +239,7 @@ tdm_pp_destroy_internal(tdm_private_pp *private_pp) { tdm_private_display *private_display; tdm_func_pp *func_pp; - tdm_buffer_info *b = NULL, *bb = NULL; + tdm_pp_private_buffer *b = NULL, *bb = NULL; TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED()); @@ -242,50 +253,24 @@ tdm_pp_destroy_internal(tdm_private_pp *private_pp) func_pp->pp_destroy(private_pp->pp_backend); - if (!LIST_IS_EMPTY(&private_pp->src_pending_buffer_list)) { - TDM_WRN("pp(%p) not finished:", private_pp); - tdm_buffer_list_dump(&private_pp->src_pending_buffer_list); - - LIST_FOR_EACH_ENTRY_SAFE(b, bb, &private_pp->src_pending_buffer_list, link) { - LIST_DEL(&b->link); - _pthread_mutex_unlock(&private_display->lock); - tdm_buffer_unref_backend(b->buffer); - _pthread_mutex_lock(&private_display->lock); - } - } - - if (!LIST_IS_EMPTY(&private_pp->dst_pending_buffer_list)) { + if (!LIST_IS_EMPTY(&private_pp->pending_buffer_list)) { TDM_WRN("pp(%p) not finished:", private_pp); - tdm_buffer_list_dump(&private_pp->dst_pending_buffer_list); + _tdm_pp_print_list(&private_pp->pending_buffer_list); - LIST_FOR_EACH_ENTRY_SAFE(b, bb, &private_pp->dst_pending_buffer_list, link) { + LIST_FOR_EACH_ENTRY_SAFE(b, bb, &private_pp->pending_buffer_list, link) { LIST_DEL(&b->link); - _pthread_mutex_unlock(&private_display->lock); - tdm_buffer_unref_backend(b->buffer); - _pthread_mutex_lock(&private_display->lock); } } - if (!LIST_IS_EMPTY(&private_pp->src_buffer_list)) { + if (!LIST_IS_EMPTY(&private_pp->buffer_list)) { TDM_WRN("pp(%p) not finished:", private_pp); - tdm_buffer_list_dump(&private_pp->src_buffer_list); + _tdm_pp_print_list(&private_pp->buffer_list); - LIST_FOR_EACH_ENTRY_SAFE(b, bb, &private_pp->src_buffer_list, link) { + LIST_FOR_EACH_ENTRY_SAFE(b, bb, &private_pp->buffer_list, link) { LIST_DEL(&b->link); _pthread_mutex_unlock(&private_display->lock); - tdm_buffer_unref_backend(b->buffer); - _pthread_mutex_lock(&private_display->lock); - } - } - - if (!LIST_IS_EMPTY(&private_pp->dst_buffer_list)) { - TDM_WRN("pp(%p) not finished:", private_pp); - tdm_buffer_list_dump(&private_pp->dst_buffer_list); - - LIST_FOR_EACH_ENTRY_SAFE(b, bb, &private_pp->dst_buffer_list, link) { - LIST_DEL(&b->link); - _pthread_mutex_unlock(&private_display->lock); - tdm_buffer_unref_backend(b->buffer); + tdm_buffer_unref_backend(b->src); + tdm_buffer_unref_backend(b->dst); _pthread_mutex_lock(&private_display->lock); } } @@ -347,6 +332,8 @@ tdm_pp_set_info(tdm_pp *pp, tdm_info_pp *info) EXTERN tdm_error tdm_pp_attach(tdm_pp *pp, tbm_surface_h src, tbm_surface_h dst) { + tdm_pp_private_buffer *pp_buffer; + PP_FUNC_ENTRY(); TDM_RETURN_VAL_IF_FAIL(src != NULL, TDM_ERROR_INVALID_PARAMETER); @@ -362,8 +349,8 @@ tdm_pp_attach(tdm_pp *pp, tbm_surface_h src, tbm_surface_h dst) if (tdm_display_check_module_abi(private_display, 1, 2) && private_display->caps_pp.max_attach_count > 0) { - int length = LIST_LENGTH(&private_pp->src_pending_buffer_list) + - LIST_LENGTH(&private_pp->src_buffer_list); + int length = LIST_LENGTH(&private_pp->pending_buffer_list) + + LIST_LENGTH(&private_pp->buffer_list); if (length >= private_display->caps_pp.max_attach_count) { _pthread_mutex_unlock(&private_display->lock); TDM_DBG("failed: too many attached!! max_attach_count(%d)", @@ -372,29 +359,30 @@ tdm_pp_attach(tdm_pp *pp, tbm_surface_h src, tbm_surface_h dst) } } - ret = _tdm_pp_check_if_exist(private_pp, src, dst); - if (ret != TDM_ERROR_NONE) { + pp_buffer = calloc(1, sizeof *pp_buffer); + if (!pp_buffer) { _pthread_mutex_unlock(&private_display->lock); - return ret; + TDM_ERR("alloc failed"); + return TDM_ERROR_OUT_OF_MEMORY; } ret = func_pp->pp_attach(private_pp->pp_backend, src, dst); TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE); - if (ret == TDM_ERROR_NONE) { - tdm_buffer_info *buf_info; - - if ((buf_info = tdm_buffer_get_info(src))) - LIST_ADDTAIL(&buf_info->link, &private_pp->src_pending_buffer_list); + if (ret != TDM_ERROR_NONE) { + free(pp_buffer); + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("attach failed"); + return ret; + } - if ((buf_info = tdm_buffer_get_info(dst))) - LIST_ADDTAIL(&buf_info->link, &private_pp->dst_pending_buffer_list); + LIST_ADDTAIL(&pp_buffer->link, &private_pp->pending_buffer_list); + pp_buffer->src = tdm_buffer_ref_backend(src); + pp_buffer->dst = tdm_buffer_ref_backend(dst); - if (tdm_debug_buffer) { - TDM_INFO("pp(%p) attached:", private_pp); - tdm_buffer_list_dump(&private_pp->src_pending_buffer_list); - tdm_buffer_list_dump(&private_pp->dst_pending_buffer_list); - } + if (tdm_debug_buffer) { + TDM_INFO("pp(%p) attached:", private_pp); + _tdm_pp_print_list(&private_pp->pending_buffer_list); } _pthread_mutex_unlock(&private_display->lock); @@ -405,7 +393,8 @@ tdm_pp_attach(tdm_pp *pp, tbm_surface_h src, tbm_surface_h dst) EXTERN tdm_error tdm_pp_commit(tdm_pp *pp) { - tdm_buffer_info *b = NULL, *bb = NULL; + tdm_pp_private_buffer *b = NULL, *bb = NULL; + struct list_head commit_buffer_list; PP_FUNC_ENTRY(); @@ -417,26 +406,28 @@ tdm_pp_commit(tdm_pp *pp) return TDM_ERROR_NOT_IMPLEMENTED; } + LIST_INITHEAD(&commit_buffer_list); + + LIST_FOR_EACH_ENTRY_SAFE(b, bb, &private_pp->pending_buffer_list, link) { + LIST_DEL(&b->link); + LIST_ADDTAIL(&b->link, &private_pp->buffer_list); + LIST_ADDTAIL(&b->commit_link, &commit_buffer_list); + } + ret = func_pp->pp_commit(private_pp->pp_backend); TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE); - if (ret == TDM_ERROR_NONE) { - LIST_FOR_EACH_ENTRY_SAFE(b, bb, &private_pp->src_pending_buffer_list, link) { - LIST_DEL(&b->link); - tdm_buffer_ref_backend(b->buffer); - LIST_ADDTAIL(&b->link, &private_pp->src_buffer_list); - } + LIST_FOR_EACH_ENTRY_SAFE(b, bb, &commit_buffer_list, commit_link) { + if (!_tdm_pp_find_buffer(&private_pp->buffer_list, b)) + continue; - LIST_FOR_EACH_ENTRY_SAFE(b, bb, &private_pp->dst_pending_buffer_list, link) { + LIST_DEL(&b->commit_link); + + if (ret != TDM_ERROR_NONE) { + tdm_buffer_unref_backend(b->src); + tdm_buffer_unref_backend(b->dst); LIST_DEL(&b->link); - tdm_buffer_ref_backend(b->buffer); - LIST_ADDTAIL(&b->link, &private_pp->dst_buffer_list); } - } else { - LIST_FOR_EACH_ENTRY_SAFE(b, bb, &private_pp->src_pending_buffer_list, link) - LIST_DEL(&b->link); - LIST_FOR_EACH_ENTRY_SAFE(b, bb, &private_pp->dst_pending_buffer_list, link) - LIST_DEL(&b->link); } _pthread_mutex_unlock(&private_display->lock); diff --git a/src/tdm_private.h b/src/tdm_private.h index 8d07f03..db7426e 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -203,10 +203,8 @@ struct _tdm_private_pp { tdm_pp *pp_backend; - struct list_head src_pending_buffer_list; - struct list_head dst_pending_buffer_list; - struct list_head src_buffer_list; - struct list_head dst_buffer_list; + struct list_head pending_buffer_list; + struct list_head buffer_list; pid_t owner_tid; }; -- 2.7.4 From b1c923196d528ec5202ddf5b44fbbc64ed708e91 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Tue, 14 Jun 2016 12:43:29 +0900 Subject: [PATCH 08/16] fix syntax error Change-Id: I00cb5908d5786e9013301c6e1f01eee47d26605d --- src/tdm_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tdm_helper.c b/src/tdm_helper.c index 354e2e8..1363979 100644 --- a/src/tdm_helper.c +++ b/src/tdm_helper.c @@ -178,7 +178,7 @@ tdm_helper_dump_buffer(tbm_surface_h buffer, const char *file) TDM_RETURN_IF_FAIL(buffer != NULL); TDM_RETURN_IF_FAIL(file != NULL); - ret = tbm_surface_map(buffer, TBM_DEVICE_CPU, &info); + ret = tbm_surface_map(buffer, TBM_OPTION_READ, &info); TDM_RETURN_IF_FAIL(ret == TBM_SURFACE_ERROR_NONE); len = strnlen(file, 1024); -- 2.7.4 From 53e61274b17a6fe63de8d75059a9310c2289d1a6 Mon Sep 17 00:00:00 2001 From: Roman Marchenko Date: Wed, 15 Jun 2016 17:15:47 +0300 Subject: [PATCH 09/16] fix deadlock Change-Id: I426b1067f729db754611824642b13e9ced8a7a9b Signed-off-by: Roman Marchenko --- src/tdm_display.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tdm_display.c b/src/tdm_display.c index ef0b1ca..11e09bc 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -949,8 +949,10 @@ tdm_output_cb_commit(tdm_output *output_backend, unsigned int sequence, _pthread_mutex_lock(&private_display->lock); if (private_layer->buffer_queue) { + _pthread_mutex_unlock(&private_display->lock); tbm_surface_queue_release(private_layer->buffer_queue, private_layer->showing_buffer); + _pthread_mutex_lock(&private_display->lock); } } @@ -1624,6 +1626,7 @@ _tbm_layer_queue_acquirable_cb(tbm_surface_queue_h surface_queue, void *data) if (ret == TDM_ERROR_NONE) { if (private_layer->waiting_buffer) { + TDM_DBG("layer(%p) drop waiting_buffer(%p)", private_layer, private_layer->waiting_buffer); _pthread_mutex_unlock(&private_display->lock); tdm_buffer_unref_backend(private_layer->waiting_buffer); tbm_surface_queue_release(private_layer->buffer_queue, -- 2.7.4 From 22c00978a791c5234748071b162f9d33fdfa67e6 Mon Sep 17 00:00:00 2001 From: Changyeon Lee Date: Wed, 15 Jun 2016 19:51:09 +0900 Subject: [PATCH 10/16] Allow output name "primary" in tdm_client_wait_vblank Change-Id: I39b4e5fa789ccda07a7b1156ecc11482237818cf --- src/tdm_server.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tdm_server.c b/src/tdm_server.c index 99ff6d0..1233deb 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -264,6 +264,9 @@ _tdm_server_client_cb_wait_vblank(struct wl_client *client, found = client_info->vblank_output; } + if (!strncmp(name, "primary", TDM_NAME_LEN)) + found = tdm_display_get_output(private_loop->dpy, 0, NULL); + if (!found) { int count = 0, i; -- 2.7.4 From 863ce920d44c0c1630c2dd0bdbb12a9bff23a19e Mon Sep 17 00:00:00 2001 From: Junkyeong Kim Date: Wed, 22 Jun 2016 21:44:19 +0900 Subject: [PATCH 11/16] fix some syntax errors initialize variables before using erase unreacherble codes Change-Id: I5ea6f89be8aeea2025950f0713fcc60dd43b1f7f Signed-off-by: Junkyeong Kim --- include/tdm_list.h | 2 +- src/tdm.c | 2 +- src/tdm_display.c | 6 ++---- src/tdm_helper.c | 2 -- src/tdm_pp.c | 2 +- src/tdm_thread.c | 2 +- 6 files changed, 6 insertions(+), 10 deletions(-) diff --git a/include/tdm_list.h b/include/tdm_list.h index 6255cd0..2fd1db0 100644 --- a/include/tdm_list.h +++ b/include/tdm_list.h @@ -150,7 +150,7 @@ static inline int list_length(struct list_head *item) #define LIST_FIND_ITEM(item, head, type, member, found) \ do { \ - type *pos; \ + type *pos = NULL; \ found = NULL; \ LIST_FOR_EACH_ENTRY(pos, head, member) \ if (pos == item) { found = item; break; } \ diff --git a/src/tdm.c b/src/tdm.c index 996fb9a..be9d26b 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -610,7 +610,7 @@ _tdm_display_update_internal(tdm_private_display *private_display, { tdm_output **outputs = NULL; int output_count = 0, i; - tdm_error ret; + tdm_error ret = TDM_ERROR_NONE; LIST_INITHEAD(&private_display->output_list); LIST_INITHEAD(&private_display->pp_list); diff --git a/src/tdm_display.c b/src/tdm_display.c index 11e09bc..e691501 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -1150,9 +1150,7 @@ tdm_output_set_dpms(tdm_output *output, tdm_output_dpms dpms_value) tdm_func_output *func_output; OUTPUT_FUNC_ENTRY(); - if (dpms_value < TDM_OUTPUT_DPMS_ON) - dpms_value = TDM_OUTPUT_DPMS_ON; - else if (dpms_value > TDM_OUTPUT_DPMS_OFF) + if (dpms_value > TDM_OUTPUT_DPMS_OFF) dpms_value = TDM_OUTPUT_DPMS_OFF; _pthread_mutex_lock(&private_display->lock); @@ -1238,7 +1236,7 @@ tdm_output_call_change_handler_internal(tdm_private_output *private_output, tdm_value value) { tdm_private_display *private_display; - tdm_private_change_handler *change_handler; + tdm_private_change_handler *change_handler = NULL; TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED()); TDM_RETURN_IF_FAIL(private_output); diff --git a/src/tdm_helper.c b/src/tdm_helper.c index 1363979..4172fe8 100644 --- a/src/tdm_helper.c +++ b/src/tdm_helper.c @@ -408,8 +408,6 @@ _tdm_helper_buffer_convert(tbm_surface_h srcbuf, tbm_surface_h dstbuf, cant_convert: if (src_img) pixman_image_unref(src_img); - if (dst_img) - pixman_image_unref(dst_img); tbm_surface_unmap(srcbuf); tbm_surface_unmap(dstbuf); diff --git a/src/tdm_pp.c b/src/tdm_pp.c index a440c41..9075c8b 100644 --- a/src/tdm_pp.c +++ b/src/tdm_pp.c @@ -112,7 +112,7 @@ tdm_pp_cb_done(tdm_pp *pp_backend, tbm_surface_h src, tbm_surface_h dst, { tdm_private_pp *private_pp = user_data; tdm_private_display *private_display = private_pp->private_display; - tdm_pp_private_buffer *pp_buffer, *first_entry; + tdm_pp_private_buffer *pp_buffer = NULL, *first_entry = NULL; TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED()); diff --git a/src/tdm_thread.c b/src/tdm_thread.c index aff3f39..0d24561 100644 --- a/src/tdm_thread.c +++ b/src/tdm_thread.c @@ -240,7 +240,7 @@ tdm_thread_handle_cb(tdm_private_loop *private_loop) tdm_private_thread *private_thread; tdm_thread_cb_base *base; char buffer[1024]; - int len, i; + unsigned int len, i; /* DON'T check TDM_MUTEX_IS_LOCKED here */ -- 2.7.4 From 0aa74b282285fd0d935a2347c668a68ff4a41276 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Mon, 27 Jun 2016 14:24:38 +0900 Subject: [PATCH 12/16] remove the pthread-stub dependency Change-Id: I78c92c2f6bef32161be2a1d0815c6774a7945905 --- configure.ac | 2 +- packaging/libtdm.spec | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index ff06dd9..66a57ec 100644 --- a/configure.ac +++ b/configure.ac @@ -37,7 +37,7 @@ fi PKG_CHECK_MODULES(WAYLAND_SCANNER, wayland-scanner >= 1.7.0) -PKG_CHECK_MODULES(TDM, dlog libtbm pthread-stubs libpng pixman-1 wayland-server) +PKG_CHECK_MODULES(TDM, dlog libtbm libpng pixman-1 wayland-server) PKG_CHECK_MODULES(TTRACE, [ttrace], diff --git a/packaging/libtdm.spec b/packaging/libtdm.spec index 077db23..11cec9a 100644 --- a/packaging/libtdm.spec +++ b/packaging/libtdm.spec @@ -6,7 +6,6 @@ Group: Development/Libraries License: MIT Source0: %{name}-%{version}.tar.gz Source1001: %{name}.manifest -BuildRequires: pkgconfig(pthread-stubs) BuildRequires: pkgconfig(libtbm) BuildRequires: pkgconfig(libpng) BuildRequires: pkgconfig(ttrace) -- 2.7.4 From 633c7139cfeb77dd009c0e99fb0d33498009b056 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Mon, 27 Jun 2016 21:53:28 +0900 Subject: [PATCH 13/16] add tdm_layer_get_buffer_flags There can be the layer which can set and display the specific buffer with the specific flags. Therefore, tdm user has to know the buffer flags to create the buffer which can be set to the specified layer. Change-Id: I1df658f4cbb4ca6019a7df1fe72c77db3d6db401 --- include/tdm.h | 9 +++++++++ include/tdm_backend.h | 9 ++++++++- src/tdm_display.c | 23 +++++++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/include/tdm.h b/include/tdm.h index 4af34d7..6ba4996 100644 --- a/include/tdm.h +++ b/include/tdm.h @@ -653,6 +653,15 @@ tdm_capture * tdm_layer_create_capture(tdm_layer *layer, tdm_error *error); /** + * @brief Get buffer flags from a layer object + * @param[in] layer A layer object + * @param[out] flags a buffer flags value + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +tdm_error +tdm_layer_get_buffer_flags(tdm_layer *layer, unsigned int *flags); + +/** * @brief Destroy a pp object * @param[in] pp A pp object * @see tdm_display_create_pp diff --git a/include/tdm_backend.h b/include/tdm_backend.h index 8967b52..0256e92 100644 --- a/include/tdm_backend.h +++ b/include/tdm_backend.h @@ -621,6 +621,14 @@ typedef struct _tdm_func_layer { */ tdm_capture *(*layer_create_capture)(tdm_layer *layer, tdm_error *error); + /** + * @brief Get buffer flags which the layer can support. + * @param[in] layer A layer object + * @param[out] flags The buffer flags which should be the tbm_bo flags + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*layer_get_buffer_flags)(tdm_layer *layer, unsigned int *flags); + void (*reserved1)(void); void (*reserved2)(void); void (*reserved3)(void); @@ -628,7 +636,6 @@ typedef struct _tdm_func_layer { void (*reserved5)(void); void (*reserved6)(void); void (*reserved7)(void); - void (*reserved8)(void); } tdm_func_layer; /** diff --git a/src/tdm_display.c b/src/tdm_display.c index e691501..17c02aa 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -1839,3 +1839,26 @@ tdm_layer_create_capture(tdm_layer *layer, tdm_error *error) return capture; } + +EXTERN tdm_error +tdm_layer_get_buffer_flags(tdm_layer *layer, unsigned int *flags) +{ + tdm_func_layer *func_layer; + LAYER_FUNC_ENTRY(); + + _pthread_mutex_lock(&private_display->lock); + + func_layer = &private_display->func_layer; + + if (!func_layer->layer_get_buffer_flags) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + ret = func_layer->layer_get_buffer_flags(private_layer->layer_backend, flags); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} -- 2.7.4 From ee90ba719ca9dfe34a3bc2fc58a9ba93a0d95ab9 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 22 Jun 2016 13:15:39 +0900 Subject: [PATCH 14/16] correct the retry condition of poll() Change-Id: I7b2cdd80ccc903811ed791dead112932b887469b --- client/tdm_client.h | 2 +- src/tdm_display.c | 2 +- src/tdm_thread.c | 2 +- tools/tdm_test_client.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/tdm_client.h b/client/tdm_client.h index a6a7a09..fff8808 100644 --- a/client/tdm_client.h +++ b/client/tdm_client.h @@ -113,7 +113,7 @@ tdm_client_destroy(tdm_client *client); * while(1) { * ret = poll(&fds, 1, -1); * if (ret < 0) { - * if (errno == EBUSY) + * if (errno == EINTR || errno == EAGAIN) * continue; * else { * //error handling diff --git a/src/tdm_display.c b/src/tdm_display.c index 17c02aa..cab04a6 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -417,7 +417,7 @@ tdm_display_handle_events(tdm_display *dpy) TDM_INFO("fd(%d) polling in", fd); while (poll(&fds, 1, -1) < 0) { - if (errno == EBUSY) /* normal case */ + if (errno == EINTR || errno == EAGAIN) /* normal case */ continue; else { TDM_ERR("poll failed: %m"); diff --git a/src/tdm_thread.c b/src/tdm_thread.c index 0d24561..3ae6d0b 100644 --- a/src/tdm_thread.c +++ b/src/tdm_thread.c @@ -99,7 +99,7 @@ _tdm_thread_main(void *data) TDM_INFO("fd(%d) polling out", fd); if (ret < 0) { - if (errno == EBUSY) /* normal case */ + if (errno == EINTR || errno == EAGAIN) /* normal case */ continue; else { TDM_ERR("poll failed: %m"); diff --git a/tools/tdm_test_client.c b/tools/tdm_test_client.c index 499d1f5..7268628 100644 --- a/tools/tdm_test_client.c +++ b/tools/tdm_test_client.c @@ -121,7 +121,7 @@ main(int argc, char *argv[]) if (!sync) { ret = poll(&fds, 1, -1); if (ret < 0) { - if (errno == EBUSY) /* normal case */ + if (errno == EINTR || errno == EAGAIN) /* normal case */ continue; else { printf("poll failed: %m\n"); -- 2.7.4 From 3c28bfc94c9f680bc7bb5f8c2ca2aaa4b7b5081c Mon Sep 17 00:00:00 2001 From: Boram Park Date: Mon, 27 Jun 2016 13:35:24 +0900 Subject: [PATCH 15/16] add tdm_common.h to share enumeration and structure with client Change-Id: I6c64905cb8363cc99f3742fe40934872d5d2cb33 --- include/Makefile.am | 1 + include/tdm.h | 8 --- include/tdm_common.h | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/tdm_types.h | 67 +------------------------- packaging/libtdm.spec | 2 + src/tdm_capture.c | 2 +- src/tdm_display.c | 49 ++----------------- src/tdm_macro.h | 38 +++++++++++++++ src/tdm_private.h | 9 +--- src/tdm_server.c | 4 +- 10 files changed, 181 insertions(+), 130 deletions(-) create mode 100644 include/tdm_common.h diff --git a/include/Makefile.am b/include/Makefile.am index b47ee00..aa1d502 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,6 +1,7 @@ libtdmincludedir = ${includedir} libtdminclude_HEADERS = \ tdm.h \ + tdm_common.h \ tdm_types.h \ tdm_list.h \ tdm_log.h \ diff --git a/include/tdm.h b/include/tdm.h index 6ba4996..47afc19 100644 --- a/include/tdm.h +++ b/include/tdm.h @@ -65,14 +65,6 @@ typedef enum { } tdm_display_capability; /** - * @brief The output change enumeration of #tdm_output_change_handler - */ -typedef enum { - TDM_OUTPUT_CHANGE_CONNECTION = (1 << 0), /**< connection chagne */ - TDM_OUTPUT_CHANGE_DPMS = (1 << 1), /**< dpms change */ -} tdm_output_change_type; - -/** * @brief The output change handler * @details This handler will be called when the status of a output object is * changed in runtime. diff --git a/include/tdm_common.h b/include/tdm_common.h new file mode 100644 index 0000000..79cdf69 --- /dev/null +++ b/include/tdm_common.h @@ -0,0 +1,131 @@ +/************************************************************************** + * + * libtdm + * + * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Contact: Eunchul Kim , + * JinYoung Jeon , + * Taeheon Kim , + * YoungJun Cho , + * SooChan Lim , + * Boram Park + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * +**************************************************************************/ + +#ifndef _TDM_COMMON_H_ +#define _TDM_COMMON_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define TDM_NAME_LEN 64 + +/** + * @file tdm_common.h + * @brief The header file which defines Enumerations and Structures for TDM + * frontend, backend and client. + */ + +/** + * @brief The error enumeration + */ +typedef enum { + TDM_ERROR_NONE = 0, /**< none */ + TDM_ERROR_BAD_REQUEST = -1, /**< bad request */ + TDM_ERROR_OPERATION_FAILED = -2, /**< operaion failed */ + TDM_ERROR_INVALID_PARAMETER = -3, /**< wrong input parameter */ + TDM_ERROR_PERMISSION_DENIED = -4, /**< access denied */ + TDM_ERROR_BUSY = -5, /**< hardware resource busy */ + TDM_ERROR_OUT_OF_MEMORY = -6, /**< no free memory */ + TDM_ERROR_BAD_MODULE = -7, /**< bad backend module */ + TDM_ERROR_NOT_IMPLEMENTED = -8, /**< not implemented */ + TDM_ERROR_NO_CAPABILITY = -9, /**< no capability */ + TDM_ERROR_DPMS_OFF = -10, /**< dpms off */ +} tdm_error; + +/** + * @brief The output change enumeration of #tdm_output_change_handler + */ +typedef enum { + TDM_OUTPUT_CHANGE_CONNECTION = (1 << 0), /**< connection chagne */ + TDM_OUTPUT_CHANGE_DPMS = (1 << 1), /**< dpms change */ +} tdm_output_change_type; + +/** + * @brief The output connection status enumeration + */ +typedef enum { + TDM_OUTPUT_CONN_STATUS_DISCONNECTED, /**< output disconnected */ + TDM_OUTPUT_CONN_STATUS_CONNECTED, /**< output connected */ + TDM_OUTPUT_CONN_STATUS_MODE_SETTED, /**< output connected and setted a mode */ +} tdm_output_conn_status; + +/** + * @brief The DPMS enumeration + * @details bit compatible with the libdrm definitions. + */ +typedef enum { + TDM_OUTPUT_DPMS_ON, /**< On */ + TDM_OUTPUT_DPMS_STANDBY, /**< StandBy */ + TDM_OUTPUT_DPMS_SUSPEND, /**< Suspend */ + TDM_OUTPUT_DPMS_OFF, /**< Off */ +} tdm_output_dpms; + +/** + * @brief The size structure + */ +typedef struct _tdm_size { + unsigned int h; /**< width */ + unsigned int v; /**< height */ +} tdm_size; + +/** + * @brief The pos structure + */ +typedef struct _tdm_pos { + unsigned int x; + unsigned int y; + unsigned int w; + unsigned int h; +} tdm_pos; + +/** + * @brief The value union + */ +typedef union { + void *ptr; + int32_t s32; + uint32_t u32; + int64_t s64; + uint64_t u64; +} tdm_value; + +#ifdef __cplusplus +} +#endif + +#endif /* _TDM_COMMON_H_ */ diff --git a/include/tdm_types.h b/include/tdm_types.h index a6b68fa..53988f2 100755 --- a/include/tdm_types.h +++ b/include/tdm_types.h @@ -42,8 +42,6 @@ extern "C" { #endif -#define TDM_NAME_LEN 64 - /** * @file tdm_types.h * @brief The header file which defines Enumerations and Structures for frontend and backend. @@ -59,21 +57,7 @@ extern "C" { * @endcode */ -/** - * @brief The error enumeration - */ -typedef enum { - TDM_ERROR_NONE = 0, /**< none */ - TDM_ERROR_BAD_REQUEST = -1, /**< bad request */ - TDM_ERROR_OPERATION_FAILED = -2, /**< operaion failed */ - TDM_ERROR_INVALID_PARAMETER = -3, /**< wrong input parameter */ - TDM_ERROR_PERMISSION_DENIED = -4, /**< access denied */ - TDM_ERROR_BUSY = -5, /**< hardware resource busy */ - TDM_ERROR_OUT_OF_MEMORY = -6, /**< no free memory */ - TDM_ERROR_BAD_MODULE = -7, /**< bad backend module */ - TDM_ERROR_NOT_IMPLEMENTED = -8, /**< not implemented */ - TDM_ERROR_NO_CAPABILITY = -9, /**< no capability */ -} tdm_error; +#include /** * @brief The transform enumeration(rotate, flip) @@ -91,15 +75,6 @@ typedef enum { /** * @brief The output connection status enumeration - */ -typedef enum { - TDM_OUTPUT_CONN_STATUS_DISCONNECTED, /**< output disconnected */ - TDM_OUTPUT_CONN_STATUS_CONNECTED, /**< output connected */ - TDM_OUTPUT_CONN_STATUS_MODE_SETTED, /**< output connected and setted a mode */ -} tdm_output_conn_status; - -/** - * @brief The output connection status enumeration * @details bit compatible with the libdrm definitions. */ typedef enum { @@ -123,17 +98,6 @@ typedef enum { } tdm_output_type; /** - * @brief The DPMS enumeration - * @details bit compatible with the libdrm definitions. - */ -typedef enum { - TDM_OUTPUT_DPMS_ON, /**< On */ - TDM_OUTPUT_DPMS_STANDBY, /**< StandBy */ - TDM_OUTPUT_DPMS_SUSPEND, /**< Suspend */ - TDM_OUTPUT_DPMS_OFF, /**< Off */ -} tdm_output_dpms; - -/** * @brief The layer capability enumeration * @details * A layer can have one of CURSOR, PRIMARY and OVERLAY capability. And a layer @@ -253,35 +217,6 @@ typedef struct _tdm_prop { } tdm_prop; /** - * @brief The size structure - */ -typedef struct _tdm_size { - unsigned int h; /**< width */ - unsigned int v; /**< height */ -} tdm_size; - -/** - * @brief The pos structure - */ -typedef struct _tdm_pos { - unsigned int x; - unsigned int y; - unsigned int w; - unsigned int h; -} tdm_pos; - -/** - * @brief The value union - */ -typedef union { - void *ptr; - int32_t s32; - uint32_t u32; - int64_t s64; - uint64_t u64; -} tdm_value; - -/** * @brief The info config structure */ typedef struct _tdm_info_config { diff --git a/packaging/libtdm.spec b/packaging/libtdm.spec index 11cec9a..28bb692 100644 --- a/packaging/libtdm.spec +++ b/packaging/libtdm.spec @@ -35,6 +35,7 @@ Tizen Display Manager Client Library Summary: Client library for Tizen Display Manager Group: Development/Libraries Requires: libtdm-client = %{version} +Requires: libtdm-devel %description client-devel Tizen Display Manager Client Library headers @@ -100,6 +101,7 @@ rm -f %{_unitdir_user}/default.target.wants/tdm-socket-user.path %manifest %{name}.manifest %defattr(-,root,root,-) %{_includedir}/tdm.h +%{_includedir}/tdm_common.h %{_includedir}/tdm_backend.h %{_includedir}/tdm_helper.h %{_includedir}/tdm_list.h diff --git a/src/tdm_capture.c b/src/tdm_capture.c index ea67044..8f562ab 100644 --- a/src/tdm_capture.c +++ b/src/tdm_capture.c @@ -403,7 +403,7 @@ tdm_capture_commit(tdm_capture *capture) private_output = private_capture->private_output; if (private_output->current_dpms_value > TDM_OUTPUT_DPMS_ON) { TDM_ERR("output(%d) dpms: %s", private_output->pipe, - dpms_str(private_output->current_dpms_value)); + tdm_dpms_str(private_output->current_dpms_value)); _pthread_mutex_unlock(&private_display->lock); return TDM_ERROR_BAD_REQUEST; } diff --git a/src/tdm_display.c b/src/tdm_display.c index cab04a6..2c09442 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -102,47 +102,6 @@ private_output = private_layer->private_output; \ private_display = private_output->private_display -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) - -struct type_name { - int type; - const char *name; -}; - -#define type_name_fn(res) \ -const char * res##_str(int type) \ -{ \ - unsigned int i; \ - for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \ - if (res##_names[i].type == type) \ - return res##_names[i].name; \ - } \ - return "(invalid)"; \ -} - -struct type_name dpms_names[] = { - { TDM_OUTPUT_DPMS_ON, "on" }, - { TDM_OUTPUT_DPMS_STANDBY, "standby" }, - { TDM_OUTPUT_DPMS_SUSPEND, "suspend" }, - { TDM_OUTPUT_DPMS_OFF, "off" }, -}; - -INTERN type_name_fn(dpms) - -struct type_name status_names[] = { - { TDM_OUTPUT_CONN_STATUS_DISCONNECTED, "disconnected" }, - { TDM_OUTPUT_CONN_STATUS_CONNECTED, "connected" }, - { TDM_OUTPUT_CONN_STATUS_MODE_SETTED, "mode_setted" }, -}; - -INTERN type_name_fn(status) - -INTERN const char* -tdm_get_dpms_str(tdm_output_dpms dpms_value) -{ - return dpms_str(dpms_value); -} - INTERN tdm_error _tdm_display_lock(tdm_display *dpy, const char *func) { @@ -988,7 +947,7 @@ tdm_output_wait_vblank(tdm_output *output, int interval, int sync, if (private_output->current_dpms_value > TDM_OUTPUT_DPMS_ON) { TDM_ERR("output(%d) dpms: %s", private_output->pipe, - dpms_str(private_output->current_dpms_value)); + tdm_dpms_str(private_output->current_dpms_value)); _pthread_mutex_unlock(&private_display->lock); return TDM_ERROR_BAD_REQUEST; } @@ -1082,7 +1041,7 @@ tdm_output_commit(tdm_output *output, int sync, tdm_output_commit_handler func, if (private_output->current_dpms_value > TDM_OUTPUT_DPMS_ON) { TDM_ERR("output(%d) dpms: %s", private_output->pipe, - dpms_str(private_output->current_dpms_value)); + tdm_dpms_str(private_output->current_dpms_value)); _pthread_mutex_unlock(&private_display->lock); return TDM_ERROR_BAD_REQUEST; } @@ -1245,10 +1204,10 @@ tdm_output_call_change_handler_internal(tdm_private_output *private_output, if (!tdm_thread_in_display_thread(syscall(SYS_gettid))) { if (type & TDM_OUTPUT_CHANGE_CONNECTION) TDM_INFO("output(%d) changed: %s (%d)", - private_output->pipe, status_str(value.u32), value.u32); + private_output->pipe, tdm_status_str(value.u32), value.u32); if (type & TDM_OUTPUT_CHANGE_DPMS) TDM_INFO("output(%d) changed: dpms %s (%d)", - private_output->pipe, dpms_str(value.u32), value.u32); + private_output->pipe, tdm_dpms_str(value.u32), value.u32); } if (LIST_IS_EMPTY(change_handler_list)) diff --git a/src/tdm_macro.h b/src/tdm_macro.h index b87b4a0..659ead3 100644 --- a/src/tdm_macro.h +++ b/src/tdm_macro.h @@ -41,6 +41,8 @@ #include #include +#include + #ifdef __cplusplus extern "C" { #endif @@ -117,6 +119,42 @@ extern "C" { #define FOURCC_STR(id) C(id, 0), C(id, 8), C(id, 16), C(id, 24) #define FOURCC_ID(str) FOURCC(((char*)str)[0], ((char*)str)[1], ((char*)str)[2], ((char*)str)[3]) + +#define TDM_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +struct tdm_type_name { + int type; + const char *name; +}; + +#define TDM_TYPE_NAME_FN(res) \ +static inline const char * tdm_##res##_str(int type) \ +{ \ + unsigned int i; \ + for (i = 0; i < TDM_ARRAY_SIZE(tdm_##res##_names); i++) { \ + if (tdm_##res##_names[i].type == type) \ + return tdm_##res##_names[i].name; \ + } \ + return "(invalid)"; \ +} + +static struct tdm_type_name tdm_dpms_names[] = { + { TDM_OUTPUT_DPMS_ON, "on" }, + { TDM_OUTPUT_DPMS_STANDBY, "standby" }, + { TDM_OUTPUT_DPMS_SUSPEND, "suspend" }, + { TDM_OUTPUT_DPMS_OFF, "off" }, +}; + +TDM_TYPE_NAME_FN(dpms) + +static struct tdm_type_name tdm_status_names[] = { + { TDM_OUTPUT_CONN_STATUS_DISCONNECTED, "disconnected" }, + { TDM_OUTPUT_CONN_STATUS_CONNECTED, "connected" }, + { TDM_OUTPUT_CONN_STATUS_MODE_SETTED, "mode_setted" }, +}; + +TDM_TYPE_NAME_FN(status) + #ifdef __cplusplus } #endif diff --git a/src/tdm_private.h b/src/tdm_private.h index db7426e..bdb1a20 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -84,11 +85,6 @@ extern int tdm_debug_thread; #define TDM_TRACE_END() #endif -#define prototype_name_fn(res) const char * res##_str(int type) - -prototype_name_fn(dpms); -prototype_name_fn(status); - typedef enum { TDM_CAPTURE_TARGET_OUTPUT, TDM_CAPTURE_TARGET_LAYER, @@ -304,9 +300,6 @@ typedef struct _tdm_buffer_info { struct list_head link; } tdm_buffer_info; -const char* -tdm_get_dpms_str(tdm_output_dpms dpms_value); - int tdm_display_check_module_abi(tdm_private_display *private_display, int abimaj, int abimin); diff --git a/src/tdm_server.c b/src/tdm_server.c index 1233deb..b71c2a2 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -321,8 +321,8 @@ _tdm_server_client_cb_wait_vblank(struct wl_client *client, if (dpms_value != TDM_OUTPUT_DPMS_ON && !sw_timer) { wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_DPMS_OFF, - "dpms '%s'", tdm_get_dpms_str(dpms_value)); - TDM_ERR("dpms '%s'", tdm_get_dpms_str(dpms_value)); + "dpms '%s'", tdm_dpms_str(dpms_value)); + TDM_ERR("dpms '%s'", tdm_dpms_str(dpms_value)); return; } -- 2.7.4 From 6708e849656ae248c39792318f65135870b51730 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Mon, 27 Jun 2016 13:37:44 +0900 Subject: [PATCH 16/16] support the tdm dynamic fps and the dpms on/off event Change-Id: I3fdc26887c19b5ddc66a5a4c1d7eca64cfa9f013 --- client/Makefile.am | 3 +- client/tdm_client.c | 673 ++++++++++++++++++++++++++++++----- client/tdm_client.h | 93 +++-- client/tdm_client_types.h | 112 ++++++ include/tdm_backend.h | 15 + include/tdm_list.h | 5 + include/tdm_types.h | 15 - packaging/libtdm.spec | 1 + protocol/tdm.xml | 72 ++-- src/Makefile.am | 1 + src/tdm_display.c | 23 +- src/tdm_private.h | 26 +- src/tdm_server.c | 516 +++++++++++++-------------- src/tdm_vblank.c | 879 ++++++++++++++++++++++++++++++++++++++++++++++ tools/Makefile.am | 3 +- tools/tdm_test_client.c | 355 +++++++++++++++++-- 16 files changed, 2303 insertions(+), 489 deletions(-) create mode 100644 client/tdm_client_types.h mode change 100755 => 100644 include/tdm_types.h create mode 100644 src/tdm_vblank.c diff --git a/client/Makefile.am b/client/Makefile.am index 14192b0..b41c0c6 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -1,6 +1,7 @@ libtdm_clientincludedir = ${includedir} libtdm_clientinclude_HEADERS = \ - tdm_client.h + tdm_client.h \ + tdm_client_types.h pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libtdm-client.pc diff --git a/client/tdm_client.c b/client/tdm_client.c index 2560a87..e3679d2 100644 --- a/client/tdm_client.c +++ b/client/tdm_client.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "tdm_client.h" #include "tdm_log.h" @@ -50,23 +51,202 @@ int tdm_debug; +typedef struct _tdm_private_client_vblank tdm_private_client_vblank; + typedef struct _tdm_private_client { struct wl_display *display; struct wl_registry *registry; struct wl_tdm *tdm; - struct wl_tdm_client *tdm_client; - struct list_head vblank_list; + struct list_head output_list; + + tdm_private_client_vblank *temp_vblank; } tdm_private_client; -typedef struct _tdm_client_vblank_info { +typedef struct _tdm_private_client_output { + tdm_private_client *private_client; + + char name[TDM_NAME_LEN]; + struct wl_tdm_output *output; + int width; + int height; + int refresh; + tdm_output_conn_status connection; + tdm_output_dpms dpms; + struct list_head vblank_list; + struct list_head change_handler_list; + + unsigned int req_id; + struct list_head link; +} tdm_private_client_output; + +struct _tdm_private_client_vblank { + tdm_private_client_output *private_output; + struct wl_tdm_vblank *vblank; + struct list_head wait_list; + + unsigned int sync; + unsigned int fps; + int offset; + unsigned int enable_fake; + + unsigned int started; + + struct list_head link; +}; + +typedef struct _tdm_client_output_handler_info { + tdm_private_client_output *private_output; + + tdm_client_output_change_handler func; + void *user_data; + + struct list_head link; +} tdm_client_output_handler_info; + +typedef struct _tdm_client_wait_info { + tdm_private_client_vblank *private_vblank; + tdm_client_vblank_handler func; + void *user_data; + + unsigned int req_id; unsigned int req_sec; unsigned int req_usec; - void *user_data; int need_free; -} tdm_client_vblank_info; + + struct list_head link; +} tdm_client_wait_info; + +static void +_tdm_client_vblank_cb_done(void *data, struct wl_tdm_vblank *wl_tdm_vblank, + uint32_t req_id, uint32_t sequence, uint32_t tv_sec, + uint32_t tv_usec, uint32_t error) +{ + tdm_private_client_vblank *private_vblank = data; + tdm_client_wait_info *w = NULL, *ww = NULL; + + TDM_RETURN_IF_FAIL(private_vblank != NULL); + + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->wait_list, link) { + if (w->req_id != req_id) + continue; + + if (w->func) + w->func(private_vblank, error, sequence, tv_sec, tv_usec, w->user_data); + + if (w->need_free) { + LIST_DEL(&w->link); + free(w); + } else + w->need_free = 1; + return; + } +} + +static const struct wl_tdm_vblank_listener tdm_client_vblank_listener = { + _tdm_client_vblank_cb_done, +}; + +static void +_tdm_client_output_destroy(tdm_private_client_output *private_output) +{ + tdm_private_client_vblank *v = NULL, *vv = NULL; + tdm_client_output_handler_info *h = NULL, *hh = NULL; + + LIST_DEL(&private_output->link); + + LIST_FOR_EACH_ENTRY_SAFE(v, vv, &private_output->vblank_list, link) { + TDM_ERR("vblanks SHOULD be destroyed first!"); + LIST_DEL(&v->link); + v->private_output = NULL; + } + + LIST_FOR_EACH_ENTRY_SAFE(h, hh, &private_output->change_handler_list, link) { + LIST_DEL(&h->link); + free(h); + } + + wl_tdm_output_destroy(private_output->output); + + free(private_output); +} + +static void +_tdm_client_output_cb_mode(void *data, struct wl_tdm_output *wl_tdm_output, + uint32_t width, uint32_t height, uint32_t refresh) +{ + tdm_private_client_output *private_output = (tdm_private_client_output*)data; + + TDM_RETURN_IF_FAIL(private_output != NULL); + + private_output->width = width; + private_output->height = height; + private_output->refresh = refresh; + + TDM_DBG("private_output(%p) wl_tbm_output@%d width(%d) height(%d) refresh(%d)", + private_output, wl_proxy_get_id((struct wl_proxy*)private_output->output), + width, height, refresh); +} + +static void +_tdm_client_output_cb_connection(void *data, struct wl_tdm_output *wl_tdm_output, uint32_t value) +{ + tdm_private_client_output *private_output = (tdm_private_client_output*)data; + tdm_client_output_handler_info *h = NULL; + tdm_value v; + + TDM_RETURN_IF_FAIL(private_output != NULL); + + if (private_output->connection == value) + return; + + private_output->connection = value; + + TDM_DBG("private_output(%p) wl_tbm_output@%d connection(%d)", + private_output, + wl_proxy_get_id((struct wl_proxy*)private_output->output), + value); + + v.u32 = value; + LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) { + if (h->func) + h->func(private_output, TDM_OUTPUT_CHANGE_CONNECTION, v, h->user_data); + } +} + +static void +_tdm_client_output_cb_dpms(void *data, struct wl_tdm_output *wl_tdm_output, uint32_t value) +{ + tdm_private_client_output *private_output = (tdm_private_client_output*)data; + tdm_client_output_handler_info *h = NULL; + tdm_value v; + + TDM_RETURN_IF_FAIL(private_output != NULL); + + if (private_output->dpms == value) + return; + + private_output->dpms = value; + + TDM_DBG("private_output(%p) wl_tbm_output@%d dpms(%d)", + private_output, + wl_proxy_get_id((struct wl_proxy*)private_output->output), + value); + + v.u32 = value; + LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) { + if (h->func) + h->func(private_output, TDM_OUTPUT_CHANGE_DPMS, v, h->user_data); + } +} + +static const struct wl_tdm_output_listener tdm_client_output_listener = { + _tdm_client_output_cb_mode, + _tdm_client_output_cb_connection, + _tdm_client_output_cb_dpms, +}; static void _tdm_client_cb_global(void *data, struct wl_registry *registry, @@ -95,7 +275,7 @@ static const struct wl_registry_listener tdm_client_registry_listener = { }; tdm_client* -tdm_client_create(tdm_client_error *error) +tdm_client_create(tdm_error *error) { tdm_private_client *private_client; const char *debug; @@ -104,14 +284,16 @@ tdm_client_create(tdm_client_error *error) if (debug && (strstr(debug, "1"))) tdm_debug = 1; - private_client = calloc(1, sizeof * private_client); + private_client = calloc(1, sizeof *private_client); if (!private_client) { TDM_ERR("alloc failed"); if (error) - *error = TDM_CLIENT_ERROR_OUT_OF_MEMORY; + *error = TDM_ERROR_OUT_OF_MEMORY; return NULL; } + LIST_INITHEAD(&private_client->output_list); + private_client->display = wl_display_connect("tdm-socket"); TDM_GOTO_IF_FAIL(private_client->display != NULL, create_failed); @@ -125,19 +307,14 @@ tdm_client_create(tdm_client_error *error) /* check global objects */ TDM_GOTO_IF_FAIL(private_client->tdm != NULL, create_failed); - LIST_INITHEAD(&private_client->vblank_list); - - private_client->tdm_client = wl_tdm_create_client(private_client->tdm); - TDM_GOTO_IF_FAIL(private_client->tdm_client != NULL, create_failed); - if (error) - *error = TDM_CLIENT_ERROR_NONE; + *error = TDM_ERROR_NONE; return (tdm_client*)private_client; create_failed: tdm_client_destroy((tdm_client*)private_client); if (error) - *error = TDM_CLIENT_ERROR_OPERATION_FAILED; + *error = TDM_ERROR_OPERATION_FAILED; return NULL; } @@ -145,19 +322,18 @@ void tdm_client_destroy(tdm_client *client) { tdm_private_client *private_client = (tdm_private_client*)client; - tdm_client_vblank_info *v = NULL, *vv = NULL; + tdm_private_client_output *o = NULL, *oo = NULL; if (!private_client) return; - LIST_FOR_EACH_ENTRY_SAFE(v, vv, &private_client->vblank_list, link) { - LIST_DEL(&v->link); - wl_tdm_vblank_destroy(v->vblank); - free(v); + if (private_client->temp_vblank) + tdm_client_vblank_destroy(private_client->temp_vblank); + + LIST_FOR_EACH_ENTRY_SAFE(o, oo, &private_client->output_list, link) { + _tdm_client_output_destroy(o); } - if (private_client->tdm_client) - wl_tdm_client_destroy(private_client->tdm_client); if (private_client->tdm) wl_tdm_destroy(private_client->tdm); if (private_client->registry) @@ -168,129 +344,446 @@ tdm_client_destroy(tdm_client *client) free(private_client); } -tdm_client_error +tdm_error tdm_client_get_fd(tdm_client *client, int *fd) { tdm_private_client *private_client; - TDM_RETURN_VAL_IF_FAIL(client != NULL, TDM_CLIENT_ERROR_INVALID_PARAMETER); - TDM_RETURN_VAL_IF_FAIL(fd != NULL, TDM_CLIENT_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(client != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(fd != NULL, TDM_ERROR_INVALID_PARAMETER); private_client = (tdm_private_client*)client; *fd = wl_display_get_fd(private_client->display); if (*fd < 0) - return TDM_CLIENT_ERROR_OPERATION_FAILED; + return TDM_ERROR_OPERATION_FAILED; - return TDM_CLIENT_ERROR_NONE; + return TDM_ERROR_NONE; } -tdm_client_error +tdm_error tdm_client_handle_events(tdm_client *client) { tdm_private_client *private_client; - TDM_RETURN_VAL_IF_FAIL(client != NULL, TDM_CLIENT_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(client != NULL, TDM_ERROR_INVALID_PARAMETER); private_client = (tdm_private_client*)client; wl_display_dispatch(private_client->display); - return TDM_CLIENT_ERROR_NONE; + return TDM_ERROR_NONE; } +typedef struct _tdm_client_vblank_temp { + tdm_client_vblank_handler2 func; + void *user_data; +} tdm_client_vblank_temp; + static void -_tdm_client_cb_vblank_done(void *data, struct wl_tdm_vblank *vblank, - uint32_t sequence, uint32_t tv_sec, uint32_t tv_usec) +_tdm_client_vblank_handler_temp(tdm_client_vblank *vblank, tdm_error error, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, void *user_data) { - tdm_client_vblank_info *vblank_info = (tdm_client_vblank_info*)data; + tdm_client_vblank_temp *vblank_temp = user_data; - TDM_RETURN_IF_FAIL(vblank_info != NULL); + TDM_RETURN_IF_FAIL(vblank_temp != NULL); - if (vblank_info->vblank != vblank) - TDM_NEVER_GET_HERE(); + if (vblank_temp->func) + vblank_temp->func(sequence, tv_sec, tv_usec, vblank_temp->user_data); - TDM_DBG("vblank_info(%p) wl_tbm_vblank@%d", vblank_info, - wl_proxy_get_id((struct wl_proxy *)vblank)); - - if (vblank_info->func) - vblank_info->func(sequence, tv_sec, tv_usec, vblank_info->user_data); - - if (vblank_info->need_free) { - LIST_DEL(&vblank_info->link); - free(vblank_info); - } else { - vblank_info->need_free = 1; - } + free(vblank_temp); } -static const struct wl_tdm_vblank_listener tdm_client_vblank_listener = { - _tdm_client_cb_vblank_done, -}; - -tdm_client_error +tdm_error tdm_client_wait_vblank(tdm_client *client, char *name, int sw_timer, int interval, int sync, - tdm_client_vblank_handler func, void *user_data) + tdm_client_vblank_handler2 func, void *user_data) { tdm_private_client *private_client = (tdm_private_client*)client; - tdm_client_vblank_info *vblank_info; - struct timespec tp; - int ret = 0; + tdm_client_output *output; + tdm_client_vblank_temp *vblank_temp; + tdm_error ret; + + TDM_RETURN_VAL_IF_FAIL(private_client != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER); + + if (!private_client->temp_vblank) { + output = tdm_client_get_output(client, name, &ret); + TDM_RETURN_VAL_IF_FAIL(output != NULL, ret); + + private_client->temp_vblank = tdm_client_output_create_vblank(output, &ret); + TDM_RETURN_VAL_IF_FAIL(private_client->temp_vblank != NULL, ret); + } + + tdm_client_vblank_set_enable_fake(private_client->temp_vblank, sw_timer); + tdm_client_vblank_set_sync(private_client->temp_vblank, sync); + + vblank_temp = calloc(1, sizeof *vblank_temp); + TDM_RETURN_VAL_IF_FAIL(vblank_temp != NULL, TDM_ERROR_OUT_OF_MEMORY); + + vblank_temp->func = func; + vblank_temp->user_data = user_data; - TDM_RETURN_VAL_IF_FAIL(name != NULL, TDM_CLIENT_ERROR_INVALID_PARAMETER); - TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_CLIENT_ERROR_INVALID_PARAMETER); - TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_CLIENT_ERROR_INVALID_PARAMETER); - TDM_RETURN_VAL_IF_FAIL(private_client != NULL, TDM_CLIENT_ERROR_INVALID_PARAMETER); - TDM_RETURN_VAL_IF_FAIL(private_client->tdm_client != NULL, TDM_CLIENT_ERROR_INVALID_PARAMETER); + return tdm_client_vblank_wait(private_client->temp_vblank, interval, _tdm_client_vblank_handler_temp, vblank_temp); +} + +tdm_client_output* +tdm_client_get_output(tdm_client *client, char *name, tdm_error *error) +{ + tdm_private_client *private_client; + tdm_private_client_output *private_output = NULL; - vblank_info = calloc(1, sizeof * vblank_info); - if (!vblank_info) { + if (error) + *error = TDM_ERROR_NONE; + + if (!client) { + TDM_ERR("'!client' failed"); + if (error) + *error = TDM_ERROR_INVALID_PARAMETER; + return NULL; + } + + private_client = (tdm_private_client*)client; + + if (!name) + name = "primary"; + + LIST_FOR_EACH_ENTRY(private_output, &private_client->output_list, link) { + if (!strncmp(private_output->name, name, TDM_NAME_LEN)) + return (tdm_client_output*)private_output; + } + + private_output = calloc(1, sizeof *private_output); + if (!private_output) { TDM_ERR("alloc failed"); - return TDM_CLIENT_ERROR_OUT_OF_MEMORY; + if (error) + *error = TDM_ERROR_OUT_OF_MEMORY; + return NULL; } - clock_gettime(CLOCK_MONOTONIC, &tp); + private_output->private_client = private_client; - vblank_info->req_sec = (unsigned int)tp.tv_sec; - vblank_info->req_usec = (unsigned int)(tp.tv_nsec / 1000); - vblank_info->need_free = (sync) ? 0 : 1; + snprintf(private_output->name, TDM_NAME_LEN, "%s", name); + private_output->output = wl_tdm_create_output(private_client->tdm, private_output->name); + if (!private_output->output) { + TDM_ERR("couldn't create output resource"); + free(private_output); + if (error) + *error = TDM_ERROR_OUT_OF_MEMORY; + return NULL; + } + + LIST_INITHEAD(&private_output->vblank_list); + LIST_INITHEAD(&private_output->change_handler_list); + LIST_ADDTAIL(&private_output->link, &private_client->output_list); + + wl_tdm_output_add_listener(private_output->output, + &tdm_client_output_listener, private_output); + wl_display_roundtrip(private_client->display); + + return (tdm_client_output*)private_output; +} + +tdm_error +tdm_client_output_add_change_handler(tdm_client_output *output, + tdm_client_output_change_handler func, + void *user_data) +{ + tdm_private_client_output *private_output; + tdm_client_output_handler_info *h; + + TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_output = (tdm_private_client_output*)output; + + h = calloc(1, sizeof *h); + TDM_RETURN_VAL_IF_FAIL(h != NULL, TDM_ERROR_OUT_OF_MEMORY); + + h->private_output = private_output; + h->func = func; + h->user_data = user_data; + LIST_ADDTAIL(&h->link, &private_output->change_handler_list); + + return TDM_ERROR_NOT_IMPLEMENTED; +} + +void +tdm_client_output_remove_change_handler(tdm_client_output *output, + tdm_client_output_change_handler func, + void *user_data) +{ + tdm_private_client_output *private_output; + tdm_client_output_handler_info *h = NULL; + + TDM_RETURN_IF_FAIL(output != NULL); + TDM_RETURN_IF_FAIL(func != NULL); + + private_output = (tdm_private_client_output*)output; + + LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) { + if (h->func != func || h->user_data != user_data) + continue; + + LIST_DEL(&h->link); + free(h); + return; + } +} + +tdm_error +tdm_client_output_get_refresh_rate(tdm_client_output *output, unsigned int *refresh) +{ + tdm_private_client_output *private_output; + + TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(refresh != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_output = (tdm_private_client_output*)output; + + *refresh = private_output->refresh; + + return TDM_ERROR_NONE; +} + +tdm_error +tdm_client_output_get_conn_status(tdm_client_output *output, tdm_output_conn_status *status) +{ + tdm_private_client_output *private_output; + + TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(status != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_output = (tdm_private_client_output*)output; + + *status = private_output->connection; + + return TDM_ERROR_NONE; +} - vblank_info->vblank = - wl_tdm_client_wait_vblank(private_client->tdm_client, - name, sw_timer, interval, - vblank_info->req_sec, vblank_info->req_usec); - if (!vblank_info->vblank) { +tdm_error +tdm_client_output_get_dpms(tdm_client_output *output, tdm_output_dpms *dpms) +{ + tdm_private_client_output *private_output; + + TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(dpms != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_output = (tdm_private_client_output*)output; + + *dpms = private_output->dpms; + + return TDM_ERROR_NONE; +} + +tdm_client_vblank* +tdm_client_output_create_vblank(tdm_client_output *output, tdm_error *error) +{ + tdm_private_client_output *private_output; + tdm_private_client_vblank *private_vblank; + + if (error) + *error = TDM_ERROR_NONE; + + if (!output) { + TDM_ERR("'!output' failed"); + if (error) + *error = TDM_ERROR_INVALID_PARAMETER; + return NULL; + } + + private_output = (tdm_private_client_output*)output; + + private_vblank = calloc(1, sizeof *private_vblank); + if (!private_vblank) { + TDM_ERR("alloc failed"); + if (error) + *error = TDM_ERROR_OUT_OF_MEMORY; + return NULL; + } + + private_vblank->private_output = private_output; + + private_vblank->vblank = wl_tdm_output_create_vblank(private_output->output); + if (!private_vblank->vblank) { TDM_ERR("couldn't create vblank resource"); - free(vblank_info); - return TDM_CLIENT_ERROR_OUT_OF_MEMORY; + free(private_vblank); + if (error) + *error = TDM_ERROR_OUT_OF_MEMORY; + return NULL; + } + + /* initial value */ + private_vblank->fps = private_output->refresh; + private_vblank->offset = 0; + private_vblank->enable_fake = 0; + + LIST_INITHEAD(&private_vblank->wait_list); + LIST_ADDTAIL(&private_vblank->link, &private_output->vblank_list); + + wl_tdm_vblank_add_listener(private_vblank->vblank, + &tdm_client_vblank_listener, private_vblank); + + return (tdm_client_vblank*)private_vblank; +} + +void +tdm_client_vblank_destroy(tdm_client_vblank *vblank) +{ + tdm_private_client_vblank *private_vblank; + tdm_client_wait_info *w = NULL, *ww = NULL; + + TDM_RETURN_IF_FAIL(vblank != NULL); + + private_vblank = vblank; + LIST_DEL(&private_vblank->link); + + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->wait_list, link) { + LIST_DEL(&w->link); + free(w); } - TDM_DBG("vblank_info(%p) wl_tbm_vblank@%d", vblank_info, - wl_proxy_get_id((struct wl_proxy *)vblank_info->vblank)); + wl_tdm_vblank_destroy(private_vblank->vblank); + + free(private_vblank); +} + +tdm_error +tdm_client_vblank_set_sync(tdm_client_vblank *vblank, unsigned int sync) +{ + tdm_private_client_vblank *private_vblank; + + TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_vblank = vblank; + private_vblank->sync = sync; + + return TDM_ERROR_NONE; +} + +tdm_error +tdm_client_vblank_set_fps(tdm_client_vblank *vblank, unsigned int fps) +{ + tdm_private_client_vblank *private_vblank; + + TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(fps > 0, TDM_ERROR_INVALID_PARAMETER); + + private_vblank = vblank; + TDM_RETURN_VAL_IF_FAIL(private_vblank->started == 0, TDM_ERROR_BAD_REQUEST); + + if (private_vblank->fps == fps) + return TDM_ERROR_NONE; + private_vblank->fps = fps; + + wl_tdm_vblank_set_fps(private_vblank->vblank, fps); + + return TDM_ERROR_NONE; +} + +tdm_error +tdm_client_vblank_set_offset(tdm_client_vblank *vblank, int offset_ms) +{ + tdm_private_client_vblank *private_vblank; - wl_tdm_vblank_add_listener(vblank_info->vblank, - &tdm_client_vblank_listener, vblank_info); + TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_vblank = vblank; + TDM_RETURN_VAL_IF_FAIL(private_vblank->started == 0, TDM_ERROR_BAD_REQUEST); + + if (private_vblank->offset == offset_ms) + return TDM_ERROR_NONE; + private_vblank->offset = offset_ms; + + wl_tdm_vblank_set_offset(private_vblank->vblank, offset_ms); + + return TDM_ERROR_NONE; +} + +tdm_error +tdm_client_vblank_set_enable_fake(tdm_client_vblank *vblank, unsigned int enable_fake) +{ + tdm_private_client_vblank *private_vblank; + + TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_vblank = vblank; + + if (private_vblank->enable_fake == enable_fake) + return TDM_ERROR_NONE; + private_vblank->enable_fake = enable_fake; + + wl_tdm_vblank_set_enable_fake(private_vblank->vblank, enable_fake); + + return TDM_ERROR_NONE; +} + +tdm_error +tdm_client_vblank_wait(tdm_client_vblank *vblank, unsigned int interval, tdm_client_vblank_handler func, void *user_data) +{ + tdm_private_client *private_client; + tdm_private_client_output *private_output; + tdm_private_client_vblank *private_vblank; + tdm_client_wait_info *w; + struct timespec tp; + int ret = 0; + + TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER); + /* can't support "interval 0" and "getting current_msc" things because + * there is a socket communication between TDM client and server. It's impossible + * to return the current msc or sequence immediately. + */ + TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_ERROR_INVALID_PARAMETER); + + private_vblank = vblank; + private_output = private_vblank->private_output; + private_client = private_output->private_client; + + if (!private_vblank->started) + private_vblank->started = 1; + + if (private_output->dpms != TDM_OUTPUT_DPMS_ON && !private_vblank->enable_fake) { + TDM_INFO("dpms off"); + return TDM_ERROR_DPMS_OFF; + } + + w = calloc(1, sizeof *w); + if (!w) { + TDM_ERR("alloc failed"); + return TDM_ERROR_OUT_OF_MEMORY; + } + + w->private_vblank = private_vblank; + w->func = func; + w->user_data = user_data; + + LIST_ADDTAIL(&w->link, &private_vblank->wait_list); + + clock_gettime(CLOCK_MONOTONIC, &tp); + w->req_id = ++private_output->req_id; + w->req_sec = (unsigned int)tp.tv_sec; + w->req_usec = (unsigned int)(tp.tv_nsec / 1000); + w->need_free = (private_vblank->sync) ? 0 : 1; - vblank_info->func = func; - vblank_info->user_data = user_data; - LIST_ADDTAIL(&vblank_info->link, &private_client->vblank_list); + wl_tdm_vblank_wait_vblank(private_vblank->vblank, interval, w->req_id, w->req_sec, w->req_usec); - if (!sync) { + if (!private_vblank->sync) { wl_display_flush(private_client->display); - return TDM_CLIENT_ERROR_NONE; + return TDM_ERROR_NONE; } - while (ret != -1 && !vblank_info->need_free) + while (ret != -1 && !w->need_free) ret = wl_display_dispatch(private_client->display); clock_gettime(CLOCK_MONOTONIC, &tp); TDM_DBG("block during %d us", ((unsigned int)(tp.tv_sec * 1000000) + (unsigned int)(tp.tv_nsec / 1000)) - - (vblank_info->req_sec * 1000000 + vblank_info->req_usec)); + - (w->req_sec * 1000000 + w->req_usec)); - LIST_DEL(&vblank_info->link); - free(vblank_info); + LIST_DEL(&w->link); + free(w); - return TDM_CLIENT_ERROR_NONE; + return TDM_ERROR_NONE; } diff --git a/client/tdm_client.h b/client/tdm_client.h index fff8808..2f1c64f 100644 --- a/client/tdm_client.h +++ b/client/tdm_client.h @@ -49,39 +49,16 @@ extern "C" { * @endcode */ -/** - * @brief The client error enumeration - */ -typedef enum { - TDM_CLIENT_ERROR_NONE = 0, /**< none */ - TDM_CLIENT_ERROR_OPERATION_FAILED = -1, /**< operaion failed */ - TDM_CLIENT_ERROR_INVALID_PARAMETER = -2, /**< wrong input parameter */ - TDM_CLIENT_ERROR_PERMISSION_DENIED = -3, /**< access denied */ - TDM_CLIENT_ERROR_OUT_OF_MEMORY = -4, /**< no free memory */ - TDM_CLIENT_ERROR_DPMS_OFF = -5, /**< dpms off */ -} tdm_client_error; - -/** - * @brief The TDM client object - */ -typedef void *tdm_client; - -/** - * @brief The client vblank handler - * @see #tdm_client_wait_vblank - */ -typedef void -(*tdm_client_vblank_handler)(unsigned int sequence, unsigned int tv_sec, - unsigned int tv_usec, void *user_data); +#include /** * @brief Create a TDM client object. - * @param[out] error #TDM_CLIENT_ERROR_NONE if success. Otherwise, error value. + * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. * @return A TDM client object if success. Otherwise, NULL. * @see #tdm_client_destroy */ tdm_client* -tdm_client_create(tdm_client_error *error); +tdm_client_create(tdm_error *error); /** * @brief Destroy a TDM client object @@ -95,14 +72,14 @@ tdm_client_destroy(tdm_client *client); * @brief Get the file descriptor * @param[in] client A TDM client object * @param[out] fd The file descriptor - * @return #TDM_CLIENT_ERROR_NONE if success. Otherwise, error value. + * @return #TDM_ERROR_NONE if success. Otherwise, error value. * @see #tdm_client_handle_events * @par Example * @code * #include //for a client of TDM * * err = tdm_client_get_fd(client, &fd); - * if (err != TDM_CLIENT_ERROR_NONE) { + * if (err != TDM_ERROR_NONE) { * //error handling * } * @@ -121,26 +98,27 @@ tdm_client_destroy(tdm_client *client); * } * * err = tdm_client_handle_events(client); - * if (err != TDM_CLIENT_ERROR_NONE) { + * if (err != TDM_ERROR_NONE) { * //error handling * } * } * @endcode */ -tdm_client_error +tdm_error tdm_client_get_fd(tdm_client *client, int *fd); /** * @brief Handle the events of the given file descriptor * @param[in] client A TDM client object - * @return #TDM_CLIENT_ERROR_NONE if success. Otherwise, error value. + * @return #TDM_ERROR_NONE if success. Otherwise, error value. * @see #tdm_client_get_fd */ -tdm_client_error +tdm_error tdm_client_handle_events(tdm_client *client); /** * @brief Wait for VBLANK + * @deprecated * @details After interval vblanks, a client vblank handler will be called. * If 'sw_timer' param is 1 in case of DPMS off, TDM will use the SW timer and * call a client vblank handler. Otherwise, this function will return error. @@ -151,13 +129,58 @@ tdm_client_handle_events(tdm_client *client); * @param[in] sync 0: asynchronous, 1:synchronous * @param[in] func A client vblank handler * @param[in] user_data The user data - * @return #TDM_CLIENT_ERROR_NONE if success. Otherwise, error value. + * @return #TDM_ERROR_NONE if success. Otherwise, error value. * @see #tdm_client_vblank_handler */ -tdm_client_error +tdm_error tdm_client_wait_vblank(tdm_client *client, char *name, int sw_timer, int interval, int sync, - tdm_client_vblank_handler func, void *user_data); + tdm_client_vblank_handler2 func, void *user_data); + + +tdm_client_output* +tdm_client_get_output(tdm_client *client, char *name, tdm_error *error); + +tdm_error +tdm_client_output_add_change_handler(tdm_client_output *output, + tdm_client_output_change_handler func, + void *user_data); + +void +tdm_client_output_remove_change_handler(tdm_client_output *output, + tdm_client_output_change_handler func, + void *user_data); + +tdm_error +tdm_client_output_get_refresh_rate(tdm_client_output *output, unsigned int *refresh); + +tdm_error +tdm_client_output_get_conn_status(tdm_client_output *output, tdm_output_conn_status *status); + +tdm_error +tdm_client_output_get_dpms(tdm_client_output *output, tdm_output_dpms *dpms); + +tdm_client_vblank* +tdm_client_output_create_vblank(tdm_client_output *output, tdm_error *error); + +void +tdm_client_vblank_destroy(tdm_client_vblank *vblank); + +tdm_error +tdm_client_vblank_set_sync(tdm_client_vblank *vblank, unsigned int sync); + +tdm_error +tdm_client_vblank_set_fps(tdm_client_vblank *vblank, unsigned int fps); + +tdm_error +tdm_client_vblank_set_offset(tdm_client_vblank *vblank, int offset_ms); + +tdm_error +tdm_client_vblank_set_enable_fake(tdm_client_vblank *vblank, unsigned int enable_fake); + +tdm_error +tdm_client_vblank_wait(tdm_client_vblank *vblank, unsigned int interval, tdm_client_vblank_handler func, void *user_data); + #ifdef __cplusplus } diff --git a/client/tdm_client_types.h b/client/tdm_client_types.h new file mode 100644 index 0000000..f6f6355 --- /dev/null +++ b/client/tdm_client_types.h @@ -0,0 +1,112 @@ +/************************************************************************** + * + * libtdm + * + * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Contact: Eunchul Kim , + * JinYoung Jeon , + * Taeheon Kim , + * YoungJun Cho , + * SooChan Lim , + * Boram Park + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * +**************************************************************************/ + +#ifndef _TDM_CLIENT_TYPES_H_ +#define _TDM_CLIENT_TYPES_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file tdm_client_types.h + * @brief The header file which defines Enumerations and Structures for client. + */ + +#include + +/** + * @deprecated + */ +typedef tdm_error tdm_client_error; + +/** + * @brief The client error enumeration + * @deprecated + */ +enum { + TDM_CLIENT_ERROR_NONE = 0, /**< none */ + TDM_CLIENT_ERROR_OPERATION_FAILED = -1, /**< operaion failed */ + TDM_CLIENT_ERROR_INVALID_PARAMETER = -2, /**< wrong input parameter */ + TDM_CLIENT_ERROR_PERMISSION_DENIED = -3, /**< access denied */ + TDM_CLIENT_ERROR_OUT_OF_MEMORY = -4, /**< no free memory */ + TDM_CLIENT_ERROR_DPMS_OFF = -5, /**< dpms off */ +}; + +/** + * @deprecated + */ +typedef void +(*tdm_client_vblank_handler2)(unsigned int sequence, unsigned int tv_sec, + unsigned int tv_usec, void *user_data); + +/** + * @brief The TDM client object + */ +typedef void tdm_client; + +/** + * @brief The TDM client output object + */ +typedef void tdm_client_output; + +/** + * @brief The TDM client vblank object + */ +typedef void tdm_client_vblank; + +typedef void +(*tdm_client_output_change_handler)(tdm_client_output *output, + tdm_output_change_type type, + tdm_value value, + void *user_data); + +/** + * @brief The client vblank handler + * @see #tdm_client_wait_vblank + */ +typedef void +(*tdm_client_vblank_handler)(tdm_client_vblank *vblank, + tdm_error error, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + void *user_data); + +#ifdef __cplusplus +} +#endif + +#endif /* _TDM_CLIENT_TYPES_H_ */ diff --git a/include/tdm_backend.h b/include/tdm_backend.h index 0256e92..77b1e9b 100644 --- a/include/tdm_backend.h +++ b/include/tdm_backend.h @@ -788,6 +788,21 @@ typedef struct _tdm_func_capture { void (*reserved8)(void); } tdm_func_capture; +/** + * @brief The tdm event source + */ +typedef void tdm_event_loop_source; + +/** + * @brief The fd source handler + */ +typedef tdm_error (*tdm_event_loop_fd_handler)(int fd, tdm_event_loop_mask mask, void *user_data); + +/** + * @brief The timer source handler + */ +typedef tdm_error (*tdm_event_loop_timer_handler)(void *user_data); + #define TDM_BACKEND_MINOR_VERSION_MASK 0x0000FFFF #define TDM_BACKEND_MAJOR_VERSION_MASK 0xFFFF0000 #define TDM_BACKEND_GET_ABI_MINOR(v) ((v) & TDM_BACKEND_MINOR_VERSION_MASK) diff --git a/include/tdm_list.h b/include/tdm_list.h index 2fd1db0..7475894 100644 --- a/include/tdm_list.h +++ b/include/tdm_list.h @@ -126,6 +126,11 @@ static inline int list_length(struct list_head *item) &pos->member != (head); \ pos = container_of(pos->member.next, pos, member)) +#define LIST_FOR_EACH_ENTRY_REV(pos, head, member) \ + for (pos = container_of((head)->prev, pos, member); \ + &pos->member != (head); \ + pos = container_of(pos->member.prev, pos, member)) + #define LIST_FOR_EACH_ENTRY_SAFE(pos, storage, head, member) \ for (pos = container_of((head)->next, pos, member), \ storage = container_of(pos->member.next, pos, member); \ diff --git a/include/tdm_types.h b/include/tdm_types.h old mode 100755 new mode 100644 index 53988f2..050dad8 --- a/include/tdm_types.h +++ b/include/tdm_types.h @@ -297,21 +297,6 @@ typedef void (*tdm_output_commit_handler)(tdm_output *output, unsigned int seque unsigned int tv_sec, unsigned int tv_usec, void *user_data); -/** - * @brief The tdm event source - */ -typedef void tdm_event_loop_source; - -/** - * @brief The fd source handler - */ -typedef tdm_error (*tdm_event_loop_fd_handler)(int fd, tdm_event_loop_mask mask, void *user_data); - -/** - * @brief The timer source handler - */ -typedef tdm_error (*tdm_event_loop_timer_handler)(void *user_data); - #ifdef __cplusplus } #endif diff --git a/packaging/libtdm.spec b/packaging/libtdm.spec index 28bb692..52c83b0 100644 --- a/packaging/libtdm.spec +++ b/packaging/libtdm.spec @@ -120,6 +120,7 @@ rm -f %{_unitdir_user}/default.target.wants/tdm-socket-user.path %manifest %{name}.manifest %defattr(-,root,root,-) %{_includedir}/tdm_client.h +%{_includedir}/tdm_client_types.h %{_libdir}/pkgconfig/libtdm-client.pc %{_libdir}/libtdm-client.so diff --git a/protocol/tdm.xml b/protocol/tdm.xml index 7564670..308fe8c 100644 --- a/protocol/tdm.xml +++ b/protocol/tdm.xml @@ -8,42 +8,33 @@ TDM uses the wayland protocol to communicate between tdm client and tdm server. - - + + + - + - - TDM uses the wayland protocol to communicate between tdm client and tdm server. - + + + + + - - - - - + + + - - + + + - - When wl_tdm_client.wait_vblank is called, wl_tdm_vblank resource is created. - And when TDM server gets the HW vblank, TDM server will send the 'done' - event of wl_tdm_vblank interface to let the TDM client get the HW vblank. - If 'sw_timer' param is 1, TDM server will use the SW timer in case of DPMS off. - And TDM server will create the fake vblank event and send it to TDM client. - Otherwise, TDM server will return 'dpms_off' error. - + + - - - - - @@ -51,11 +42,40 @@ + + + + + + + + + + + + + + + + + + + When wl_tdm_vblank.wait_vblank is called, TDM server will send the wl_tdm_vblank.done + event which is aligned with HW vblank. Even if DPMS is off, it will send + wl_tdm_vblank.done events by using SW timer. + + + + + + + + diff --git a/src/Makefile.am b/src/Makefile.am index 245c2fa..4d62318 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,6 +14,7 @@ libtdm_la_SOURCES = \ $(top_srcdir)/protocol/tdm-protocol.c \ tdm_backend.c \ tdm_server.c \ + tdm_vblank.c \ tdm_event_loop.c \ tdm_thread.c \ tdm_helper.c \ diff --git a/src/tdm_display.c b/src/tdm_display.c index 2c09442..c3bd904 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -491,7 +491,7 @@ tdm_output_cb_status(tdm_output *output_backend, tdm_output_conn_status status, tdm_output_call_change_handler_internal(private_output, &private_output->change_handler_list_sub, TDM_OUTPUT_CHANGE_CONNECTION, - value); + value, 0); ret = tdm_thread_send_cb(private_display->private_loop, &output_status.base); TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE); @@ -506,7 +506,7 @@ tdm_output_cb_status(tdm_output *output_backend, tdm_output_conn_status status, tdm_output_call_change_handler_internal(private_output, &private_output->change_handler_list_main, TDM_OUTPUT_CHANGE_CONNECTION, - value); + value, 0); } EXTERN tdm_error @@ -521,12 +521,6 @@ tdm_output_add_change_handler(tdm_output *output, pthread_mutex_lock(&private_display->lock); - if (!private_output->regist_change_cb) { - _pthread_mutex_unlock(&private_display->lock); - TDM_ERR("not implemented!!"); - return TDM_ERROR_NOT_IMPLEMENTED; - } - change_handler = calloc(1, sizeof(tdm_private_change_handler)); if (!change_handler) { TDM_ERR("failed: alloc memory"); @@ -1138,7 +1132,13 @@ tdm_output_set_dpms(tdm_output *output, tdm_output_dpms dpms_value) tdm_output_call_change_handler_internal(private_output, &private_output->change_handler_list_main, TDM_OUTPUT_CHANGE_DPMS, - value); + value, 0); + + //TODO: safe? We can't take care of the user_data of callback functions. + tdm_output_call_change_handler_internal(private_output, + &private_output->change_handler_list_sub, + TDM_OUTPUT_CHANGE_DPMS, + value, 1); } _pthread_mutex_unlock(&private_display->lock); @@ -1192,7 +1192,8 @@ INTERN void tdm_output_call_change_handler_internal(tdm_private_output *private_output, struct list_head *change_handler_list, tdm_output_change_type type, - tdm_value value) + tdm_value value, + int no_check_thread_id) { tdm_private_display *private_display; tdm_private_change_handler *change_handler = NULL; @@ -1214,7 +1215,7 @@ tdm_output_call_change_handler_internal(tdm_private_output *private_output, return; LIST_FOR_EACH_ENTRY(change_handler, change_handler_list, link) { - if (change_handler->owner_tid != syscall(SYS_gettid)) + if (!no_check_thread_id && change_handler->owner_tid != syscall(SYS_gettid)) TDM_NEVER_GET_HERE(); _pthread_mutex_unlock(&private_display->lock); diff --git a/src/tdm_private.h b/src/tdm_private.h index bdb1a20..05fb7a9 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -331,7 +332,8 @@ void tdm_output_call_change_handler_internal(tdm_private_output *private_output, struct list_head *change_handler_list, tdm_output_change_type type, - tdm_value value); + tdm_value value, + int no_check_thread_id); tdm_private_pp * tdm_pp_create_internal(tdm_private_display *private_display, tdm_error *error); @@ -511,6 +513,28 @@ tdm_error tdm_display_update_output(tdm_private_display *private_display, tdm_output *output_backend, int pipe); + +/** + * @brief The tdm vblank object + */ +typedef void tdm_vblank; + +typedef void (*tdm_vblank_handler)(tdm_vblank *vblank, tdm_error error, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, void *user_data); + +tdm_vblank* +tdm_vblank_create(tdm_display *dpy, tdm_output *output, tdm_error *error); +void +tdm_vblank_destroy(tdm_vblank *vblank); +tdm_error +tdm_vblank_set_fps(tdm_vblank *vblank, unsigned int fps); +tdm_error +tdm_vblank_set_offset(tdm_vblank *vblank, int offset); +tdm_error +tdm_vblank_set_enable_fake(tdm_vblank *vblank, unsigned int enable_fake); +tdm_error +tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec, unsigned int interval, tdm_vblank_handler func, void *user_data); + #ifdef __cplusplus } #endif diff --git a/src/tdm_server.c b/src/tdm_server.c index b71c2a2..bb8ff6c 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -52,173 +52,150 @@ struct _tdm_private_server { tdm_private_loop *private_loop; - struct list_head client_list; - struct list_head vblank_list; + struct list_head output_list; + struct list_head wait_list; }; -typedef struct _tdm_server_client_info { +typedef struct _tdm_server_output_info { struct list_head link; tdm_private_server *private_server; struct wl_resource *resource; - - tdm_output *vblank_output; - double vblank_gap; - unsigned int last_tv_sec; - unsigned int last_tv_usec; -} tdm_server_client_info; + tdm_output *output; + struct list_head vblank_list; +} tdm_server_output_info; typedef struct _tdm_server_vblank_info { struct list_head link; - tdm_server_client_info *client_info; + tdm_server_output_info *output_info; struct wl_resource *resource; - unsigned int req_sec; - unsigned int req_usec; - - tdm_event_loop_source *timer_source; - unsigned int timer_target_sec; - unsigned int timer_target_usec; + tdm_vblank *vblank; } tdm_server_vblank_info; -static tdm_private_server *keep_private_server; -static int tdm_debug_server; - -static void -_tdm_server_send_done(tdm_server_vblank_info *vblank_info, unsigned int sequence, - unsigned int tv_sec, unsigned int tv_usec); +typedef struct _tdm_server_wait_info { + struct list_head link; + tdm_server_vblank_info *vblank_info; -static tdm_error -_tdm_server_cb_timer(void *user_data) -{ - tdm_server_vblank_info *vblank_info = (tdm_server_vblank_info*)user_data; + unsigned int req_id; +} tdm_server_wait_info; - _tdm_server_send_done(vblank_info, 0, - vblank_info->timer_target_sec, - vblank_info->timer_target_usec); +static tdm_private_server *keep_private_server; - return TDM_ERROR_NONE; -} +static void destroy_wait(tdm_server_wait_info *wait_info); -static tdm_error -_tdm_server_update_timer(tdm_server_vblank_info *vblank_info, int interval) +static tdm_output* +_tdm_server_find_output(tdm_private_server *private_server, const char *name) { - tdm_server_client_info *client_info = vblank_info->client_info; - tdm_private_server *private_server = client_info->private_server; tdm_private_loop *private_loop = private_server->private_loop; - unsigned long last, prev_req, req, curr, next; - unsigned int ms_delay; - tdm_error ret; + tdm_output *found = NULL; - ret = tdm_display_lock(private_loop->dpy); - TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret); - - if (!vblank_info->timer_source) { - vblank_info->timer_source = - tdm_event_loop_add_timer_handler(private_loop->dpy, - _tdm_server_cb_timer, - vblank_info, - NULL); - if (!vblank_info->timer_source) { - TDM_ERR("couldn't add timer"); - tdm_display_unlock(private_loop->dpy); - return TDM_ERROR_OPERATION_FAILED; - } - } + if (!strncasecmp(name, "primary", 7) || !strncasecmp(name, "default", 7)) + found = tdm_display_get_output(private_loop->dpy, 0, NULL); - last = (unsigned long)client_info->last_tv_sec * 1000000 + client_info->last_tv_usec; - req = (unsigned long)vblank_info->req_sec * 1000000 + vblank_info->req_usec; - curr = tdm_helper_get_time_in_micros(); + if (!found) { + int count = 0, i; - prev_req = last + (unsigned int)((req - last) / client_info->vblank_gap) * client_info->vblank_gap; - next = prev_req + (unsigned long)(client_info->vblank_gap * interval); + tdm_display_get_output_count(private_loop->dpy, &count); - while (next < curr) - next += (unsigned long)client_info->vblank_gap; + for (i = 0; i < count; i++) { + tdm_output *output = tdm_display_get_output(private_loop->dpy, i, NULL); + tdm_output_conn_status status; + const char *model = NULL; + tdm_error ret; - TDM_DBG("last(%.6lu) req(%.6lu) curr(%.6lu) prev_req(%.6lu) next(%.6lu)", - last, req, curr, prev_req, next); + ret = tdm_output_get_conn_status(output, &status); + if (ret || status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) + continue; - ms_delay = (unsigned int)ceil((double)(next - curr) / 1000); - if (ms_delay == 0) - ms_delay = 1; + ret = tdm_output_get_model_info(output, NULL, &model, NULL); + if (ret || !model) + continue; - TDM_DBG("delay(%lu) ms_delay(%d)", next - curr, ms_delay); + if (strncmp(model, name, TDM_NAME_LEN)) + continue; - ret = tdm_event_loop_source_timer_update(vblank_info->timer_source, ms_delay); - if (ret != TDM_ERROR_NONE) { - tdm_event_loop_source_remove(vblank_info->timer_source); - vblank_info->timer_source = NULL; - tdm_display_unlock(private_loop->dpy); - TDM_ERR("couldn't update timer"); - return TDM_ERROR_OPERATION_FAILED; + found = output; + break; + } } - TDM_DBG("timer tick: %d us", (int)(next - last)); - - vblank_info->timer_target_sec = next / 1000000; - vblank_info->timer_target_usec = next % 1000000; - - tdm_display_unlock(private_loop->dpy); - - return TDM_ERROR_NONE; + return found; } static void -_tdm_server_send_done(tdm_server_vblank_info *vblank_info, unsigned int sequence, - unsigned int tv_sec, unsigned int tv_usec) +_tdm_server_send_done(tdm_server_wait_info *wait_info, tdm_error error, + unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec) { - tdm_server_vblank_info *found; - tdm_server_client_info *client_info; - unsigned long vtime = (tv_sec * 1000000) + tv_usec; - unsigned long curr = tdm_helper_get_time_in_micros(); + tdm_server_wait_info *found; + tdm_server_vblank_info *vblank_info; if (!keep_private_server) return; - LIST_FIND_ITEM(vblank_info, &keep_private_server->vblank_list, - tdm_server_vblank_info, link, found); + LIST_FIND_ITEM(wait_info, &keep_private_server->wait_list, + tdm_server_wait_info, link, found); if (!found) { - TDM_DBG("vblank_info(%p) is destroyed", vblank_info); + TDM_DBG("wait_info(%p) is destroyed", wait_info); return; } - client_info = vblank_info->client_info; - client_info->last_tv_sec = tv_sec; - client_info->last_tv_usec = tv_usec; - - TDM_DBG("wl_tdm_vblank@%d done. tv(%lu) curr(%lu)", - wl_resource_get_id(vblank_info->resource), vtime, curr); + TDM_DBG("req_id(%d) done", wait_info->req_id); - if (tdm_debug_server) { - if (curr - vtime > 1000) /* 1ms */ - TDM_WRN("delay: %d us", (int)(curr - vtime)); - } + vblank_info = wait_info->vblank_info; + wl_tdm_vblank_send_done(vblank_info->resource, wait_info->req_id, + sequence, tv_sec, tv_usec, error); + destroy_wait(wait_info); +} - wl_tdm_vblank_send_done(vblank_info->resource, sequence, tv_sec, tv_usec); - wl_resource_destroy(vblank_info->resource); +static void +_tdm_server_cb_vblank(tdm_vblank *vblank, tdm_error error, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, void *user_data) +{ + _tdm_server_send_done((tdm_server_wait_info*)user_data, error, sequence, tv_sec, tv_usec); } static void -_tdm_server_cb_output_vblank(tdm_output *output, unsigned int sequence, - unsigned int tv_sec, unsigned int tv_usec, - void *user_data) +_tdm_server_cb_output_change(tdm_output *output, tdm_output_change_type type, + tdm_value value, void *user_data) { - tdm_server_vblank_info *vblank_info = (tdm_server_vblank_info*)user_data; + tdm_server_output_info *output_info = user_data; + + TDM_RETURN_IF_FAIL(output_info != NULL); + + switch (type) { + case TDM_OUTPUT_CHANGE_DPMS: + wl_tdm_output_send_dpms(output_info->resource, value.u32); + break; + case TDM_OUTPUT_CHANGE_CONNECTION: + wl_tdm_output_send_connection(output_info->resource, value.u32); + break; + default: + break; + } +} - _tdm_server_send_done(vblank_info, sequence, tv_sec, tv_usec); +static void +destroy_wait(tdm_server_wait_info *wait_info) +{ + LIST_DEL(&wait_info->link); + free(wait_info); } static void destroy_vblank_callback(struct wl_resource *resource) { tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource); + tdm_server_wait_info *w = NULL, *ww = NULL; + + TDM_RETURN_IF_FAIL(vblank_info != NULL); - if (vblank_info->timer_source) { - tdm_private_server *private_server = vblank_info->client_info->private_server; + if (vblank_info->vblank) + tdm_vblank_destroy(vblank_info->vblank); - tdm_display_lock(private_server->private_loop->dpy); - tdm_event_loop_source_remove(vblank_info->timer_source); - tdm_display_unlock(private_server->private_loop->dpy); + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &keep_private_server->wait_list, link) { + if (w->vblank_info == vblank_info) { + destroy_wait(w); + } } LIST_DEL(&vblank_info->link); @@ -226,213 +203,217 @@ destroy_vblank_callback(struct wl_resource *resource) } static void -_tdm_server_client_cb_destroy(struct wl_client *client, struct wl_resource *resource) +_tdm_server_vblank_cb_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void -_tdm_server_client_cb_wait_vblank(struct wl_client *client, - struct wl_resource *resource, - uint32_t id, const char *name, - int32_t sw_timer, int32_t interval, - uint32_t req_sec, uint32_t req_usec) +_tdm_server_vblank_cb_set_fps(struct wl_client *client, struct wl_resource *resource, uint32_t fps) { - tdm_server_client_info *client_info = wl_resource_get_user_data(resource); - tdm_private_server *private_server = client_info->private_server; - tdm_private_loop *private_loop = private_server->private_loop; - tdm_server_vblank_info *vblank_info; - struct wl_resource *vblank_resource; - tdm_output *found = NULL; - tdm_output_dpms dpms_value = TDM_OUTPUT_DPMS_ON; - tdm_error ret; - const char *model; - - TDM_DBG("The tdm client requests vblank"); + tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource); - if (tdm_debug_server) { - unsigned long curr = tdm_helper_get_time_in_micros(); - unsigned long reqtime = (req_sec * 1000000) + req_usec; - if (curr - reqtime > 1000) /* 1ms */ - TDM_WRN("delay(req): %d us", (int)(curr - reqtime)); - } + tdm_vblank_set_fps(vblank_info->vblank, fps); +} - if (client_info->vblank_output) { - model = NULL; - ret = tdm_output_get_model_info(client_info->vblank_output, NULL, &model, NULL); - if (model && !strncmp(model, name, TDM_NAME_LEN)) - found = client_info->vblank_output; - } +static void +_tdm_server_vblank_cb_set_offset(struct wl_client *client, struct wl_resource *resource, int32_t offset) +{ + tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource); - if (!strncmp(name, "primary", TDM_NAME_LEN)) - found = tdm_display_get_output(private_loop->dpy, 0, NULL); + tdm_vblank_set_offset(vblank_info->vblank, offset); +} - if (!found) { - int count = 0, i; +static void +_tdm_server_vblank_cb_set_enable_fake(struct wl_client *client, struct wl_resource *resource, uint32_t enable_fake) +{ + tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource); - tdm_display_get_output_count(private_loop->dpy, &count); + tdm_vblank_set_enable_fake(vblank_info->vblank, enable_fake); +} - for (i = 0; i < count; i++) { - tdm_output *output = tdm_display_get_output(private_loop->dpy, i, NULL); - tdm_output_conn_status status; +static void +_tdm_server_vblank_cb_wait_vblank(struct wl_client *client, struct wl_resource *resource, + uint32_t interval, uint32_t req_id, uint32_t req_sec, uint32_t req_usec) +{ + tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource); + tdm_server_output_info *output_info = vblank_info->output_info; + tdm_private_server *private_server = output_info->private_server; + tdm_server_wait_info *wait_info; + tdm_error ret; - ret = tdm_output_get_conn_status(output, &status); - if (ret || status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) - continue; + wait_info = calloc(1, sizeof *wait_info); + if (!wait_info) { + TDM_ERR("alloc failed"); + ret = TDM_ERROR_OUT_OF_MEMORY; + goto wait_failed; + } - model = NULL; - ret = tdm_output_get_model_info(output, NULL, &model, NULL); - if (ret || !model) - continue; + LIST_ADDTAIL(&wait_info->link, &private_server->wait_list); + wait_info->vblank_info = vblank_info; + wait_info->req_id = req_id; - if (strncmp(model, name, TDM_NAME_LEN)) - continue; + TDM_DBG("req_id(%d) wait", req_id); - found = output; - break; - } - } + ret = tdm_vblank_wait(vblank_info->vblank, req_sec, req_usec, interval, _tdm_server_cb_vblank, wait_info); + TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, wait_failed); - if (!found) { - wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_INVALID_NAME, - "There is no '%s' output", name); - TDM_ERR("There is no '%s' output", name); - return; - } + return; +wait_failed: + wl_tdm_vblank_send_done(vblank_info->resource, req_id, 0, 0, 0, ret); +} - if (client_info->vblank_output != found) { - const tdm_output_mode *mode = NULL; +static const struct wl_tdm_vblank_interface tdm_vblank_implementation = { + _tdm_server_vblank_cb_destroy, + _tdm_server_vblank_cb_set_fps, + _tdm_server_vblank_cb_set_offset, + _tdm_server_vblank_cb_set_enable_fake, + _tdm_server_vblank_cb_wait_vblank, +}; - client_info->vblank_output = found; +static void +_tdm_server_output_cb_destroy(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} - tdm_output_get_mode(client_info->vblank_output, &mode); - if (!mode || mode->vrefresh <= 0) { - wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_OPERATION_FAILED, - "couldn't get mode of %s", name); - TDM_ERR("couldn't get mode of %s", name); - return; - } +static void +_tdm_server_output_cb_create_vblank(struct wl_client *client, struct wl_resource *resource, uint32_t id) +{ + tdm_server_output_info *output_info = wl_resource_get_user_data(resource); + tdm_private_server *private_server = output_info->private_server; + tdm_private_loop *private_loop = private_server->private_loop; + struct wl_resource *vblank_resource; + tdm_vblank *vblank; + tdm_server_vblank_info *vblank_info; - client_info->vblank_gap = (double)1000000 / mode->vrefresh; - TDM_INFO("vblank_gap(%.6lf)", client_info->vblank_gap); + vblank_resource = + wl_resource_create(client, &wl_tdm_vblank_interface, + wl_resource_get_version(resource), id); + if (!vblank_resource) { + wl_resource_post_no_memory(resource); + TDM_ERR("wl_resource_create failed"); + return; } - tdm_output_get_dpms(found, &dpms_value); - - if (dpms_value != TDM_OUTPUT_DPMS_ON && !sw_timer) { - wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_DPMS_OFF, - "dpms '%s'", tdm_dpms_str(dpms_value)); - TDM_ERR("dpms '%s'", tdm_dpms_str(dpms_value)); + vblank = tdm_vblank_create(private_loop->dpy, output_info->output, NULL); + if (!vblank) { + wl_resource_post_no_memory(resource); + wl_resource_destroy(vblank_resource); + TDM_ERR("tdm_vblank_create failed"); return; } - vblank_info = calloc(1, sizeof * vblank_info); + vblank_info = calloc(1, sizeof *vblank_info); if (!vblank_info) { wl_resource_post_no_memory(resource); + wl_resource_destroy(vblank_resource); + tdm_vblank_destroy(vblank); TDM_ERR("alloc failed"); return; } - TDM_DBG("wl_tdm_vblank@%d output(%s) interval(%d)", id, name, interval); - - vblank_resource = - wl_resource_create(client, &wl_tdm_vblank_interface, - wl_resource_get_version(resource), id); - if (!vblank_resource) { - wl_resource_post_no_memory(resource); - TDM_ERR("wl_resource_create failed"); - goto free_info; - } - + LIST_ADDTAIL(&vblank_info->link, &output_info->vblank_list); + vblank_info->output_info = output_info; vblank_info->resource = vblank_resource; - vblank_info->client_info = client_info; - vblank_info->req_sec = req_sec; - vblank_info->req_usec = req_usec; - - if (dpms_value == TDM_OUTPUT_DPMS_ON) { - ret = tdm_output_wait_vblank(found, interval, 0, - _tdm_server_cb_output_vblank, vblank_info); - if (ret != TDM_ERROR_NONE) { - wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_OPERATION_FAILED, - "couldn't wait vblank for %s", name); - TDM_ERR("couldn't wait vblank for %s", name); - goto destroy_resource; - } - } else if (sw_timer) { - ret = _tdm_server_update_timer(vblank_info, interval); - if (ret != TDM_ERROR_NONE) { - wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_OPERATION_FAILED, - "couldn't update timer for %s", name); - TDM_ERR("couldn't update timer for %s", name); - goto destroy_resource; - } - } else { - wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_OPERATION_FAILED, - "bad implementation"); - TDM_NEVER_GET_HERE(); - goto destroy_resource; - } + vblank_info->vblank = vblank; - wl_resource_set_implementation(vblank_resource, NULL, vblank_info, - destroy_vblank_callback); + wl_resource_set_implementation(vblank_resource, &tdm_vblank_implementation, + vblank_info, destroy_vblank_callback); - LIST_ADDTAIL(&vblank_info->link, &private_server->vblank_list); return; -destroy_resource: - wl_resource_destroy(vblank_resource); -free_info: - free(vblank_info); } -static const struct wl_tdm_client_interface tdm_client_implementation = { - _tdm_server_client_cb_destroy, - _tdm_server_client_cb_wait_vblank, +static const struct wl_tdm_output_interface tdm_output_implementation = { + _tdm_server_output_cb_destroy, + _tdm_server_output_cb_create_vblank, }; static void -destroy_client_callback(struct wl_resource *resource) +destroy_output_callback(struct wl_resource *resource) { - tdm_server_client_info *client_info = wl_resource_get_user_data(resource); - LIST_DEL(&client_info->link); - free(client_info); + tdm_server_output_info *output_info = wl_resource_get_user_data(resource); + tdm_server_vblank_info *v = NULL, *vv = NULL; + + TDM_RETURN_IF_FAIL(output_info != NULL); + + tdm_output_remove_change_handler(output_info->output, + _tdm_server_cb_output_change, output_info); + + LIST_FOR_EACH_ENTRY_SAFE(v, vv, &output_info->vblank_list, link) { + wl_resource_destroy(v->resource); + } + + LIST_DEL(&output_info->link); + free(output_info); } static void -_tdm_server_cb_create_client(struct wl_client *client, - struct wl_resource *resource, uint32_t id) +_tdm_server_cb_create_output(struct wl_client *client, struct wl_resource *resource, + const char *name, uint32_t id) { tdm_private_server *private_server = wl_resource_get_user_data(resource); - tdm_server_client_info *client_info; - struct wl_resource *client_resource; + tdm_server_output_info *output_info; + struct wl_resource *output_resource = NULL; + tdm_output *output; + const tdm_output_mode *mode = NULL; + tdm_output_dpms dpms_value; + tdm_output_conn_status status; + + output = _tdm_server_find_output(private_server, name); + if (!output) { + TDM_ERR("There is no '%s' output", name); + wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, + "There is no '%s' output", name); + return; + } - client_info = calloc(1, sizeof * client_info); - if (!client_info) { - wl_resource_post_no_memory(resource); - TDM_ERR("alloc failed"); + tdm_output_get_mode(output, &mode); + if (!mode) { + TDM_ERR("no mode for '%s' output", name); + wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, + "no mode for '%s' output", name); return; } - client_resource = - wl_resource_create(client, &wl_tdm_client_interface, + tdm_output_get_dpms(output, &dpms_value); + tdm_output_get_conn_status(output, &status); + + output_resource = + wl_resource_create(client, &wl_tdm_output_interface, wl_resource_get_version(resource), id); - if (!client_resource) { + if (!output_resource) { wl_resource_post_no_memory(resource); - free(client_info); TDM_ERR("wl_resource_create failed"); return; } - client_info->private_server = private_server; - client_info->resource = client_resource; + output_info = calloc(1, sizeof * output_info); + if (!output_info) { + wl_resource_post_no_memory(resource); + wl_resource_destroy(output_resource); + TDM_ERR("alloc failed"); + return; + } + + LIST_ADDTAIL(&output_info->link, &private_server->output_list); + output_info->private_server = private_server; + output_info->resource = output_resource; + output_info->output = output; + LIST_INITHEAD(&output_info->vblank_list); - wl_resource_set_implementation(client_resource, &tdm_client_implementation, - client_info, destroy_client_callback); + tdm_output_add_change_handler(output, _tdm_server_cb_output_change, output_info); - LIST_ADDTAIL(&client_info->link, &private_server->client_list); + wl_resource_set_implementation(output_resource, &tdm_output_implementation, + output_info, destroy_output_callback); + + wl_tdm_output_send_mode(output_resource, mode->hdisplay, mode->vdisplay, mode->vrefresh); + wl_tdm_output_send_dpms(output_resource, dpms_value); + wl_tdm_output_send_connection(output_resource, status); } static const struct wl_tdm_interface tdm_implementation = { - _tdm_server_cb_create_client, + _tdm_server_cb_create_output, }; static void @@ -456,11 +437,6 @@ INTERN tdm_error tdm_server_init(tdm_private_loop *private_loop) { tdm_private_server *private_server; - const char *debug; - - debug = getenv("TDM_DEBUG_SERVER"); - if (debug && (strstr(debug, "1"))) - tdm_debug_server = 1; if (private_loop->private_server) return TDM_ERROR_NONE; @@ -479,8 +455,8 @@ tdm_server_init(tdm_private_loop *private_loop) return TDM_ERROR_OUT_OF_MEMORY; } - LIST_INITHEAD(&private_server->client_list); - LIST_INITHEAD(&private_server->vblank_list); + LIST_INITHEAD(&private_server->output_list); + LIST_INITHEAD(&private_server->wait_list); if (!wl_global_create(private_loop->wl_display, &wl_tdm_interface, 1, private_server, _tdm_server_bind)) { @@ -499,8 +475,8 @@ tdm_server_init(tdm_private_loop *private_loop) INTERN void tdm_server_deinit(tdm_private_loop *private_loop) { - tdm_server_vblank_info *v = NULL, *vv = NULL; - tdm_server_client_info *c = NULL, *cc = NULL; + tdm_server_output_info *o = NULL, *oo = NULL; + tdm_server_wait_info *w = NULL, *ww = NULL; tdm_private_server *private_server; if (!private_loop->private_server) @@ -508,12 +484,12 @@ tdm_server_deinit(tdm_private_loop *private_loop) private_server = private_loop->private_server; - LIST_FOR_EACH_ENTRY_SAFE(v, vv, &private_server->vblank_list, link) { - wl_resource_destroy(v->resource); + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_server->wait_list, link) { + destroy_wait(w); } - LIST_FOR_EACH_ENTRY_SAFE(c, cc, &private_server->client_list, link) { - wl_resource_destroy(c->resource); + LIST_FOR_EACH_ENTRY_SAFE(o, oo, &private_server->output_list, link) { + wl_resource_destroy(o->resource); } free(private_server); diff --git a/src/tdm_vblank.c b/src/tdm_vblank.c new file mode 100644 index 0000000..f5ec5f9 --- /dev/null +++ b/src/tdm_vblank.c @@ -0,0 +1,879 @@ +/************************************************************************** + * + * libtdm + * + * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Contact: Eunchul Kim , + * JinYoung Jeon , + * Taeheon Kim , + * YoungJun Cho , + * SooChan Lim , + * Boram Park + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * +**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "tdm.h" +#include "tdm_private.h" +#include "tdm_list.h" + +/* CAUTION: + * - tdm vblank doesn't care about thread things. + * - DO NOT use the TDM internal functions here. + * However, the internal function which does lock/unlock the mutex of + * private_display in itself can be called. + * - DO NOT use the tdm_private_display structure here. + */ + +/* TDM vblank + * - aligned by HW vblank + * - use a tdm_event_loop_source object only. + */ + +#define VER(fmt,arg...) TDM_ERR("[%p] "fmt, private_vblank, ##arg) +#define VWR(fmt,arg...) TDM_WRN("[%p] "fmt, private_vblank, ##arg) +#define VIN(fmt,arg...) TDM_INFO("[%p] "fmt, private_vblank, ##arg) +#define VDB(fmt,arg...) TDM_DBG("[%p] "fmt, private_vblank, ##arg) + +typedef struct _tdm_vblank_wait_info tdm_vblank_wait_info; + +typedef struct _tdm_private_vblank { + struct list_head link; + + tdm_display *dpy; + tdm_output *output; + tdm_output_dpms dpms; + unsigned int vrefresh; + + unsigned int check_HW_or_SW; + unsigned int fps; + int offset; + unsigned int enable_fake; + + double vblank_gap; + unsigned int last_seq; + unsigned int last_tv_sec; + unsigned int last_tv_usec; + + /* for HW */ + double HW_vblank_gap; + unsigned int HW_enable; + unsigned int HW_quotient; + struct list_head HW_wait_list; + + /* for SW */ + tdm_event_loop_source *SW_timer; + struct list_head SW_pending_wait_list; + struct list_head SW_wait_list; +#if 0 + tdm_vblank_wait_info *SW_align_wait; + double SW_align_offset; + unsigned int SW_align_sec; + unsigned int SW_align_usec; +#endif +} tdm_private_vblank; + +struct _tdm_vblank_wait_info { + struct list_head link; + struct list_head valid_link; + + unsigned int stamp; + + unsigned int req_sec; + unsigned int req_usec; + unsigned int interval; + + tdm_vblank_handler func; + void *user_data; + tdm_private_vblank *private_vblank; + + /* target_sec can be 0 when last_tv_sec is 0 because we can't calculate + * target_sec without last_tv_sec. So we have to call tdm_output_wait_vblank + * to fill last_tv_sec at the first time. + */ + unsigned int target_sec; + unsigned int target_usec; + unsigned int target_seq; + int target_hw_interval; +}; + +static struct list_head vblank_list; +static struct list_head valid_wait_list; +static unsigned int vblank_list_inited; +static unsigned int stamp = 0; + +static tdm_error _tdm_vblank_wait_SW(tdm_vblank_wait_info *wait_info); +#if 0 +static void _tdm_vblank_sw_timer_align(tdm_private_vblank *private_vblank); +#endif + +#if 0 +static void +_print_list(struct list_head *list) +{ + tdm_vblank_wait_info *w = NULL; + + LIST_FOR_EACH_ENTRY(w, list, link) { + printf(" %d", w->interval); + } + printf("\n"); +} +#endif + +static inline unsigned int +_tdm_vblank_check_valid(tdm_vblank_wait_info *wait_info) +{ + tdm_vblank_wait_info *w = NULL; + + if (!wait_info) + return 0; + + LIST_FOR_EACH_ENTRY(w, &valid_wait_list, valid_link) { + if (w->stamp == wait_info->stamp) + return 1; + } + + return 0; +} + +static inline unsigned int +_tdm_vblank_find_wait(tdm_vblank_wait_info *wait_info, struct list_head *list) +{ + tdm_vblank_wait_info *w = NULL; + + if (!wait_info) + return 0; + + LIST_FOR_EACH_ENTRY(w, list, link) { + if (w->stamp == wait_info->stamp) + return 1; + } + + return 0; +} + +static inline void +_tdm_vblank_insert_wait(tdm_vblank_wait_info *wait_info, struct list_head *list) +{ + tdm_vblank_wait_info *w = NULL; + + LIST_FOR_EACH_ENTRY_REV(w, list, link) { + /* If last_tv_sec == 0, we can't calculate target_sec. */ + if (wait_info->target_sec == 0) { + if (w->interval > wait_info->interval) + continue; + list = &w->link; + break; + } else { + if (w->target_sec > wait_info->target_sec) + continue; + if (w->target_usec > wait_info->target_usec) + continue; + list = &w->link; + } + break; + } + + LIST_ADDTAIL(&wait_info->link, list->next); +} + +static void +_tdm_vblank_change_to_SW(tdm_private_vblank *private_vblank) +{ + tdm_vblank_wait_info *w = NULL, *ww = NULL; + + VIN("Change to SW"); + + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->HW_wait_list, link) { + LIST_DEL(&w->link); + _tdm_vblank_wait_SW(w); + } +} + +static void +_tdm_vblank_free_HW_wait(tdm_private_vblank *private_vblank, tdm_error error, unsigned int call_cb) +{ + tdm_vblank_wait_info *w = NULL, *ww = NULL; + + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->HW_wait_list, link) { + LIST_DEL(&w->link); + LIST_DEL(&w->valid_link); + + if (call_cb && w->func) + w->func(private_vblank, error, 0, 0, 0, w->user_data); + + free(w); + } +} + +static void +_tdm_vblank_cb_output_change(tdm_output *output, tdm_output_change_type type, + tdm_value value, void *user_data) +{ + tdm_private_vblank *private_vblank = user_data; + + TDM_RETURN_IF_FAIL(private_vblank != NULL); + + switch (type) { + case TDM_OUTPUT_CHANGE_DPMS: + if (private_vblank->dpms == value.u32) + break; + VIN("dpms %s", tdm_dpms_str(value.u32)); + private_vblank->dpms = value.u32; + private_vblank->check_HW_or_SW = 1; + if (private_vblank->dpms != TDM_OUTPUT_DPMS_ON) { +#if 0 + if (private_vblank->SW_align_wait) { + LIST_DEL(&private_vblank->SW_align_wait->valid_link); + free(private_vblank->SW_align_wait); + private_vblank->SW_align_wait = NULL; + } +#endif + + if (private_vblank->enable_fake) + _tdm_vblank_change_to_SW(private_vblank); + else + _tdm_vblank_free_HW_wait(private_vblank, TDM_ERROR_DPMS_OFF, 1); + } + break; + case TDM_OUTPUT_CHANGE_CONNECTION: + VIN("output %s", tdm_status_str(value.u32)); + if (value.u32 == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) + _tdm_vblank_free_HW_wait(private_vblank, 0, 0); + break; + default: + break; + } +} + +tdm_vblank* +tdm_vblank_create(tdm_display *dpy, tdm_output *output, tdm_error *error) +{ + tdm_private_vblank *private_vblank; + const tdm_output_mode *mode = NULL; + tdm_output_dpms dpms = TDM_OUTPUT_DPMS_ON; + tdm_error ret; + + TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(dpy != NULL, TDM_ERROR_INVALID_PARAMETER, NULL); + TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(output != NULL, TDM_ERROR_INVALID_PARAMETER, NULL); + + if (error) + *error = TDM_ERROR_NONE; + + if (!vblank_list_inited) { + LIST_INITHEAD(&vblank_list); + LIST_INITHEAD(&valid_wait_list); + vblank_list_inited = 1; + } + + tdm_output_get_mode(output, &mode); + if (!mode) { + if (error) + *error = TDM_ERROR_OPERATION_FAILED; + TDM_ERR("no mode"); + return NULL; + } + + tdm_output_get_dpms(output, &dpms); + + private_vblank = calloc(1, sizeof *private_vblank); + if (!private_vblank) { + if (error) + *error = TDM_ERROR_OUT_OF_MEMORY; + VER("alloc failed"); + return NULL; + } + + tdm_output_add_change_handler(output, _tdm_vblank_cb_output_change, private_vblank); + + private_vblank->dpy = dpy; + private_vblank->output = output; + private_vblank->dpms = dpms; + private_vblank->vrefresh = mode->vrefresh; + private_vblank->HW_vblank_gap = (double)1000000 / private_vblank->vrefresh; + + private_vblank->check_HW_or_SW = 1; + private_vblank->fps = mode->vrefresh; + + LIST_INITHEAD(&private_vblank->HW_wait_list); + + LIST_INITHEAD(&private_vblank->SW_pending_wait_list); + LIST_INITHEAD(&private_vblank->SW_wait_list); + + LIST_ADD(&private_vblank->link, &vblank_list); + + VDB("created. vrefresh(%d) dpms(%d)", + private_vblank->vrefresh, private_vblank->dpms); + + return (tdm_vblank*)private_vblank; +} + +void +tdm_vblank_destroy(tdm_vblank *vblank) +{ + tdm_private_vblank *private_vblank = vblank; + tdm_vblank_wait_info *w = NULL, *ww = NULL; + + if (!private_vblank) + return; + + LIST_DEL(&private_vblank->link); + +#if 0 + if (private_vblank->SW_align_wait) { + LIST_DEL(&private_vblank->SW_align_wait->valid_link); + free(private_vblank->SW_align_wait); + } +#endif + + if (private_vblank->SW_timer) { + if (tdm_display_lock(private_vblank->dpy) == TDM_ERROR_NONE) { + tdm_event_loop_source_remove(private_vblank->SW_timer); + tdm_display_unlock(private_vblank->dpy); + } + } + + tdm_output_remove_change_handler(private_vblank->output, + _tdm_vblank_cb_output_change, private_vblank); + + _tdm_vblank_free_HW_wait(private_vblank, 0, 0); + + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_pending_wait_list, link) { + LIST_DEL(&w->link); + LIST_DEL(&w->valid_link); + free(w); + } + + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_wait_list, link) { + LIST_DEL(&w->link); + LIST_DEL(&w->valid_link); + free(w); + } + + VIN("destroyed"); + + free(private_vblank); +} + +tdm_error +tdm_vblank_set_fps(tdm_vblank *vblank, unsigned int fps) +{ + tdm_private_vblank *private_vblank = vblank; + + TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(fps > 0, TDM_ERROR_INVALID_PARAMETER); + + if (private_vblank->fps == fps) + return TDM_ERROR_NONE; + + private_vblank->fps = fps; + private_vblank->check_HW_or_SW = 1; + + VDB("fps(%d)", private_vblank->fps); + + return TDM_ERROR_NONE; +} + +tdm_error +tdm_vblank_set_offset(tdm_vblank *vblank, int offset) +{ + tdm_private_vblank *private_vblank = vblank; + + TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_INVALID_PARAMETER); + + if (private_vblank->offset == offset) + return TDM_ERROR_NONE; + + private_vblank->offset = offset; + private_vblank->check_HW_or_SW = 1; + + VDB("offset(%d)", private_vblank->offset); + + return TDM_ERROR_NONE; +} + +tdm_error +tdm_vblank_set_enable_fake(tdm_vblank *vblank, unsigned int enable_fake) +{ + tdm_private_vblank *private_vblank = vblank; + + TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_INVALID_PARAMETER); + + if (private_vblank->enable_fake == enable_fake) + return TDM_ERROR_NONE; + + private_vblank->enable_fake = enable_fake; + + VDB("enable_fake(%d)", private_vblank->enable_fake); + + return TDM_ERROR_NONE; +} + +static void +_tdm_vblank_cb_vblank_HW(tdm_output *output, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, + void *user_data) +{ + tdm_vblank_wait_info *wait_info = user_data; + tdm_private_vblank *private_vblank; + + if (!_tdm_vblank_check_valid(wait_info)) { + TDM_DBG("can't find wait(%p) from valid_wait_list", wait_info); + return; + } + + private_vblank = wait_info->private_vblank; + TDM_RETURN_IF_FAIL(private_vblank != NULL); + + if (!_tdm_vblank_find_wait(wait_info, &private_vblank->HW_wait_list)) { + VDB("can't find wait(%p)", wait_info); + return; + } + + LIST_DEL(&wait_info->link); + LIST_DEL(&wait_info->valid_link); + + private_vblank->last_seq = wait_info->target_seq; + private_vblank->last_tv_sec = tv_sec; + private_vblank->last_tv_usec = tv_usec; + + if (wait_info->func) + wait_info->func(private_vblank, TDM_ERROR_NONE, private_vblank->last_seq, + tv_sec, tv_usec, wait_info->user_data); + + VDB("wait(%p) done", wait_info); + + free(wait_info); +} + +static tdm_error +_tdm_vblank_wait_HW(tdm_vblank_wait_info *wait_info) +{ + tdm_private_vblank *private_vblank = wait_info->private_vblank; + tdm_error ret; + + TDM_RETURN_VAL_IF_FAIL(wait_info->target_hw_interval > 0, TDM_ERROR_OPERATION_FAILED); + + _tdm_vblank_insert_wait(wait_info, &private_vblank->HW_wait_list); + + ret = tdm_output_wait_vblank(private_vblank->output, wait_info->target_hw_interval, 0, + _tdm_vblank_cb_vblank_HW, wait_info); + + if (ret != TDM_ERROR_NONE) { + VER("wait(%p) failed", wait_info); + LIST_DEL(&wait_info->link); + return ret; + } + + VDB("wait(%p) waiting", wait_info); + + return TDM_ERROR_NONE; +} + +static tdm_error +_tdm_vblank_cb_vblank_SW(void *user_data) +{ + tdm_private_vblank *private_vblank = user_data; + tdm_vblank_wait_info *first_wait_info = NULL, *w = NULL, *ww = NULL; + + TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_OPERATION_FAILED); + + first_wait_info = container_of(private_vblank->SW_wait_list.next, first_wait_info, link); + TDM_RETURN_VAL_IF_FAIL(first_wait_info != NULL, TDM_ERROR_OPERATION_FAILED); + + VDB("wait(%p) done", first_wait_info); + + private_vblank->last_seq = first_wait_info->target_seq; + private_vblank->last_tv_sec = first_wait_info->target_sec; + private_vblank->last_tv_usec = first_wait_info->target_usec; + + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_wait_list, link) { + if (w->target_sec != first_wait_info->target_sec || + w->target_usec != first_wait_info->target_usec) + break; + + LIST_DEL(&w->link); + LIST_DEL(&w->valid_link); + + if (w->func) + w->func(private_vblank, TDM_ERROR_NONE, w->target_seq, + w->target_sec, w->target_usec, + w->user_data); + + free(w); + } + + return TDM_ERROR_NONE; +} + +static void +_tdm_vblank_cb_vblank_SW_first(tdm_output *output, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, + void *user_data) +{ + tdm_vblank_wait_info *wait_info = user_data; + tdm_private_vblank *private_vblank; + tdm_vblank_wait_info *w = NULL, *ww = NULL; + unsigned int min_interval = 0; + unsigned long last; + + if (!_tdm_vblank_check_valid(wait_info)) + return; + + private_vblank = wait_info->private_vblank; + TDM_RETURN_IF_FAIL(private_vblank != NULL); + + w = container_of((&private_vblank->SW_pending_wait_list)->next, w, link); + TDM_RETURN_IF_FAIL(w != NULL); + + VDB("wait(%p) done", w); + + min_interval = w->interval; + + last = (unsigned long)tv_sec * 1000000 + tv_usec; + last -= private_vblank->offset * 1000; + + private_vblank->last_seq = min_interval; + private_vblank->last_tv_sec = last / 1000000; + private_vblank->last_tv_usec = last % 1000000; + + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_pending_wait_list, link) { + if (w->interval == min_interval) { + LIST_DEL(&w->link); + LIST_DEL(&w->valid_link); + + if (w->func) + w->func(private_vblank, TDM_ERROR_NONE, private_vblank->last_seq, + tv_sec, tv_usec, w->user_data); + free(w); + } else { + LIST_DEL(&w->link); + w->interval -= min_interval; + _tdm_vblank_wait_SW(w); + } + } +} + +static tdm_error +_tdm_vblank_sw_timer_update(tdm_private_vblank *private_vblank) +{ + tdm_vblank_wait_info *first_wait_info = NULL; + unsigned long curr, target; + int ms_delay; + tdm_error ret; + + first_wait_info = container_of(private_vblank->SW_wait_list.next, first_wait_info, link); + curr = tdm_helper_get_time_in_micros(); + target = first_wait_info->target_sec * 1000000 + first_wait_info->target_usec; + + if (target < curr) + ms_delay = 1; + else + ms_delay = ceil((double)(target - curr) / 1000); + + if (ms_delay < 1) + ms_delay = 1; + + VDB("wait(%p) curr(%4lu) target(%4lu) ms_delay(%d)", + first_wait_info, curr, target, ms_delay); + + ret = tdm_display_lock(private_vblank->dpy); + TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret); + + if (!private_vblank->SW_timer) { + private_vblank->SW_timer = + tdm_event_loop_add_timer_handler(private_vblank->dpy, + _tdm_vblank_cb_vblank_SW, + private_vblank, + &ret); + if (!private_vblank->SW_timer) { + tdm_display_unlock(private_vblank->dpy); + VER("couldn't add timer"); + return ret; + } + VIN("Use SW vblank"); + } + + ret = tdm_event_loop_source_timer_update(private_vblank->SW_timer, ms_delay); + if (ret != TDM_ERROR_NONE) { + tdm_display_unlock(private_vblank->dpy); + VER("couldn't update timer"); + return ret; + } + + tdm_display_unlock(private_vblank->dpy); + + return TDM_ERROR_NONE; +} + +#if 0 +static void +_tdm_vblank_cb_vblank_align(tdm_output *output, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, + void *user_data) +{ + tdm_vblank_wait_info *align_info = user_data; + tdm_private_vblank *private_vblank; + unsigned int diff_sec, diff_usec; + + if (!_tdm_vblank_check_valid(align_info)) + return; + + private_vblank = align_info->private_vblank; + TDM_RETURN_IF_FAIL(private_vblank != NULL); + + private_vblank->SW_align_wait = NULL; + private_vblank->SW_align_sec = tv_sec; + private_vblank->SW_align_usec = tv_usec; + + LIST_DEL(&align_info->valid_link); + + if (tv_usec > align_info->req_usec) { + diff_usec = tv_usec - align_info->req_usec; + diff_sec = tv_sec - align_info->req_sec; + } else { + diff_usec = 1000000 + tv_usec - align_info->req_usec; + diff_sec = tv_sec - align_info->req_sec - 1; + } + + private_vblank->SW_align_offset = (double)(1000000 - diff_sec * 1000000 - diff_usec) / private_vblank->vrefresh; + + free(align_info); + + /* align vblank continously only if non HW and DPMS on */ + if (!private_vblank->HW_enable && private_vblank->dpms == TDM_OUTPUT_DPMS_ON) + _tdm_vblank_sw_timer_align(private_vblank); +} + +static void +_tdm_vblank_sw_timer_align(tdm_private_vblank *private_vblank) +{ + tdm_vblank_wait_info *align_info; + unsigned long curr; + tdm_error ret; + + if (private_vblank->SW_align_wait) + return; + + TDM_RETURN_IF_FAIL(private_vblank->dpms == TDM_OUTPUT_DPMS_ON); + + align_info = calloc(1, sizeof *align_info); + if (!align_info) { + VER("alloc failed"); + return; + } + + LIST_ADDTAIL(&align_info->valid_link, &valid_wait_list); + align_info->stamp = ++stamp; + align_info->private_vblank = private_vblank; + + curr = tdm_helper_get_time_in_micros(); + align_info->req_sec = curr / 1000000; + align_info->req_usec = curr % 1000000; + + ret = tdm_output_wait_vblank(private_vblank->output, private_vblank->vrefresh, 0, + _tdm_vblank_cb_vblank_align, align_info); + if (ret != TDM_ERROR_NONE) { + LIST_DEL(&align_info->valid_link); + free(align_info); + return; + } + + private_vblank->SW_align_wait = align_info; +} +#endif + +static tdm_error +_tdm_vblank_wait_SW(tdm_vblank_wait_info *wait_info) +{ + tdm_private_vblank *private_vblank = wait_info->private_vblank; + tdm_error ret; + + if (private_vblank->last_tv_sec == 0 && private_vblank->dpms == TDM_OUTPUT_DPMS_ON) { + unsigned int do_wait = LIST_IS_EMPTY(&private_vblank->SW_pending_wait_list); + _tdm_vblank_insert_wait(wait_info, &private_vblank->SW_pending_wait_list); + if (do_wait) { + ret = tdm_output_wait_vblank(private_vblank->output, 1, 0, + _tdm_vblank_cb_vblank_SW_first, wait_info); + if (ret != TDM_ERROR_NONE) { + LIST_DEL(&wait_info->link); + return ret; + } + VDB("wait(%p) waiting", wait_info); + } + return TDM_ERROR_NONE; + } + + TDM_RETURN_VAL_IF_FAIL(wait_info->target_sec > 0, TDM_ERROR_OPERATION_FAILED); + + _tdm_vblank_insert_wait(wait_info, &private_vblank->SW_wait_list); + + ret = _tdm_vblank_sw_timer_update(private_vblank); + if (ret != TDM_ERROR_NONE) { + VER("couldn't update sw timer"); + return ret; + } + + return TDM_ERROR_NONE; +} + +static void +_tdm_vblank_calculate_target(tdm_vblank_wait_info *wait_info) +{ + tdm_private_vblank *private_vblank = wait_info->private_vblank; + unsigned long last, prev, req, curr, target; + unsigned int skip = 0; + + curr = tdm_helper_get_time_in_micros(); + + if (!private_vblank->HW_enable) { + if (private_vblank->last_tv_sec == 0) { + /* If last == 0 and DPMS == on, we will use HW vblank to sync with HW vblank. */ + if (private_vblank->dpms == TDM_OUTPUT_DPMS_ON) { + return; + } else { + private_vblank->last_tv_sec = curr / 1000000; + private_vblank->last_tv_usec = curr % 1000000; + } + } + } + + /* last can be 0 when HW enable. But it doesn't matter if HW enable. */ + if (!private_vblank->HW_enable) + TDM_RETURN_IF_FAIL(private_vblank->last_tv_sec != 0); + + last = (unsigned long)private_vblank->last_tv_sec * 1000000 + private_vblank->last_tv_usec; + req = (unsigned long)wait_info->req_sec * 1000000 + wait_info->req_usec; + skip = (unsigned int)((req - last) / private_vblank->vblank_gap); + prev = last + skip * private_vblank->vblank_gap; + + if (private_vblank->last_seq == 0) + skip = 0; + + skip += wait_info->interval; + + if (private_vblank->HW_enable) { + unsigned int hw_skip = (unsigned int)((curr - prev) / private_vblank->HW_vblank_gap); + + wait_info->target_hw_interval = wait_info->interval * private_vblank->HW_quotient; + wait_info->target_hw_interval -= hw_skip; + + if (wait_info->target_hw_interval < 1) + wait_info->target_hw_interval = 1; + + target = prev + wait_info->target_hw_interval * private_vblank->HW_vblank_gap; + } else { + target = prev + (unsigned long)(private_vblank->vblank_gap * wait_info->interval); + + while (target < curr) { + target += (unsigned long)private_vblank->vblank_gap; + skip++; + } + } + + VDB("target_seq(%d) last_seq(%d) skip(%d)", + wait_info->target_seq, private_vblank->last_seq, skip); + +#if 0 + target -= (private_vblank->SW_align_offset * skip * private_vblank->HW_quotient); +#endif + + wait_info->target_seq = private_vblank->last_seq + skip; + wait_info->target_sec = target / 1000000; + wait_info->target_usec = target % 1000000; + + VDB("wait(%p) last(%4lu) req(%4lu) prev(%4lu) curr(%4lu) skip(%d) hw_interval(%d) target(%4lu,%4lu)", + wait_info, last, req - last, prev - last, curr - last, + skip, wait_info->target_hw_interval, target, target - last); +} + +tdm_error +tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec, + unsigned int interval, tdm_vblank_handler func, void *user_data) +{ + tdm_private_vblank *private_vblank = vblank; + tdm_vblank_wait_info *wait_info; + tdm_error ret; + + TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER); + + if (private_vblank->dpms != TDM_OUTPUT_DPMS_ON && !private_vblank->enable_fake) { + VIN("can't wait a vblank because of DPMS off"); + return TDM_ERROR_DPMS_OFF; + } + +#if 0 + if (!private_vblank->SW_align_wait && private_vblank->dpms == TDM_OUTPUT_DPMS_ON) + _tdm_vblank_sw_timer_align(private_vblank); +#endif + + if (private_vblank->check_HW_or_SW) { + private_vblank->check_HW_or_SW = 0; + private_vblank->vblank_gap = (double)1000000 / private_vblank->fps; + private_vblank->HW_quotient = private_vblank->vrefresh / private_vblank->fps; + + if (private_vblank->dpms == TDM_OUTPUT_DPMS_ON && + !(private_vblank->vrefresh % private_vblank->fps)) { + private_vblank->HW_enable = 1; + VIN("Use HW vblank"); + } else { + private_vblank->HW_enable = 0; + VIN("Use SW vblank"); + } + } + + wait_info = calloc(1, sizeof *wait_info); + if (!wait_info) { + VER("alloc failed"); + return TDM_ERROR_OUT_OF_MEMORY; + } + + LIST_ADDTAIL(&wait_info->valid_link, &valid_wait_list); + wait_info->stamp = ++stamp; + wait_info->req_sec = req_sec; + wait_info->req_usec = req_usec; + wait_info->interval = interval; + wait_info->func = func; + wait_info->user_data = user_data; + wait_info->private_vblank = private_vblank; + + _tdm_vblank_calculate_target(wait_info); + + if (private_vblank->HW_enable) + ret = _tdm_vblank_wait_HW(wait_info); + else + ret = _tdm_vblank_wait_SW(wait_info); + + if (ret != TDM_ERROR_NONE) { + LIST_DEL(&wait_info->valid_link); + free(wait_info); + return ret; + } + + return TDM_ERROR_NONE; +} diff --git a/tools/Makefile.am b/tools/Makefile.am index 4863833..05edfb5 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -8,7 +8,8 @@ tdm_test_client_LDFLAGS = ${LDFLAGS} tdm_test_client_CFLAGS = \ $(TDM_CFLAGS) \ -I$(top_srcdir)/include \ - -I$(top_srcdir)/client + -I$(top_srcdir)/client \ + -I$(top_srcdir)/src tdm_test_client_LDADD = \ $(TDM_LIBS) \ $(top_builddir)/client/libtdm-client.la diff --git a/tools/tdm_test_client.c b/tools/tdm_test_client.c index 7268628..2b61c03 100644 --- a/tools/tdm_test_client.c +++ b/tools/tdm_test_client.c @@ -34,72 +34,303 @@ **************************************************************************/ #include +#include #include #include #include #include #include +#include "tdm_macro.h" + #include #include -static int +int tdm_debug; + +typedef struct _tdm_test_client_arg { + char output_name[512]; + int fps; + int sync; + int interval; + int offset; + int enable_fake; +} tdm_test_client_arg; + +typedef struct _tdm_test_client { + tdm_test_client_arg args; + + int do_query; + int do_vblank; + int waiting; + + tdm_client *client; +} tdm_test_client; + +struct typestrings { + int type; + char string[512]; +}; + +struct optstrings { + int type; + char opt[512]; + char desc[512]; + char arg[512]; + char ex[512]; +}; + +enum { + OPT_QRY, + OPT_TST, + OPT_GNR, +}; + +static struct typestrings typestrs[] = { + {OPT_QRY, "Query"}, + {OPT_TST, "Test"}, + {OPT_GNR, "General"}, +}; + +static struct optstrings optstrs[] = { + {OPT_QRY, "qo", "output objects info", "", "primary"}, + {OPT_TST, "v", "vblank test", "[,][@][#][+][*fake]", "primary,0@60#1+0*1"}, +}; + +#define DELIM "!@#^&*+-|," + +static char* +strtostr(char *buf, int len, char *str, char *delim) +{ + char *end; + end = strpbrk(str, delim); + if (end) + len = ((end - str + 1) < len) ? (end - str + 1) : len; + else { + int l = strlen(str); + len = ((l + 1) < len) ? (l + 1) : len; + } + snprintf(buf, len, "%s", str); + return str + len - 1; +} + +static void +usage(char *app_name) +{ + int type_size = sizeof(typestrs) / sizeof(struct typestrings); + int opt_size = sizeof(optstrs) / sizeof(struct optstrings); + int t; + + printf("usage: %s \n\n", app_name); + + for (t = 0; t < type_size; t++) { + int o, f = 1; + + for (o = 0; o < opt_size; o++) + if (optstrs[o].type == typestrs[t].type) { + if (f == 1) + printf(" %s options:\n\n", typestrs[t].string); + printf("\t-%s\t%s\n", optstrs[o].opt, optstrs[o].desc); + printf("\t\t%s\n", optstrs[o].arg); + printf("\t\tex) %s\n", optstrs[o].ex); + f = 0; + } + } + + exit(0); +} + +//"" +static void +parse_arg_qo(tdm_test_client *data, char *p) +{ + strtostr(data->args.output_name, 512, p, DELIM); +} + +//"[,][@][#][+][*fake]" +static void +parse_arg_v(tdm_test_client *data, char *p) +{ + char *end = p; + + end = strtostr(data->args.output_name, 512, p, DELIM); + + if (*end == ',') { + p = end + 1; + data->args.sync = strtol(p, &end, 10); + } + + if (*end == '@') { + p = end + 1; + data->args.fps = strtol(p, &end, 10); + } + + if (*end == '#') { + p = end + 1; + data->args.interval = strtol(p, &end, 10); + } + + if (*end == '+' || *end == '-') { + p = end; + data->args.offset = strtol(p, &end, 10); + } + + if (*end == '*') { + p = end + 1; + data->args.enable_fake= strtol(p, &end, 10); + } +} + +static void +parse_args(tdm_test_client *data, int argc, char *argv[]) +{ + int size = sizeof(optstrs) / sizeof(struct optstrings); + int i, j = 0; + + if (argc < 2) { + usage(argv[0]); + exit(1); + } + + memset(data, 0, sizeof *data); + data->args.interval = 1; + + for (i = 1; i < argc; i++) { + for (j = 0; j < size; j++) { + if (!strncmp(argv[i]+1, "qo", 512)) { + data->do_query = 1; + parse_arg_qo(data, argv[++i]); + break; + } else if (!strncmp(argv[i]+1, "v", 512)) { + data->do_vblank = 1; + parse_arg_v(data, argv[++i]); + break; + } else { + usage(argv[0]); + exit(1); + } + } + } +} + +static unsigned long get_time_in_micros(void) { struct timespec tp; if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) - return (int)(tp.tv_sec * 1000000) + (tp.tv_nsec / 1000L); + return (unsigned long)(tp.tv_sec * 1000000) + (tp.tv_nsec / 1000L); return 0; } static void -_client_vblank_handler(unsigned int sequence, unsigned int tv_sec, - unsigned int tv_usec, void *user_data) +_client_vblank_handler(tdm_client_vblank *vblank, tdm_error error, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, void *user_data) { - int client, vblank; - static int prev = 0; + tdm_test_client *data = user_data; + unsigned long cur, vbl; + static unsigned long p_vbl = 0; + + data->waiting = 0; + + if (error == TDM_ERROR_DPMS_OFF) { + printf("exit: dpms off\n"); + exit(1); + } + + if (error != TDM_ERROR_NONE) { + printf("exit: error(%d)\n", error); + exit(1); + } - client = get_time_in_micros(); - vblank = tv_sec * 1000000 + tv_usec; + cur = get_time_in_micros(); + vbl = (unsigned long)tv_sec * (unsigned long)1000000 + (unsigned long)tv_usec; - if (vblank - prev > 16966 || vblank - prev < 16366) /* +0.3 ~ -0.3 ms */ - printf("vblank : %d us\n", vblank - prev); + printf("vblank : %ld us vbl(%lu)\n", vbl - p_vbl, vbl); - if (client - vblank > 2000) /* 2ms */ - printf("kernel -> tdm-client: %d us\n", client - vblank); + if (cur - vbl > 2000) /* 2ms */ + printf("kernel -> tdm-client: %ld us\n", cur - vbl); - prev = vblank; + if (tdm_debug) { + static unsigned long p_cur = 0; + printf("vblank event interval: %ld %ld\n", + vbl - p_vbl, cur - p_cur); + p_cur = cur; + } + + p_vbl = vbl; } +static char *conn_str[3] = {"disconnected", "connected", "mode_setted"}; +static char *dpms_str[4] = {"on", "standy", "suspend", "off"}; -int -main(int argc, char *argv[]) +static void +_client_output_handler(tdm_client_output *output, tdm_output_change_type type, + tdm_value value, void *user_data) { - tdm_client *client; - tdm_client_error error; + if (type == TDM_OUTPUT_CHANGE_CONNECTION) + printf("output %s.\n", conn_str[value.u32]); + else if (type == TDM_OUTPUT_CHANGE_DPMS) + printf("dpms %s.\n", dpms_str[value.u32]); +} + +static void +do_query(tdm_test_client *data) +{ + tdm_client_output *output; + tdm_output_conn_status status; + tdm_output_dpms dpms; + unsigned int refresh; + tdm_error error; + + output = tdm_client_get_output(data->client, NULL, &error); + if (error != TDM_ERROR_NONE) { + printf("tdm_client_get_output failed\n"); + return; + } + + tdm_client_output_get_conn_status(output, &status); + tdm_client_output_get_dpms(output, &dpms); + tdm_client_output_get_refresh_rate(output, &refresh); + + printf("tdm_output \"%s\"\n", data->args.output_name); + printf("\tstatus : %s\n", conn_str[status]); + printf("\tdpms : %s\n", dpms_str[dpms]); + printf("\trefresh : %d\n", refresh); +} + +static void +do_vblank(tdm_test_client *data) +{ + tdm_client_output *output; + tdm_client_vblank *vblank = NULL; + tdm_error error; int fd = -1; struct pollfd fds; - int sync; - if (argc < 2) { - printf("Usage: %s\n", argv[0]); - printf("\t%s 0 : non-sync\n", argv[0]); - printf("\t%s 1 : sync\n", argv[0]); - exit(1); + output = tdm_client_get_output(data->client, NULL, &error); + if (error != TDM_ERROR_NONE) { + printf("tdm_client_get_output failed\n"); + return; } - sync = atoi(argv[1]); + tdm_client_output_add_change_handler(output, _client_output_handler, NULL); - client = tdm_client_create(&error); - if (error != TDM_CLIENT_ERROR_NONE) { - printf("tdm_client_create failed\n"); - exit(1); + vblank = tdm_client_output_create_vblank(output, &error); + if (error != TDM_ERROR_NONE) { + printf("tdm_client_output_create_vblank failed\n"); + return; } - error = tdm_client_get_fd(client, &fd); - if (error != TDM_CLIENT_ERROR_NONE || fd < 0) { + tdm_client_vblank_set_enable_fake(vblank, data->args.enable_fake); + tdm_client_vblank_set_sync(vblank, data->args.sync); + if (data->args.fps > 0) + tdm_client_vblank_set_fps(vblank, data->args.fps); + tdm_client_vblank_set_offset(vblank, data->args.offset); + + error = tdm_client_get_fd(data->client, &fd); + if (error != TDM_ERROR_NONE || fd < 0) { printf("tdm_client_get_fd failed\n"); goto done; } @@ -111,14 +342,21 @@ main(int argc, char *argv[]) while (1) { int ret; - error = tdm_client_wait_vblank(client, "unknown-0", 1, 1, sync, - _client_vblank_handler, NULL); - if (error != TDM_CLIENT_ERROR_NONE) { - printf("tdm_client_wait_vblank failed\n"); - goto done; + if (!data->waiting) { + error = tdm_client_vblank_wait(vblank, data->args.interval, + _client_vblank_handler, data); + if (error == TDM_ERROR_DPMS_OFF) { + printf("tdm_client_vblank_wait failed (dpms off)\n"); + goto done; + } + if (error != TDM_ERROR_NONE) { + printf("tdm_client_vblank_wait failed (error: %d)\n", error); + goto done; + } + data->waiting = 1; } - if (!sync) { + if (!data->args.sync) { ret = poll(&fds, 1, -1); if (ret < 0) { if (errno == EINTR || errno == EAGAIN) /* normal case */ @@ -129,13 +367,52 @@ main(int argc, char *argv[]) } } - error = tdm_client_handle_events(client); - if (error != TDM_CLIENT_ERROR_NONE) + error = tdm_client_handle_events(data->client); + if (error != TDM_ERROR_NONE) { printf("tdm_client_handle_events failed\n"); + goto done; + } } } done: - tdm_client_destroy(client); + if (vblank) + tdm_client_vblank_destroy(vblank); +} + +static tdm_test_client ttc_data; + +int +main(int argc, char *argv[]) +{ + tdm_test_client *data = &ttc_data; + tdm_error error; + const char *debug; + + debug = getenv("TDM_DEBUG"); + if (debug && (strstr(debug, "1"))) + tdm_debug = 1; + + parse_args(data, argc, argv); + + printf("sync(%d) fps(%d) interval(%d) offset(%d) enable_fake(%d)\n", + data->args.sync, data->args.fps, data->args.interval, + data->args.offset, data->args.enable_fake); + + data->client = tdm_client_create(&error); + if (error != TDM_ERROR_NONE) { + printf("tdm_client_create failed\n"); + goto done; + } + + if (data->do_query) + do_query(data); + if (data->do_vblank) + do_vblank(data); + +done: + if (data->client) + tdm_client_destroy(data->client); + return 0; } -- 2.7.4