hwc: implement the first draft of HWC based on exynos implamantation. 65/164865/5
authorRoman Marchenko <r.marchenko@samsung.com>
Thu, 3 Aug 2017 12:37:08 +0000 (15:37 +0300)
committerSooChan Lim <sc1.lim@samsung.com>
Tue, 26 Dec 2017 11:16:57 +0000 (11:16 +0000)
Change-Id: I1fa0efabb65c890e32c9a8541cabcbb2527b0c16
Signed-off-by: Roman Marchenko <r.marchenko@samsung.com>
src/tdm_vc4.c
src/tdm_vc4.h
src/tdm_vc4_display.c

index 110908f..4c5e801 100644 (file)
@@ -231,7 +231,9 @@ tdm_vc4_init(tdm_display *dpy, tdm_error *error)
        tdm_func_display vc4_func_display;
        tdm_func_output vc4_func_output;
        tdm_func_layer vc4_func_layer;
+       tdm_func_hwc_window vc4_func_hwc_window;
        tdm_error ret;
+       char *str;
 
        if (!dpy) {
                TDM_ERR("display is null");
@@ -255,6 +257,12 @@ tdm_vc4_init(tdm_display *dpy, tdm_error *error)
                return NULL;
        }
 
+       str = getenv("TDM_HWC");
+       if (str) {
+               char *end;
+               vc4_data->hwc_mode = strtol(str, &end, 10);;
+       }
+
        LIST_INITHEAD(&vc4_data->output_list);
        LIST_INITHEAD(&vc4_data->buffer_list);
 
@@ -280,6 +288,23 @@ tdm_vc4_init(tdm_display *dpy, tdm_error *error)
 #ifdef HAVE_UDEV
        vc4_func_output.output_set_status_handler = vc4_output_set_status_handler;
 #endif
+       if (vc4_data->hwc_mode) {
+               vc4_func_output.output_hwc_create_window = vc4_output_hwc_window_create;
+               vc4_func_output.output_hwc_destroy_window = vc4_output_hwc_window_destroy;
+               vc4_func_output.output_hwc_validate = vc4_output_hwc_validate;
+               vc4_func_output.output_hwc_get_changed_composition_types = vc4_output_hwc_get_changed_composition_types;
+               vc4_func_output.output_hwc_accept_changes = vc4_output_hwc_accept_changes;
+               vc4_func_output.output_hwc_get_target_buffer_queue = vc4_output_hwc_get_target_buffer_queue;
+               vc4_func_output.output_hwc_set_client_target_buffer = vc4_output_hwc_set_client_target_buffer;
+
+               memset(&vc4_func_hwc_window, 0, sizeof(vc4_func_hwc_window));
+               vc4_func_hwc_window.hwc_window_get_tbm_buffer_queue = vc4_hwc_window_get_tbm_buffer_queue;
+               vc4_func_hwc_window.hwc_window_set_buffer = vc4_hwc_window_set_buffer;
+               vc4_func_hwc_window.hwc_window_set_composition_type = vc4_hwc_window_set_composition_type;
+               vc4_func_hwc_window.hwc_window_set_info = vc4_hwc_window_set_info;
+               vc4_func_hwc_window.hwc_window_set_buffer_damage = vc4_hwc_window_set_buffer_damage;
+               vc4_func_hwc_window.hwc_window_set_zpos = vc4_hwc_window_set_zpos;
+       }
 
        memset(&vc4_func_layer, 0, sizeof(vc4_func_layer));
        vc4_func_layer.layer_get_capability = vc4_layer_get_capability;
@@ -302,6 +327,11 @@ tdm_vc4_init(tdm_display *dpy, tdm_error *error)
        if (ret != TDM_ERROR_NONE)
                goto failed;
 
