--- /dev/null
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "tdm_virtual.h"
+
+typedef struct _tdm_virtual_output_data tdm_virtual_output_data;
+typedef struct _tdm_virtual_voutput_data tdm_virtual_voutput_data;
+typedef struct _tdm_virtual_layer_data tdm_virtual_layer_data;
+typedef struct _tdm_virtual_event_data tdm_virtual_event_data;
+
+typedef enum {
+ TDM_VIRTUAL_EVENT_TYPE_WAIT,
+ TDM_VIRTUAL_EVENT_TYPE_COMMIT,
+ TDM_VIRTUAL_EVENT_TYPE_VCOMMIT,
+} tdm_virtual_event_type;
+
+struct _tdm_virtual_event_data {
+ struct list_head link;
+
+ tdm_virtual_event_type type;
+ tdm_virtual_output_data *output_data;
+ void *user_data;
+};
+
+struct _tdm_virtual_output_data {
+ struct list_head link;
+
+ /* data which are fixed at initializing */
+ tdm_virtual_data *virtual_data;
+ tdm_virtual_voutput_data *voutput_data;
+
+ char name[TDM_NAME_LEN]; /* output name */
+ uint32_t pipe;
+ tdm_output_type connector_type;
+ struct list_head layer_list;
+ tdm_virtual_layer_data *primary_layer;
+
+ tdm_output_dpms dpms;
+
+ /* not fixed data below */
+ tdm_output_vblank_handler vblank_func;
+ tdm_output_commit_handler commit_func;
+ void *commit_user_data;
+
+ tdm_output_conn_status status;
+ tdm_output_status_handler status_func;
+ void *status_user_data;
+
+ 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_virtual_voutput_data {
+ struct list_head link;
+
+ /* data which are fixed at initializing */
+ tdm_virtual_data *virtual_data;
+ tdm_virtual_output_data *output_data;
+
+ char name[TDM_NAME_LEN]; /* output name */
+
+ tdm_voutput_commit_handler vcommit_func;
+
+ tdm_output_mode *output_modes;
+ int mode_count;
+
+ unsigned int mmwidth;
+ unsigned int mmheight;
+};
+
+
+struct _tdm_virtual_layer_data {
+ struct list_head link;
+
+ /* data which are fixed at initializing */
+ tdm_virtual_data *virtual_data;
+ tdm_virtual_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_virtual_display_cb_event(tdm_virtual_output_data *output_data, tdm_virtual_event_data *event_data,
+ unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec)
+{
+ tdm_virtual_voutput_data *voutput_data = NULL;
+
+ switch (event_data->type) {
+ case TDM_VIRTUAL_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_VIRTUAL_EVENT_TYPE_COMMIT:
+ if (output_data->commit_func)
+ output_data->commit_func(output_data, sequence, tv_sec, tv_usec, event_data->user_data);
+ break;
+ case TDM_VIRTUAL_EVENT_TYPE_VCOMMIT:
+ voutput_data = output_data->voutput_data;
+ voutput_data->vcommit_func(voutput_data, sequence, tv_sec, tv_usec, NULL);
+ default:
+ break;
+ }
+}
+
+static void _tdm_virtual_get_current_time(unsigned int *tv_sec, unsigned int *tv_usec)
+{
+ struct timespec tp;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
+ *tv_sec = tp.tv_sec;
+ *tv_usec = tp.tv_nsec / 1000;
+ } else {
+ *tv_sec = *tv_usec = 0;
+ }
+}
+
+static tdm_error
+_tdm_virtual_display_cb_timeout(void *user_data)
+{
+ tdm_virtual_output_data *output_data = user_data;
+ tdm_virtual_event_data *e = NULL, *ee = NULL;
+ unsigned int tv_sec, tv_usec;
+ static unsigned int sequence = 0;
+
+ sequence++;
+
+ _tdm_virtual_get_current_time(&tv_sec, &tv_usec);
+
+ LIST_FOR_EACH_ENTRY_SAFE(e, ee, &output_data->timer_event_list, link) {
+ LIST_DEL(&e->link);
+ _tdm_virtual_display_cb_event(output_data, e, sequence, tv_sec, tv_usec);
+ free(e);
+ }
+
+ return TDM_ERROR_NONE;
+}
+
+static tdm_error
+_tdm_virtual_display_wait_vblank(tdm_virtual_output_data *output_data, int interval, tdm_virtual_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->current_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;
+ }
+
+ if (interval == -1)
+ ms = 1;
+ else
+ ms = ((double)1000.0 / output_data->current_mode->vrefresh) * interval;
+
+ 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_virtual_display_destroy_layer_list(tdm_virtual_data *virtual_data)
+{
+ tdm_virtual_output_data *o = NULL;
+
+ LIST_FOR_EACH_ENTRY(o, &virtual_data->output_list, link) {
+ tdm_virtual_layer_data *l = NULL, *ll = NULL;
+ LIST_FOR_EACH_ENTRY_SAFE(l, ll, &o->layer_list, link) {
+ LIST_DEL(&l->link);
+ free(l);
+ }
+ }
+}
+
+void
+tdm_virtual_display_destroy_output_list(tdm_virtual_data *virtual_data)
+{
+ tdm_virtual_output_data *o = NULL, *oo = NULL;
+
+ if (LIST_IS_EMPTY(&virtual_data->output_list))
+ return;
+
+ _tdm_virtual_display_destroy_layer_list(virtual_data);
+
+ LIST_FOR_EACH_ENTRY_SAFE(o, oo, &virtual_data->output_list, link) {
+ LIST_DEL(&o->link);
+
+ if (!LIST_IS_EMPTY(&o->timer_event_list)) {
+ tdm_virtual_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);
+ }
+}
+
+tdm_error
+virtual_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 **
+virtual_display_get_outputs(tdm_backend_data *bdata, int *count, tdm_error *error)
+{
+ tdm_virtual_data *virtual_data = bdata;
+ tdm_virtual_output_data *output_data = NULL;
+ tdm_output **outputs;
+ tdm_error ret;
+ int i;
+
+ RETURN_VAL_IF_FAIL(virtual_data, NULL);
+ RETURN_VAL_IF_FAIL(count, NULL);
+
+ *count = 0;
+ LIST_FOR_EACH_ENTRY(output_data, &virtual_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_virtual_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, &virtual_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
+virtual_display_get_fd(tdm_backend_data *bdata, int *fd)
+{
+ tdm_virtual_data *virtual_data = bdata;
+
+ RETURN_VAL_IF_FAIL(virtual_data, TDM_ERROR_INVALID_PARAMETER);
+ RETURN_VAL_IF_FAIL(fd, TDM_ERROR_INVALID_PARAMETER);
+
+ *fd = virtual_data->pipe[0];
+
+ return TDM_ERROR_NONE;
+}
+
+tdm_error
+virtual_display_handle_events(tdm_backend_data *bdata)
+{
+ return TDM_ERROR_NONE;
+}
+
+tdm_error
+virtual_output_get_capability(tdm_output *output, tdm_caps_output *caps)
+{
+ tdm_virtual_output_data *output_data = output;
+ tdm_virtual_voutput_data *voutput_data = NULL;
+ tdm_error ret;
+
+ RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER);
+ RETURN_VAL_IF_FAIL(caps, TDM_ERROR_INVALID_PARAMETER);
+
+ voutput_data = output_data->voutput_data;
+
+ memset(caps, 0, sizeof(tdm_caps_output));
+
+ snprintf(caps->maker, TDM_NAME_LEN, "virtual");
+ snprintf(caps->model, TDM_NAME_LEN, "virtual");
+ snprintf(caps->name, TDM_NAME_LEN, "%s", output_data->name);
+
+ caps->status = output_data->status;
+ caps->type = output_data->connector_type;
+ caps->type_id = 0;
+
+ if (output_data->status == TDM_OUTPUT_CONN_STATUS_CONNECTED ||
+ output_data->status == TDM_OUTPUT_CONN_STATUS_MODE_SETTED) {
+ caps->mode_count = voutput_data->mode_count;
+ if (voutput_data->mode_count != 0) {
+ caps->modes = calloc(voutput_data->mode_count, sizeof(tdm_output_mode));
+ if (!caps->modes) {
+ ret = TDM_ERROR_OUT_OF_MEMORY;
+ TDM_ERR("alloc failed\n");
+ goto failed_get;
+ }
+ memcpy(caps->modes, voutput_data->output_modes, voutput_data->mode_count * sizeof(tdm_output_mode));
+ }
+
+ caps->mmWidth = voutput_data->mmwidth;
+ caps->mmHeight = voutput_data->mmheight;
+ } else {
+ caps->modes = NULL;
+ caps->mode_count = 0;
+ caps->mmWidth = 0;
+ caps->mmHeight = 0;
+ }
+ 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 **
+virtual_output_get_layers(tdm_output *output, int *count, tdm_error *error)
+{
+ tdm_virtual_output_data *output_data = output;
+ tdm_virtual_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_virtual_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
+virtual_output_wait_vblank(tdm_output *output, int interval, int sync, void *user_data)
+{
+ tdm_virtual_output_data *output_data = output;
+ tdm_virtual_event_data *event_data;
+ tdm_error ret;
+
+ RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER);
+
+ event_data = calloc(1, sizeof(tdm_virtual_event_data));
+ if (!event_data) {
+ TDM_ERR("alloc failed");
+ return TDM_ERROR_OUT_OF_MEMORY;
+ }
+
+ event_data->type = TDM_VIRTUAL_EVENT_TYPE_WAIT;
+ event_data->output_data = output_data;
+ event_data->user_data = user_data;
+
+ ret = _tdm_virtual_display_wait_vblank(output_data, interval, event_data);
+ if (ret != TDM_ERROR_NONE) {
+ free(event_data);
+ return ret;
+ }
+
+ return TDM_ERROR_NONE;
+}
+
+tdm_error
+virtual_output_set_vblank_handler(tdm_output *output, tdm_output_vblank_handler func)
+{
+ tdm_virtual_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
+virtual_output_commit(tdm_output *output, int sync, void *user_data)
+{
+ tdm_virtual_output_data *output_data = output;
+ tdm_virtual_voutput_data *voutput_data = NULL;
+ tdm_virtual_layer_data *layer_data = NULL;
+ tdm_virtual_event_data *event_data;
+ tdm_error ret;
+
+ RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER);
+
+ voutput_data = output_data->voutput_data;
+ RETURN_VAL_IF_FAIL(voutput_data, TDM_ERROR_INVALID_PARAMETER);
+
+ if (output_data->mode_changed) {
+ output_data->mode_changed = 0;
+ output_data->status = TDM_OUTPUT_CONN_STATUS_MODE_SETTED;
+
+ if (output_data->status_func)
+ output_data->status_func(output_data, TDM_OUTPUT_CONN_STATUS_MODE_SETTED,
+ output_data->status_user_data);
+ }
+
+ if (voutput_data->vcommit_func) {
+ layer_data = output_data->primary_layer;
+ if (layer_data->display_buffer_changed) {
+ output_data->commit_user_data = user_data;
+ event_data = calloc(1, sizeof(tdm_virtual_event_data));
+ if (!event_data) {
+ TDM_ERR("alloc failed");
+ return TDM_ERROR_OUT_OF_MEMORY;
+ }
+
+ event_data->type = TDM_VIRTUAL_EVENT_TYPE_VCOMMIT;
+ event_data->output_data = output_data;
+ event_data->user_data = user_data;
+
+ ret = _tdm_virtual_display_wait_vblank(output_data, 1, event_data);
+ if (ret != TDM_ERROR_NONE) {
+ free(event_data);
+ return ret;
+ }
+ }
+ } else {
+ event_data = calloc(1, sizeof(tdm_virtual_event_data));
+ if (!event_data) {
+ TDM_ERR("alloc failed");
+ return TDM_ERROR_OUT_OF_MEMORY;
+ }
+
+ event_data->type = TDM_VIRTUAL_EVENT_TYPE_COMMIT;
+ event_data->output_data = output_data;
+ event_data->user_data = user_data;
+
+ ret = _tdm_virtual_display_wait_vblank(output_data, 1, event_data);
+ if (ret != TDM_ERROR_NONE) {
+ free(event_data);
+ return ret;
+ }
+ }
+
+ return TDM_ERROR_NONE;
+}
+
+tdm_error
+virtual_output_set_commit_handler(tdm_output *output, tdm_output_commit_handler func)
+{
+ tdm_virtual_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
+virtual_output_set_dpms(tdm_output *output, tdm_output_dpms dpms_value)
+{
+ tdm_virtual_output_data *output_data = output;
+
+ RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER);
+ RETURN_VAL_IF_FAIL(dpms_value <= TDM_OUTPUT_DPMS_OFF, TDM_ERROR_INVALID_PARAMETER);
+
+ TDM_DBG("dpms change [%d] -> [%d]", output_data->dpms, dpms_value);
+
+ output_data->dpms = dpms_value;
+
+ return TDM_ERROR_NONE;
+}
+
+tdm_error
+virtual_output_get_dpms(tdm_output *output, tdm_output_dpms *dpms_value)
+{
+ tdm_virtual_output_data *output_data = output;
+
+ RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER);
+ RETURN_VAL_IF_FAIL(dpms_value, TDM_ERROR_INVALID_PARAMETER);
+
+ *dpms_value = output_data->dpms;
+
+ return TDM_ERROR_NONE;
+}
+
+tdm_error
+virtual_output_set_mode(tdm_output *output, const tdm_output_mode *mode)
+{
+ tdm_virtual_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
+virtual_output_get_mode(tdm_output *output, const tdm_output_mode **mode)
+{
+ tdm_virtual_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
+virtual_output_set_status_handler(tdm_output *output,
+ tdm_output_status_handler func, void *user_data)
+{
+ tdm_virtual_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->status_func = func;
+ output_data->status_user_data = user_data;
+
+ return TDM_ERROR_NONE;
+}
+
+tdm_error
+virtual_output_set_available_mode(tdm_voutput *voutput, const tdm_output_mode *modes, int count)
+{
+ tdm_virtual_voutput_data *voutput_data = voutput;
+ tdm_virtual_output_data *output_data = NULL;
+
+ RETURN_VAL_IF_FAIL(voutput_data, TDM_ERROR_INVALID_PARAMETER);
+ RETURN_VAL_IF_FAIL(modes, TDM_ERROR_INVALID_PARAMETER);
+ RETURN_VAL_IF_FAIL(count > 0, TDM_ERROR_INVALID_PARAMETER);
+
+ output_data = voutput_data->output_data;
+
+ /* set available mode only permittied disconnect status */
+ RETURN_VAL_IF_FAIL(output_data->status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED, TDM_ERROR_BUSY);
+
+ if (voutput_data->output_modes)
+ free(voutput_data->output_modes);
+ voutput_data->output_modes = NULL;
+
+ voutput_data->output_modes = calloc(1, count * sizeof(tdm_output_mode));
+ RETURN_VAL_IF_FAIL(voutput_data->output_modes != NULL, TDM_ERROR_OUT_OF_MEMORY);
+
+ memcpy(voutput_data->output_modes, modes, count * sizeof(tdm_output_mode));
+ voutput_data->mode_count = count;
+
+ return TDM_ERROR_NONE;
+}
+
+tdm_error
+virtual_output_set_physical_size(tdm_voutput *voutput, unsigned int mmwidth, unsigned int mmheight)
+{
+ tdm_virtual_voutput_data *voutput_data = voutput;
+
+ RETURN_VAL_IF_FAIL(voutput_data, TDM_ERROR_INVALID_PARAMETER);
+ RETURN_VAL_IF_FAIL(mmwidth != 0, TDM_ERROR_INVALID_PARAMETER);
+ RETURN_VAL_IF_FAIL(mmheight != 0, TDM_ERROR_INVALID_PARAMETER);
+
+ voutput_data->mmwidth = mmwidth;
+ voutput_data->mmheight = mmheight;
+
+ return TDM_ERROR_NONE;
+}
+
+tdm_error
+virtual_output_connect(tdm_voutput *voutput)
+{
+ tdm_virtual_voutput_data *voutput_data = voutput;
+ tdm_virtual_output_data *output_data = NULL;
+
+ RETURN_VAL_IF_FAIL(voutput_data, TDM_ERROR_INVALID_PARAMETER);
+
+ output_data = voutput_data->output_data;
+
+ if (output_data->status == TDM_OUTPUT_CONN_STATUS_CONNECTED ||
+ output_data->status == TDM_OUTPUT_CONN_STATUS_MODE_SETTED)
+ return TDM_ERROR_NONE;
+
+ output_data->status = TDM_OUTPUT_CONN_STATUS_CONNECTED;
+
+ if (output_data->status_func)
+ output_data->status_func(output_data, output_data->status,
+ output_data->status_user_data);
+
+ return TDM_ERROR_NONE;
+}
+
+tdm_error
+virtual_output_disconnect(tdm_voutput *voutput)
+{
+
+ tdm_virtual_voutput_data *voutput_data = voutput;
+ tdm_virtual_output_data *output_data = NULL;
+
+ RETURN_VAL_IF_FAIL(voutput_data, TDM_ERROR_INVALID_PARAMETER);
+
+ output_data = voutput_data->output_data;
+
+ if (output_data->status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED)
+ return TDM_ERROR_NONE;
+
+ output_data->status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
+
+ if (output_data->status_func)
+ output_data->status_func(output_data, output_data->status,
+ output_data->status_user_data);
+
+ return TDM_ERROR_NONE;
+}
+
+tdm_error
+virtual_layer_get_capability(tdm_layer *layer, tdm_caps_layer *caps)
+{
+ tdm_virtual_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
+virtual_layer_set_info(tdm_layer *layer, tdm_info_layer *info)
+{
+ tdm_virtual_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
+virtual_layer_get_info(tdm_layer *layer, tdm_info_layer *info)
+{
+ tdm_virtual_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
+virtual_layer_set_buffer(tdm_layer *layer, tbm_surface_h buffer)
+{
+ tdm_virtual_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
+virtual_layer_unset_buffer(tdm_layer *layer)
+{
+ tdm_virtual_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;
+}
+
+tdm_voutput *
+virtual_output_create(tdm_backend_data *bdata, const char *name, tdm_error *error)
+{
+ tdm_virtual_data *virtual_data = bdata;
+ tdm_virtual_voutput_data *voutput_data = NULL;
+ tdm_virtual_output_data *output_data = NULL;
+ tdm_virtual_layer_data *layer_data = NULL;
+ tdm_error ret;
+
+ if (!virtual_data || !name) {
+ TDM_ERR("invalid parameter");
+ *error = TDM_ERROR_INVALID_PARAMETER;
+ return NULL;
+ }
+
+ voutput_data = calloc(1, sizeof(tdm_virtual_voutput_data));
+ if (!voutput_data) {
+ TDM_ERR("alloc failed");
+ *error = TDM_ERROR_OUT_OF_MEMORY;
+ return NULL;
+ }
+
+ voutput_data->output_modes = calloc(1, sizeof(tdm_output_mode));
+ if (!voutput_data->output_modes) {
+ TDM_ERR("alloc failed");
+ ret = TDM_ERROR_OUT_OF_MEMORY;
+ goto create_fail;
+ }
+
+ /* default mode */
+ snprintf(voutput_data->output_modes->name, TDM_NAME_LEN, "640x480");
+ voutput_data->output_modes->vrefresh = 30;
+ voutput_data->output_modes->clock = 25200;
+ voutput_data->output_modes->hdisplay = 640;
+ voutput_data->output_modes->hsync_start = 656;
+ voutput_data->output_modes->hsync_end = 752;
+ voutput_data->output_modes->htotal = 800;
+ voutput_data->output_modes->hskew = 0;
+ voutput_data->output_modes->vdisplay = 480;
+ voutput_data->output_modes->vsync_start = 490;
+ voutput_data->output_modes->vsync_end = 492;
+ voutput_data->output_modes->vtotal = 525;
+ voutput_data->output_modes->vscan = 0;
+ voutput_data->output_modes->flags = 0;
+ voutput_data->output_modes->type = 0;
+ voutput_data->mode_count = 1;
+
+ voutput_data->mmwidth = 10;
+ voutput_data->mmheight = 10;
+
+ voutput_data->virtual_data = virtual_data;
+
+ output_data = calloc(1, sizeof(tdm_virtual_output_data));
+ if (!output_data) {
+ TDM_ERR("alloc failed");
+ *error = TDM_ERROR_OUT_OF_MEMORY;
+ goto create_fail;
+ }
+ voutput_data->output_data = output_data;
+
+ LIST_INITHEAD(&output_data->layer_list);
+
+ output_data->virtual_data = virtual_data;
+ output_data->voutput_data = voutput_data;
+ output_data->pipe = 0;
+ output_data->connector_type = TDM_OUTPUT_TYPE_Unknown;
+ output_data->status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
+
+ if (name) {
+ snprintf(voutput_data->name, TDM_NAME_LEN, "%s", name);
+ snprintf(output_data->name, TDM_NAME_LEN, "%s", name);
+ } else {
+ snprintf(voutput_data->name, TDM_NAME_LEN, "unknown");
+ snprintf(output_data->name, TDM_NAME_LEN, "%s", name);
+ }
+
+ output_data->timer = tdm_event_loop_add_timer_handler(virtual_data->dpy,
+ _tdm_virtual_display_cb_timeout,
+ output_data,
+ &ret);
+ if (!output_data->timer) goto create_fail;
+
+ LIST_INITHEAD(&output_data->timer_event_list);
+
+ /* The TDM virtual backend output support only one layer. */
+ layer_data = calloc(1, sizeof(tdm_virtual_layer_data));
+ if (!layer_data) {
+ TDM_ERR("alloc failed");
+ ret = TDM_ERROR_OUT_OF_MEMORY;
+ goto create_fail;
+ }
+
+ layer_data->virtual_data = virtual_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(&voutput_data->link, &virtual_data->voutput_list);
+ LIST_ADDTAIL(&output_data->link, &virtual_data->output_list);
+ LIST_ADDTAIL(&layer_data->link, &output_data->layer_list);
+
+ *error = TDM_ERROR_NONE;
+
+ TDM_DBG("virtual output create(%s)(%p)(%p)", output_data->name, voutput_data, output_data);
+
+ return voutput_data;
+
+create_fail:
+ if (layer_data) free(layer_data);
+ if (output_data) free(output_data);
+ if (voutput_data->output_modes) free(voutput_data->output_modes);
+ if (voutput_data) free(voutput_data);
+
+ *error = ret;
+
+ return NULL;
+}
+
+tdm_error
+virtual_output_destroy(tdm_voutput *voutput)
+{
+ tdm_virtual_data *virtual_data = NULL;
+ tdm_virtual_voutput_data *vo, *voutput_data = voutput;
+ int find = 0;
+
+ RETURN_VAL_IF_FAIL(voutput_data, TDM_ERROR_INVALID_PARAMETER);
+
+ virtual_data = voutput_data->virtual_data;
+
+ LIST_FOR_EACH_ENTRY(vo, &virtual_data->voutput_list, link) {
+ if (vo == voutput_data) {
+ find = 1;
+ break;
+ }
+ }
+
+ if (find) {
+ tdm_virtual_layer_data *l = NULL, *ll = NULL;
+ tdm_virtual_output_data *output_data = NULL;
+
+ TDM_DBG("virtual output destroy(%s)", voutput_data->name);
+
+ output_data = voutput_data->output_data;
+
+ if (!LIST_IS_EMPTY(&output_data->timer_event_list)) {
+ tdm_virtual_event_data *e = NULL, *ee = NULL;
+ LIST_FOR_EACH_ENTRY_SAFE(e, ee, &output_data->timer_event_list, link) {
+ LIST_DEL(&e->link);
+ free(e);
+ }
+ }
+
+ if (output_data->timer)
+ tdm_event_loop_source_remove(output_data->timer);
+
+ LIST_FOR_EACH_ENTRY_SAFE(l, ll, &output_data->layer_list, link) {
+ LIST_DEL(&l->link);
+ free(l);
+ }
+
+ LIST_DEL(&output_data->link);
+ free(output_data);
+
+ if (voutput_data->output_modes)
+ free(voutput_data->output_modes);
+ LIST_DEL(&voutput_data->link);
+ free(voutput_data);
+ } else
+ return TDM_ERROR_INVALID_PARAMETER;
+
+ return TDM_ERROR_NONE;
+}
+
+tdm_output *
+virtual_output_get_output(tdm_voutput *voutput, tdm_error *error)
+{
+ tdm_virtual_voutput_data *voutput_data = voutput;
+
+ RETURN_VAL_IF_FAIL(voutput_data, NULL);
+
+ if (!voutput_data) {
+ *error = TDM_ERROR_INVALID_PARAMETER;
+ return NULL;
+ }
+
+ *error = TDM_ERROR_NONE;
+
+ return voutput_data->output_data;
+}
+
+tdm_error
+virtual_output_set_commit_func(tdm_voutput *voutput, tdm_voutput_commit_handler commit_func)
+{
+ tdm_virtual_voutput_data *voutput_data = voutput;
+
+ RETURN_VAL_IF_FAIL(voutput_data, TDM_ERROR_INVALID_PARAMETER);
+ RETURN_VAL_IF_FAIL(commit_func, TDM_ERROR_INVALID_PARAMETER);
+
+ voutput_data->vcommit_func = commit_func;
+
+ return TDM_ERROR_NONE;
+}
+
+tdm_error
+virtual_output_commit_done(tdm_voutput *voutput)
+{
+ tdm_virtual_voutput_data *voutput_data = voutput;
+ tdm_virtual_output_data *output_data = NULL;
+ unsigned int tv_sec, tv_usec;
+ static unsigned int sequence = 0;
+ tdm_virtual_event_data *event_data;
+ tdm_error ret = TDM_ERROR_NONE;
+
+ RETURN_VAL_IF_FAIL(voutput_data, TDM_ERROR_INVALID_PARAMETER);
+ output_data = voutput_data->output_data;
+
+ sequence++;
+
+ _tdm_virtual_get_current_time(&tv_sec, &tv_usec);
+
+ event_data = calloc(1, sizeof(tdm_virtual_event_data));
+ if (!event_data) {
+ TDM_ERR("alloc failed");
+ return TDM_ERROR_OUT_OF_MEMORY;
+ }
+
+ event_data->type = TDM_VIRTUAL_EVENT_TYPE_COMMIT;
+ event_data->output_data = output_data;
+ event_data->user_data = output_data->commit_user_data;
+
+ ret = _tdm_virtual_display_wait_vblank(output_data, 1, event_data);
+ if (ret != TDM_ERROR_NONE) {
+ free(event_data);
+ return ret;
+ }
+
+ return TDM_ERROR_NONE;
+}
+