From dbf220bc30ca25a0cd05cb0d1d8626215d7470a7 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 13 Sep 2017 16:12:54 +0900 Subject: [PATCH 01/16] load a dummy module if failed to load a default module Change-Id: I29efec59122b4e6b4a9666b901edf214fd09221b --- src/tdm.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/tdm.c b/src/tdm.c index 7723018..a27247b 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -659,7 +659,8 @@ tdm_display_update(tdm_display *dpy) } #define SUFFIX_MODULE ".so" -#define DEFAULT_MODULE "libtdm-default"SUFFIX_MODULE +#define TDM_DEFAULT_MODULE "libtdm-default"SUFFIX_MODULE +#define TDM_DUMMY_MODULE "libtdm-dummy"SUFFIX_MODULE int tdm_debug_module; int tdm_debug_dump; @@ -844,13 +845,18 @@ _tdm_display_load_module(tdm_private_display *private_display) module_name = getenv("TDM_MODULE"); if (!module_name) - module_name = DEFAULT_MODULE; + module_name = TDM_DEFAULT_MODULE; /* load bufmgr priv from default lib */ ret = _tdm_display_load_module_with_file(private_display, module_name); if (ret == TDM_ERROR_NONE) return TDM_ERROR_NONE; + /* load bufmgr priv from dummy lib */ + ret = _tdm_display_load_module_with_file(private_display, TDM_DUMMY_MODULE); + if (ret == TDM_ERROR_NONE) + return TDM_ERROR_NONE; + /* load bufmgr priv from configured path */ n = scandir(TDM_MODULE_PATH, &namelist, 0, alphasort); if (n < 0) { -- 2.7.4 From daa2f47914cf0fbba3cc20359fc95b5fec77ade7 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 13 Sep 2017 12:41:19 +0900 Subject: [PATCH 02/16] backend: add dummy backend. Change-Id: I5829d55e3f14d531c55adab3fde9bcaf145464cd --- Makefile.am | 4 +- backends/Makefile.am | 1 + backends/dummy/Makefile.am | 14 + backends/dummy/tdm_dummy.c | 134 ++++++++ backends/dummy/tdm_dummy.h | 76 +++++ backends/dummy/tdm_dummy_display.c | 636 +++++++++++++++++++++++++++++++++++++ configure.ac | 3 + packaging/libtdm.spec | 1 + 8 files changed, 867 insertions(+), 2 deletions(-) create mode 100644 backends/Makefile.am create mode 100644 backends/dummy/Makefile.am create mode 100644 backends/dummy/tdm_dummy.c create mode 100644 backends/dummy/tdm_dummy.h create mode 100644 backends/dummy/tdm_dummy_display.c diff --git a/Makefile.am b/Makefile.am index 292911f..8b6604d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,8 +1,8 @@ if HAVE_UTEST -SUBDIRS = . include protocol common src client tools ut +SUBDIRS = . include protocol common src backends client tools ut else -SUBDIRS = . include protocol common src client tools +SUBDIRS = . include protocol common src backends client tools endif pkgconfigdir = $(libdir)/pkgconfig diff --git a/backends/Makefile.am b/backends/Makefile.am new file mode 100644 index 0000000..4a66860 --- /dev/null +++ b/backends/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = dummy diff --git a/backends/dummy/Makefile.am b/backends/dummy/Makefile.am new file mode 100644 index 0000000..459ffe1 --- /dev/null +++ b/backends/dummy/Makefile.am @@ -0,0 +1,14 @@ +AM_CFLAGS = \ + $(CFLAGS) \ + $(TDM_CFLAGS) \ + -I$(top_srcdir)/include + +libtdm_dummy_la_LTLIBRARIES = libtdm-dummy.la +libtdm_dummy_ladir = $(TDM_MODULE_PATH) +libtdm_dummy_la_LDFLAGS = -module -avoid-version +libtdm_dummy_la_LIBADD = $(TDM_LIBS) $(top_builddir)/src/libtdm.la + +libtdm_dummy_la_SOURCES = \ + tdm_dummy_display.c \ + tdm_dummy.c + diff --git a/backends/dummy/tdm_dummy.c b/backends/dummy/tdm_dummy.c new file mode 100644 index 0000000..a39f579 --- /dev/null +++ b/backends/dummy/tdm_dummy.c @@ -0,0 +1,134 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "tdm_dummy.h" + +static tdm_dummy_data *dummy_data; + +void +tdm_dummy_deinit(tdm_backend_data *bdata) +{ + if (dummy_data != bdata) + return; + + TDM_INFO("deinit"); + + tdm_dummy_display_destroy_output_list(dummy_data); + + if (dummy_data->pipe[0] >= 0) + close(dummy_data->pipe[0]); + if (dummy_data->pipe[1] >= 0) + close(dummy_data->pipe[1]); + + free(dummy_data); + dummy_data = NULL; +} + +tdm_backend_data * +tdm_dummy_init(tdm_display *dpy, tdm_error *error) +{ + tdm_func_display dummy_func_display; + tdm_func_output dummy_func_output; + tdm_func_layer dummy_func_layer; + tdm_error ret; + + if (!dpy) { + TDM_ERR("display is null"); + if (error) + *error = TDM_ERROR_INVALID_PARAMETER; + return NULL; + } + + if (dummy_data) { + TDM_ERR("failed: init twice"); + if (error) + *error = TDM_ERROR_BAD_REQUEST; + return NULL; + } + + dummy_data = calloc(1, sizeof(tdm_dummy_data)); + if (!dummy_data) { + TDM_ERR("alloc failed"); + if (error) + *error = TDM_ERROR_OUT_OF_MEMORY; + return NULL; + } + + LIST_INITHEAD(&dummy_data->output_list); + LIST_INITHEAD(&dummy_data->buffer_list); + + memset(&dummy_func_display, 0, sizeof(dummy_func_display)); + dummy_func_display.display_get_capability = dummy_display_get_capability; + dummy_func_display.display_get_outputs = dummy_display_get_outputs; + dummy_func_display.display_get_fd = dummy_display_get_fd; + dummy_func_display.display_handle_events = dummy_display_handle_events; + + memset(&dummy_func_output, 0, sizeof(dummy_func_output)); + dummy_func_output.output_get_capability = dummy_output_get_capability; + dummy_func_output.output_get_layers = dummy_output_get_layers; + dummy_func_output.output_wait_vblank = dummy_output_wait_vblank; + dummy_func_output.output_set_vblank_handler = dummy_output_set_vblank_handler; + dummy_func_output.output_commit = dummy_output_commit; + dummy_func_output.output_set_commit_handler = dummy_output_set_commit_handler; + dummy_func_output.output_set_mode = dummy_output_set_mode; + dummy_func_output.output_get_mode = dummy_output_get_mode; + + memset(&dummy_func_layer, 0, sizeof(dummy_func_layer)); + dummy_func_layer.layer_get_capability = dummy_layer_get_capability; + dummy_func_layer.layer_set_info = dummy_layer_set_info; + dummy_func_layer.layer_get_info = dummy_layer_get_info; + dummy_func_layer.layer_set_buffer = dummy_layer_set_buffer; + dummy_func_layer.layer_unset_buffer = dummy_layer_unset_buffer; + + ret = tdm_backend_register_func_display(dpy, &dummy_func_display); + if (ret != TDM_ERROR_NONE) + goto failed; + + ret = tdm_backend_register_func_output(dpy, &dummy_func_output); + if (ret != TDM_ERROR_NONE) + goto failed; + + ret = tdm_backend_register_func_layer(dpy, &dummy_func_layer); + if (ret != TDM_ERROR_NONE) + goto failed; + + dummy_data->dpy = dpy; + + if (pipe(dummy_data->pipe) < 0) { + TDM_ERR("failed get pipe: %m"); + ret = TDM_ERROR_OPERATION_FAILED; + goto failed; + } + + ret = tdm_dummy_display_create_output_list(dummy_data); + if (ret != TDM_ERROR_NONE) + goto failed; + + ret = tdm_dummy_display_create_layer_list(dummy_data); + if (ret != TDM_ERROR_NONE) + goto failed; + + if (error) + *error = TDM_ERROR_NONE; + + TDM_INFO("init success!"); + + return (tdm_backend_data *)dummy_data; +failed: + if (error) + *error = ret; + + tdm_dummy_deinit(dummy_data); + + TDM_ERR("init failed!"); + return NULL; +} + +tdm_backend_module tdm_backend_module_data = { + "Dummy", + "Samsung", + TDM_BACKEND_SET_ABI_VERSION(1, 1), + tdm_dummy_init, + tdm_dummy_deinit +}; diff --git a/backends/dummy/tdm_dummy.h b/backends/dummy/tdm_dummy.h new file mode 100644 index 0000000..a55f4c4 --- /dev/null +++ b/backends/dummy/tdm_dummy.h @@ -0,0 +1,76 @@ +#ifndef _TDM_DUMMY_H_ +#define _TDM_DUMMY_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* dummy backend functions (display) */ +tdm_error dummy_display_get_capability(tdm_backend_data *bdata, tdm_caps_display *caps); +tdm_output** dummy_display_get_outputs(tdm_backend_data *bdata, int *count, tdm_error *error); +tdm_error dummy_display_get_fd(tdm_backend_data *bdata, int *fd); +tdm_error dummy_display_handle_events(tdm_backend_data *bdata); + +tdm_error dummy_output_get_capability(tdm_output *output, tdm_caps_output *caps); +tdm_layer** dummy_output_get_layers(tdm_output *output, int *count, tdm_error *error); +tdm_error dummy_output_wait_vblank(tdm_output *output, int interval, int sync, void *user_data); +tdm_error dummy_output_set_vblank_handler(tdm_output *output, tdm_output_vblank_handler func); +tdm_error dummy_output_commit(tdm_output *output, int sync, void *user_data); +tdm_error dummy_output_set_commit_handler(tdm_output *output, tdm_output_commit_handler func); +tdm_error dummy_output_set_mode(tdm_output *output, const tdm_output_mode *mode); +tdm_error dummy_output_get_mode(tdm_output *output, const tdm_output_mode **mode); + +tdm_error dummy_layer_get_capability(tdm_layer *layer, tdm_caps_layer *caps); +tdm_error dummy_layer_set_info(tdm_layer *layer, tdm_info_layer *info); +tdm_error dummy_layer_get_info(tdm_layer *layer, tdm_info_layer *info); +tdm_error dummy_layer_set_buffer(tdm_layer *layer, tbm_surface_h buffer); +tdm_error dummy_layer_unset_buffer(tdm_layer *layer); + +#define RETURN_VAL_IF_FAIL(cond, val) {\ + if (!(cond)) {\ + TDM_ERR("'%s' failed", #cond);\ + return val;\ + }\ +} + +#define GOTO_IF_FAIL(cond, val) {\ + if (!(cond)) {\ + TDM_ERR("'%s' failed", #cond);\ + goto val;\ + }\ +} + +typedef struct _tdm_dummy_data +{ + tdm_display *dpy; + + int pipe[2]; + + struct list_head output_list; + struct list_head buffer_list; +} tdm_dummy_data; + +tdm_error tdm_dummy_display_create_output_list(tdm_dummy_data *dummy_data); +void tdm_dummy_display_destroy_output_list(tdm_dummy_data *dummy_data); +tdm_error tdm_dummy_display_create_layer_list(tdm_dummy_data *dummy_data); + +#endif /* _TDM_DUMMY_H_ */ diff --git a/backends/dummy/tdm_dummy_display.c b/backends/dummy/tdm_dummy_display.c new file mode 100644 index 0000000..5e64fb3 --- /dev/null +++ b/backends/dummy/tdm_dummy_display.c @@ -0,0 +1,636 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "tdm_dummy.h" + +typedef struct _tdm_dummy_output_data tdm_dummy_output_data; +typedef struct _tdm_dummy_layer_data tdm_dummy_layer_data; +typedef struct _tdm_dummy_event_data tdm_dummy_event_data; + +typedef enum { + TDM_DUMMY_EVENT_TYPE_WAIT, + TDM_DUMMY_EVENT_TYPE_COMMIT, +} tdm_dummy_event_type; + +struct _tdm_dummy_event_data { + struct list_head link; + + tdm_dummy_event_type type; + tdm_dummy_output_data *output_data; + void *user_data; +}; + +struct _tdm_dummy_output_data { + struct list_head link; + + /* data which are fixed at initializing */ + tdm_dummy_data *dummy_data; + + uint32_t pipe; + tdm_output_mode *output_mode; + tdm_output_type connector_type; + struct list_head layer_list; + tdm_dummy_layer_data *primary_layer; + + /* not fixed data below */ + tdm_output_vblank_handler vblank_func; + tdm_output_commit_handler commit_func; + + tdm_output_conn_status status; + + int mode_changed; + const tdm_output_mode *current_mode; + + tdm_event_loop_source *timer; + unsigned int timer_waiting; + struct list_head timer_event_list; +}; + +struct _tdm_dummy_layer_data { + struct list_head link; + + /* data which are fixed at initializing */ + tdm_dummy_data *dummy_data; + tdm_dummy_output_data *output_data; + tdm_layer_capability capabilities; + int zpos; + + /* not fixed data below */ + tdm_info_layer info; + int info_changed; + + tbm_surface_h display_buffer; + int display_buffer_changed; +}; + +static void +_tdm_dummy_display_cb_event(tdm_dummy_output_data *output_data, tdm_dummy_event_data *event_data, + unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec) +{ + switch (event_data->type) { + case TDM_DUMMY_EVENT_TYPE_WAIT: + if (output_data->vblank_func) + output_data->vblank_func(output_data, sequence, tv_sec, tv_usec, event_data->user_data); + break; + case TDM_DUMMY_EVENT_TYPE_COMMIT: + if (output_data->commit_func) + output_data->commit_func(output_data, sequence, tv_sec, tv_usec, event_data->user_data); + break; + default: + break; + } +} + +static tdm_error +_tdm_dummy_display_cb_timeout(void *user_data) +{ + tdm_dummy_output_data *output_data = user_data; + tdm_dummy_event_data *e = NULL, *ee = NULL; + unsigned int tv_sec, tv_usec; + static unsigned int sequence = 0; + struct timespec tp; + + sequence++; + + if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) { + tv_sec = tp.tv_sec; + tv_usec = tp.tv_nsec / 1000; + } else { + tv_sec = tv_usec = 0; + } + + LIST_FOR_EACH_ENTRY_SAFE(e, ee, &output_data->timer_event_list, link) { + LIST_DEL(&e->link); + _tdm_dummy_display_cb_event(output_data, e, sequence, tv_sec, tv_usec); + free(e); + } + + return TDM_ERROR_NONE; +} + +static tdm_error +_tdm_dummy_display_wait_vblank(tdm_dummy_output_data *output_data, tdm_dummy_event_data *event_data) +{ + tdm_error ret; + unsigned int ms; + + RETURN_VAL_IF_FAIL(output_data->timer != NULL, TDM_ERROR_OPERATION_FAILED); + RETURN_VAL_IF_FAIL(output_data->output_mode->vrefresh > 0, TDM_ERROR_OPERATION_FAILED); + + if (output_data->timer_waiting) { + LIST_ADDTAIL(&event_data->link, &output_data->timer_event_list); + return TDM_ERROR_NONE; + } + + ms = 1000 / output_data->output_mode->vrefresh; + + ret = tdm_event_loop_source_timer_update(output_data->timer, ms); + if (ret != TDM_ERROR_NONE) + return ret; + + LIST_ADDTAIL(&event_data->link, &output_data->timer_event_list); + + return TDM_ERROR_NONE; +} + +static void +_tdm_dummy_display_destroy_layer_list(tdm_dummy_data *dummy_data) +{ + tdm_dummy_output_data *o = NULL; + + LIST_FOR_EACH_ENTRY(o, &dummy_data->output_list, link) { + tdm_dummy_layer_data *l = NULL, *ll = NULL; + LIST_FOR_EACH_ENTRY_SAFE(l, ll, &o->layer_list, link) { + LIST_DEL(&l->link); + free(l); + } + } +} + +tdm_error +tdm_dummy_display_create_layer_list(tdm_dummy_data *dummy_data) +{ + tdm_dummy_output_data *output_data = NULL; + tdm_error ret = TDM_ERROR_NONE; + + if (LIST_IS_EMPTY(&dummy_data->output_list)) { + TDM_ERR("no output"); + return TDM_ERROR_OPERATION_FAILED; + } + + /* The TDM dummy backend only support one output. */ + LIST_FOR_EACH_ENTRY(output_data, &dummy_data->output_list, link) { + tdm_dummy_layer_data *layer_data = calloc(1, sizeof(tdm_dummy_layer_data)); + if (!layer_data) { + TDM_ERR("alloc failed"); + ret = TDM_ERROR_OUT_OF_MEMORY; + goto failed; + } + + layer_data->dummy_data = dummy_data; + layer_data->output_data = output_data; + layer_data->zpos = 0; + + layer_data->capabilities = TDM_LAYER_CAPABILITY_PRIMARY | TDM_LAYER_CAPABILITY_GRAPHIC; + output_data->primary_layer = layer_data; + + LIST_ADDTAIL(&layer_data->link, &output_data->layer_list); + } + + return TDM_ERROR_NONE; +failed: + _tdm_dummy_display_destroy_layer_list(dummy_data); + return ret; +} + +void +tdm_dummy_display_destroy_output_list(tdm_dummy_data *dummy_data) +{ + tdm_dummy_output_data *o = NULL, *oo = NULL; + + if (LIST_IS_EMPTY(&dummy_data->output_list)) + return; + + _tdm_dummy_display_destroy_layer_list(dummy_data); + + LIST_FOR_EACH_ENTRY_SAFE(o, oo, &dummy_data->output_list, link) { + LIST_DEL(&o->link); + + if (!LIST_IS_EMPTY(&o->timer_event_list)) { + tdm_dummy_event_data *e = NULL, *ee = NULL; + LIST_FOR_EACH_ENTRY_SAFE(e, ee, &o->timer_event_list, link) { + LIST_DEL(&e->link); + free(e); + } + } + + if (o->timer) + tdm_event_loop_source_remove(o->timer); + + free(o->output_mode); + free(o); + } +} + +tdm_error +tdm_dummy_display_create_output_list(tdm_dummy_data *dummy_data) +{ + tdm_dummy_output_data *output_data; + tdm_error ret; + + RETURN_VAL_IF_FAIL(LIST_IS_EMPTY(&dummy_data->output_list), TDM_ERROR_OPERATION_FAILED); + + output_data = calloc(1, sizeof(tdm_dummy_output_data)); + if (!output_data) { + TDM_ERR("alloc failed"); + ret = TDM_ERROR_OUT_OF_MEMORY; + goto failed_create; + } + + LIST_INITHEAD(&output_data->layer_list); + + output_data->dummy_data = dummy_data; + output_data->pipe = 0; + output_data->connector_type = TDM_OUTPUT_TYPE_Unknown; + output_data->status = TDM_OUTPUT_CONN_STATUS_CONNECTED; + + output_data->output_mode = calloc(1, sizeof(tdm_output_mode)); + if (!output_data->output_mode) { + TDM_ERR("alloc failed"); + free(output_data); + ret = TDM_ERROR_OUT_OF_MEMORY; + goto failed_create; + } + + snprintf(output_data->output_mode->name, TDM_NAME_LEN, "640x480"); + output_data->output_mode->vrefresh = 30; + output_data->output_mode->clock = 25200; + output_data->output_mode->hdisplay = 640; + output_data->output_mode->hsync_start = 656; + output_data->output_mode->hsync_end = 752; + output_data->output_mode->htotal = 800; + output_data->output_mode->hskew = 0; + output_data->output_mode->vdisplay = 480; + output_data->output_mode->vsync_start = 490; + output_data->output_mode->vsync_end = 492; + output_data->output_mode->vtotal = 525; + output_data->output_mode->vscan = 0; + output_data->output_mode->flags = 0; + output_data->output_mode->type = 0; + + output_data->timer = tdm_event_loop_add_timer_handler(dummy_data->dpy, + _tdm_dummy_display_cb_timeout, + output_data, + &ret); + if (!output_data->timer) { + free(output_data); + return ret; + } + + LIST_INITHEAD(&output_data->timer_event_list); + + LIST_ADDTAIL(&output_data->link, &dummy_data->output_list); + + return TDM_ERROR_NONE; +failed_create: + tdm_dummy_display_destroy_output_list(dummy_data); + return ret; +} + +tdm_error +dummy_display_get_capability(tdm_backend_data *bdata, tdm_caps_display *caps) +{ + RETURN_VAL_IF_FAIL(caps, TDM_ERROR_INVALID_PARAMETER); + + caps->max_layer_count = -1; /* not defined */ + + return TDM_ERROR_NONE; +} + +tdm_output ** +dummy_display_get_outputs(tdm_backend_data *bdata, int *count, tdm_error *error) +{ + tdm_dummy_data *dummy_data = bdata; + tdm_dummy_output_data *output_data = NULL; + tdm_output **outputs; + tdm_error ret; + int i; + + RETURN_VAL_IF_FAIL(dummy_data, NULL); + RETURN_VAL_IF_FAIL(count, NULL); + + *count = 0; + LIST_FOR_EACH_ENTRY(output_data, &dummy_data->output_list, link) + (*count)++; + + if (*count == 0) { + ret = TDM_ERROR_NONE; + goto failed_get; + } + + /* will be freed in frontend */ + outputs = calloc(*count, sizeof(tdm_dummy_output_data *)); + if (!outputs) { + TDM_ERR("failed: alloc memory"); + *count = 0; + ret = TDM_ERROR_OUT_OF_MEMORY; + goto failed_get; + } + + i = 0; + LIST_FOR_EACH_ENTRY(output_data, &dummy_data->output_list, link) + outputs[i++] = output_data; + + if (error) + *error = TDM_ERROR_NONE; + + return outputs; +failed_get: + if (error) + *error = ret; + return NULL; +} + +tdm_error +dummy_display_get_fd(tdm_backend_data *bdata, int *fd) +{ + tdm_dummy_data *dummy_data = bdata; + + RETURN_VAL_IF_FAIL(dummy_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(fd, TDM_ERROR_INVALID_PARAMETER); + + *fd = dummy_data->pipe[0]; + + return TDM_ERROR_NONE; +} + +tdm_error +dummy_display_handle_events(tdm_backend_data *bdata) +{ + return TDM_ERROR_NONE; +} + +tdm_error +dummy_output_get_capability(tdm_output *output, tdm_caps_output *caps) +{ + tdm_dummy_output_data *output_data = output; + tdm_error ret; + + RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(caps, TDM_ERROR_INVALID_PARAMETER); + + memset(caps, 0, sizeof(tdm_caps_output)); + + snprintf(caps->maker, TDM_NAME_LEN, "dummy"); + snprintf(caps->model, TDM_NAME_LEN, "dummy"); + snprintf(caps->name, TDM_NAME_LEN, "dummy"); + + caps->status = output_data->status; + caps->type = output_data->connector_type; + caps->type_id = 0; + + caps->mode_count = 1; + caps->modes = calloc(1, sizeof(tdm_output_mode)); + if (!caps->modes) { + ret = TDM_ERROR_OUT_OF_MEMORY; + TDM_ERR("alloc failed\n"); + goto failed_get; + } + + *caps->modes = *output_data->output_mode; + + caps->mmWidth = 640; + caps->mmHeight = 480; + caps->subpixel = 1; + + caps->min_w = -1; + caps->min_h = -1; + caps->max_w = -1; + caps->max_h = -1; + caps->preferred_align = -1; + + caps->prop_count = 0; + + return TDM_ERROR_NONE; +failed_get: + memset(caps, 0, sizeof(tdm_caps_output)); + return ret; +} + +tdm_layer ** +dummy_output_get_layers(tdm_output *output, int *count, tdm_error *error) +{ + tdm_dummy_output_data *output_data = output; + tdm_dummy_layer_data *layer_data = NULL; + tdm_layer **layers; + tdm_error ret; + int i; + + RETURN_VAL_IF_FAIL(output_data, NULL); + RETURN_VAL_IF_FAIL(count, NULL); + + *count = 0; + LIST_FOR_EACH_ENTRY(layer_data, &output_data->layer_list, link) + (*count)++; + + if (*count == 0) { + ret = TDM_ERROR_NONE; + goto failed_get; + } + + /* will be freed in frontend */ + layers = calloc(*count, sizeof(tdm_dummy_layer_data *)); + if (!layers) { + TDM_ERR("failed: alloc memory"); + *count = 0; + ret = TDM_ERROR_OUT_OF_MEMORY; + goto failed_get; + } + + i = 0; + LIST_FOR_EACH_ENTRY(layer_data, &output_data->layer_list, link) + layers[i++] = layer_data; + + if (error) + *error = TDM_ERROR_NONE; + + return layers; +failed_get: + if (error) + *error = ret; + return NULL; +} + +tdm_error +dummy_output_wait_vblank(tdm_output *output, int interval, int sync, void *user_data) +{ + tdm_dummy_output_data *output_data = output; + tdm_dummy_event_data *event_data; + tdm_error ret; + + RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER); + + event_data = calloc(1, sizeof(tdm_dummy_event_data)); + if (!event_data) { + TDM_ERR("alloc failed"); + return TDM_ERROR_OUT_OF_MEMORY; + } + + event_data->type = TDM_DUMMY_EVENT_TYPE_WAIT; + event_data->output_data = output_data; + event_data->user_data = user_data; + + ret = _tdm_dummy_display_wait_vblank(output_data, event_data); + if (ret != TDM_ERROR_NONE) { + free(event_data); + return ret; + } + + return TDM_ERROR_NONE; +} + +tdm_error +dummy_output_set_vblank_handler(tdm_output *output, tdm_output_vblank_handler func) +{ + tdm_dummy_output_data *output_data = output; + + RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(func, TDM_ERROR_INVALID_PARAMETER); + + output_data->vblank_func = func; + + return TDM_ERROR_NONE; +} + +tdm_error +dummy_output_commit(tdm_output *output, int sync, void *user_data) +{ + tdm_dummy_output_data *output_data = output; + tdm_dummy_event_data *event_data; + tdm_error ret; + + RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER); + + event_data = calloc(1, sizeof(tdm_dummy_event_data)); + if (!event_data) { + TDM_ERR("alloc failed"); + return TDM_ERROR_OUT_OF_MEMORY; + } + + event_data->type = TDM_DUMMY_EVENT_TYPE_COMMIT; + event_data->output_data = output_data; + event_data->user_data = user_data; + + ret = _tdm_dummy_display_wait_vblank(output_data, event_data); + if (ret != TDM_ERROR_NONE) { + free(event_data); + return ret; + } + + return TDM_ERROR_NONE; +} + +tdm_error +dummy_output_set_commit_handler(tdm_output *output, tdm_output_commit_handler func) +{ + tdm_dummy_output_data *output_data = output; + + RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(func, TDM_ERROR_INVALID_PARAMETER); + + output_data->commit_func = func; + + return TDM_ERROR_NONE; +} + +tdm_error +dummy_output_set_mode(tdm_output *output, const tdm_output_mode *mode) +{ + tdm_dummy_output_data *output_data = output; + + RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(mode, TDM_ERROR_INVALID_PARAMETER); + + output_data->current_mode = mode; + output_data->mode_changed = 1; + + return TDM_ERROR_NONE; +} + +tdm_error +dummy_output_get_mode(tdm_output *output, const tdm_output_mode **mode) +{ + tdm_dummy_output_data *output_data = output; + + RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(mode, TDM_ERROR_INVALID_PARAMETER); + + *mode = output_data->current_mode; + + return TDM_ERROR_NONE; +} + +tdm_error +dummy_layer_get_capability(tdm_layer *layer, tdm_caps_layer *caps) +{ + tdm_dummy_layer_data *layer_data = layer; + + RETURN_VAL_IF_FAIL(layer_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(caps, TDM_ERROR_INVALID_PARAMETER); + + memset(caps, 0, sizeof(tdm_caps_layer)); + + caps->capabilities = layer_data->capabilities; + caps->zpos = layer_data->zpos; + + caps->format_count = 2; + caps->formats = calloc(caps->format_count, sizeof(tbm_format)); + if (!caps->formats) { + TDM_ERR("alloc failed\n"); + free(caps->formats); + memset(caps, 0, sizeof(tdm_caps_layer)); + return TDM_ERROR_OUT_OF_MEMORY; + } + + caps->formats[0] = TBM_FORMAT_ARGB8888; + caps->formats[1] = TBM_FORMAT_XRGB8888; + + caps->prop_count = 0; + + return TDM_ERROR_NONE; +} + +tdm_error +dummy_layer_set_info(tdm_layer *layer, tdm_info_layer *info) +{ + tdm_dummy_layer_data *layer_data = layer; + + RETURN_VAL_IF_FAIL(layer_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(info, TDM_ERROR_INVALID_PARAMETER); + + layer_data->info = *info; + layer_data->info_changed = 1; + + return TDM_ERROR_NONE; +} + +tdm_error +dummy_layer_get_info(tdm_layer *layer, tdm_info_layer *info) +{ + tdm_dummy_layer_data *layer_data = layer; + + RETURN_VAL_IF_FAIL(layer_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(info, TDM_ERROR_INVALID_PARAMETER); + + *info = layer_data->info; + + return TDM_ERROR_NONE; +} + +tdm_error +dummy_layer_set_buffer(tdm_layer *layer, tbm_surface_h buffer) +{ + tdm_dummy_layer_data *layer_data = layer; + + RETURN_VAL_IF_FAIL(layer_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(buffer, TDM_ERROR_INVALID_PARAMETER); + + layer_data->display_buffer = buffer; + layer_data->display_buffer_changed = 1; + + return TDM_ERROR_NONE; +} + +tdm_error +dummy_layer_unset_buffer(tdm_layer *layer) +{ + tdm_dummy_layer_data *layer_data = layer; + + RETURN_VAL_IF_FAIL(layer_data, TDM_ERROR_INVALID_PARAMETER); + + layer_data->display_buffer = NULL; + layer_data->display_buffer_changed = 1; + + return TDM_ERROR_NONE; +} diff --git a/configure.ac b/configure.ac index a4cb6b8..40a3816 100644 --- a/configure.ac +++ b/configure.ac @@ -70,6 +70,7 @@ AC_ARG_WITH(tdm-module-path, AS_HELP_STRING([--with-tdm-module-path=PATH], [tdm [ TDM_MODULE_PATH="$withval" ], [ TDM_MODULE_PATH="${DEFAULT_TDM_MODULE_PATH}" ]) AC_DEFINE_UNQUOTED(TDM_MODULE_PATH, "${TDM_MODULE_PATH}", [Directory for the modules of tdm]) +AC_SUBST(TDM_MODULE_PATH) AC_SUBST([TDM_MAJOR_VERSION], [tdm_major_version]) AC_SUBST([TDM_MINOR_VERSION], [tdm_minor_version]) @@ -89,6 +90,8 @@ AC_OUTPUT([ protocol/Makefile common/Makefile src/Makefile + backends/Makefile + backends/dummy/Makefile client/libtdm-client.pc client/Makefile tools/Makefile diff --git a/packaging/libtdm.spec b/packaging/libtdm.spec index 32c0e1c..60fd5ed 100644 --- a/packaging/libtdm.spec +++ b/packaging/libtdm.spec @@ -101,6 +101,7 @@ rm -f %{_unitdir_user}/basic.target.wants/tdm-socket-user.path %defattr(-,root,root,-) %license COPYING %{_libdir}/libtdm.so.* +%{_libdir}/tdm/libtdm-dummy.so %attr(750,root,root) %{_bindir}/tdm-monitor %{_unitdir_user}/tdm-socket-user.path %{_unitdir_user}/tdm-socket-user.service -- 2.7.4 From bd5f11a1123f3974242c490a0a6d2aa166ab3688 Mon Sep 17 00:00:00 2001 From: Junkyeong Kim Date: Mon, 18 Sep 2017 15:39:48 +0900 Subject: [PATCH 03/16] do not execute wl_resource_get_client Change-Id: If9f9f636c9b1206943d4f6644d65ada660f64397 Signed-off-by: Junkyeong Kim --- src/tdm_server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tdm_server.c b/src/tdm_server.c index a8f7175..d528c10 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -651,7 +651,7 @@ _tdm_server_cb_debug(struct wl_client *client, struct wl_resource *resource, con size = sizeof(message) - len; m = message; - wl_client_flush(wl_resource_get_client(resource)); + wl_client_flush(client); while (size > 0) { char buffer[TDM_DEBUG_REPLY_MSG_LEN]; -- 2.7.4 From 712c798e9c572a356d38c8b4f03bfaf380d620ae Mon Sep 17 00:00:00 2001 From: Boram Park Date: Fri, 15 Sep 2017 18:54:52 +0900 Subject: [PATCH 04/16] not using setenv, getenv for drm fd Change-Id: I2557864152f310fc10b4bd9f0d26032720dba396 --- include/tdm_helper.h | 8 ------ src/tdm_helper.c | 70 ++++------------------------------------------------ 2 files changed, 5 insertions(+), 73 deletions(-) diff --git a/include/tdm_helper.h b/include/tdm_helper.h index 1459e72..3d1d252 100644 --- a/include/tdm_helper.h +++ b/include/tdm_helper.h @@ -144,10 +144,6 @@ tdm_helper_convert_buffer(tbm_surface_h srcbuf, tbm_surface_h dstbuf, * @details * This function will dup the fd of the given enviroment variable. The Caller * @b SHOULD close the fd. - * \n - * In DRM system, a drm-master-fd @b SHOULD be shared between TDM backend and - * TBM backend in display server side by using "TDM_DRM_MASTER_FD" - * and "TBM_DRM_MASTER_FD". * @param[in] env The given enviroment variable * @return fd if success. Otherwise, -1. * @see #tdm_helper_set_fd() @@ -156,10 +152,6 @@ int tdm_helper_get_fd(const char *env); /** * @brief Set the given fd to the give enviroment variable. - * @details - * In DRM system, a drm-master-fd @b SHOULD be shared between TDM backend and - * TBM backend in display server side by using "TDM_DRM_MASTER_FD" - * and "TBM_DRM_MASTER_FD". * @param[in] env The given enviroment variable * @param[in] fd The given fd * @see #tdm_helper_get_fd() diff --git a/src/tdm_helper.c b/src/tdm_helper.c index fc271fb..b8e0d1b 100644 --- a/src/tdm_helper.c +++ b/src/tdm_helper.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -580,79 +581,18 @@ unmap_srcbuf: EXTERN int tdm_helper_get_fd(const char *env) { - const char *value; - int fd, newfd, flags, ret; - char *end; - errno = 0; - - value = (const char*)getenv(env); - if (!value) - return -1; - - const long int sl = strtol(value, &end, 10); - if (end == value) { - TDM_ERR("%s: not a decimal number\n", value); - return -1; - } else if (*end != '\0') { - TDM_ERR("%s: extra characters at end of input: %s\n", value, end); - return -1; - } else if ((sl == LONG_MIN || sl == LONG_MAX) && errno == ERANGE) { - TDM_ERR("%s out of range of type long\n", value); - return -1; - } else if (sl >= INT_MAX) { - TDM_ERR("%ld greater than INT_MAX\n", sl); - return -1; - } else if (sl <= INT_MIN) { - TDM_ERR("%ld less than INT_MIN\n", sl); - return -1; - } else { - fd = (int)sl; - if (fd < 0) { - TDM_ERR("%d out of fd range\n", fd); - return -1; - } - } - - flags = fcntl(fd, F_GETFD); - if (flags == -1) { - TDM_ERR("fcntl failed: %m"); + if (strncmp(env, "TBM_DRM_MASTER_FD", 17) && strncmp(env, "TDM_DRM_MASTER_FD", 17)) { + TDM_INFO("DEPRECATED! '%s'", env); return -1; } - newfd = dup(fd); - if (newfd < 0) { - TDM_ERR("dup failed: %m"); - return -1; - } - - TDM_INFO("%s: fd(%d) newfd(%d)", env, fd, newfd); - - ret = fcntl(newfd, F_SETFD, flags | FD_CLOEXEC); - if (ret == -1) { - TDM_ERR("fcntl failed: %m"); - close(newfd); - return -1; - } - - return newfd; + return tbm_drm_helper_get_master_fd(); } EXTERN void tdm_helper_set_fd(const char *env, int fd) { - char buf[32]; - int ret; - - snprintf(buf, sizeof(buf), "%d", fd); - - ret = setenv(env, (const char*)buf, 1); - if (ret) { - TDM_ERR("setenv failed: %m"); - return; - } - - if (fd >= 0) - TDM_INFO("%s: fd(%d)", env, fd); + TDM_INFO("DEPRECATED!"); } EXTERN void -- 2.7.4 From f00c5a0988a6cae11042bfbedca88257650f5ee9 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Fri, 22 Sep 2017 10:46:16 +0900 Subject: [PATCH 05/16] layer: pending layer's data until committed The layer's info and buffer information should be applied when tdm_layer_commit called. Otherwise, when we call tdm_layer_commit for A layer, the changes of B layer can be applied to backend unintentionally. Change-Id: I5cfdde9ed9f918174aa5a0785bad48c93c4b2091 --- src/tdm_layer.c | 119 ++++++++++++++++++++++++++++++++++++++++++------------ src/tdm_output.c | 7 ++++ src/tdm_private.h | 8 ++++ 3 files changed, 109 insertions(+), 25 deletions(-) diff --git a/src/tdm_layer.c b/src/tdm_layer.c index c6128a8..ea30779 100644 --- a/src/tdm_layer.c +++ b/src/tdm_layer.c @@ -81,6 +81,7 @@ static void _tbm_layer_queue_acquirable_cb(tbm_surface_queue_h surface_queue, vo static void _tbm_layer_queue_destroy_cb(tbm_surface_queue_h surface_queue, void *data); static void _tdm_layer_cb_output_commit(tdm_output *output, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data); +static void _tdm_layer_reset_pending_data(tdm_private_layer *private_layer); EXTERN tdm_error tdm_layer_get_capabilities(tdm_layer *layer, tdm_layer_capability *capabilities) @@ -242,8 +243,8 @@ tdm_layer_set_info(tdm_layer *layer, tdm_info_layer *info) info->dst_pos.w, info->dst_pos.h, info->transform); - ret = func_layer->layer_set_info(private_layer->layer_backend, info); - TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE); + private_layer->pending_info_changed = 1; + private_layer->pending_info = *info; _pthread_mutex_unlock(&private_display->lock); @@ -335,6 +336,8 @@ _tdm_layer_free_all_buffers(tdm_private_layer *private_layer) LIST_INITHEAD(&clone_list); + _tdm_layer_reset_pending_data(private_layer); + if (private_layer->waiting_buffer) { _tdm_layer_free_buffer(private_layer, private_layer->waiting_buffer); private_layer->waiting_buffer = NULL; @@ -400,7 +403,6 @@ EXTERN tdm_error tdm_layer_set_buffer(tdm_layer *layer, tbm_surface_h buffer) { tdm_func_layer *func_layer; - tdm_private_layer_buffer *layer_buffer; LAYER_FUNC_ENTRY(); @@ -430,32 +432,22 @@ tdm_layer_set_buffer(tdm_layer *layer, tbm_surface_h buffer) return TDM_ERROR_NOT_IMPLEMENTED; } - layer_buffer = calloc(1, sizeof(tdm_private_layer_buffer)); - if (!layer_buffer) { - _pthread_mutex_unlock(&private_display->lock); - TDM_ERR("alloc failed"); - return TDM_ERROR_OUT_OF_MEMORY; - } - LIST_INITHEAD(&layer_buffer->link); + private_layer->pending_buffer_changed = 1; - ret = func_layer->layer_set_buffer(private_layer->layer_backend, buffer); - TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE); + if (private_layer->pending_buffer) { + tbm_surface_internal_unref(private_layer->pending_buffer); - /* dump buffer */ - if (tdm_dump_enable && !(private_layer->caps.capabilities & TDM_LAYER_CAPABILITY_VIDEO)) - _tdm_layer_dump_buffer(layer, buffer); + if (tdm_debug_module & TDM_DEBUG_BUFFER) + TDM_INFO("layer(%p) pending_buffer(%p) skipped", + private_layer, private_layer->pending_buffer); + } - if (ret == TDM_ERROR_NONE) { - if (private_layer->waiting_buffer) - _tdm_layer_free_buffer(private_layer, private_layer->waiting_buffer); + tbm_surface_internal_ref(buffer); + private_layer->pending_buffer = buffer; - private_layer->waiting_buffer = layer_buffer; - private_layer->waiting_buffer->buffer = tdm_buffer_ref_backend(buffer); - if (tdm_debug_module & TDM_DEBUG_BUFFER) - TDM_INFO("layer(%p) waiting_buffer(%p)", - private_layer, private_layer->waiting_buffer->buffer); - } else - _tdm_layer_free_buffer(private_layer, layer_buffer); + if (tdm_debug_module & TDM_DEBUG_BUFFER) + TDM_INFO("layer(%p) pending_buffer(%p)", + private_layer, private_layer->pending_buffer); _pthread_mutex_unlock(&private_display->lock); @@ -745,6 +737,72 @@ _tdm_layer_commit_possible(tdm_private_layer *private_layer) return 1; } +static void +_tdm_layer_reset_pending_data(tdm_private_layer *private_layer) +{ + private_layer->pending_info_changed = 0; + memset(&private_layer->pending_info, 0, sizeof private_layer->pending_info); + + private_layer->pending_buffer_changed = 0; + if (private_layer->pending_buffer) { + tbm_surface_internal_unref(private_layer->pending_buffer); + private_layer->pending_buffer = NULL; + } +} + +INTERN tdm_error +tdm_layer_commit_pending_data(tdm_private_layer *private_layer) +{ + tdm_private_output *private_output = private_layer->private_output; + tdm_private_display *private_display = private_output->private_display; + tdm_func_layer *func_layer; + tdm_error ret = TDM_ERROR_NONE; + + func_layer = &private_display->func_layer; + + if (private_layer->pending_info_changed) { + ret = func_layer->layer_set_info(private_layer->layer_backend, &private_layer->pending_info); + TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done); + } + + if (private_layer->pending_buffer_changed) { + tdm_private_layer_buffer *layer_buffer; + + layer_buffer = calloc(1, sizeof(tdm_private_layer_buffer)); + TDM_GOTO_IF_FAIL(layer_buffer != NULL, done); + + LIST_INITHEAD(&layer_buffer->link); + + ret = func_layer->layer_set_buffer(private_layer->layer_backend, private_layer->pending_buffer); + TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE); + + if (tdm_debug_module & TDM_DEBUG_BUFFER) + TDM_INFO("layer(%p) pending_buffer(%p) committed", + private_layer, private_layer->pending_buffer); + + /* dump buffer */ + if (tdm_dump_enable && !(private_layer->caps.capabilities & TDM_LAYER_CAPABILITY_VIDEO)) + _tdm_layer_dump_buffer(private_layer, private_layer->pending_buffer); + + if (ret == TDM_ERROR_NONE) { + if (private_layer->waiting_buffer) + _tdm_layer_free_buffer(private_layer, private_layer->waiting_buffer); + + private_layer->waiting_buffer = layer_buffer; + private_layer->waiting_buffer->buffer = tdm_buffer_ref_backend(private_layer->pending_buffer); + if (tdm_debug_module & TDM_DEBUG_BUFFER) + TDM_INFO("layer(%p) waiting_buffer(%p)", + private_layer, private_layer->waiting_buffer->buffer); + } else + _tdm_layer_free_buffer(private_layer, layer_buffer); + } + +done: + _tdm_layer_reset_pending_data(private_layer); + + return ret; +} + /* CAUTION: Once _tdm_layer_commit returns success, the layer commit handler MUST be called always. * That is, even if we get error in _tdm_layer_got_output_vblank() function for some reasons, * the layer commit handler MUST be called. @@ -863,6 +921,14 @@ tdm_layer_commit(tdm_layer *layer, tdm_layer_commit_handler func, void *user_dat return TDM_ERROR_DPMS_OFF; } + /* don't call this inside of _tdm_layer_commit */ + ret = tdm_layer_commit_pending_data(private_layer); + if (ret != TDM_ERROR_NONE) { + TDM_ERR("layer(%p) committing pending data failed", layer); + _pthread_mutex_unlock(&private_display->lock); + return ret; + } + ret = _tdm_layer_commit(private_layer, func, user_data); _pthread_mutex_unlock(&private_display->lock); @@ -991,6 +1057,9 @@ _tbm_layer_queue_acquirable_cb(tbm_surface_queue_h surface_queue, void *data) return; } + /* we don't need to handle pending data here because the changes in this function + * should be applied immediately. we can't expect calling tdm_layer_commit. + */ ret = func_layer->layer_set_buffer(private_layer->layer_backend, surface); TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE); diff --git a/src/tdm_output.c b/src/tdm_output.c index f6d780a..b37bf4f 100644 --- a/src/tdm_output.c +++ b/src/tdm_output.c @@ -960,6 +960,8 @@ EXTERN tdm_error tdm_output_commit(tdm_output *output, int sync, tdm_output_commit_handler func, void *user_data) { + tdm_private_layer *private_layer = NULL; + OUTPUT_FUNC_ENTRY(); _pthread_mutex_lock(&private_display->lock); @@ -988,6 +990,11 @@ tdm_output_commit(tdm_output *output, int sync, tdm_output_commit_handler func, if (tdm_debug_module & TDM_DEBUG_COMMIT) TDM_INFO("output(%d) commit", private_output->pipe); + /* apply the pending data of all layers */ + LIST_FOR_EACH_ENTRY(private_layer, &private_output->layer_list, link) { + tdm_layer_commit_pending_data(private_layer); + } + ret = tdm_output_commit_internal(output, sync, func, user_data); _pthread_mutex_unlock(&private_display->lock); diff --git a/src/tdm_private.h b/src/tdm_private.h index cecf66d..522ae57 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -233,6 +233,12 @@ struct _tdm_private_layer { tdm_caps_layer caps; tdm_layer *layer_backend; + /* pending data until committed */ + unsigned int pending_info_changed; + tdm_info_layer pending_info; + unsigned int pending_buffer_changed; + tbm_surface_h pending_buffer; + /* When a buffer is set to a layer, it will be stored to waiting_buffer. * And when a layer is committed, it will be moved to committed_buffer. * Finally when a commit handler is called, it will be moved to showing_buffer. @@ -436,6 +442,8 @@ tdm_output_remove_commit_handler_internal(tdm_output *output, tdm_output_commit_ void tdm_layer_remove_commit_handler_internal(tdm_layer *layer, tdm_layer_commit_handler func, void *user_data); +tdm_error +tdm_layer_commit_pending_data(tdm_private_layer *private_layer); void tdm_layer_committed(tdm_private_layer *private_layer, tdm_private_layer_buffer **committed_buffer); -- 2.7.4 From eac2630a6cfdbc02466d0f11863c32f91e02bb42 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Fri, 22 Sep 2017 16:57:01 +0900 Subject: [PATCH 06/16] package version up to 1.8.0 Change-Id: Ib8fb6088111712c0e36e685dd351aeb2d1771488 --- packaging/libtdm.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/libtdm.spec b/packaging/libtdm.spec index 60fd5ed..cb46040 100644 --- a/packaging/libtdm.spec +++ b/packaging/libtdm.spec @@ -1,7 +1,7 @@ %bcond_with utest Name: libtdm -Version: 1.7.8 +Version: 1.8.0 Release: 0 Summary: User Library of Tizen Display Manager Group: Development/Libraries -- 2.7.4 From 2980da18ae3a442dd7cbce9b085a3acd87e06c93 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 27 Sep 2017 10:31:16 +0900 Subject: [PATCH 07/16] remove unnecessary condition Change-Id: Ib27a298da0b3194c1f42feca95739f53f4e05bb1 --- client/tdm_monitor.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/client/tdm_monitor.c b/client/tdm_monitor.c index cdfa1f7..60ce16d 100644 --- a/client/tdm_monitor.c +++ b/client/tdm_monitor.c @@ -164,12 +164,9 @@ main(int argc, char ** argv) while (!info->debug.done && ret >= 0) ret = wl_display_dispatch(info->display); - if (info->tdm) - wl_tdm_destroy(info->tdm); - if (info->registry) - wl_registry_destroy(info->registry); - if (info->display) - wl_display_disconnect(info->display); + wl_tdm_destroy(info->tdm); + wl_registry_destroy(info->registry); + wl_display_disconnect(info->display); return 0; } -- 2.7.4 From 3f859445a835fd5d7958272363095313931b6cce Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 27 Sep 2017 10:32:30 +0900 Subject: [PATCH 08/16] add validation check for getenv Change-Id: I88df5bdfeb5049b0ba3c84fb841a9d062a9c01d9 --- src/tdm.c | 18 +++++++++++++++--- src/tdm_server.c | 7 ++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/tdm.c b/src/tdm.c index a27247b..d942f3a 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -773,8 +773,13 @@ _tdm_display_load_module_with_file(tdm_private_display *private_display, void *module; tdm_error ret; double stamp; + int size; - snprintf(path, sizeof(path), TDM_MODULE_PATH "/%s", file); + size = snprintf(path, sizeof(path), TDM_MODULE_PATH "/%s", file); + if (size >= (int)sizeof(path)) { + TDM_WRN("too long: %s/%s", TDM_MODULE_PATH, file); + return TDM_ERROR_BAD_MODULE; + }; stamp = tdm_helper_get_time(); TDM_TRACE_BEGIN(Load_Backend); @@ -839,16 +844,23 @@ static tdm_error _tdm_display_load_module(tdm_private_display *private_display) { const char *module_name; + char module[TDM_NAME_LEN]; struct dirent **namelist; - int n; + int n, size; tdm_error ret = 0; module_name = getenv("TDM_MODULE"); if (!module_name) module_name = TDM_DEFAULT_MODULE; + size = snprintf(module, sizeof(module), "%s", module_name); + if (size >= (int)sizeof(module)) { + TDM_ERR("too long: %s", module_name); + return TDM_ERROR_OPERATION_FAILED; + }; + /* load bufmgr priv from default lib */ - ret = _tdm_display_load_module_with_file(private_display, module_name); + ret = _tdm_display_load_module_with_file(private_display, (const char*)module); if (ret == TDM_ERROR_NONE) return TDM_ERROR_NONE; diff --git a/src/tdm_server.c b/src/tdm_server.c index d528c10..21c0c9d 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -790,6 +790,7 @@ _tdm_socket_init(tdm_private_loop *private_loop) { const char *dir = NULL; char socket_path[128]; + int size; int ret = -1; uid_t uid; gid_t gid; @@ -800,7 +801,11 @@ _tdm_socket_init(tdm_private_loop *private_loop) return; } - snprintf(socket_path, sizeof(socket_path), "%s/%s", dir, "tdm-socket"); + size = snprintf(socket_path, sizeof(socket_path), "%s/%s", dir, "tdm-socket"); + if (size >= (int)sizeof(socket_path)) { + TDM_WRN("too long: %s/tdm-socket", dir); + return; + }; ret = chmod(socket_path, 509); if (ret < 0) { -- 2.7.4 From 6323735bc3dbab2defe7920e087ce21687c55b5a Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 27 Sep 2017 15:39:30 +0900 Subject: [PATCH 09/16] package version up to 1.8.1 Change-Id: I7744c6b4582f0e5f3dde7a09bcb15ea0ec8d1d3e --- packaging/libtdm.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/libtdm.spec b/packaging/libtdm.spec index cb46040..fb8effa 100644 --- a/packaging/libtdm.spec +++ b/packaging/libtdm.spec @@ -1,7 +1,7 @@ %bcond_with utest Name: libtdm -Version: 1.8.0 +Version: 1.8.1 Release: 0 Summary: User Library of Tizen Display Manager Group: Development/Libraries -- 2.7.4 From ea5d8b69554417c15440caaf500710b2adf85657 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 27 Sep 2017 17:37:21 +0900 Subject: [PATCH 10/16] set drm_fd to tbm Change-Id: If4e231b807744f14621dfb4ccc710191b6d09aab --- src/tdm_helper.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/tdm_helper.c b/src/tdm_helper.c index b8e0d1b..a22e0cc 100644 --- a/src/tdm_helper.c +++ b/src/tdm_helper.c @@ -592,7 +592,12 @@ tdm_helper_get_fd(const char *env) EXTERN void tdm_helper_set_fd(const char *env, int fd) { - TDM_INFO("DEPRECATED!"); + if (strncmp(env, "TBM_DRM_MASTER_FD", 17) && strncmp(env, "TDM_DRM_MASTER_FD", 17)) { + TDM_INFO("DEPRECATED! '%s'", env); + return; + } + + tbm_drm_helper_set_tbm_master_fd(fd); } EXTERN void -- 2.7.4 From d3f88ed1621fe5faa747ae128a0b1788c1d027a2 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Fri, 29 Sep 2017 11:28:54 +0900 Subject: [PATCH 11/16] Revert "implementation for SCREEN_PREROTATION_HINT" This reverts commit f4aa0253c0f401b86bf1769e200b216cd2d8b66d. Change-Id: Ic15d3a1815bbf9eab57240cc50514c9937bcf662 --- tools/tdm_test_server.c | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/tools/tdm_test_server.c b/tools/tdm_test_server.c index 0c30519..b42a0fb 100644 --- a/tools/tdm_test_server.c +++ b/tools/tdm_test_server.c @@ -760,7 +760,6 @@ interpret_args(tdm_test_server *data) } } -static unsigned int tts_screen_prerotation_hint; static tdm_test_server tts_data; static int tts_buffer_key; #define TTS_BUFFER_KEY ((unsigned long)&tts_buffer_key) @@ -770,8 +769,6 @@ buffer_allocate(int width, int height, int format, int flags) { tdm_test_server_buffer *tb = calloc(1, sizeof *tb); TDM_EXIT_IF_FAIL(tb != NULL); - if (tts_screen_prerotation_hint % 180) - TDM_SWAP(width, height); tb->b = tbm_surface_internal_create_with_flags(width, height, format, flags); TDM_EXIT_IF_FAIL(tb->b != NULL); tdm_helper_clear_buffer(tb->b); @@ -859,14 +856,6 @@ main(int argc, char *argv[]) char temp[TDM_SERVER_REPLY_MSG_LEN]; int len = sizeof temp; tdm_error ret; - const char *value; - - value = (const char*)getenv("SCREEN_PREROTATION_HINT"); - if (value) { - char *end; - tts_screen_prerotation_hint = strtol(value, &end, 10); - printf("SCREEN_PREROTATION_HINT = %d", tts_screen_prerotation_hint); - } memset(data, 0, sizeof * data); LIST_INITHEAD(&data->output_list); @@ -1054,22 +1043,12 @@ layer_setup(tdm_test_server_layer *l, tbm_surface_h b) /* The size and format information should be same with buffer's */ tbm_surface_get_info(b, &info); - if (tts_screen_prerotation_hint % 180) { - if (IS_RGB(info.format)) { - l->info.src_config.size.h = info.height; - l->info.src_config.size.v = info.planes[0].stride >> 2; - } else { - l->info.src_config.size.h = info.height; - l->info.src_config.size.v = info.planes[0].stride; - } + if (IS_RGB(info.format)) { + l->info.src_config.size.h = info.planes[0].stride >> 2; + l->info.src_config.size.v = info.height; } else { - if (IS_RGB(info.format)) { - l->info.src_config.size.h = info.planes[0].stride >> 2; - l->info.src_config.size.v = info.height; - } else { - l->info.src_config.size.h = info.planes[0].stride; - l->info.src_config.size.v = info.height; - } + l->info.src_config.size.h = info.planes[0].stride; + l->info.src_config.size.v = info.height; } l->info.src_config.format = info.format; -- 2.7.4 From 313aa28db9eb590c553779645debbfd577ad6599 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Fri, 29 Sep 2017 11:39:40 +0900 Subject: [PATCH 12/16] test: set enviroment for testing Change-Id: I0fdd86a9c15e33b25eaf1fa0482c94472f4b025b --- tools/tdm_test_server.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tools/tdm_test_server.c b/tools/tdm_test_server.c index b42a0fb..ff7b542 100644 --- a/tools/tdm_test_server.c +++ b/tools/tdm_test_server.c @@ -857,6 +857,26 @@ main(int argc, char *argv[]) int len = sizeof temp; tdm_error ret; +#if 1 /* for testing */ + const char *s = (const char*)getenv("XDG_RUNTIME_DIR"); + if (!s) { + char buf[32]; + snprintf(buf, sizeof(buf), "/run"); + int ret = setenv("XDG_RUNTIME_DIR", (const char*)buf, 1); + if (ret != 0) + exit(0); + } + + s = (const char*)getenv("TBM_DISPLAY_SERVER"); + if (!s) { + char buf[32]; + snprintf(buf, sizeof(buf), "1"); + int ret = setenv("TBM_DISPLAY_SERVER", (const char*)buf, 1); + if (ret != 0) + exit(0); + } +#endif + memset(data, 0, sizeof * data); LIST_INITHEAD(&data->output_list); LIST_INITHEAD(&data->pp_list); -- 2.7.4 From 7252950fc1c0a6a39058a9046dabb619196fca22 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Fri, 29 Sep 2017 14:08:45 +0900 Subject: [PATCH 13/16] remove link before calling the user handler Inside of the user handler, list can be changed. And it could make crash. Change-Id: Ib49bc110e826ff7b34b6c8bc2224b5006b1972ba --- src/tdm_layer.c | 3 ++- src/tdm_output.c | 7 +++++-- src/tdm_server.c | 6 ++++-- src/tdm_vblank.c | 5 +++-- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/tdm_layer.c b/src/tdm_layer.c index ea30779..cb4ef55 100644 --- a/src/tdm_layer.c +++ b/src/tdm_layer.c @@ -644,6 +644,8 @@ _tdm_layer_cb_output_commit(tdm_output *output, unsigned int sequence, if (!found) return; + LIST_DEL(&layer_commit_handler->link); + private_layer = layer_commit_handler->private_layer; if (tdm_debug_module & TDM_DEBUG_COMMIT) @@ -661,7 +663,6 @@ _tdm_layer_cb_output_commit(tdm_output *output, unsigned int sequence, _pthread_mutex_lock(&private_display->lock); } - LIST_DEL(&layer_commit_handler->link); free(layer_commit_handler); _pthread_mutex_unlock(&private_display->lock); diff --git a/src/tdm_output.c b/src/tdm_output.c index b37bf4f..2374a36 100644 --- a/src/tdm_output.c +++ b/src/tdm_output.c @@ -605,9 +605,11 @@ tdm_output_cb_vblank(tdm_output *output_backend, unsigned int sequence, if (tdm_debug_module & TDM_DEBUG_COMMIT) TDM_INFO("handler(%p)", v); + LIST_DEL(&v->link); + if (v->func) v->func(v->private_output, sequence, tv_sec, tv_usec, v->user_data); - LIST_DEL(&v->link); + free(v); } _pthread_mutex_lock(&private_display->lock); @@ -651,6 +653,8 @@ tdm_output_cb_commit(tdm_output *output_backend, unsigned int sequence, return; } + LIST_DEL(&output_commit_handler->link); + if (tdm_debug_module & TDM_DEBUG_COMMIT) { TDM_INFO("----------------------------------------- output(%d) committed", private_output->pipe); TDM_INFO("handler(%p)", output_commit_handler); @@ -671,7 +675,6 @@ tdm_output_cb_commit(tdm_output *output_backend, unsigned int sequence, _pthread_mutex_lock(&private_display->lock); } - LIST_DEL(&output_commit_handler->link); free(output_commit_handler); if (tdm_debug_module & TDM_DEBUG_COMMIT) diff --git a/src/tdm_server.c b/src/tdm_server.c index 21c0c9d..5d7159a 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -242,6 +242,8 @@ destroy_vblank_callback(struct wl_resource *resource) TDM_RETURN_IF_FAIL(vblank_info != NULL); + LIST_DEL(&vblank_info->link); + if (vblank_info->vblank) tdm_vblank_destroy(vblank_info->vblank); @@ -250,7 +252,6 @@ destroy_vblank_callback(struct wl_resource *resource) destroy_wait(w); } - LIST_DEL(&vblank_info->link); free(vblank_info); } @@ -549,6 +550,8 @@ destroy_output_callback(struct wl_resource *resource) TDM_RETURN_IF_FAIL(output_info != NULL); + LIST_DEL(&output_info->link); + tdm_output_remove_change_handler(output_info->output, _tdm_server_cb_output_change, output_info); @@ -556,7 +559,6 @@ destroy_output_callback(struct wl_resource *resource) wl_resource_destroy(v->resource); } - LIST_DEL(&output_info->link); free(output_info); } diff --git a/src/tdm_vblank.c b/src/tdm_vblank.c index 2c5eca4..931842d 100644 --- a/src/tdm_vblank.c +++ b/src/tdm_vblank.c @@ -802,6 +802,9 @@ _tdm_vblank_cb_vblank_HW(tdm_output *output, unsigned int sequence, VWR("couldn't update sw timer"); } + LIST_DEL(&wait_info->link); + _tdm_vblank_valid_list_del(&wait_info->valid_link); + /* tv_sec & tv_usec shouldn't be zero. But sometimes they are zero in some hardware. * We need to prohibit that last_time becomes zero because "last_time == 0" means * that a tdm_vblank object is just created and doesn't have the first vblank event. @@ -822,8 +825,6 @@ _tdm_vblank_cb_vblank_HW(tdm_output *output, unsigned int sequence, wait_info->func(private_vblank, TDM_ERROR_NONE, wait_info->target_seq, tv_sec, tv_usec, wait_info->user_data); - LIST_DEL(&wait_info->link); - _tdm_vblank_valid_list_del(&wait_info->valid_link); free(wait_info); } -- 2.7.4 From d447b0346a3f5e0e032a8ba0a2be0f0fce85953b Mon Sep 17 00:00:00 2001 From: Boram Park Date: Tue, 10 Oct 2017 17:12:26 +0900 Subject: [PATCH 14/16] use strncpy, strncat instead of snprintf for getenv Change-Id: Ia83f826590de742b5469163e9c38bdc51deff372 --- src/tdm.c | 9 +++------ src/tdm_server.c | 13 ++++++------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/tdm.c b/src/tdm.c index d942f3a..5db2923 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -846,18 +846,15 @@ _tdm_display_load_module(tdm_private_display *private_display) const char *module_name; char module[TDM_NAME_LEN]; struct dirent **namelist; - int n, size; + int n; tdm_error ret = 0; module_name = getenv("TDM_MODULE"); if (!module_name) module_name = TDM_DEFAULT_MODULE; - size = snprintf(module, sizeof(module), "%s", module_name); - if (size >= (int)sizeof(module)) { - TDM_ERR("too long: %s", module_name); - return TDM_ERROR_OPERATION_FAILED; - }; + strncpy(module, module_name, TDM_NAME_LEN - 1); + module[TDM_NAME_LEN - 1] = '\0'; /* load bufmgr priv from default lib */ ret = _tdm_display_load_module_with_file(private_display, (const char*)module); diff --git a/src/tdm_server.c b/src/tdm_server.c index 5d7159a..3ade1e5 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -791,8 +791,7 @@ static void _tdm_socket_init(tdm_private_loop *private_loop) { const char *dir = NULL; - char socket_path[128]; - int size; + char socket_path[TDM_NAME_LEN * 2]; int ret = -1; uid_t uid; gid_t gid; @@ -803,11 +802,11 @@ _tdm_socket_init(tdm_private_loop *private_loop) return; } - size = snprintf(socket_path, sizeof(socket_path), "%s/%s", dir, "tdm-socket"); - if (size >= (int)sizeof(socket_path)) { - TDM_WRN("too long: %s/tdm-socket", dir); - return; - }; + strncpy(socket_path, dir, TDM_NAME_LEN - 1); + socket_path[TDM_NAME_LEN - 1] = '\0'; + + strncat(socket_path, "/tdm-socket", 11); + socket_path[TDM_NAME_LEN + 10] = '\0'; ret = chmod(socket_path, 509); if (ret < 0) { -- 2.7.4 From 8f26e2a577a18ea0ffeabac7b9ccd9949052d106 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Tue, 10 Oct 2017 17:23:51 +0900 Subject: [PATCH 15/16] package version up to 1.8.2 Change-Id: I540bc9a7eb324f344b1bbaa913b1c2c61921008a --- packaging/libtdm.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/libtdm.spec b/packaging/libtdm.spec index fb8effa..85443b3 100644 --- a/packaging/libtdm.spec +++ b/packaging/libtdm.spec @@ -1,7 +1,7 @@ %bcond_with utest Name: libtdm -Version: 1.8.1 +Version: 1.8.2 Release: 0 Summary: User Library of Tizen Display Manager Group: Development/Libraries -- 2.7.4 From 8f35b8bbce307ac6c3f0fb6cbdb9889823fcf59b Mon Sep 17 00:00:00 2001 From: Roman Marchenko Date: Fri, 26 May 2017 12:54:14 +0300 Subject: [PATCH 16/16] [hwc] add impl of HWC API add the output functions for a backend module: - output_hwc_window_create; - output_hwc_window_destroy; - output_hwc_validate; - output_hwc_get_changed_composition_types; - output_hwc_accept_changes; - output_hwc_get_target_surface_queue; - output_hwc_set_client_target_buffer; create the window functions for a backend module - hwc_window_get_tbm_surface_queue; - hwc_window_set_buffer; - hwc_window_set_composition_type; - hwc_window_set_info; - hwc_window_set_surface_damage; - hwc_window_set_zpos; - hwc_window_set_flags; - hwc_window_unset_flags; - hwc_window_video_get_capability; - hwc_window_video_get_supported_format; Change-Id: I464fd37e5ce6e2f1548b210e935e98d3541b43cd Signed-off-by: Roman Marchenko --- include/tdm.h | 276 ++++++++++++++++++++++++++++++ include/tdm_backend.h | 261 +++++++++++++++++++++++++++++ include/tdm_common.h | 23 +++ include/tdm_types.h | 109 ++++++++++++ src/Makefile.am | 1 + src/tdm.c | 23 +++ src/tdm_backend.c | 41 +++++ src/tdm_hwc_window.c | 454 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/tdm_output.c | 334 +++++++++++++++++++++++++++++++++++++ src/tdm_private.h | 45 ++++- src/tdm_thread.c | 5 + 11 files changed, 1571 insertions(+), 1 deletion(-) create mode 100644 src/tdm_hwc_window.c diff --git a/include/tdm.h b/include/tdm.h index a7695c1..56d7dea 100644 --- a/include/tdm.h +++ b/include/tdm.h @@ -62,6 +62,7 @@ extern "C" { typedef enum { TDM_DISPLAY_CAPABILITY_PP = (1 << 0), /**< if hardware supports pp operation */ TDM_DISPLAY_CAPABILITY_CAPTURE = (1 << 1), /**< if hardware supports capture operation */ + TDM_DISPLAY_CAPABILITY_HWC = (1 << 2), /**< if hardware supports hwc operation @since 2.0.0*/ } tdm_display_capability; /** @@ -569,6 +570,152 @@ tdm_capture * tdm_output_create_capture(tdm_output *output, tdm_error *error); /** + * @brief Creates a new window on the given display. + * @param[in] output A output object + * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. + * @return A created window object + * @since 2.0.0 + */ +tdm_hwc_window * +tdm_output_hwc_create_window(tdm_output *output, tdm_error *error); + +/** + * @brief Destroys the given window. + * @param[in] output A output object + * @param[in] window the pointer of the window to destroy + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 2.0.0 + */ +tdm_error +tdm_output_hwc_destroy_window(tdm_output *output, tdm_hwc_window *hwc_window); + +/** + * @brief Set the client(relative to the TDM) target buffer + * @details Sets the buffer which will receive the output of client composition. + * Window marked as TDM_COMPOSITION_CLIENT or TDM_COMPOSITION_DEVICE_CANDIDATE + * will be composited into this buffer prior to the call to tdm_output_commit(), + * and windows not marked as TDM_COMPOSITION_CLIENT and + * TDM_COMPOSITION_DEVICE_CANDIDATE should be composited with this buffer by the + * device. + * + * The buffer handle provided may be null if no windows are being composited by + * the client. This must not result in an error (unless an invalid display + * handle is also provided). + * + * The damage parameter describes a buffer damage region as defined in the + * description of tdm_hwc_window_set_buffer_damage(). + * + * Will be called before tdm_output_commit() if any of the layers are marked as + * TDM_COMPOSITION_CLIENT or TDM_COMPOSITION_DEVICE_CANDIDATE. If no layers are + * so marked, then it is not necessary to call this function. It is not necessary + * to call tdm_output_hwc_validate() after changing the target through this function. + * @param[in] output A output object + * @param[in] target The new target buffer + * @param[in] damage The buffer damage region + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 2.0.0 + */ +tdm_error +tdm_output_hwc_set_client_target_buffer(tdm_output *output, tbm_surface_h target_buffer, + tdm_hwc_region damage); + +/** + * @brief Validate the output + * @details Instructs the device to inspect all of the layer state and + * determine if there are any composition type changes necessary before + * presenting the output. Permitted changes are described in the definition + * of tdm_hwc_window_composition_t above. + * @param[in] output A output object + * @param[out] num_types The number of composition type changes required by + * the device; if greater than 0, the client must either set and validate new + * types, or call tdm_output_hwc_accept_changes() to accept the changes returned by + * tdm_output_hwc_get_changed_composition_types(); must be the same as the number of + * changes returned by tdm_output_hwc_get_changed_composition_types (see the + * declaration of that function for more information); pointer will be non-NULL + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 2.0.0 + */ +tdm_error +tdm_output_hwc_validate(tdm_output *output, uint32_t *num_types); + +/** + * @brief Set the 'need to validate' handler for the 'output' + * + * @details During backend's work it may need to ask for the revalidation + * (make client (E20) do tdm_output_hwc_validate() again), so a 'hndl' will + * be called as such need happen. Only one handler per output is supported. + * + * @param[in] output The output object a hndl is going to be registered for. + * @param[in] hndl The handler which will be called on the 'need to validate' event. + * + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 2.0.0 + */ +tdm_error +tdm_output_hwc_set_need_validate_handler(tdm_output *output, + tdm_output_need_validate_handler hndl); + +/** + * @brief Get changed composition types + * @details Retrieves the windows for which the device requires a different + * composition type than had been set prior to the last call to tdm_output_hwc_validate(). + * The client will either update its state with these types and call + * tdm_output_hwc_accept_changes, or will set new types and attempt to validate the + * display again. + * windows and types may be NULL to retrieve the number of elements which + * will be returned. The number of elements returned must be the same as the + * value returned in num_types from the last call to tdm_output_hwc_validate(). + * @param[in] output A output object + * @param[out] num_elements If windows or types were NULL, the number of layers + * and types which would have been returned; if both were non-NULL, the + * number of elements returned in layers and types, which must not exceed + * the value stored in num_elements prior to the call; pointer will be + * non-NULL + * @param[in] output A output object + * @param[out] windows An array of windows + * @param[out] composition_types An array of composition types, each corresponding + * to an element of windows + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 2.0.0 + */ +tdm_error +tdm_output_hwc_get_changed_composition_types(tdm_output *output,uint32_t *num_elements, + tdm_hwc_window **hwc_window, + tdm_hwc_window_composition *composition_types); + +/** + * @brief Accepts the changes required by the device + * @details Accepts the changes required by the device from the previous + * tdm_output_hwc_validate() call (which may be queried using + * tdm_output_get_chaged_composition_types()) and revalidates the display. This + * function is equivalent to requesting the changed types from + * tdm_output_get_chaged_composition_types(), setting those types on the + * corresponding windows, and then calling tdm_output_hwc_validate again. + * After this call it must be valid to present this display. Calling this after + * tdm_output_hwc_validate() returns 0 changes must succeed with TDM_ERROR_NONE, but + * should have no other effect. + * @param[in] output A output object + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 2.0.0 + */ +tdm_error +tdm_output_hwc_accept_changes(tdm_output *output); + +/** + * @brief Get a target buffer queue + * @details Buffers from target buffer queue will receive the output of + * client composition. Window marked as TDM_COMPOSITION_CLIENT or + * TDM_COMPOSITION_DEVICE_CANDIDATE will be composited into this buffers + * prior to the call to tdm_output_commit(). + * @param[in] output A output object + * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. + * @return A buffer queue + * @since 2.0.0 + */ +tbm_surface_queue_h +tdm_output_hwc_get_target_buffer_queue(tdm_output *output, tdm_error *error); + +/** * @brief Get the capabilities of a layer object. * @param[in] layer A layer object * @param[out] capabilities The capabilities of a layer object @@ -788,6 +935,135 @@ tdm_error tdm_layer_get_buffer_flags(tdm_layer *layer, unsigned int *flags); /** + * @brief Get a buffer queue for the window object + * @details These buffers are used to composite by hardware a client content in + * the nocomp mode. + * @param[in] hwc_window A window object + * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. + * @return A tbm buffer queue + * @since 2.0.0 + */ +tbm_surface_queue_h +tdm_hwc_window_get_tbm_buffer_queue(tdm_hwc_window *hwc_window, tdm_error *error); + +/** + * @brief Sets the desired Z order of the given window. A window with + * a greater Z value occludes a window with a lesser Z value. + * @param[in] hwc_window A window object + * @param[in] z the new Z order + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 2.0.0 + */ +tdm_error +tdm_hwc_window_set_zpos(tdm_hwc_window *hwc_window, uint32_t zpos); + +/** + * @brief Sets the desired composition type of the given window. + * @details During tdm_output_hwc_validate(), the device may request changes to + * the composition types of any of the layers as described in the definition + * of tdm_hwc_window_composition_t above. + * @param[in] hwc_window A window object + * @param[in] composition_type The new composition type + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 2.0.0 + */ +tdm_error +tdm_hwc_window_set_composition_type(tdm_hwc_window *hwc_window, + tdm_hwc_window_composition composition_type); + +/** + * @brief Set the buffer damage + * @details Provides the region of the source buffer which has been modified + * since the last frame. This region does not need to be validated before + * calling tdm_output_commit(). + * Once set through this function, the damage region remains the same until a + * subsequent call to this function. + * If damage.num_rects > 0, then it may be assumed that any portion of the source + * buffer not covered by one of the rects has not been modified this frame. If + * damage.num_rects == 0, then the whole source buffer must be treated as if it + * has been modified. + * If the layer's contents are not modified relative to the prior frame, damage + * will contain exactly one empty rect([0, 0, 0, 0]). + * The damage rects are relative to the pre-transformed buffer, and their origin + * is the top-left corner. They will not exceed the dimensions of the latched + * buffer. + * @param[in] hwc_window A window object + * @param[in] damage The new buffer damage region + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 2.0.0 + */ +tdm_error +tdm_hwc_window_set_buffer_damage(tdm_hwc_window *hwc_window, tdm_hwc_region damage); + +/** + * @brief Set the information to a window object + * @details The information will be applied when the output object of a window + * object is committed. + * @param[in] hwc_window A window object + * @param[in] info The information + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 2.0.0 + */ +tdm_error +tdm_hwc_window_set_info(tdm_hwc_window *hwc_window, tdm_hwc_window_info *info); + +/** + * @brief Set a TBM buffer to a window object + * @details A TBM buffer will be applied when the output object of a layer + * object is committed. + * @param[in] hwc_window A window object + * @param[in] buffer A TDM buffer + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 2.0.0 + */ +tdm_error +tdm_hwc_window_set_buffer(tdm_hwc_window *hwc_window, tbm_surface_h buffer); + +/** + * @brief Set a flags to a window object + * @param[in] hwc_window A window object + * @param[in] flags A hwc_window flags + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 2.0.0 + */ +tdm_error +tdm_hwc_window_set_flags(tdm_hwc_window *hwc_window, tdm_hwc_window_flag flags); + +/** + * @brief Unset a flags from a window object + * @param[in] hwc_window A window object + * @param[in] flags A hwc_window flags + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 2.0.0 + */ +tdm_error +tdm_hwc_window_unset_flags(tdm_hwc_window *hwc_window, tdm_hwc_window_flag flags); + +/** + * @brief Get the window video capability + * @param[in] hwc_window A window object + * @param[out] video_capability A hwc window video capability + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 2.0.0 + */ +tdm_error +tdm_hwc_window_video_get_capability(tdm_hwc_window *hwc_window, + tdm_hwc_window_video_capability *video_capability); + +/** + * @brief Get the window video supported format + * @param[in] hwc_window A window object + * @param[out] formats A hwc window supported formats + * @param[out] count A number of the hwc window supported formats + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 2.0.0 + */ +tdm_error +tdm_hwc_window_video_get_supported_format(tdm_hwc_window *hwc_window, + const tbm_format **formats, + int *count); + +/** * @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 2de8c7b..f189d43 100644 --- a/include/tdm_backend.h +++ b/include/tdm_backend.h @@ -37,6 +37,7 @@ #define _TDM_BACKEND_H_ #include +#include #include "tdm_types.h" @@ -541,6 +542,124 @@ typedef struct _tdm_func_output { */ tdm_error (*output_set_dpms_async)(tdm_output *output, tdm_output_dpms dpms_value); + /** + * @brief Creates a new window on the given display. + * @param[in] output A output object + * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. + * @return A created window object + * @since 2.0.0 + */ + tdm_hwc_window *(*output_hwc_create_window)(tdm_output *output, tdm_error *error); + + /** + * @brief Destroys the given window. + * @param[in] output A output object + * @param[in] window the pointer of the window to destroy + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 2.0.0 + */ + tdm_error (*output_hwc_destroy_window)(tdm_output *output, tdm_hwc_window *hwc_window); + + /** + * @brief Set the client(relative to the TDM) target buffer + * @details Sets the buffer which will receive the output of client composition. + * Window marked as TDM_COMPOSITION_CLIENT or TDM_COMPOSITION_DEVICE_CANDIDATE + * will be composited into this buffer prior to the call to output_commit(), + * and windows not marked as TDM_COMPOSITION_CLIENT and + * TDM_COMPOSITION_DEVICE_CANDIDATE should be composited with this buffer by the + * device. + * + * The buffer handle provided may be null if no windows are being composited by + * the client. This must not result in an error (unless an invalid display + * handle is also provided). + * + * The damage parameter describes a buffer damage region as defined in the + * description of hwc_window_set_buffer_damage(). + * + * Will be called before output_commit() if any of the layers are marked as + * TDM_COMPOSITION_CLIENT or TDM_COMPOSITION_DEVICE_CANDIDATE. If no layers are + * so marked, then it is not necessary to call this function. It is not necessary + * to call output_hwc_validate() after changing the target through this function. + * @param[in] output A output object + * @param[in] target The new target buffer + * @param[in] damage The buffer damage region + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 2.0.0 + */ + tdm_error (*output_hwc_set_client_target_buffer)(tdm_output *output, tbm_surface_h target_buffer, + tdm_hwc_region damage); + + /** + * @brief Validate the output + * @details Instructs the device to inspect all of the layer state and + * determine if there are any composition type changes necessary before + * presenting the output. Permitted changes are described in the definition + * of tdm_composition_t above. + * @param[in] output A output object + * @param[out] num_types The number of composition type changes required by + * the device; if greater than 0, the client must either set and validate new + * types, or call output_hwc_accept_changes() to accept the changes returned by + * output_hwc_get_changed_composition_types(); must be the same as the number of + * changes returned by output_hwc_get_changed_composition_types (see the + * declaration of that function for more information); pointer will be non-NULL + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 2.0.0 + */ + tdm_error (*output_hwc_validate)(tdm_output *output, uint32_t *num_types); + + /** + * @brief Get changed composition types + * @details Retrieves the windows for which the device requires a different + * composition type than had been set prior to the last call to output_hwc_validate(). + * The client will either update its state with these types and call + * output_hwc_accept_changes, or will set new types and attempt to validate the + * display again. + * layers and types may be NULL to retrieve the number of elements which + * will be returned. The number of elements returned must be the same as the + * value returned in num_types from the last call to output_hwc_validate(). + * @param[in] output A output object + * @param[out] num_elements If windows or types were NULL, the number of layers + * and types which would have been returned; if both were non-NULL, the + * number of elements returned in layers and types, which must not exceed + * the value stored in num_elements prior to the call; pointer will be + * non-NULL + * @param[out] windows An array of windows + * @param[out] composition_types An array of composition types, each + * corresponding to an element of windows + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 2.0.0 + */ + tdm_error (*output_hwc_get_changed_composition_types)(tdm_output *output, + uint32_t *num_elements, + tdm_hwc_window **hwc_window, + tdm_hwc_window_composition *composition_types); + /** + * @brief Accepts the changes required by the device + * @details Accepts the changes required by the device from the previous + * output_hwc_validate() call (which may be queried using + * output_get_chaged_composition_types()) and revalidates the display. This + * function is equivalent to requesting the changed types from + * output_get_chaged_composition_types(), setting those types on the + * corresponding windows, and then calling output_hwc_validate again. + * After this call it must be valid to present this display. Calling this after + * output_hwc_validate() returns 0 changes must succeed with TDM_ERROR_NONE, but + * should have no other effect. + * @param[in] output A output object + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 2.0.0 + */ + tdm_error (*output_hwc_accept_changes)(tdm_output *output); + + /** + * @brief Get a target buffer queue + * @param[in] output A output object + * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. + * @return A buffer queue + * @since 2.0.0 + */ + tbm_surface_queue_h (*output_hwc_get_target_buffer_queue)(tdm_output *output, + tdm_error *error); + void (*reserved3)(void); void (*reserved4)(void); void (*reserved5)(void); @@ -679,6 +798,125 @@ typedef struct _tdm_func_layer { } tdm_func_layer; /** + * @brief The window functions for a backend module. + * @since 2.0.0 + */ +typedef struct _tdm_func_window { + /** + * @brief Get a buffer queue for the window object + * @param[in] hwc_window A window object + * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. + * @return A buffer queue + */ + tbm_surface_queue_h (*hwc_window_get_tbm_buffer_queue)(tdm_hwc_window *hwc_window, + tdm_error *error); + + /** + * @brief Sets the desired Z order (height) of the given window. A window with + * a greater Z value occludes a window with a lesser Z value. + * @param[in] hwc_window A window object + * @param[in] z the new Z order + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*hwc_window_set_zpos)(tdm_hwc_window *hwc_window, uint32_t zpos); + + /** + * @brief Sets the desired composition type of the given window. + * @details During output_hwc_validate(), the device may request changes to + * the composition types of any of the layers as described in the definition + * of tdm_hwc_window_composition_t above. + * @param[in] hwc_window A window object + * @param[in] composition_type The new composition type + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*hwc_window_set_composition_type)(tdm_hwc_window *hwc_window, + tdm_hwc_window_composition composition_type); + + /** + * @brief Set the buffer damage + * @details Provides the region of the source buffer which has been modified + * since the last frame. This region does not need to be validated before + * calling output_commit(). + * Once set through this function, the damage region remains the same until a + * subsequent call to this function. + * If damage.num_rects > 0, then it may be assumed that any portion of the source + * buffer not covered by one of the rects has not been modified this frame. If + * damage.num_rects == 0, then the whole source buffer must be treated as if it + * has been modified. + * If the layer's contents are not modified relative to the prior frame, damage + * will contain exactly one empty rect([0, 0, 0, 0]). + * The damage rects are relative to the pre-transformed buffer, and their origin + * is the top-left corner. They will not exceed the dimensions of the latched + * buffer. + * @param[in] hwc_window A window object + * @param[in] damage The new buffer damage region + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*hwc_window_set_buffer_damage)(tdm_hwc_window *hwc_window, + tdm_hwc_region damage); + + /** + * @brief Set the information to a window object + * @details The information will be applied when the output object + * of a layer object is committed. + * @param[in] hwc_window A window object + * @param[in] info The geometry information + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*hwc_window_set_info)(tdm_hwc_window *hwc_window, + tdm_hwc_window_info *info); + + /** + * @brief Set a TDM buffer to a window object + * @details A TDM buffer will be applied when the output object + * of a layer object is committed. + * @param[in] hwc_window A window object + * @param[in] buffer A TDM buffer + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*hwc_window_set_buffer)(tdm_hwc_window *hwc_window, + tbm_surface_h buffer); + + /** + * @brief Set a flags to a window object + * @param[in] hwc_window A window object + * @param[in] flags A hwc_window flags + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*hwc_window_set_flags)(tdm_hwc_window *hwc_window, + tdm_hwc_window_flag flags); + + /** + * @brief Unset a flags from a window object + * @param[in] hwc_window A window object + * @param[in] flags A hwc_window flags + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*hwc_window_unset_flags)(tdm_hwc_window *hwc_window, + tdm_hwc_window_flag flags); + + /** + * @brief Get the window video capability + * @param[in] hwc_window A window object + * @param[out] video_capability A hwc window video capability + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*hwc_window_video_get_capability)(tdm_hwc_window *hwc_window, + tdm_hwc_window_video_capability *video_capability); + + /** + * @brief Get the window video supported format + * @param[in] hwc_window A window object + * @param[out] formats A hwc window supported formats + * @param[out] count A number of the hwc window supported formats + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*hwc_window_video_get_supported_format)(tdm_hwc_window *hwc_window, + const tbm_format **formats, + int *count); +} tdm_func_hwc_window; + +/** * @brief The pp functions for a backend module. */ typedef struct _tdm_func_pp { @@ -933,6 +1171,19 @@ tdm_error tdm_backend_register_func_layer(tdm_display *dpy, tdm_func_layer *func_layer); /** + * @brief Register the backend hwc_window functions to a display + * @param[in] dpy A display object + * @param[in] func_hwc_window hwc_window functions + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @see tdm_backend_register_func_display, tdm_backend_register_func_output + * @remarks + * A backend module @b SHOULD set the backend hwc_window functions at least. + * @since 2.0.0 + */ +tdm_error +tdm_backend_register_func_hwc_window(tdm_display *dpy, tdm_func_hwc_window *func_hwc_window); + +/** * @brief Register the backend pp functions to a display * @param[in] dpy A display object * @param[in] func_pp pp functions @@ -1076,6 +1327,16 @@ tdm_event_loop_source_timer_update(tdm_event_loop_source *source, unsigned int m void tdm_event_loop_source_remove(tdm_event_loop_source *source); +/** + * @brief Trigger a 'need to validate' event. + * @param[in] output The output the event should be triggered for. + * @note The global display lock has to be locked before the call to this function. + * @see #tdm_output_hwc_set_need_validate_handler + * @since 2.0.0 + */ +tdm_error +tdm_backend_trigger_need_validate_event(tdm_output *output); + #ifdef __cplusplus } #endif diff --git a/include/tdm_common.h b/include/tdm_common.h index 054cb5e..6ba2784 100644 --- a/include/tdm_common.h +++ b/include/tdm_common.h @@ -130,6 +130,16 @@ typedef enum { } tdm_layer_capability; /** + * @brief The hwc window capability enumeration + * @since 2.0.0 + */ +typedef enum { + TDM_HWC_WINDOW_VIDEO_CAPABILITY_SCALE = (1 << 1), /**< if a hwc window video has scale capability */ + TDM_HWC_WINDOW_VIDEO_CAPABILITY_TRANSFORM = (1 << 2), /**< if a hwc window video has transform capability */ + TDM_HWC_WINDOW_VIDEO_CAPABILITY_SCANOUT = (1 << 3), /**< if a hwc_window video allows a scanout buffer only */ +} tdm_hwc_window_video_capability; + +/** * @brief The pp capability enumeration */ typedef enum { @@ -262,6 +272,11 @@ typedef struct _tdm_pos { unsigned int h; } tdm_pos; +typedef struct _tdm_hwc_region { + unsigned int num_rects; + tdm_pos const *rects; +} tdm_hwc_region; + /** * @brief The tdm value type enumeration */ @@ -285,6 +300,14 @@ typedef union { uint64_t u64; } tdm_value; +/** + * @brief The hwc window flag enumeration + * @since 2.0.0 + */ +typedef enum { + TDM_HWC_WINDOW_FLAG_SKIP = (1 << 0), +} tdm_hwc_window_flag; + #ifdef __cplusplus } #endif diff --git a/include/tdm_types.h b/include/tdm_types.h index 2cda6d6..58a787f 100644 --- a/include/tdm_types.h +++ b/include/tdm_types.h @@ -107,6 +107,16 @@ typedef struct _tdm_info_layer { } tdm_info_layer; /** + * @brief The hwc window info structure + * @since 2.0.0 + */ +typedef struct _tdm_hwc_window_info { + tdm_info_config src_config; + tdm_pos dst_pos; + tdm_transform transform; +} tdm_hwc_window_info; + +/** * @brief The pp info structre */ typedef struct _tdm_info_pp { @@ -129,6 +139,93 @@ typedef struct _tdm_info_capture { } tdm_info_capture; /** + * @brief Possible composition types for a given window + * @since 2.0.0 + */ +typedef enum { + /** The client will composite this window into the client target window + * + * User can choose this type for window to avoid a hardware composition for + * this window. + * + * User has to set this type for the window which has the TDM_COMPOSITION_CLIENT_CANDIDATE + * type. (see the TDM_COMPOSITION_CLIENT_CANDIDATE type) + * + * The device must not request any composition type changes for windows of + * this type. + */ + TDM_COMPOSITION_CLIENT = 0, + + /* Set by the HWC after tdm_output_hwc_validate(). + * + * If the HWC decided that it doesn't want/can't to continue the composition for + * the window through a hardware overlay or other similar way it'll change + * a type to the TDM_COMPOSITION_CLIENT_CANDIDATE type. + * + * This transition can happen only if the window has the TDM_COMPOSITION_DEVICE + * or the TDM_COMPOSITION_VIDEO type already. + * + * If an user changed type of a window from the TDM_COMPOSITION_DEVICE or the + * TDM_COMPOSITION_VIDEO type to the the TDM_COMPOSITION_CLIENT type, the type + * will be rejected to the TDM_COMPOSITION_CLIENT_CANDIDATE type. + * + * The user has to composite this window itself. + * + * The underlying hardware overlay is owned by this window till a type being + * changed to the TDM_COMPOSITION_CLIENT type, but it's not possible to set a + * buffer for this window after a type's been changed to the + * TDM_COMPOSITION_CLIENT_CANDIDATE type. + * + * This transitional state is used to get rid of blinking at a transition from + * the TDM_COMPOSITION_DEVICE/TDM_COMPOSITION_VIDEO type to the + * TDM_COMPOSITION_CLIENT type where the hw has to wait till a buffer, which was + * on a hw overlay, get composited to the fb_target and only after this happens + * unset(or set another window on) this hw overlay. + * + * User has to inform the HWC (the HWC got no way to know when it happens) after a buffer, + * which was on a hw overlay, get composited to the fb_target by setting a type of this + * window to the TDM_COMPOSITION_CLIENT type, it causes a type of this window being changed + * to TDM_COMPOSITION_CLIENT. + */ + TDM_COMPOSITION_CLIENT_CANDIDATE = 5, + + /** Set by the client before tdm_output_hwc_validate(). + * + * Upon tdm_output_hwc_validate(), the device may request a change from this type to + * TDM_COMPOSITION_DEVICE or TDM_COMPOSITION_CLIENT. */ + TDM_COMPOSITION_DEVICE_CANDIDATE = 2, + + /** Set by the HWC after tdm_output_hwc_validate(). + * + * The device will handle the composition of this window through a hardware + * overlay or other similar means. + * + * Upon tdm_output_hwc_validate(), the device may request a change from this type to + * TDM_COMPOSITION_CLIENT or TDM_COMPOSITION_DEVICE_CANDIDATE. */ + TDM_COMPOSITION_DEVICE = 1, + + /** Similar to DEVICE, but the position of this layer may also be set + * asynchronously through layer_set_cursor_position. If this functionality is not + * supported on a layer that the client sets to TDM_COMPOSITION_CURSOR, the + * device must request that the composition type of that layer is changed to + * TDM_COMPOSITION_CLIENT upon the next call to tdm_output_hwc_validate(). + * + * Upon tdm_output_hwc_validate(), the device may request a change from this type to + * either TDM_COMPOSITION_DEVICE or TDM_COMPOSITION_CLIENT. Changing to + * TDM_COMPOSITION_DEVICE will prevent the use of layer_set_cursor_position but + * still permit the device to composite the layer. */ + TDM_COMPOSITION_CURSOR = 3, + + /** The device will handle the composition of this layer through a hardware + * overlay or other similar means. + * + * Upon tdm_output_hwc_validate(), the device may request a change from this type to + * either TDM_COMPOSITION_DEVICE or TDM_COMPOSITION_CLIENT, but it is + * unlikely that content will display correctly in these cases. */ + TDM_COMPOSITION_VIDEO = 4, +} tdm_hwc_window_composition; + +/** * @brief The tdm display object */ typedef void tdm_display; @@ -144,6 +241,12 @@ typedef void tdm_output; typedef void tdm_layer; /** + * @brief The tdm window object + * @since 2.0.0 + */ +typedef void tdm_hwc_window; + +/** * @brief The tdm capture object */ typedef void tdm_capture; @@ -193,6 +296,12 @@ typedef void (*tdm_pp_done_handler)(tdm_pp *pp, tbm_surface_h src, typedef void (*tdm_capture_done_handler)(tdm_capture *capture, tbm_surface_h buffer, void *user_data); +/** + * @brief The 'need to validate' handler of an output object + * @since 2.0.0 + */ +typedef void (*tdm_output_need_validate_handler)(tdm_output *output); + #ifdef __cplusplus } #endif diff --git a/src/Makefile.am b/src/Makefile.am index 87169ff..8542130 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -22,6 +22,7 @@ libtdm_la_SOURCES = \ tdm_display.c \ tdm_output.c \ tdm_layer.c \ + tdm_hwc_window.c \ tdm_pp.c \ tdm_capture.c \ tdm_monitor_server.c \ diff --git a/src/tdm.c b/src/tdm.c index 5db2923..bb737bb 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -152,6 +152,7 @@ _tdm_display_destroy_private_output(tdm_private_output *private_output) { tdm_private_display *private_display = private_output->private_display; tdm_private_layer *l = NULL, *ll = NULL; + tdm_private_hwc_window *hw = NULL, *hww = NULL; tdm_private_capture *c = NULL, *cc = NULL; tdm_private_vblank_handler *v = NULL, *vv = NULL; tdm_private_output_commit_handler *om = NULL, *omm = NULL; @@ -202,6 +203,9 @@ _tdm_display_destroy_private_output(tdm_private_output *private_output) LIST_FOR_EACH_ENTRY_SAFE(c, cc, &private_output->capture_list, link) tdm_capture_destroy_internal(c); + LIST_FOR_EACH_ENTRY_SAFE(hw, hww, &private_output->hwc_window_list, link) + tdm_hwc_window_destroy_internal(hw); + LIST_FOR_EACH_ENTRY_SAFE(l, ll, &private_output->layer_list, link) _tdm_display_destroy_private_layer(l); @@ -402,6 +406,7 @@ tdm_display_update_output(tdm_private_display *private_display, private_output->index = pipe; LIST_INITHEAD(&private_output->layer_list); + LIST_INITHEAD(&private_output->hwc_window_list); LIST_INITHEAD(&private_output->capture_list); LIST_INITHEAD(&private_output->vblank_handler_list); LIST_INITHEAD(&private_output->output_commit_handler_list); @@ -945,6 +950,13 @@ tdm_display_init(tdm_error *error) tdm_display_enable_commit_per_vblank(private_display, enable); } + str = getenv("TDM_HWC"); + if (str) { + char *end; + int enable = strtol(str, &end, 10); + private_display->hwc_enable = enable; + } + if (pthread_mutex_init(&private_display->lock, NULL)) { ret = TDM_ERROR_OPERATION_FAILED; TDM_ERR("mutex init failed: %m"); @@ -969,6 +981,9 @@ tdm_display_init(tdm_error *error) if (ret != TDM_ERROR_NONE) goto failed_load; + if (!(private_display->capabilities & TDM_DISPLAY_CAPABILITY_HWC)) + private_display->hwc_enable = 0; + stamp2 = tdm_helper_get_time(); TDM_DBG("loading backend time: %.3f ms", (stamp2 - stamp1) * 1000.0); stamp1 = stamp2; @@ -1006,6 +1021,12 @@ tdm_display_init(tdm_error *error) private_display->init_count = 1; private_display->commit_type = TDM_COMMIT_TYPE_NONE; + if (private_display->hwc_enable) { + tdm_private_output *o = NULL; + LIST_FOR_EACH_ENTRY(o, &private_display->output_list, link) + tdm_output_need_validate_event_init(o); + } + g_private_display = private_display; if (error) @@ -1210,6 +1231,8 @@ tdm_display_enable_dump(tdm_private_display *private_display, const char *dump_s tdm_debug_dump |= TDM_DUMP_FLAG_PP; } else if (!strncmp(arg, "capture", 7)) { tdm_debug_dump |= TDM_DUMP_FLAG_CAPTURE; + } else if (!strncmp(arg, "window", 6)) { + tdm_debug_dump |= TDM_DUMP_FLAG_WINDOW; } else goto done; diff --git a/src/tdm_backend.c b/src/tdm_backend.c index acbde8e..b5e50c6 100644 --- a/src/tdm_backend.c +++ b/src/tdm_backend.c @@ -128,6 +128,27 @@ tdm_backend_register_func_layer(tdm_display *dpy, tdm_func_layer *func_layer) } EXTERN tdm_error +tdm_backend_register_func_hwc_window(tdm_display *dpy, tdm_func_hwc_window *func_hwc_window) +{ + tdm_backend_module *module; + + TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED); + + BACKEND_FUNC_ENTRY(); + + TDM_RETURN_VAL_IF_FAIL(func_hwc_window != NULL, TDM_ERROR_INVALID_PARAMETER); + + module = private_display->module_data; + if (_check_abi_version(module, 2, 0) < 0) + return TDM_ERROR_BAD_MODULE; + + private_display->capabilities |= TDM_DISPLAY_CAPABILITY_HWC; + private_display->func_hwc_window = *func_hwc_window; + + return TDM_ERROR_NONE; +} + +EXTERN tdm_error tdm_backend_register_func_pp(tdm_display *dpy, tdm_func_pp *func_pp) { TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED); @@ -160,3 +181,23 @@ tdm_backend_register_func_capture(tdm_display *dpy, return TDM_ERROR_NONE; } +EXTERN tdm_error +tdm_backend_trigger_need_validate_event(tdm_output *output) +{ + tdm_private_output *private_output; + uint64_t value; + int res; + + TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_output = (tdm_private_output*)output; + value = 1; + + /* do not lock the global display lock here */ + + res = write(private_output->need_validate.event_fd, &value, sizeof(value)); + if (res < 0) + return TDM_ERROR_OPERATION_FAILED; + + return TDM_ERROR_NONE; +} diff --git a/src/tdm_hwc_window.c b/src/tdm_hwc_window.c new file mode 100644 index 0000000..0fffd7d --- /dev/null +++ b/src/tdm_hwc_window.c @@ -0,0 +1,454 @@ +/************************************************************************** + * + * 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_backend.h" +#include "tdm_private.h" +#include "tdm_helper.h" + +#define COUNT_MAX 10 + +#define HWC_WINDOW_FUNC_ENTRY() \ + tdm_private_display *private_display; \ + tdm_private_output *private_output; \ + tdm_private_hwc_window *private_hwc_window; \ + tdm_error ret = TDM_ERROR_NONE; /* default TDM_ERROR_NONE */\ + TDM_RETURN_VAL_IF_FAIL(hwc_window != NULL, TDM_ERROR_INVALID_PARAMETER); \ + private_hwc_window = (tdm_private_hwc_window*)hwc_window; \ + private_output = private_hwc_window->private_output; \ + private_display = private_output->private_display + +#define HWC_WINDOW_FUNC_ENTRY_ERROR() \ + tdm_private_display *private_display; \ + tdm_private_output *private_output; \ + tdm_private_hwc_window *private_hwc_window; \ + tdm_error ret = TDM_ERROR_NONE; /* default TDM_ERROR_NONE */\ + TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(hwc_window != NULL, TDM_ERROR_INVALID_PARAMETER, NULL); \ + private_hwc_window = (tdm_private_hwc_window*)hwc_window; \ + private_output = private_hwc_window->private_output; \ + private_display = private_output->private_display + +#define HWC_WINDOW_FUNC_ENTRY_VOID_RETURN() \ + tdm_private_display *private_display; \ + tdm_private_output *private_output; \ + tdm_private_hwc_window *private_hwc_window; \ + tdm_error ret = TDM_ERROR_NONE; /* default TDM_ERROR_NONE */\ + TDM_RETURN_IF_FAIL(hwc_window != NULL); \ + private_hwc_window = (tdm_private_hwc_window*)hwc_window; \ + private_output = private_hwc_window->private_output; \ + private_display = private_output->private_display + +tbm_surface_queue_h +tdm_hwc_window_get_tbm_buffer_queue(tdm_hwc_window *hwc_window, tdm_error *error) +{ + tdm_func_hwc_window *func_hwc_window = NULL; + tbm_surface_queue_h queue = NULL; + + HWC_WINDOW_FUNC_ENTRY_ERROR(); + + _pthread_mutex_lock(&private_display->lock); + + func_hwc_window = &private_display->func_hwc_window; + + if (!func_hwc_window->hwc_window_get_tbm_buffer_queue) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + if (error) + *error = TDM_ERROR_NOT_IMPLEMENTED; + return NULL; + } + + queue = func_hwc_window->hwc_window_get_tbm_buffer_queue(private_hwc_window->hwc_window_backend, error); + + _pthread_mutex_unlock(&private_display->lock); + + return queue; +} + +EXTERN tdm_error +tdm_hwc_window_set_zpos(tdm_hwc_window *hwc_window, uint32_t zpos) +{ + tdm_func_hwc_window *func_hwc_window = NULL; + + HWC_WINDOW_FUNC_ENTRY(); + + _pthread_mutex_lock(&private_display->lock); + + func_hwc_window = &private_display->func_hwc_window; + + if (!func_hwc_window->hwc_window_set_zpos) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + ret = func_hwc_window->hwc_window_set_zpos(private_hwc_window->hwc_window_backend, zpos); + if (ret == TDM_ERROR_NONE) + private_hwc_window->zpos = zpos; + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +EXTERN tdm_error +tdm_hwc_window_set_composition_type(tdm_hwc_window *hwc_window, + tdm_hwc_window_composition composition_type) +{ + tdm_func_hwc_window *func_hwc_window = NULL; + + HWC_WINDOW_FUNC_ENTRY(); + + _pthread_mutex_lock(&private_display->lock); + + func_hwc_window = &private_display->func_hwc_window; + + if (!func_hwc_window->hwc_window_set_composition_type) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + ret = func_hwc_window->hwc_window_set_composition_type(private_hwc_window->hwc_window_backend, composition_type); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +EXTERN tdm_error +tdm_hwc_window_set_buffer_damage(tdm_hwc_window *hwc_window, tdm_hwc_region damage) +{ + tdm_func_hwc_window *func_hwc_window = NULL; + + HWC_WINDOW_FUNC_ENTRY(); + + _pthread_mutex_lock(&private_display->lock); + + func_hwc_window = &private_display->func_hwc_window; + + if (!func_hwc_window->hwc_window_set_buffer_damage) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + ret = func_hwc_window->hwc_window_set_buffer_damage(private_hwc_window->hwc_window_backend, damage); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + + +EXTERN tdm_error +tdm_hwc_window_set_info(tdm_hwc_window *hwc_window, tdm_hwc_window_info *info) +{ + tdm_func_hwc_window *func_hwc_window = NULL; + char fmtstr[128]; + + HWC_WINDOW_FUNC_ENTRY(); + + TDM_RETURN_VAL_IF_FAIL(info != NULL, TDM_ERROR_INVALID_PARAMETER); + + _pthread_mutex_lock(&private_display->lock); + + func_hwc_window = &private_display->func_hwc_window; + + if (!func_hwc_window->hwc_window_set_info) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + if (info->src_config.format) + snprintf(fmtstr, 128, "%c%c%c%c", FOURCC_STR(info->src_config.format)); + else + snprintf(fmtstr, 128, "NONE"); + + TDM_INFO("hwc_window(%p) info: src(%dx%d %d,%d %dx%d %s) dst(%d,%d %dx%d) trans(%d)", + private_hwc_window, info->src_config.size.h, info->src_config.size.v, + info->src_config.pos.x, info->src_config.pos.y, + info->src_config.pos.w, info->src_config.pos.h, + fmtstr, + info->dst_pos.x, info->dst_pos.y, + info->dst_pos.w, info->dst_pos.h, + info->transform); + + ret = func_hwc_window->hwc_window_set_info(private_hwc_window->hwc_window_backend, info); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +static void +_tdm_window_dump_buffer(tdm_hwc_window *hwc_window, tbm_surface_h buffer) +{ + tdm_private_hwc_window *private_window = (tdm_private_hwc_window *)hwc_window; + tdm_private_output *private_output = private_window->private_output; + unsigned int pipe; + uint32_t zpos; + char fname[PATH_MAX]; + + pipe = private_output->pipe; + zpos = private_window->zpos; + + snprintf(fname, sizeof(fname), "tdm_%d_win_%d", pipe, zpos); + + tbm_surface_internal_dump_buffer(buffer, fname); + TDM_DBG("%s dump excute", fname); + + return; +} + +EXTERN tdm_error +tdm_hwc_window_set_buffer(tdm_hwc_window *hwc_window, tbm_surface_h buffer) +{ + tdm_func_hwc_window *func_hwc_window; + + HWC_WINDOW_FUNC_ENTRY(); + + _pthread_mutex_lock(&private_display->lock); + + if ((tdm_debug_dump & TDM_DUMP_FLAG_WINDOW) && buffer) { + char str[TDM_PATH_LEN]; + static int i; + snprintf(str, TDM_PATH_LEN, "window_%d_%d_%03d", + private_output->index, private_hwc_window->zpos, i++); + tdm_helper_dump_buffer_str(buffer, tdm_debug_dump_dir, str); + } + + func_hwc_window = &private_display->func_hwc_window; + + if (!func_hwc_window->hwc_window_set_buffer) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + /* dump buffer */ + if (tdm_dump_enable && buffer) + _tdm_window_dump_buffer(hwc_window, buffer); + + ret = func_hwc_window->hwc_window_set_buffer(private_hwc_window->hwc_window_backend, buffer); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +INTERN tdm_hwc_window * +tdm_hwc_window_create_internal(tdm_private_output *private_output, + tdm_error *error) +{ + tdm_private_display *private_display = private_output->private_display; + tdm_func_output *func_output = &private_display->func_output; + tdm_private_hwc_window *private_hwc_window = NULL; + tdm_hwc_window *hwc_window_backend = NULL; + tdm_error ret = TDM_ERROR_NONE; + + TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), NULL); + + if (!func_output->output_hwc_create_window) { + if (error) + *error = TDM_ERROR_BAD_MODULE; + return NULL; + } + + hwc_window_backend = func_output->output_hwc_create_window( + private_output->output_backend, &ret); + if (ret != TDM_ERROR_NONE) { + if (error) + *error = ret; + return NULL; + } + + private_hwc_window = calloc(1, sizeof(tdm_private_capture)); + if (!private_hwc_window) { + TDM_ERR("failed: alloc memory"); + func_output->output_hwc_destroy_window(private_output->output_backend, hwc_window_backend); + if (error) + *error = TDM_ERROR_OUT_OF_MEMORY; + return NULL; + } + + LIST_ADD(&private_hwc_window->link, &private_output->hwc_window_list); + + private_hwc_window->private_display = private_display; + private_hwc_window->private_output = private_output; + private_hwc_window->hwc_window_backend = hwc_window_backend; + + TDM_DBG("hwc_window(%p) create", private_hwc_window); + + if (error) + *error = TDM_ERROR_NONE; + + return private_hwc_window; +} + +INTERN tdm_error +tdm_hwc_window_destroy_internal(tdm_private_hwc_window * private_hwc_window) +{ + tdm_private_display *private_display; + tdm_private_output *private_output; + tdm_func_output *func_output; + + TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED); + + if (!private_hwc_window) + return TDM_ERROR_OPERATION_FAILED; + + private_display = private_hwc_window->private_display; + private_output = private_hwc_window->private_output; + + LIST_DEL(&private_hwc_window->link); + + func_output = &private_display->func_output; + func_output->output_hwc_destroy_window(private_output->output_backend, private_hwc_window->hwc_window_backend); + + free(private_hwc_window); + return TDM_ERROR_NONE; +} + +EXTERN tdm_error +tdm_hwc_window_set_flags(tdm_hwc_window *hwc_window, tdm_hwc_window_flag flags) +{ + tdm_func_hwc_window *func_hwc_window = NULL; + + HWC_WINDOW_FUNC_ENTRY(); + + _pthread_mutex_lock(&private_display->lock); + + func_hwc_window = &private_display->func_hwc_window; + + if (!func_hwc_window->hwc_window_set_flags) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + ret = func_hwc_window->hwc_window_set_flags(private_hwc_window->hwc_window_backend, flags); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +EXTERN tdm_error +tdm_hwc_window_unset_flags(tdm_hwc_window *hwc_window, tdm_hwc_window_flag flags) +{ + tdm_func_hwc_window *func_hwc_window = NULL; + + HWC_WINDOW_FUNC_ENTRY(); + + _pthread_mutex_lock(&private_display->lock); + + func_hwc_window = &private_display->func_hwc_window; + + if (!func_hwc_window->hwc_window_unset_flags) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + ret = func_hwc_window->hwc_window_unset_flags(private_hwc_window->hwc_window_backend, flags); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +EXTERN tdm_error +tdm_hwc_window_video_get_capability(tdm_hwc_window *hwc_window, + tdm_hwc_window_video_capability *video_capability) +{ + tdm_func_hwc_window *func_hwc_window = NULL; + + HWC_WINDOW_FUNC_ENTRY(); + + TDM_RETURN_VAL_IF_FAIL(video_capability != NULL, TDM_ERROR_INVALID_PARAMETER); + + _pthread_mutex_lock(&private_display->lock); + + func_hwc_window = &private_display->func_hwc_window; + + if (!func_hwc_window->hwc_window_video_get_capability) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + ret = func_hwc_window->hwc_window_video_get_capability(private_hwc_window->hwc_window_backend, + video_capability); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +EXTERN tdm_error +tdm_hwc_window_video_get_supported_format(tdm_hwc_window *hwc_window, + const tbm_format **formats, + int *count) +{ + tdm_func_hwc_window *func_hwc_window = NULL; + + HWC_WINDOW_FUNC_ENTRY(); + + TDM_RETURN_VAL_IF_FAIL(formats != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(count != NULL, TDM_ERROR_INVALID_PARAMETER); + + _pthread_mutex_lock(&private_display->lock); + + func_hwc_window = &private_display->func_hwc_window; + + if (!func_hwc_window->hwc_window_video_get_supported_format) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + ret = func_hwc_window->hwc_window_video_get_supported_format(private_hwc_window->hwc_window_backend, + formats, count); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} diff --git a/src/tdm_output.c b/src/tdm_output.c index 2374a36..98b1a5a 100644 --- a/src/tdm_output.c +++ b/src/tdm_output.c @@ -60,6 +60,21 @@ private_output = (tdm_private_output*)output; \ private_display = private_output->private_display + +static tdm_private_hwc_window * +_tdm_output_find_private_hwc_window(tdm_private_output *private_output, + tdm_hwc_window *hwc_window_backend) +{ + tdm_private_hwc_window *private_hwc_window = NULL; + + LIST_FOR_EACH_ENTRY(private_hwc_window, &private_output->hwc_window_list, link) { + if (private_hwc_window->hwc_window_backend == hwc_window_backend) + return private_hwc_window; + } + + return NULL; +} + EXTERN tdm_error tdm_output_get_model_info(tdm_output *output, const char **maker, const char **model, const char **name) @@ -283,6 +298,12 @@ tdm_output_get_layer_count(tdm_output *output, int *count) _pthread_mutex_lock(&private_display->lock); + if (private_display->hwc_enable) { + *count = 0; + _pthread_mutex_unlock(&private_display->lock); + return TDM_ERROR_NONE; + } + *count = 0; LIST_FOR_EACH_ENTRY(private_layer, &private_output->layer_list, link) (*count)++; @@ -309,6 +330,13 @@ tdm_output_get_layer(tdm_output *output, int index, tdm_error *error) if (error) *error = TDM_ERROR_NONE; + if (private_display->hwc_enable) { + _pthread_mutex_unlock(&private_display->lock); + if (error) + *error = TDM_ERROR_INVALID_PARAMETER; + return NULL; + } + LIST_FOR_EACH_ENTRY(private_layer, &private_output->layer_list, link) { if (private_layer->index == index) { _pthread_mutex_unlock(&private_display->lock); @@ -1310,6 +1338,250 @@ tdm_output_create_capture(tdm_output *output, tdm_error *error) return capture; } +EXTERN tdm_hwc_window * +tdm_output_hwc_create_window(tdm_output *output, tdm_error *error) +{ + tdm_hwc_window *hwc_window = NULL; + + OUTPUT_FUNC_ENTRY_ERROR(); + + _pthread_mutex_lock(&private_display->lock); + + if (private_display->hwc_enable) + hwc_window = (tdm_hwc_window *)tdm_hwc_window_create_internal(private_output, error); + else + if (error) + *error = TDM_ERROR_BAD_REQUEST; + + _pthread_mutex_unlock(&private_display->lock); + + return hwc_window; +} + +EXTERN tdm_error +tdm_output_hwc_destroy_window(tdm_output *output, tdm_hwc_window *hwc_window) +{ + OUTPUT_FUNC_ENTRY(); + + TDM_RETURN_VAL_IF_FAIL(hwc_window != NULL, TDM_ERROR_INVALID_PARAMETER); + + _pthread_mutex_lock(&private_display->lock); + + ret = tdm_hwc_window_destroy_internal(hwc_window); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +EXTERN tdm_error +tdm_output_hwc_validate(tdm_output *output, uint32_t *num_types) +{ + tdm_func_output *func_output = NULL; + + OUTPUT_FUNC_ENTRY(); + + TDM_RETURN_VAL_IF_FAIL(num_types != NULL, TDM_ERROR_INVALID_PARAMETER); + + _pthread_mutex_lock(&private_display->lock); + + func_output = &private_display->func_output; + + if (!func_output->output_hwc_validate) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + ret = func_output->output_hwc_validate(private_output->output_backend, num_types); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +EXTERN tdm_error +tdm_output_hwc_set_need_validate_handler(tdm_output *output, + tdm_output_need_validate_handler hndl) +{ + OUTPUT_FUNC_ENTRY(); + + TDM_RETURN_VAL_IF_FAIL(hndl != NULL, TDM_ERROR_INVALID_PARAMETER); + + _pthread_mutex_lock(&private_display->lock); + + /* there's no reason to allow this */ + if (private_output->need_validate.hndl) { + + _pthread_mutex_unlock(&private_display->lock); + return TDM_ERROR_OPERATION_FAILED; + } + + private_output->need_validate.hndl = hndl; + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +EXTERN tdm_error +tdm_output_hwc_get_changed_composition_types(tdm_output *output, + uint32_t *num_elements, + tdm_hwc_window **hwc_window, + tdm_hwc_window_composition *composition_types) +{ + tdm_func_output *func_output = NULL; + tdm_private_hwc_window * private_hwc_window = NULL; + int i = 0; + + OUTPUT_FUNC_ENTRY(); + + TDM_RETURN_VAL_IF_FAIL(num_elements != NULL, TDM_ERROR_INVALID_PARAMETER); + + _pthread_mutex_lock(&private_display->lock); + + func_output = &private_display->func_output; + + if (!func_output->output_hwc_get_changed_composition_types) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + ret = func_output->output_hwc_get_changed_composition_types(private_output->output_backend, + num_elements, hwc_window, composition_types); + if (ret != TDM_ERROR_NONE) { + _pthread_mutex_unlock(&private_display->lock); + return ret; + } + + if (hwc_window == NULL || composition_types == NULL) { + _pthread_mutex_unlock(&private_display->lock); + return TDM_ERROR_NONE; + } + + for (i = 0; i < *num_elements; i++) { + + private_hwc_window = _tdm_output_find_private_hwc_window(private_output, hwc_window[i]); + + if (private_hwc_window == NULL) { + TDM_ERR("failed! This should never happen!"); + func_output->output_hwc_destroy_window(private_output->output_backend, hwc_window[i]); + *num_elements = 0; + _pthread_mutex_unlock(&private_display->lock); + return TDM_ERROR_OPERATION_FAILED; + } + + hwc_window[i] = (tdm_hwc_window*)private_hwc_window; + } + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +tdm_error +tdm_output_hwc_accept_changes(tdm_output *output) +{ + tdm_func_output *func_output = NULL; + + OUTPUT_FUNC_ENTRY(); + + _pthread_mutex_lock(&private_display->lock); + + func_output = &private_display->func_output; + + if (!func_output->output_hwc_validate) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + ret = func_output->output_hwc_accept_changes(private_output->output_backend); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +tbm_surface_queue_h +tdm_output_hwc_get_target_buffer_queue(tdm_output *output, tdm_error *error) +{ + tdm_func_output *func_output = NULL; + tbm_surface_queue_h queue = NULL; + + OUTPUT_FUNC_ENTRY_ERROR(); + + _pthread_mutex_lock(&private_display->lock); + + func_output = &private_display->func_output; + + if (!func_output->output_hwc_get_target_buffer_queue) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return NULL; + } + + queue = func_output->output_hwc_get_target_buffer_queue(private_output->output_backend, error); + + _pthread_mutex_unlock(&private_display->lock); + + return queue; +} + +static void +_tdm_target_window_dump_buffer(tdm_private_output *private_output, tbm_surface_h buffer) +{ + unsigned int pipe; + char fname[PATH_MAX]; + + pipe = private_output->pipe; + + snprintf(fname, sizeof(fname), "tdm_%d_target_win", pipe); + + tbm_surface_internal_dump_buffer(buffer, fname); + TDM_DBG("%s dump excute", fname); + + return; +} + +EXTERN tdm_error +tdm_output_hwc_set_client_target_buffer(tdm_output *output, tbm_surface_h target_buffer, + tdm_hwc_region damage) +{ + tdm_func_output *func_output = NULL; + + OUTPUT_FUNC_ENTRY(); + + _pthread_mutex_lock(&private_display->lock); + + if (tdm_debug_dump & TDM_DUMP_FLAG_WINDOW) { + char str[TDM_PATH_LEN]; + static int i; + snprintf(str, TDM_PATH_LEN, "target_window_%d_%03d", + private_output->index, i++); + tdm_helper_dump_buffer_str(target_buffer, tdm_debug_dump_dir, str); + } + + func_output = &private_display->func_output; + + if (!func_output->output_hwc_set_client_target_buffer) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + /* dump buffer */ + if (tdm_dump_enable) + _tdm_target_window_dump_buffer((tdm_private_output *)output, target_buffer); + + ret = func_output->output_hwc_set_client_target_buffer(private_output->output_backend, target_buffer, damage); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + INTERN void tdm_output_call_change_handler_internal(tdm_private_output *private_output, struct list_head *change_handler_list, @@ -1346,3 +1618,65 @@ tdm_output_call_change_handler_internal(tdm_private_output *private_output, _pthread_mutex_lock(&private_display->lock); } } + + +/* gets called on behalf of the ecore-main-loop thread */ +INTERN tdm_error +tdm_output_cb_need_validate(tdm_private_output *private_output) +{ + TDM_RETURN_VAL_IF_FAIL(private_output != NULL, TDM_ERROR_INVALID_PARAMETER); + + TDM_INFO("tdm-backend asks for revalidation for the output:%p.", private_output); + + if (private_output->need_validate.hndl) + private_output->need_validate.hndl((tdm_output*)private_output); + + return TDM_ERROR_NONE; +} + +/* gets called on behalf of the tdm-thread */ +static tdm_error +_need_validate_handler(int fd, tdm_event_loop_mask mask, void *user_data) +{ + tdm_thread_cb_need_validate ev; + tdm_private_output *private_output; + tdm_error ret; + + private_output = (tdm_private_output *)user_data; + + ev.base.type = TDM_THREAD_CB_NEED_VALIDATE; + ev.base.length = sizeof ev; + ev.o = private_output; + + ret = tdm_thread_send_cb(private_output->private_display->private_loop, &ev.base); + TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE); + + TDM_INFO("tdm-thread: get a 'need to revalidate' event for the ouptut:%p.", private_output); + + /* who cares about this? */ + return TDM_ERROR_NONE; +} + +INTERN tdm_error +tdm_output_need_validate_event_init(tdm_output *output) +{ + int fd; + + OUTPUT_FUNC_ENTRY(); + + TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED); + + /* build in eventfd fds into event_loop listened & handled by the tdm-thread */ + fd = eventfd(0, 0); + TDM_WARNING_IF_FAIL(fd >= 0); + + tdm_event_loop_add_fd_handler(private_display, fd, TDM_EVENT_LOOP_READABLE, + _need_validate_handler, private_output, &ret); + TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE); + + private_output->need_validate.event_fd = fd; + + TDM_INFO("register an output:%p for the revalidation, event_fd:%d.", private_output, fd); + + return ret; +} diff --git a/src/tdm_private.h b/src/tdm_private.h index 522ae57..6bfbe10 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -110,6 +111,7 @@ enum { TDM_DUMP_FLAG_LAYER = (1 << 0), TDM_DUMP_FLAG_PP = (1 << 1), TDM_DUMP_FLAG_CAPTURE = (1 << 2), + TDM_DUMP_FLAG_WINDOW = (1 << 3), }; #define TDM_DUMP_DIR "/tmp" @@ -123,6 +125,7 @@ typedef enum { typedef struct _tdm_private_display tdm_private_display; typedef struct _tdm_private_output tdm_private_output; typedef struct _tdm_private_layer tdm_private_layer; +typedef struct _tdm_private_hwc_window tdm_private_hwc_window; typedef struct _tdm_private_pp tdm_private_pp; typedef struct _tdm_private_capture tdm_private_capture; typedef struct _tdm_private_loop tdm_private_loop; @@ -152,6 +155,7 @@ struct _tdm_private_display { tdm_func_display func_display; tdm_func_output func_output; tdm_func_layer func_layer; + tdm_func_hwc_window func_hwc_window; tdm_func_pp func_pp; tdm_func_capture func_capture; @@ -177,6 +181,8 @@ struct _tdm_private_display { int commit_per_vblank; tdm_commit_type commit_type; + int hwc_enable; + int print_fps; }; @@ -202,6 +208,7 @@ struct _tdm_private_output { int regist_dpms_cb; struct list_head layer_list; + struct list_head hwc_window_list; struct list_head capture_list; struct list_head vblank_handler_list; struct list_head output_commit_handler_list; @@ -220,6 +227,12 @@ struct _tdm_private_output { /* TODO: temp solution for handling DPMS things in sub-htread */ tdm_event_loop_source *dpms_changed_timer; + + struct { + /* look at the tdm_output_set_need_revalidate_handler() declaration for the details */ + tdm_output_need_validate_handler hndl; + int event_fd; + } need_validate; }; struct _tdm_private_layer { @@ -257,6 +270,19 @@ struct _tdm_private_layer { unsigned int fps_count; }; +struct _tdm_private_hwc_window { + struct list_head link; + + int index; + uint32_t zpos; + + tdm_private_display *private_display; + tdm_private_output *private_output; + + tdm_hwc_window *hwc_window_backend; +}; + + struct _tdm_private_pp { struct list_head link; @@ -427,6 +453,9 @@ tdm_output_cb_status(tdm_output *output_backend, tdm_output_conn_status status, void tdm_output_cb_dpms(tdm_output *output_backend, tdm_output_dpms dpms, void *user_data); +INTERN tdm_error +tdm_output_cb_need_validate(tdm_private_output *private_output); + tdm_error tdm_output_wait_vblank_add_front(tdm_output *output, int interval, int sync, tdm_output_vblank_handler func, void *user_data); @@ -435,6 +464,9 @@ tdm_output_commit_internal(tdm_output *output, int sync, tdm_output_commit_handl tdm_error tdm_output_get_dpms_internal(tdm_output *output, tdm_output_dpms *dpms_value); +tdm_error +tdm_output_need_validate_event_init(tdm_output *output); + void tdm_output_remove_vblank_handler_internal(tdm_output *output, tdm_output_vblank_handler func, void *user_data); void @@ -476,6 +508,11 @@ tdm_pp_create_internal(tdm_private_display *private_display, tdm_error *error); void tdm_pp_destroy_internal(tdm_private_pp *private_pp); +tdm_hwc_window * +tdm_hwc_window_create_internal(tdm_private_output *private_output, tdm_error *error); +tdm_error +tdm_hwc_window_destroy_internal(tdm_private_hwc_window * private_hwc_window); + tdm_private_capture * tdm_capture_create_output_internal(tdm_private_output *private_output, tdm_error *error); @@ -523,6 +560,7 @@ typedef enum { TDM_THREAD_CB_PP_DONE, TDM_THREAD_CB_CAPTURE_DONE, TDM_THREAD_CB_VBLANK_SW, + TDM_THREAD_CB_NEED_VALIDATE, } tdm_thread_cb_type; typedef struct _tdm_thread_cb_base tdm_thread_cb_base; @@ -533,6 +571,7 @@ typedef struct _tdm_thread_cb_output_dpms tdm_thread_cb_output_dpms; typedef struct _tdm_thread_cb_pp_done tdm_thread_cb_pp_done; typedef struct _tdm_thread_cb_capture_done tdm_thread_cb_capture_done; typedef struct _tdm_thread_cb_vblank_sw tdm_thread_cb_vblank_sw; +typedef struct _tdm_thread_cb_need_validate tdm_thread_cb_need_validate; struct _tdm_thread_cb_base { tdm_thread_cb_type type; @@ -582,6 +621,11 @@ struct _tdm_thread_cb_vblank_sw { double vblank_stamp; }; +struct _tdm_thread_cb_need_validate { + tdm_thread_cb_base base; + tdm_private_output *o; +}; + tdm_error tdm_thread_init(tdm_private_loop *private_loop); void @@ -706,7 +750,6 @@ tdm_display_enable_fps(tdm_private_display *private_display, int enable); void tdm_monitor_server_command(tdm_display *dpy, const char *options, char *reply, int *len); - struct argument_details { char type; int nullable; diff --git a/src/tdm_thread.c b/src/tdm_thread.c index 4659740..8f624e1 100644 --- a/src/tdm_thread.c +++ b/src/tdm_thread.c @@ -356,6 +356,11 @@ tdm_thread_handle_cb(tdm_private_loop *private_loop) tdm_vblank_cb_vblank_SW(NULL, vblank_sw->vblank_stamp); break; } + case TDM_THREAD_CB_NEED_VALIDATE: { + tdm_thread_cb_need_validate *ev = (tdm_thread_cb_need_validate*)base; + tdm_output_cb_need_validate(ev->o); + break; + } default: break; } -- 2.7.4