+       if (vc4_data->hwc_mode) {
+               ret = tdm_backend_register_func_hwc_window(dpy, &vc4_func_hwc_window);
+               if (ret != TDM_ERROR_NONE)
+                       goto failed;
+       }
        vc4_data->dpy = dpy;
 
        /* The drm master fd can be opened by a tbm backend module in
index 6ed6ad3..d0077ac 100644 (file)
@@ -21,6 +21,7 @@
 #include <xf86drmMode.h>
 #include <tbm_surface.h>
 #include <tbm_surface_internal.h>
+#include <tbm_surface_queue.h>
 #include <tdm_backend.h>
 #include <tdm_log.h>
 #include <tdm_list.h>
@@ -47,6 +48,17 @@ tdm_error    vc4_output_get_dpms(tdm_output *output, tdm_output_dpms *dpms_value);
 tdm_error      vc4_output_set_mode(tdm_output *output, const tdm_output_mode *mode);
 tdm_error      vc4_output_get_mode(tdm_output *output, const tdm_output_mode **mode);
 tdm_error      vc4_output_set_status_handler(tdm_output *output, tdm_output_status_handler func, void *user_data);
+tdm_hwc_window * vc4_output_hwc_window_create(tdm_output *output, tdm_error *error);
+tdm_error      vc4_output_hwc_window_destroy(tdm_output *output, tdm_hwc_window *hwc_window);
+tdm_error      vc4_output_hwc_validate(tdm_output *output, uint32_t *num_types);
+tdm_error      vc4_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_error      vc4_output_hwc_accept_changes(tdm_output *output);
+tbm_surface_queue_h vc4_output_hwc_get_target_buffer_queue(tdm_output *output, tdm_error *error);
+tdm_error      vc4_output_hwc_set_client_target_buffer(tdm_output *output, tbm_surface_h buffer,
+                                                                       tdm_hwc_region damage, tdm_hwc_window **composited_wnds,
+                                                                       uint32_t num_wnds);
 tdm_error      vc4_layer_get_capability(tdm_layer *layer, tdm_caps_layer *caps);
 tdm_error      vc4_layer_set_property(tdm_layer *layer, unsigned int id, tdm_value value);
 tdm_error      vc4_layer_get_property(tdm_layer *layer, unsigned int id, tdm_value *value);
@@ -54,6 +66,15 @@ tdm_error    vc4_layer_set_info(tdm_layer *layer, tdm_info_layer *info);
 tdm_error      vc4_layer_get_info(tdm_layer *layer, tdm_info_layer *info);
 tdm_error      vc4_layer_set_buffer(tdm_layer *layer, tbm_surface_h buffer);
 tdm_error      vc4_layer_unset_buffer(tdm_layer *layer);
+tdm_error      vc4_layer_get_buffer_flags(tdm_layer *layer, unsigned int *flags);
+tbm_surface_queue_h vc4_hwc_window_get_tbm_buffer_queue(tdm_hwc_window *hwc_window, tdm_error *error);
+tdm_error      vc4_hwc_window_set_zpos(tdm_hwc_window *hwc_window, uint32_t zpos);
+tdm_error      vc4_hwc_window_set_composition_type(tdm_hwc_window *hwc_window,
+                                                                                               tdm_hwc_window_composition composition_type);
+tdm_error      vc4_hwc_window_set_buffer_damage(tdm_hwc_window *hwc_window, tdm_hwc_region damage);
+tdm_error      vc4_hwc_window_set_info(tdm_hwc_window *hwc_window, tdm_hwc_window_info *info);
+tdm_error      vc4_hwc_window_set_buffer(tdm_hwc_window *hwc_window, tbm_surface_h surface);
+
 
 /* drm module internal macros, structures, functions */
 #define NEVER_GET_HERE() TDM_ERR("** NEVER GET HERE **")
@@ -110,16 +131,18 @@ typedef struct _tdm_vc4_data {
        drmModeResPtr mode_res;
        drmModePlaneResPtr plane_res;
 
+       int hwc_mode;
+
        struct list_head output_list;
        struct list_head buffer_list;
 } tdm_vc4_data;
 
-uint32_t        tdm_vc4_format_to_drm_format(tbm_format format);
-tbm_format   tdm_vc4_format_to_tbm_format(uint32_t format);
+uint32_t       tdm_vc4_format_to_drm_format(tbm_format format);
+tbm_format     tdm_vc4_format_to_tbm_format(uint32_t format);
 
-void            tdm_vc4_display_update_output_status(tdm_vc4_data *vc4_data);
+void           tdm_vc4_display_update_output_status(tdm_vc4_data *vc4_data);
 tdm_error      tdm_vc4_display_create_output_list(tdm_vc4_data *vc4_data);
-void            tdm_vc4_display_destroy_output_list(tdm_vc4_data *vc4_data);
+void           tdm_vc4_display_destroy_output_list(tdm_vc4_data *vc4_data);
 tdm_error      tdm_vc4_display_create_layer_list(tdm_vc4_data *vc4_data);
 
 #endif /* _TDM_VC4_H_ */
index 00c2c85..b3b93c1 100644 (file)
@@ -9,8 +9,15 @@
 
 #define MIN_WIDTH   32
 
+#define LIST_INSERT_AFTER(__after, __item) \
+               (__item)->prev = (__after); \
+               (__item)->next = (__after)->next; \
+               (__after)->next->prev = (__item); \
+               (__after)->next = (__item);
+
 typedef struct _tdm_vc4_output_data tdm_vc4_output_data;
 typedef struct _tdm_vc4_layer_data tdm_vc4_layer_data;
