+
+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;
+}