diff options
Diffstat (limited to 'drivers/gpu/drm/amd/dal/display_service/scaler_adj_group.c')
-rw-r--r-- | drivers/gpu/drm/amd/dal/display_service/scaler_adj_group.c | 944 |
1 files changed, 944 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/dal/display_service/scaler_adj_group.c b/drivers/gpu/drm/amd/dal/display_service/scaler_adj_group.c new file mode 100644 index 000000000000..ba894168dc76 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/scaler_adj_group.c @@ -0,0 +1,944 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * 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, sublicense, + * 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 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 NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: AMD + * + */ +#include "dal_services.h" + +#include "include/adjustment_interface.h" +#include "include/hw_sequencer_interface.h" +#include "include/hw_path_mode_set_interface.h" +#include "include/hw_adjustment_types.h" +#include "include/adjustment_types.h" +#include "include/display_service_types.h" +#include "include/logger_interface.h" +#include "include/set_mode_types.h" +#include "include/set_mode_interface.h" +#include "include/dcs_types.h" +#include "include/dcs_interface.h" +#include "include/timing_service_types.h" +#include "include/timing_service_interface.h" +#include "include/display_path_interface.h" + +#include "ds_translation.h" +#include "adjustment_container.h" +#include "scaler_adj_group.h" + +/*to chk whether underscan can be applied to current timing*/ +static bool can_scaling_be_applied( + struct adj_container *container, + enum timing_standard timing_std, + enum timing_source timing_src, + enum adjustment_id adj_id, + enum underscan_reason reason) +{ + struct adjustment_info *info = NULL; + enum signal_type type; + + if (!container) + return false; + + if (adj_id != ADJ_ID_MULTIMEDIA_PASS_THROUGH) { + info = dal_adj_info_set_get_adj_info( + &container->adj_info_set, + ADJ_ID_MULTIMEDIA_PASS_THROUGH); + if (info) { + /*if indeed multimedia pass thru,we don't need apply + * underscan*/ + if (info->adj_data.ranged.cur > 0) + return false; + } + } + type = dal_adj_container_get_signal_type(container); + if (dal_timing_service_is_ce_timing_standard(timing_std) || + (dal_is_embedded_signal(type) && timing_std == + TIMING_STANDARD_EXPLICIT)) { + if (timing_src != TIMING_SOURCE_CUSTOM && + reason != UNDERSCAN_REASON_FALL_BACK) + return true;/*we will do underscan*/ + } + return false; +} + +static bool is_pass_thru_enabled( + const struct ds_adjustment_scaler *scaler_param, + const struct ds_underscan_parameter *underscan_param, + struct adj_container *container, + enum build_path_set_reason reason) +{ + const struct adjustment_info *info; + enum underscan_reason reason_for_underscan; + + if (scaler_param->adjust_id == ADJ_ID_MULTIMEDIA_PASS_THROUGH && + scaler_param->value > 0) + return true; + + switch (reason) { + case BUILD_PATH_SET_REASON_FALLBACK_UNDERSCAN: + reason_for_underscan = UNDERSCAN_REASON_FALL_BACK; + break; + case BUILD_PATH_SET_REASON_SET_MODE: + reason_for_underscan = UNDERSCAN_REASON_PATCH_TIMING; + break; + case BUILD_PATH_SET_REASON_SET_ADJUSTMENT: + default: + reason_for_underscan = UNDERSCAN_REASON_SET_ADJUSTMENT; + break; + } + + if (can_scaling_be_applied( + container, + scaler_param->timing_standard, + scaler_param->timing_source, + scaler_param->adjust_id, + reason_for_underscan)) { + info = dal_adj_info_set_get_adj_info( + &container->adj_info_set, + ADJ_ID_MULTIMEDIA_PASS_THROUGH); + if (!info) + return false; + if (info->adj_data.ranged.cur > 0) + return true; + } + + return false; +} + +static bool build_base_avi_info_frame_parameter( + const struct ds_adjustment_scaler *scaler_param, + const struct ds_underscan_parameter *underscan_param, + struct adj_container *container, + const struct hw_path_mode *hw_path_mode, + enum build_path_set_reason reason, + enum hw_scale_options *underscan_avi_rule) +{ + struct cea861_support cea861_support = {0}; + + if (hw_path_mode->mode.ds_info.cea_vic != 0) + if (is_pass_thru_enabled( + scaler_param, underscan_param, container, reason)) + *underscan_avi_rule = HW_SCALE_OPTION_OVERSCAN; + else + *underscan_avi_rule = HW_SCALE_OPTION_UNDERSCAN; + else { + if (dal_adj_container_get_cea861_support(container, + &cea861_support) && + cea861_support.features.UNDER_SCAN == 1) + *underscan_avi_rule = HW_SCALE_OPTION_UNDERSCAN; + else + *underscan_avi_rule = HW_SCALE_OPTION_UNKNOWN; + } + return true; +} + +static bool build_avi_info_frame_parameter( + const struct ds_adjustment_scaler *scaler_param, + struct ds_underscan_parameter *underscan_param, + struct adj_container *container, + const struct hw_path_mode *hw_path_mode, + enum build_path_set_reason reason, + enum hw_scale_options *underscan_avi_rule) +{ + union cea_video_capability_data_block vcdb = { {0} }; + bool result = false; + + if (dal_adj_container_get_cea_video_cap_data_block(container, &vcdb)) { + if (hw_path_mode->mode.ds_info.DISPLAY_PREFERED_MODE == 1 && + (vcdb.bits.S_PT0 != 0 || vcdb.bits.S_PT1 != 0)) { + if (vcdb.bits.S_PT0 == 1 && vcdb.bits.S_PT1 == 0) + *underscan_avi_rule = HW_SCALE_OPTION_OVERSCAN; + else if (vcdb.bits.S_PT0 == 0 && vcdb.bits.S_PT1 == 1) + *underscan_avi_rule = HW_SCALE_OPTION_UNDERSCAN; + else + result = build_base_avi_info_frame_parameter( + scaler_param, + underscan_param, + container, + hw_path_mode, + reason, + underscan_avi_rule); + } else { + if (hw_path_mode->mode.ds_info.cea_vic != 0) { + if (vcdb.bits.S_CE0 == 1 && vcdb.bits.S_CE1 == 0) + *underscan_avi_rule = HW_SCALE_OPTION_OVERSCAN; + else if (vcdb.bits.S_CE0 == 0 && vcdb.bits.S_CE1 == 1) + *underscan_avi_rule = HW_SCALE_OPTION_UNDERSCAN; + else + result = build_base_avi_info_frame_parameter( + scaler_param, + underscan_param, + container, + hw_path_mode, + reason, + underscan_avi_rule); + } else { + if (vcdb.bits.S_IT0 == 1 && vcdb.bits.S_IT1 == 0) + *underscan_avi_rule = HW_SCALE_OPTION_OVERSCAN; + else if (vcdb.bits.S_IT0 == 0 && vcdb.bits.S_IT1 == 1) + *underscan_avi_rule = HW_SCALE_OPTION_UNDERSCAN; + else + result = build_base_avi_info_frame_parameter( + scaler_param, + underscan_param, + container, + hw_path_mode, + reason, + underscan_avi_rule); + } + } + } else + result = build_base_avi_info_frame_parameter( + scaler_param, + underscan_param, + container, + hw_path_mode, + reason, + underscan_avi_rule); + return result; +} + +/*we donot enable underscan on DP since architect decision*/ +static bool is_dp_underscan_disabled( + struct display_path *display_path, + uint32_t idx) +{ + if (display_path) { + enum signal_type signal = + dal_display_path_get_query_signal( + display_path, + SINK_LINK_INDEX); + + if (dal_is_dp_signal(signal) || + dal_is_dp_external_signal(signal)) + return true; + } + return false; +} + +static bool setup_parameter( + const struct hw_path_mode *hw_path_mode, + const struct ds_adjustment_scaler *scaler_param, + struct ds_underscan_parameter *underscan_param) +{ + if (scaler_param->flags.bits.IS_UNDERSCAN_DESC != 1 || !scaler_param || + !hw_path_mode) + return false; + + if (scaler_param->underscan_desc.width == 0 || + scaler_param->underscan_desc.height == 0 || + scaler_param->underscan_desc.width < + scaler_param->underscan_desc.x || + scaler_param->underscan_desc.height < + scaler_param->underscan_desc.y) + return false; + + if (scaler_param->underscan_desc.width > + hw_path_mode->mode.timing.h_addressable) + return false; + + if (scaler_param->underscan_desc.height > + hw_path_mode->mode.timing.v_addressable) + return false; + + dal_memset(underscan_param, 0, sizeof(*underscan_param)); + underscan_param->type = DS_UNDERSCAN_TYPE_DIMENTIONS; + underscan_param->data.dimentions.data.width = + scaler_param->underscan_desc.width; + underscan_param->data.dimentions.data.height = + scaler_param->underscan_desc.height; + underscan_param->data.dimentions.data.position_x = + scaler_param->underscan_desc.x; + underscan_param->data.dimentions.data.position_y = + scaler_param->underscan_desc.y; + underscan_param->data.dimentions.modified_boarder_x = + hw_path_mode->mode.timing.h_addressable - + scaler_param->underscan_desc.width; + underscan_param->data.dimentions.modified_boarder_y = + hw_path_mode->mode.timing.v_addressable - + scaler_param->underscan_desc.height; + + return true; + +} + +static void setup_parameters( + const struct hw_path_mode *hw_path_mode, + const struct ds_adjustment_scaler *scaler_param, + struct ds_overscan *overscan, + struct ds_underscan_parameter *underscan_param, + struct timing_info_parameter *timing_info) +{ + overscan->left = hw_path_mode->mode.overscan.left; + overscan->right = hw_path_mode->mode.overscan.right; + overscan->top = hw_path_mode->mode.overscan.top; + overscan->bottom = hw_path_mode->mode.overscan.bottom; + dal_memset(underscan_param, 0, sizeof(*underscan_param)); + underscan_param->type = DS_UNDERSCAN_TYPE_PERCENT; + underscan_param->data.percent.old_dst_x = + hw_path_mode->mode.scaling_info.dst.width; + underscan_param->data.percent.old_dst_y = + hw_path_mode->mode.scaling_info.dst.height; + underscan_param->data.percent.percent_x = + (uint32_t)scaler_param->value; + underscan_param->data.percent.percent_y = + (uint32_t)scaler_param->value; + + dal_memset(timing_info, 0, sizeof(*timing_info)); + timing_info->timing = hw_path_mode->mode.timing; + timing_info->dst_width = + hw_path_mode->mode.scaling_info.dst.width; + timing_info->dst_height = + hw_path_mode->mode.scaling_info.dst.height; + +} + + +static void extract_parameters( + const struct ds_adjustment_scaler *scaler_param, + const struct timing_info_parameter *timing_info, + const struct ds_overscan *overscan, + enum hw_scale_options underscan_avi_rule, + struct hw_path_mode *hw_path_mode) +{ + hw_path_mode->mode.overscan.left = overscan->left; + hw_path_mode->mode.overscan.right = overscan->right; + hw_path_mode->mode.overscan.top = overscan->top; + hw_path_mode->mode.overscan.bottom = overscan->bottom; + hw_path_mode->mode.scaling_info.dst.width = + timing_info->dst_width; + hw_path_mode->mode.scaling_info.dst.height = + timing_info->dst_height; + hw_path_mode->mode.underscan_rule = underscan_avi_rule; + hw_path_mode->mode.ds_info.position_x = overscan->left; + hw_path_mode->mode.ds_info.position_y = overscan->top; + hw_path_mode->mode.ds_info.TIMING_UNDERSCAN_PATCHED = 1; +} + +static void update_underscan_bundle( + const struct ds_adjustment_scaler *scaler_param, + const struct underscan_adjustment_group *group, + const struct timing_info_parameter *timing_info, + struct ds_underscan_parameter *underscan) +{ + switch (group->id_requested) { + case ADJ_ID_UNDERSCAN: + if (scaler_param->flags.bits.IS_TV == 1 && + group->current_underscan_auto == 0) { + underscan->type = DS_UNDERSCAN_TYPE_DIMENTIONS; + underscan->data.dimentions.data.width = + group->current_underscan_desc.width; + underscan->data.dimentions.data.height = + group->current_underscan_desc.height; + underscan->data.dimentions.data.position_x = + group->current_underscan_desc.x; + underscan->data.dimentions.data.position_y = + group->current_underscan_desc.y; + underscan->data.dimentions.modified_boarder_x = + timing_info->timing.h_addressable - + group->current_underscan_desc.width; + underscan->data.dimentions.modified_boarder_y = + timing_info->timing.v_addressable - + group->current_underscan_desc.height; + } else { + underscan->data.percent.percent_x = + (uint32_t)group->requested_value; + underscan->data.percent.percent_y = + (uint32_t)group->requested_value; + } + break; + case ADJ_ID_UNDERSCAN_TYPE: + if (group->requested_value == 0) { + underscan->type = DS_UNDERSCAN_TYPE_DIMENTIONS; + underscan->data.dimentions.data.width = + group->current_underscan_desc.width; + underscan->data.dimentions.data.height = + group->current_underscan_desc.height; + underscan->data.dimentions.data.position_x = + group->current_underscan_desc.x; + underscan->data.dimentions.data.position_y = + group->current_underscan_desc.y; + underscan->data.dimentions.modified_boarder_x = + timing_info->timing.h_addressable - + group->current_underscan_desc.width; + underscan->data.dimentions.modified_boarder_y = + timing_info->timing.v_addressable - + group->current_underscan_desc.height; + } else { + underscan->data.percent.percent_x = + (uint32_t)group->current_percent_x; + underscan->data.percent.percent_y = + (uint32_t)group->current_percent_y; + } + break; + default: + break; + } + +} + +static bool calculate_underscan( + const struct ds_underscan_parameter *underscan_param, + uint32_t *new_dest_x, + uint32_t *new_dest_y, + struct ds_overscan *overscan) +{ + uint32_t underscan_x; + uint32_t underscan_y; + uint32_t underscan_x2; + uint32_t underscan_y2; + + if (!underscan_param || !overscan || !new_dest_x || !new_dest_y) + return false; + + if (underscan_param->type != DS_UNDERSCAN_TYPE_PERCENT && + underscan_param->type != DS_UNDERSCAN_TYPE_DIMENTIONS) + return false; + + if (underscan_param->type == DS_UNDERSCAN_TYPE_PERCENT) { + if (underscan_param->data.percent.old_dst_x == 0 || + underscan_param->data.percent.old_dst_y == 0) + return false; + underscan_x = underscan_param->data.percent.old_dst_x * + underscan_param->data.percent.percent_x / 100; + underscan_y = underscan_param->data.percent.old_dst_y * + underscan_param->data.percent.percent_y / 100; + + if (underscan_param->data.percent.old_dst_x <= underscan_x || + underscan_param->data.percent.old_dst_y <= underscan_y) + return false; + + *new_dest_x = underscan_param->data.percent.old_dst_x - underscan_x; + *new_dest_y = underscan_param->data.percent.old_dst_y - underscan_y; + underscan_x2 = underscan_x>>1; + underscan_x -= underscan_x2; + + underscan_y2 = underscan_y>>1; + underscan_y -= underscan_y2; + + overscan->left += underscan_x; + overscan->right += underscan_x2; + overscan->bottom += underscan_y; + overscan->top += underscan_y2; + + } + /* this underscan is not centered!*/ + else { + if (underscan_param->data.dimentions.data.width == 0 || + underscan_param->data.dimentions.data.height == 0) + return false; + overscan->left += + underscan_param->data.dimentions.data.position_x; + overscan->right += + underscan_param->data.dimentions.modified_boarder_x; + overscan->right = overscan->right >= + underscan_param->data.dimentions.data.position_y ? + overscan->bottom - underscan_param-> + data.dimentions.data.position_y : 0; + *new_dest_x = underscan_param->data.dimentions.data.width; + *new_dest_y = underscan_param->data.dimentions.data.height; + } + if (overscan->left & 0x01) { + overscan->left--; + overscan->right++; + } + /*Top should be even number at interlace mode*/ + if (overscan->top & 0x01) { + overscan->top--; + overscan->bottom++; + } + return true; +} + +bool dal_scaler_adj_group_build_scaler_parameter( + const struct path_mode *path_mode, + struct adj_container *container, + enum build_path_set_reason reason, + enum adjustment_id adjust_id, + int32_t value, + const struct ds_underscan_desc *underscan_desc, + const struct display_path *display_path, + struct ds_adjustment_scaler *param) +{ + struct dcs *dcs = dal_display_path_get_dcs(display_path); + struct dcs_stereo_3d_features feature; + + if (!display_path || !path_mode || !dcs) + return false; + dal_memset(param, 0, sizeof(*param)); + param->timing_source = path_mode->mode_timing-> + mode_info.timing_source; + param->timing_standard = path_mode->mode_timing->crtc_timing. + timing_standard; + param->display_index = path_mode->display_path_index; + if (path_mode->view_3d_format != VIEW_3D_FORMAT_NONE) { + enum timing_3d_format format = + path_mode->mode_timing->crtc_timing.timing_3d_format; + feature = dal_dcs_get_stereo_3d_features(dcs, format); + if (feature.flags.SUPPORTED && !feature.flags.SCALING) + return false; + } + + if (reason == BUILD_PATH_SET_REASON_SET_ADJUSTMENT) { + if (dal_adj_container_get_default_underscan_allow(container)) + return false; + param->flags.bits.IS_FOR_SET_MODE = 0; + param->adjust_id = adjust_id; + param->value = value; + if (underscan_desc) { + param->underscan_desc = *underscan_desc; + param->flags.bits.IS_UNDERSCAN_DESC = 1; + } + } else { + param->flags.bits.IS_FOR_SET_MODE = 1; + param->adjust_id = ADJ_ID_UNDERSCAN; + param->value = 0; + } + return true; +} + +static bool build_underscan_bundle( + const struct ds_adjustment_scaler *param, + struct adj_container *container, + const struct timing_info_parameter *timing_info, + struct underscan_adjustment_group *group) +{ + struct adjustment_info *mm_pass_thur; + struct adjustment_info *underscan; + + dal_memset(group, 0, sizeof(*group)); + group->id_overscan = ADJ_ID_OVERSCAN; + group->id_underscan = ADJ_ID_UNDERSCAN; + group->id_underscan_auto = ADJ_ID_UNDERSCAN_TYPE; + group->id_multi_media_pass_thru = ADJ_ID_MULTIMEDIA_PASS_THROUGH; + + group->id_requested = param->adjust_id; + group->requested_value = param->value; + + if (!param || !container || !timing_info) + return false; + underscan = dal_adj_info_set_get_adj_info( + &container->adj_info_set, + group->id_underscan); + if (!underscan) + return false; + + mm_pass_thur = dal_adj_info_set_get_adj_info( + &container->adj_info_set, + group->id_multi_media_pass_thru); + + + group->current_percent_x = underscan->adj_data.ranged.cur; + group->current_percent_y = underscan->adj_data.ranged.cur; + + if (mm_pass_thur) + group->current_multi_media_pass_thru = + mm_pass_thur->adj_data.ranged.cur; + else + group->current_multi_media_pass_thru = 0; + if (param->flags.bits.IS_FOR_SET_MODE == 1) + group->requested_value = group->current_percent_x; + + return true; +} + +static bool build_underscan_parameters( + const struct ds_adjustment_scaler *param, + struct adj_container *container, + enum build_path_set_reason reason, + struct ds_underscan_parameter *underscan_param, + struct timing_info_parameter *timing_info, + struct ds_overscan *overscan) +{ + struct underscan_adjustment_group group; + + if (!build_underscan_bundle( + param, + container, + timing_info, + &group)) + return false; + /*update parameter if required*/ + update_underscan_bundle( + param, + &group, + timing_info, + underscan_param); + /*calculate underscan and new destination*/ + if (!calculate_underscan( + underscan_param, + &timing_info->dst_width, + &timing_info->dst_height, + overscan)) + return false; + + return true; +} + +static struct hw_path_mode *find_hw_path_mode( + const struct display_path *display_path, + struct hw_path_mode_set *hw_pms) +{ + uint32_t i; + uint32_t num_of_path; + struct hw_path_mode *mode = NULL; + struct hw_path_mode *local_mode; + + num_of_path = dal_hw_path_mode_set_get_paths_number(hw_pms); + for (i = 0; i < num_of_path; i++) { + local_mode = dal_hw_path_mode_set_get_path_by_index(hw_pms, i); + if (local_mode && local_mode->display_path == display_path) { + mode = local_mode; + break; + } + } + return mode; +} + +static bool build_hw_path_set_for_adjustment( + struct ds_dispatch *ds, + const struct ds_adjustment_scaler *param, + const struct display_path *display_path, + uint32_t disp_index, + struct hw_path_mode_set *hw_pms) +{ + struct adjustment_params adj_param; + + if (!param || !display_path || !hw_pms) + return false; + + dal_memset(&adj_param, 0, sizeof(struct adjustment_params)); + adj_param.affected_path = display_path; + adj_param.action = ADJUSTMENT_ACTION_SET_ADJUSTMENT; + adj_param.params.type = ADJUSTMENT_PAR_TYPE_TIMING; + adj_param.params.timings.ajd_id = param->adjust_id; + adj_param.params.timings.adj_id_hw = HW_ADJUSTMENT_ID_OVERSCAN; + if (param->adjust_id == ADJ_ID_UNDERSCAN_TYPE) + adj_param.params.timings.ajd_id = ADJ_ID_UNDERSCAN; + + return dal_ds_dispatch_build_hw_path_set_for_adjustment( + ds, + hw_pms, + &adj_param); +} + +bool dal_scaler_adj_group_apply_scaling( + const struct ds_adjustment_scaler *param, + struct adj_container *container, + enum build_path_set_reason reason, + struct hw_path_mode *hw_path_mode) +{ + struct ds_overscan overscan; + struct ds_underscan_parameter parameter; + struct timing_info_parameter timing_info; + enum hw_scale_options underscan_avi_rule = HW_SCALE_OPTION_UNKNOWN; + enum underscan_reason reason_for_underscan; + + build_avi_info_frame_parameter( + param, + NULL, + container, + hw_path_mode, + reason, + &hw_path_mode->mode.underscan_rule); + + switch (reason) { + case BUILD_PATH_SET_REASON_FALLBACK_UNDERSCAN: + reason_for_underscan = UNDERSCAN_REASON_FALL_BACK; + break; + case BUILD_PATH_SET_REASON_SET_MODE: + reason_for_underscan = UNDERSCAN_REASON_PATCH_TIMING; + break; + case BUILD_PATH_SET_REASON_SET_ADJUSTMENT: + default: + reason_for_underscan = UNDERSCAN_REASON_SET_ADJUSTMENT; + break; + } + + if (!can_scaling_be_applied( + container, + param->timing_standard, + param->timing_source, + param->adjust_id, + reason_for_underscan)) + return false; + + if (param->flags.bits.IS_UNDERSCAN_DESC == 0) { + if (param->flags.bits.IS_FOR_SET_MODE == 1 && + param->adjust_id != ADJ_ID_UNDERSCAN) + return false; + + if (is_dp_underscan_disabled( + hw_path_mode->display_path, + param->display_index)) + return false; + + setup_parameters( + hw_path_mode, + param, + &overscan, + ¶meter, + &timing_info); + if (!build_underscan_parameters( + param, + container, + reason, + ¶meter, + &timing_info, + &overscan)) + return false; + } else { + dal_memset( + ¶meter, + 0, + sizeof(struct ds_underscan_parameter)); + dal_memset( + &timing_info, + 0, + sizeof(struct timing_info_parameter)); + dal_memset(&overscan, 0, sizeof(struct ds_overscan)); + timing_info.timing = hw_path_mode->mode.timing; + if (!setup_parameter( + hw_path_mode, + param, + ¶meter)) + return false; + + if (!calculate_underscan( + ¶meter, + &timing_info.dst_width, + &timing_info.dst_height, + &overscan)) + return false; + } + + build_avi_info_frame_parameter( + param, + ¶meter, + container, + hw_path_mode, + reason, + &underscan_avi_rule); + + extract_parameters( + param, + &timing_info, + &overscan, + underscan_avi_rule, + hw_path_mode); + + return true; +} + +static bool prepare_underscan( + struct ds_dispatch *ds, + const struct path_mode *path_mode, + const struct ds_adjustment_scaler *param, + struct adj_container *container, + const struct display_path *display_path, + struct hw_underscan_adjustment_data **hw_underscan_data, + struct hw_path_mode_set *hw_path_set) +{ + struct hw_underscan_adjustment hw_underscan_adj = { {0} }; + struct hw_path_mode *hw_path_mode; + enum build_path_set_reason reason = + BUILD_PATH_SET_REASON_SET_ADJUSTMENT; + hw_path_set = dal_hw_path_mode_set_create(); + if (!hw_path_set) + return false; + if (!build_hw_path_set_for_adjustment( + ds, + param, + display_path, + path_mode->display_path_index, + hw_path_set)) + return false; + + + hw_path_mode = find_hw_path_mode(display_path, hw_path_set); + if (!hw_path_mode) + return false; + + if (!dal_scaler_adj_group_apply_scaling( + param, + container, + reason, + hw_path_mode)) + return false; + dal_ds_dispatch_setup_info_frame(ds, path_mode, hw_path_mode); + dal_memset(&hw_underscan_adj, + 0, + sizeof(struct hw_underscan_adjustment)); + /* no need call it build_deflicker_adjustment */ + hw_underscan_adj.hw_overscan = hw_path_mode->mode.overscan; + /*just create struct hw_underscan_adjustment_data is enough to use, + * no need a HWAdjustment */ + (*hw_underscan_data)->hw_adj_id = HW_ADJUSTMENT_ID_OVERSCAN; + (*hw_underscan_data)->hw_underscan_adj = hw_underscan_adj; + return true; +} + +static enum ds_return set_underscan_adjustment( + struct ds_dispatch *ds, + struct display_path *display_path, + enum adjustment_id adjust_id, + int32_t value, + const struct path_mode *path_mode, + struct adj_container *container) +{ + enum ds_return result = DS_ERROR; + struct hw_path_mode_set *hw_path_set = NULL; + struct ds_adjustment_scaler scaler; + struct hw_underscan_adjustment_data data = { 0 }; + struct hw_underscan_adjustment_data *hw_underscan_data = &data; + + hw_path_set = dal_hw_path_mode_set_create(); + if (!hw_path_set) + return DS_ERROR; + + if (!dal_scaler_adj_group_build_scaler_parameter( + path_mode, + container, + BUILD_PATH_SET_REASON_SET_ADJUSTMENT, + adjust_id, + value, + NULL, + display_path, + &scaler)) { + result = DS_ERROR; + goto fail; + } + + if (!prepare_underscan( + ds, + path_mode, + &scaler, + container, + display_path, + &hw_underscan_data, + hw_path_set)) { + result = DS_ERROR; + goto fail; + } + + if (dal_hw_sequencer_set_overscan_adj( + ds->hwss, + hw_path_set, + hw_underscan_data) == HWSS_RESULT_OK) + result = DS_SUCCESS; + else if (dal_adj_info_set_update_cur_value( + &container->adj_info_set, + adjust_id, + value)) + result = DS_SUCCESS; + else + result = DS_ERROR; +fail: + dal_hw_path_mode_set_destroy(&hw_path_set); + return result; +} + +enum ds_return dal_scaler_adj_group_set_adjustment( + struct ds_dispatch *ds, + const uint32_t display_index, + struct display_path *display_path, + enum adjustment_id adjust_id, + int32_t value) +{ + struct adj_container *container; + struct path_mode_set_with_data *pms_wd; + const struct path_mode *path_mode; + enum ds_return result = DS_ERROR; + const struct adjustment_info *adj_info; + + pms_wd = dal_ds_dispatch_get_active_pms_with_data(ds); + if (NULL == pms_wd) + return DS_ERROR; + container = dal_ds_dispatch_get_adj_container_for_path( + ds, display_index); + if (NULL == container) + return DS_ERROR; + + path_mode = + dal_pms_with_data_get_path_mode_for_display_index( + pms_wd, + display_index); + + if (NULL == path_mode) + return DS_ERROR; + + adj_info = dal_adj_info_set_get_adj_info( + &container->adj_info_set, + adjust_id); + if (NULL == adj_info) + return DS_ERROR; + + if (adj_info->adj_data.ranged.cur == value) { + if (dal_adj_container_is_adjustment_committed( + container, + adjust_id)) + /* we have set the same adjustment, do nothing, bypass + */ + return DS_SUCCESS; + else if (ADJ_ID_UNDERSCAN == adjust_id) { + if (value == adj_info->adj_data.ranged.def && + value == adj_info->adj_data.ranged.def) { + /* this is the initial call since our setmode's + * default: no underscan. We don't need any 0 + * underscan adjustment after setmode. only set + * committed. + */ + dal_adj_container_commit_adj( + container, + adjust_id); + return DS_SUCCESS; + } + } + } + + if (adj_info->adj_data.ranged.max < value || + adj_info->adj_data.ranged.min > value) + return DS_ERROR; + if (!dal_adj_info_set_update_cur_value( + &container->adj_info_set, + adjust_id, + value)) + return DS_ERROR; + if (adjust_id == ADJ_ID_UNDERSCAN || adjust_id == ADJ_ID_UNDERSCAN_TYPE) + result = set_underscan_adjustment( + ds, + display_path, + adjust_id, + value, + path_mode, + container); + else { + dal_logger_write(ds->dal_context->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "adj_id isn't for scaler_adj_group"); + return DS_ERROR; + } + if (result != DS_ERROR) + dal_adj_container_commit_adj(container, adjust_id); + + return result; +} |