+typedef struct _tdm_vc4_hwc_window_data tdm_vc4_hwc_window_data;
 typedef struct _tdm_vc4_event_data tdm_vc4_event_data;
 
 typedef enum {
@@ -65,6 +72,17 @@ struct _tdm_vc4_output_data {
        tbm_surface_h crtc_buffer;
        int crtc_enabled;
        unsigned int crtc_fb_id;
+
+       tdm_vc4_hwc_window_data *target_hwc_window;
+
+       int need_validate;
+       int need_target_buffer;
+       int hw_layer_count;
+       int need_set_crtc;
+       int top_layer_idx;
+       int hwc_enable;
+
+       struct list_head hwc_window_list;
 };
 
 struct _tdm_vc4_layer_data {
@@ -85,6 +103,33 @@ struct _tdm_vc4_layer_data {
        int display_buffer_changed;
 };
 
+struct _tdm_vc4_hwc_window_data {
+       struct list_head link;
+
+       /* data which are fixed at initializing */
+       tdm_vc4_data *vc4_data;
+       tdm_vc4_output_data *output_data;
+
+       /* not fixed data below */
+       int zpos;
+
+       tdm_hwc_window_info info;
+       int info_changed;
+
+       tbm_surface_h surface;
+       int display_buffer_changed;
+       int enabled_flag;
+
+       /* client_type stores the initial type given to us by client(compositor) */
+       tdm_hwc_window_composition client_type;
+       /* validated_type stores the type after running Validate */
+       tdm_hwc_window_composition validated_type;
+};
+
+tdm_hwc_window *
+_vc4_output_hwc_window_create(tdm_output *output, tdm_hwc_window_info *info,
+                                                               tdm_error *error);
+
 static drmModeModeInfoPtr
 _tdm_vc4_display_get_mode(tdm_vc4_output_data *output_data)
 {
@@ -785,6 +830,10 @@ tdm_vc4_display_destroy_output_list(tdm_vc4_data *vc4_data)
                return;
 
        LIST_FOR_EACH_ENTRY_SAFE(o, oo, &vc4_data->output_list, link) {
+
+               if (o->target_hwc_window)
+                       vc4_output_hwc_window_destroy(o, o->target_hwc_window);
+
                if (o->crtc_enabled) {
                        _tdm_vc4_display_set_crtc(vc4_data, o, 0);
                        o->crtc_enabled = 0;
@@ -967,6 +1016,12 @@ tdm_vc4_display_create_output_list(tdm_vc4_data *vc4_data)
                                                                                 &output_data->output_modes[j]);
                }
 
+               if (vc4_data->hwc_mode) {
+                       output_data->hwc_enable = 1;
+                       output_data->target_hwc_window = NULL;
+                       LIST_INITHEAD(&output_data->hwc_window_list);
+               }
+
                LIST_ADDTAIL(&output_data->link, &vc4_data->output_list);
 
                TDM_DBG("output_data(%p) connector_id(%d:%d:%d-%d) encoder_id(%d) crtc_id(%d) pipe(%d) dpms_id(%d)",
@@ -1162,6 +1217,9 @@ vc4_output_get_capability(tdm_output *output, tdm_caps_output *caps)
                drmModeFreeProperty(prop);
        }
 
+       if (output_data->hwc_enable)
+               caps->capabilities |= TDM_OUTPUT_CAPABILITY_HWC;
+
        drmModeFreeObjectProperties(props);
        drmModeFreeCrtc(crtc);
        drmModeFreeConnector(connector);
@@ -1330,6 +1388,100 @@ vc4_output_set_vblank_handler(tdm_output *output,
        return TDM_ERROR_NONE;
 }
 
+tdm_vc4_layer_data *
+_vc4_output_get_layer(tdm_vc4_output_data *output_data, int index)
+{
+       tdm_vc4_layer_data *l = NULL;
+       LIST_FOR_EACH_ENTRY(l, &output_data->layer_list, link)
+               if (l->zpos == index)
+                       return l;
+
+       return NULL;
+}
+
+static tdm_error
+_vc4_layer_attach_window(tdm_vc4_layer_data *layer_data,
+                                                       tdm_vc4_hwc_window_data *hwc_window_data)
+{
+       tdm_error ret;
+
+       if (hwc_window_data == NULL || !hwc_window_data->surface) {
+               ret = vc4_layer_unset_buffer(layer_data);
+               RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret);
+       } else {
+               ret = vc4_layer_set_info((tdm_layer *)layer_data, (tdm_info_layer *)&(hwc_window_data->info));
+               RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret);
+               RETURN_VAL_IF_FAIL(hwc_window_data->surface != NULL, TDM_ERROR_INVALID_PARAMETER);
+               ret = vc4_layer_set_buffer(layer_data, hwc_window_data->surface);
+               RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret);
+       }
+
+       return ret;
+}
+
+static int
+_get_primary_layer_index(tdm_vc4_output_data *vc4_output)
+{
+       int i = 0;
+       tdm_vc4_layer_data *layer = NULL;
+
+       LIST_FOR_EACH_ENTRY(layer, &vc4_output->layer_list, link) {
+               if (layer->capabilities & TDM_LAYER_CAPABILITY_PRIMARY)
+                       return i;
+               i++;
+       }
+
+       return 0;
+}
+
+static tdm_error
+_tdm_vc4_display_prepare_commit(tdm_vc4_output_data *output_data) {
+
+       tdm_vc4_layer_data * layer = NULL;
+       tdm_vc4_hwc_window_data *hw = NULL;
+       int num_hw_layer = 0;
+       int i;
+       int fb_idx = 0;
+
+       RETURN_VAL_IF_FAIL(output_data->need_validate == 0, TDM_ERROR_OPERATION_FAILED);
+
+       i = output_data->top_layer_idx;
+       num_hw_layer = LIST_LENGTH(&output_data->layer_list);
+       fb_idx = _get_primary_layer_index(output_data);
+
+       //set target hwc window
+       if (output_data->need_target_buffer) {
+               layer = _vc4_output_get_layer(output_data, fb_idx);
+               _vc4_layer_attach_window(layer, output_data->target_hwc_window);
+       }
+
+       //set hwc windows
+       LIST_FOR_EACH_ENTRY_REV(hw, &output_data->hwc_window_list, link) {
+               if (output_data->need_target_buffer && i == fb_idx) {
+                       i--;
+               }
+               if (hw->client_type != TDM_COMPOSITION_DEVICE)
+                       continue;
+               RETURN_VAL_IF_FAIL(i >= 0, TDM_ERROR_OPERATION_FAILED);
+               layer = _vc4_output_get_layer(output_data, i--);
+               _vc4_layer_attach_window(layer, hw);
+       }
+
+       //disable unused layer
+       while (i >= 0) {
+               layer = _vc4_output_get_layer(output_data, i--);
+               _vc4_layer_attach_window(layer, NULL);
+       }
+
+       i = output_data->top_layer_idx + 1;
+       while (i < num_hw_layer) {
+               layer = _vc4_output_get_layer(output_data, i++);
+               _vc4_layer_attach_window(layer, NULL);
+       }
+
+       return TDM_ERROR_NONE;
+}
+
 tdm_error
 vc4_output_commit(tdm_output *output, int sync, void *user_data)
 {
@@ -1343,6 +1495,11 @@ vc4_output_commit(tdm_output *output, int sync, void *user_data)
 
        vc4_data = output_data->vc4_data;
 
+       if (output_data->hwc_enable) {
+               ret = _tdm_vc4_display_prepare_commit(output_data);
+               RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret);
+       }
+
        LIST_FOR_EACH_ENTRY(layer_data, &output_data->layer_list, link) {
                ret = _tdm_vc4_display_commit_layer(layer_data);
                if (ret != TDM_ERROR_NONE)
@@ -1460,9 +1617,41 @@ vc4_output_set_mode(tdm_output *output, const tdm_output_mode *mode)
        RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER);
        RETURN_VAL_IF_FAIL(mode, TDM_ERROR_INVALID_PARAMETER);
 
+       if (output_data->hwc_enable) {
+
+               tdm_hwc_window_info info = {0};
+               tdm_error ret = TDM_ERROR_NONE;
+               tdm_vc4_hwc_window_data *target_hwc_window;
+
+               info.dst_pos.x = 0;
+               info.dst_pos.y = 0;
+               info.dst_pos.h = mode->vdisplay;;
+               info.dst_pos.w = mode->hdisplay;
+
+               info.src_config.pos.x = 0;
+               info.src_config.pos.y = 0;
+               info.src_config.pos.h = mode->vdisplay;
+               info.src_config.pos.w = mode->hdisplay;
+
+               info.src_config.size.h = mode->hdisplay;
+               info.src_config.size.v = mode->vdisplay;
+               info.src_config.format = TBM_FORMAT_ARGB8888;
+
+               target_hwc_window = _vc4_output_hwc_window_create(output_data, &info, &ret);
+               if (ret != TDM_ERROR_NONE) {
+                       TDM_ERR("create target hwc window failed (%d)", ret);
+                       return TDM_ERROR_OPERATION_FAILED;
+               }
+
+               if (output_data->target_hwc_window)
+                       vc4_output_hwc_window_destroy(output, output_data->target_hwc_window);
+
+               output_data->target_hwc_window = target_hwc_window;
+               output_data->need_set_crtc = 1;
+       }
+
        output_data->current_mode = mode;
        output_data->mode_changed = 1;
-
        return TDM_ERROR_NONE;
 }
 
@@ -1759,3 +1948,468 @@ vc4_layer_unset_buffer(tdm_layer *layer)
 
        return TDM_ERROR_NONE;
 }
+
+tdm_error
+tdm_vc4_output_insert_hwc_window(tdm_vc4_output_data *output, tdm_vc4_hwc_window_data *hwc_window)
+{
+       tdm_vc4_hwc_window_data *item = NULL;
+       LIST_FOR_EACH_ENTRY_REV(item, &output->hwc_window_list, link) {
+               if (item == hwc_window)
+                       return TDM_ERROR_OPERATION_FAILED;
+
+               if (item->zpos <= hwc_window->zpos)
+                       break;
+       }
+
+       LIST_INSERT_AFTER(&item->link, &hwc_window->link);
+
+       return TDM_ERROR_NONE;
+}
+
+int
+_vc4_output_get_changed_number(tdm_vc4_output_data *vc4_output)
+{
+       int num = 0;
+       tdm_vc4_hwc_window_data *hw = NULL;
+
+       LIST_FOR_EACH_ENTRY(hw, &vc4_output->hwc_window_list, link) {
+               if (hw->client_type != hw->validated_type)
+                       num++;
+       }
+
+       return num;
+}
+
+tdm_hwc_window *
+_vc4_output_hwc_window_create(tdm_output *output, tdm_hwc_window_info *info, tdm_error *error)
+{
+       tdm_vc4_hwc_window_data *vc4_hwc_window = NULL;
+       tdm_vc4_output_data *vc4_output = output;
+
+       if (error)
+               *error = TDM_ERROR_NONE;
+
+       if (!vc4_output) {
+               TDM_ERR("invalid params");
+               if (error)
+                       *error = TDM_ERROR_INVALID_PARAMETER;
+               return NULL;
+       }
+
+       vc4_hwc_window = calloc(1, sizeof(tdm_vc4_hwc_window_data));
+       if (!vc4_hwc_window) {
+               TDM_ERR("alloc failed");
+               if (error)
+                       *error = TDM_ERROR_OUT_OF_MEMORY;
+               return NULL;
+       }
+
+       if ((vc4_hwc_window->vc4_data = vc4_output->vc4_data) == NULL) {
+               TDM_ERR("invalid params");
+               if (error)
+                       *error = TDM_ERROR_INVALID_PARAMETER;
+               goto fail;
+       }
+
+       vc4_hwc_window->output_data = output;
+       vc4_hwc_window->zpos = 0;
+
+       if (info)
+               memcpy(&vc4_hwc_window->info, info, sizeof(tdm_hwc_window_info));
+
+       return vc4_hwc_window;
+fail:
+       if (vc4_hwc_window)
+               free(vc4_hwc_window);
+
+       return NULL;
+}
+
+tdm_hwc_window *
+vc4_output_hwc_window_create(tdm_output *output, tdm_error *error)
+{
+       tdm_vc4_hwc_window_data *vc4_hwc_window = NULL;
+       tdm_vc4_output_data *vc4_output = output;
+       tdm_error err;
+
+       vc4_hwc_window = _vc4_output_hwc_window_create(vc4_output, NULL, error);
+       if (vc4_hwc_window == NULL)
+               return NULL;
+
+       err = tdm_vc4_output_insert_hwc_window(vc4_output, vc4_hwc_window);
+       if (err != TDM_ERROR_NONE) {
+               if (error)
+                       *error = err;
+               free(vc4_hwc_window);
+               return NULL;
+       }
+
+       TDM_DBG("hwc_window(%p) create", vc4_hwc_window);
+       if (error)
+               *error = TDM_ERROR_NONE;
+
+       return vc4_hwc_window;
+}
+
+tdm_error
+vc4_output_hwc_window_destroy(tdm_output *output, tdm_hwc_window *hwc_window)
+{
+       tdm_vc4_hwc_window_data *vc4_hwc_window = hwc_window;
+
+       LIST_DEL(&vc4_hwc_window->link);
+
+       free(vc4_hwc_window);
+
+       return TDM_ERROR_NONE;
+}
+
+static const char *
+_comp_to_str(tdm_hwc_window_composition composition_type)
+{
+       if (composition_type == TDM_COMPOSITION_CLIENT)
+               return "CLIENT";
+       else if (composition_type == TDM_COMPOSITION_DEVICE_CANDIDATE)
+               return "DEVICE_CANDIDATE";
+       else if (composition_type == TDM_COMPOSITION_DEVICE)
+               return "DEVICE";
+       else if (composition_type == TDM_COMPOSITION_CURSOR)
+               return "CURSOR";
+
+       return "unknown";
+}
+
+tdm_error
+vc4_output_hwc_validate(tdm_output *output, uint32_t *num_types)
+{
+       tdm_vc4_output_data *vc4_output = output;
+       tdm_vc4_data *vc4_data = NULL;
+       RETURN_VAL_IF_FAIL(vc4_output != NULL, TDM_ERROR_INVALID_PARAMETER);
+       RETURN_VAL_IF_FAIL(num_types != NULL, TDM_ERROR_INVALID_PARAMETER);
+       tdm_vc4_hwc_window_data *hw = NULL;
+       int hw_layer_count = 0;
+       int need_target_buffer = 0;
+       int max_hw_layer = LIST_LENGTH(&vc4_output->layer_list);
+       int fb_index = _get_primary_layer_index(vc4_output);
+       int i = 0;
+       int top_layer_idx = fb_index;
+
+       vc4_data = vc4_output->vc4_data;
+       RETURN_VAL_IF_FAIL(vc4_data != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+       if (vc4_output->need_set_crtc) {
+               need_target_buffer = 1;
+
+               LIST_FOR_EACH_ENTRY(hw, &vc4_output->hwc_window_list, link) {
+                       hw->validated_type = TDM_COMPOSITION_CLIENT;
+               }
+       } else {
+               int layer_idx;
+               int num_hwc_windows = LIST_LENGTH(&vc4_output->hwc_window_list);
+
+               LIST_FOR_EACH_ENTRY(hw, &vc4_output->hwc_window_list, link) {
+                       hw->validated_type = TDM_COMPOSITION_CLIENT;
+               }
+
+               /* mark the top window */
+               if (num_hwc_windows > 1) {
+                       layer_idx = max_hw_layer;
+                       LIST_FOR_EACH_ENTRY_REV(hw, &vc4_output->hwc_window_list, link) {
+                               layer_idx--;
+                               if (hw->client_type == TDM_COMPOSITION_DEVICE && layer_idx != fb_index) {
+                                       //check format
+                                       if (IS_RGB(hw->info.src_config.format)) {
+                                               hw->validated_type = TDM_COMPOSITION_DEVICE;
+                                               hw_layer_count++;
+                                               top_layer_idx++;
+                                               continue;
+                                       }
+                               }
+                               break;
+                       }
+               }
+
+               if ((num_hwc_windows - hw_layer_count) > (fb_index + 1))
+                       need_target_buffer = 1;
+
+               max_hw_layer = fb_index + 1;
+               if (need_target_buffer)
+                       max_hw_layer--;
+
+               /* mark the bottom windows */
+               layer_idx = -1;
+               LIST_FOR_EACH_ENTRY(hw, &vc4_output->hwc_window_list, link) {
+                       layer_idx++;
+                       /* break if the remaining windows are already marked */
+                       if (hw->validated_type == TDM_COMPOSITION_DEVICE)
+                               break;
+                       if (hw->client_type == TDM_COMPOSITION_DEVICE && layer_idx < max_hw_layer) {
+                               //check format
+                               if (IS_RGB(hw->info.src_config.format)) {
+                                       hw->validated_type = TDM_COMPOSITION_DEVICE;
+                                       hw_layer_count++;
+                                       continue;
+                               }
+                       }
+
+                       need_target_buffer = 1;
+                       break;
+               }
+       }
+
+       LIST_FOR_EACH_ENTRY(hw, &vc4_output->hwc_window_list, link) {
+               if (need_target_buffer && fb_index == i++)
+                       TDM_DBG(" window(%p) target", vc4_output->target_hwc_window);
+               else
+                       TDM_DBG(" window(%p) type: %s -> %s", hw,
+                                       _comp_to_str(hw->client_type), _comp_to_str(hw->validated_type));
+       }
+
+       if (need_target_buffer)
+               hw_layer_count++;
+
+       vc4_output->need_target_buffer = need_target_buffer;
+       vc4_output->hw_layer_count = hw_layer_count;
+       vc4_output->top_layer_idx = top_layer_idx;
+
+       *num_types = _vc4_output_get_changed_number(vc4_output);
+
+       if (*num_types == 0)
+               vc4_output->need_validate = 0;
+
+       return TDM_ERROR_NONE;
+}
+
+tdm_error
+vc4_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_vc4_output_data *vc4_output = output;
+       int num = 0;
+
+       RETURN_VAL_IF_FAIL(vc4_output != NULL, TDM_ERROR_INVALID_PARAMETER);
+       RETURN_VAL_IF_FAIL(num_elements != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+       if ((hwc_window == NULL) || (composition_types == NULL)) {
+               *num_elements = _vc4_output_get_changed_number(vc4_output);
+               return TDM_ERROR_NONE;
+       }
+
+       tdm_vc4_hwc_window_data *hw = NULL;
+       LIST_FOR_EACH_ENTRY_REV(hw, &vc4_output->hwc_window_list, link) {
+
+               if (num >= *num_elements)
+                       break;
+
+               if (hw->client_type != hw->validated_type) {
+                       composition_types[num] = hw->validated_type;
+                       hwc_window[num] = hw;
+                       num++;
+               }
+       }
+
+       //set real num of changed composition types
+       *num_elements = num;
+
+       return TDM_ERROR_NONE;
+}
+
+tdm_error
+vc4_output_hwc_accept_changes(tdm_output *output)
+{
+       tdm_vc4_output_data *vc4_output = output;
+
+       RETURN_VAL_IF_FAIL(vc4_output != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+       tdm_vc4_hwc_window_data *hw = NULL;
+       LIST_FOR_EACH_ENTRY_REV(hw, &vc4_output->hwc_window_list, link)
+               hw->client_type = hw->validated_type;
+
+       vc4_output->need_validate = 0;
+
+       return TDM_ERROR_NONE;
+}
+
+tbm_surface_queue_h
+vc4_output_hwc_get_target_buffer_queue(tdm_output *output, tdm_error *error)
+{
+       tdm_vc4_output_data *vc4_output = output;
+       tbm_surface_queue_h tqueue = NULL;
+
+       if (error)
+               *error = TDM_ERROR_INVALID_PARAMETER;
+
+       RETURN_VAL_IF_FAIL(vc4_output != NULL, NULL);
+
+       if (vc4_output->target_hwc_window == NULL) {
+               if (error)
+                       *error = TDM_ERROR_OPERATION_FAILED;
+               return NULL;
+       }
+
+       tqueue = vc4_hwc_window_get_tbm_buffer_queue(vc4_output->target_hwc_window, error);
+       RETURN_VAL_IF_FAIL(tqueue, NULL);
+
+       if (error)
+               *error = TDM_ERROR_NONE;
+
+       return tqueue;
+}
+
+tdm_error
+vc4_output_hwc_set_client_target_buffer(tdm_output *output, tbm_surface_h buffer,
+                                                                       tdm_hwc_region damage, tdm_hwc_window **composited_wnds,
+                                                                       uint32_t num_wnds)
+{
+       tdm_vc4_output_data *vc4_output = output;
+       tdm_error err;
+
+       RETURN_VAL_IF_FAIL(vc4_output != NULL, TDM_ERROR_INVALID_PARAMETER);
+       RETURN_VAL_IF_FAIL(vc4_output->target_hwc_window != NULL, TDM_ERROR_OPERATION_FAILED);
+
+       err = vc4_hwc_window_set_buffer(vc4_output->target_hwc_window, buffer);
+       RETURN_VAL_IF_FAIL(err == TDM_ERROR_NONE, err);
+
+       err = vc4_hwc_window_set_buffer_damage(vc4_output->target_hwc_window, damage);
+       RETURN_VAL_IF_FAIL(err == TDM_ERROR_NONE, err);
+
+       return TDM_ERROR_NONE;
+}
+
+
+tbm_surface_queue_h
+vc4_hwc_window_get_tbm_buffer_queue(tdm_hwc_window *hwc_window, tdm_error *error)
+{
+       tdm_vc4_hwc_window_data *vc4_hwc_window = NULL;
+       tdm_vc4_output_data *vc4_output = NULL;
+       tbm_surface_queue_h tqueue = NULL;
+       tbm_format format;
+
+       if (error)
+               *error = TDM_ERROR_INVALID_PARAMETER;
+
+       RETURN_VAL_IF_FAIL(hwc_window != NULL, NULL);
+       vc4_hwc_window = hwc_window;
+       vc4_output = vc4_hwc_window->output_data;
+       RETURN_VAL_IF_FAIL(vc4_output != NULL, NULL);
+
+       int wight = vc4_hwc_window->info.src_config.size.h;
+       int hight = vc4_hwc_window->info.src_config.size.v;
+
+       format = vc4_hwc_window->info.src_config.format;
+
+       tqueue = tbm_surface_queue_create(3, wight, hight, format, TBM_BO_SCANOUT);
+       if (error)
+               *error = TDM_ERROR_OPERATION_FAILED;
+       RETURN_VAL_IF_FAIL(tqueue != NULL, NULL);
+
+       if (error)
+               *error = TDM_ERROR_NONE;
+
+       return tqueue;
+
+}
+
+tdm_error
+vc4_hwc_window_set_zpos(tdm_hwc_window *hwc_window, uint32_t zpos)
+{
+       tdm_vc4_hwc_window_data *vc4_hwc_window = hwc_window;
+       tdm_vc4_output_data *vc4_output;
+
+       RETURN_VAL_IF_FAIL(vc4_hwc_window != NULL, TDM_ERROR_INVALID_PARAMETER);
+       RETURN_VAL_IF_FAIL(zpos < 256, TDM_ERROR_INVALID_PARAMETER);
+
+       vc4_output = vc4_hwc_window->output_data;
+       RETURN_VAL_IF_FAIL(vc4_output != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+       if (vc4_hwc_window->zpos == zpos)
+               return TDM_ERROR_NONE;
+
+       LIST_DEL(&vc4_hwc_window->link);
+
+       vc4_hwc_window->zpos = zpos;
+
+       tdm_vc4_output_insert_hwc_window(vc4_output, vc4_hwc_window);
+
+       vc4_output->need_validate = 1;
+
+       return TDM_ERROR_NONE;
+
+}
+
+tdm_error
+vc4_hwc_window_set_composition_type(tdm_hwc_window *hwc_window,
+                                                                       tdm_hwc_window_composition comp_type)
+{
+       tdm_vc4_hwc_window_data *vc4_hwc_window = hwc_window;
+       tdm_vc4_output_data *vc4_output = vc4_hwc_window->output_data;
+
+       RETURN_VAL_IF_FAIL(vc4_hwc_window != NULL, TDM_ERROR_INVALID_PARAMETER);
+       RETURN_VAL_IF_FAIL(vc4_output != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+       if (vc4_hwc_window->client_type == comp_type)
+               return TDM_ERROR_NONE;
+
+       vc4_hwc_window->client_type = comp_type;
+       vc4_output->need_validate = 1;
+
+       return TDM_ERROR_NONE;
+
+}
+
+tdm_error
+vc4_hwc_window_set_buffer_damage(tdm_hwc_window *hwc_window, tdm_hwc_region damage)
+{
+       tdm_vc4_hwc_window_data *vc4_hwc_window = hwc_window;
+       tdm_vc4_output_data *vc4_output = vc4_hwc_window->output_data;
+
+       RETURN_VAL_IF_FAIL(vc4_hwc_window != NULL, TDM_ERROR_INVALID_PARAMETER);
+       RETURN_VAL_IF_FAIL(vc4_output != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+       //TODO::
+
+       return TDM_ERROR_NONE;
+}
+
+tdm_error
+vc4_hwc_window_set_info(tdm_hwc_window *hwc_window, tdm_hwc_window_info *info)
+{
+       tdm_vc4_hwc_window_data *vc4_hwc_window = hwc_window;
+       tdm_vc4_output_data *vc4_output = vc4_hwc_window->output_data;
+
+       RETURN_VAL_IF_FAIL(vc4_hwc_window != NULL, TDM_ERROR_INVALID_PARAMETER);
+       RETURN_VAL_IF_FAIL(vc4_output != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+       if (!memcmp(&vc4_hwc_window->info, info, sizeof(tdm_hwc_window_info)))
+               return TDM_ERROR_NONE;
+
+       vc4_hwc_window->info = *info;
+       vc4_output->need_validate = 1;
+
+       return TDM_ERROR_NONE;
+}
+
+tdm_error
+vc4_hwc_window_set_buffer(tdm_hwc_window *hwc_window, tbm_surface_h surface)
+{
+       tdm_vc4_hwc_window_data *vc4_hwc_window = hwc_window;
+       tdm_vc4_output_data *vc4_output;
+       tdm_vc4_data *vc4_data;
+       tdm_error err = TDM_ERROR_OPERATION_FAILED;
+
+       RETURN_VAL_IF_FAIL(vc4_hwc_window != NULL, err);
+
+       vc4_output = vc4_hwc_window->output_data;
+       vc4_data = vc4_hwc_window->vc4_data;
+
+       RETURN_VAL_IF_FAIL(vc4_output != NULL, err);
+       RETURN_VAL_IF_FAIL(vc4_data != NULL, err);
+
+       if (vc4_hwc_window->surface == surface)
+               return TDM_ERROR_NONE;
+
+       vc4_hwc_window->surface = surface;
+
+       return TDM_ERROR_NONE;
+}