diff options
Diffstat (limited to 'drivers/gpu/drm/amd/display/modules')
21 files changed, 4936 insertions, 1146 deletions
diff --git a/drivers/gpu/drm/amd/display/modules/color/color_gamma.c b/drivers/gpu/drm/amd/display/modules/color/color_gamma.c index 1f225d0d6c44..03d88e78165d 100644 --- a/drivers/gpu/drm/amd/display/modules/color/color_gamma.c +++ b/drivers/gpu/drm/amd/display/modules/color/color_gamma.c @@ -364,7 +364,7 @@ void precompute_pq(void) /* one-time pre-compute dePQ values - only for max pixel value 125 FP16 */ void precompute_de_pq(void) { - int i; + uint32_t i; struct fixed31_32 y; uint32_t begin_index, end_index; @@ -690,7 +690,7 @@ static bool find_software_points( static bool build_custom_gamma_mapping_coefficients_worker( const struct dc_gamma *ramp, struct pixel_gamma_point *coeff, - const struct hw_x_point *coordinates_x, + const struct hw_x_point *hw_coordinates_x, const struct gamma_pixel *axis_x, enum channel_name channel, uint32_t number_of_points) @@ -712,11 +712,11 @@ static bool build_custom_gamma_mapping_coefficients_worker( struct fixed31_32 right_pos; if (channel == CHANNEL_NAME_RED) - coord_x = coordinates_x[i].regamma_y_red; + coord_x = hw_coordinates_x[i].regamma_y_red; else if (channel == CHANNEL_NAME_GREEN) - coord_x = coordinates_x[i].regamma_y_green; + coord_x = hw_coordinates_x[i].regamma_y_green; else - coord_x = coordinates_x[i].regamma_y_blue; + coord_x = hw_coordinates_x[i].regamma_y_blue; if (!find_software_points( ramp, axis_x, coord_x, channel, @@ -783,7 +783,7 @@ static struct fixed31_32 calculate_mapped_value( struct pwl_float_data *rgb, const struct pixel_gamma_point *coeff, enum channel_name channel, - uint32_t max_index) + int32_t max_index) { const struct gamma_point *point; @@ -1425,7 +1425,7 @@ static void apply_lut_1d( uint32_t num_hw_points, struct dc_transfer_func_distributed_points *tf_pts) { - int i = 0; + uint32_t i = 0; int color = 0; struct fixed31_32 *regamma_y; struct fixed31_32 norm_y; @@ -1539,11 +1539,11 @@ static void build_evenly_distributed_points( } static inline void copy_rgb_regamma_to_coordinates_x( - struct hw_x_point *coordinates_x, + struct hw_x_point *hw_coordinates_x, uint32_t hw_points_num, const struct pwl_float_data_ex *rgb_ex) { - struct hw_x_point *coords = coordinates_x; + struct hw_x_point *coords = hw_coordinates_x; uint32_t i = 0; const struct pwl_float_data_ex *rgb_regamma = rgb_ex; @@ -1562,26 +1562,26 @@ static bool calculate_interpolated_hardware_curve( const struct dc_gamma *ramp, struct pixel_gamma_point *coeff128, struct pwl_float_data *rgb_user, - const struct hw_x_point *coordinates_x, + const struct hw_x_point *hw_coordinates_x, const struct gamma_pixel *axis_x, uint32_t number_of_points, struct dc_transfer_func_distributed_points *tf_pts) { const struct pixel_gamma_point *coeff = coeff128; - uint32_t max_entries = 3 - 1; + int32_t max_entries = 3 - 1; uint32_t i = 0; for (i = 0; i < 3; i++) { if (!build_custom_gamma_mapping_coefficients_worker( - ramp, coeff128, coordinates_x, axis_x, i, + ramp, coeff128, hw_coordinates_x, axis_x, i, number_of_points)) return false; } i = 0; - max_entries += ramp->num_entries; + max_entries += (int32_t)ramp->num_entries; /* TODO: float point case */ @@ -1635,7 +1635,7 @@ static bool map_regamma_hw_to_x_user( { /* setup to spare calculated ideal regamma values */ - int i = 0; + uint32_t i = 0; struct hw_x_point *coords = coords_x; const struct pwl_float_data_ex *regamma = rgb_regamma; @@ -1786,14 +1786,14 @@ bool mod_color_calculate_degamma_params(struct dc_color_caps *dc_caps, if (input_tf->tf == TRANSFER_FUNCTION_PQ) { /* just copy current rgb_regamma into tf_pts */ struct pwl_float_data_ex *curvePt = curve; - int i = 0; + int j = 0; - while (i <= MAX_HW_POINTS) { - tf_pts->red[i] = curvePt->r; - tf_pts->green[i] = curvePt->g; - tf_pts->blue[i] = curvePt->b; + while (j <= MAX_HW_POINTS) { + tf_pts->red[j] = curvePt->r; + tf_pts->green[j] = curvePt->g; + tf_pts->blue[j] = curvePt->b; ++curvePt; - ++i; + ++j; } } else { // clamps to 0-1 diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c index ca402ddcdacc..fbab100c0e7b 100644 --- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c @@ -66,6 +66,7 @@ static uint8_t is_cp_desired_hdcp1(struct mod_hdcp *hdcp) return is_auth_needed && !hdcp->connection.link.adjust.hdcp1.disable && + !is_frl_hdcp(hdcp) && !hdcp->connection.is_hdcp1_revoked; } @@ -584,6 +585,7 @@ enum mod_hdcp_operation_mode mod_hdcp_signal_type_to_operation_mode( switch (signal) { case SIGNAL_TYPE_DVI_SINGLE_LINK: case SIGNAL_TYPE_HDMI_TYPE_A: + case SIGNAL_TYPE_HDMI_FRL: mode = MOD_HDCP_MODE_DEFAULT; break; case SIGNAL_TYPE_EDP: diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h index d07387a961dd..92c128ffe2dd 100644 --- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h @@ -406,6 +406,12 @@ static inline uint8_t is_hdmi_dvi_sl_hdcp(struct mod_hdcp *hdcp) return (hdcp->connection.link.mode == MOD_HDCP_MODE_DEFAULT); } +static inline uint8_t is_frl_hdcp(struct mod_hdcp *hdcp) +{ + return (hdcp->connection.link.mode == MOD_HDCP_MODE_DEFAULT && + hdcp->connection.link.hdmi.frl_enabled); +} + /* hdcp state helpers */ static inline uint8_t current_state(struct mod_hdcp *hdcp) { diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c index 5cb979c2cf8c..f0f8e280ed30 100644 --- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c @@ -33,22 +33,28 @@ void mod_hdcp_dump_binary_message(uint8_t *msg, uint32_t msg_size, byte_size = 3, newline_size = 1, terminator_size = 1; - uint32_t line_count = msg_size / bytes_per_line, - trailing_bytes = msg_size % bytes_per_line; - uint32_t target_size = (byte_size * bytes_per_line + newline_size) * line_count + - byte_size * trailing_bytes + newline_size + terminator_size; uint32_t buf_pos = 0; uint32_t i = 0; - if (buf_size >= target_size) { - for (i = 0; i < msg_size; i++) { - if (i % bytes_per_line == 0) - buf[buf_pos++] = '\n'; - sprintf(&buf[buf_pos], "%02X ", msg[i]); - buf_pos += byte_size; - } - buf[buf_pos++] = '\0'; + /* Need room for at least the terminator. */ + if (buf_size < terminator_size) + return; + + for (i = 0; i < msg_size; i++) { + uint32_t needed = byte_size + terminator_size; + + if (i % bytes_per_line == 0) + needed += newline_size; + + if (buf_pos + needed > buf_size) + break; + + if (i % bytes_per_line == 0) + buf[buf_pos++] = '\n'; + sprintf((char *)&buf[buf_pos], "%02X ", msg[i]); + buf_pos += byte_size; } + buf[buf_pos++] = '\0'; } void mod_hdcp_log_ddc_trace(struct mod_hdcp *hdcp) diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.c index 6b7db8ec9a53..d32df3b869f9 100644 --- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.c +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.c @@ -200,6 +200,8 @@ static enum mod_hdcp_status add_display_to_topology_v3( TA_DTM_HDCP_VERSION_MAX_SUPPORTED__2_3; dtm_cmd->dtm_in_message.topology_update_v3.encoder_type = TA_DTM_ENCODER_TYPE__DIG; dtm_cmd->dtm_status = TA_DTM_STATUS__GENERIC_FAILURE; + if (is_frl_hdcp(hdcp)) + dtm_cmd->dtm_in_message.topology_update_v3.encoder_type = TA_DTM_ENCODER_TYPE__FRL; dtm_cmd->dtm_in_message.topology_update_v3.phy_id = link->phy_idx; dtm_cmd->dtm_in_message.topology_update_v3.link_hdcp_cap = link->hdcp_supported_informational; dtm_cmd->dtm_in_message.topology_update_v3.dio_output_type = link->dp.usb4_enabled ? diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.h b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.h index 7844ea91650b..1f9ac8537d53 100644 --- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.h +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.h @@ -84,6 +84,7 @@ struct ta_dtm_topology_update_input_v2 { uint32_t dig_fe; uint32_t dp_mst_vcid; uint32_t is_assr; + /*uint32_t is_frl;*/ /*todo*/ uint32_t max_hdcp_supported_version; }; @@ -91,6 +92,7 @@ struct ta_dtm_topology_update_input_v2 { /* Security code will check real HW register values and these SW enum values */ enum ta_dtm_encoder_type { TA_DTM_ENCODER_TYPE__INVALID = 0, + TA_DTM_ENCODER_TYPE__FRL = 0x20, TA_DTM_ENCODER_TYPE__DIG = 0x10 }; diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_color_types.h b/drivers/gpu/drm/amd/display/modules/inc/mod_color_types.h new file mode 100644 index 000000000000..bf7313df585b --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_color_types.h @@ -0,0 +1,47 @@ +/* + * Copyright 2019 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 + * + */ + +#ifndef MOD_COLOR_TYPES_H_ +#define MOD_COLOR_TYPES_H_ + +enum predefined_gamut_type { + gamut_type_bt709, + gamut_type_bt601, + gamut_type_adobe_rgb, + gamut_type_srgb, + gamut_type_bt2020, + gamut_type_dcip3, + gamut_type_unknown, +}; + +enum predefined_white_point_type { + white_point_type_5000k_horizon, + white_point_type_6500k_noon, + white_point_type_7500k_north_sky, + white_point_type_9300k, + white_point_type_unknown, +}; + +#endif /* MOD_COLOR_TYPES_H_ */
\ No newline at end of file diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h b/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h index 57916ed98c86..c042fb1bf49c 100644 --- a/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h @@ -90,6 +90,7 @@ struct mod_vrr_params_flip_interval { struct mod_vrr_params { bool supported; bool send_info_frame; + bool m_const_engaged; // this is used when m_const is set up in OPTC so no overriding happens from FreeSync Module enum mod_vrr_state state; uint32_t min_refresh_in_uhz; @@ -98,6 +99,8 @@ struct mod_vrr_params { uint32_t min_duration_in_us; uint32_t fixed_refresh_in_uhz; + uint32_t m_const; + struct dc_crtc_timing_adjust adjust; struct mod_vrr_params_fixed_refresh fixed; diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h b/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h index 835467225458..d492a59e0939 100644 --- a/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h @@ -114,6 +114,7 @@ struct mod_hdcp_displayport { }; struct mod_hdcp_hdmi { + uint8_t frl_enabled; uint8_t reserved; }; enum mod_hdcp_operation_mode { diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h index ddd64b7e4c04..eee8206bc531 100644 --- a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h @@ -27,6 +27,7 @@ #define MOD_INFO_PACKET_H_ #include "dm_services.h" +#include "mod_info_packet_types.h" #include "mod_shared.h" //Forward Declarations struct dc_stream_state; @@ -45,15 +46,7 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream, enum color_transfer_func tf); void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream, - struct dc_info_packet *info_packet); - -enum adaptive_sync_type { - ADAPTIVE_SYNC_TYPE_NONE = 0, - ADAPTIVE_SYNC_TYPE_DP = 1, - FREESYNC_TYPE_PCON_IN_WHITELIST = 2, - FREESYNC_TYPE_PCON_NOT_IN_WHITELIST = 3, - ADAPTIVE_SYNC_TYPE_EDP = 4, -}; + struct dc_info_packet *info_packet, int ALLMEnabled, int ALLMValue); enum adaptive_sync_sdp_version { AS_SDP_VER_0 = 0x0, diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet_types.h b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet_types.h new file mode 100644 index 000000000000..30a5259ef36b --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet_types.h @@ -0,0 +1,37 @@ +/* + * Copyright 2018 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 + * + */ + +#ifndef MOD_INFO_PACKET_TYPES_H_ +#define MOD_INFO_PACKET_TYPES_H_ + +enum adaptive_sync_type { + ADAPTIVE_SYNC_TYPE_NONE = 0, + ADAPTIVE_SYNC_TYPE_DP = 1, + FREESYNC_TYPE_PCON_IN_WHITELIST = 2, + FREESYNC_TYPE_PCON_NOT_IN_WHITELIST = 3, + ADAPTIVE_SYNC_TYPE_EDP = 4, +}; + +#endif /* MOD_INFO_PACKET_TYPES_H_ */ diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_power.h b/drivers/gpu/drm/amd/display/modules/inc/mod_power.h new file mode 100644 index 000000000000..f9814cf7bbdb --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_power.h @@ -0,0 +1,417 @@ +/* Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved. */ + +#ifndef MODULES_INC_MOD_POWER_H_ +#define MODULES_INC_MOD_POWER_H_ + +#include "dm_services.h" + +struct core_power; + +struct mod_power_init_params { + + bool disable_fractional_pwm; + + /* Use nits based brightness instead of brightness percentage + */ + bool use_nits_based_brightness; + unsigned int panel_min_millinits; + unsigned int panel_max_millinits; + + unsigned int min_backlight_pwm; + unsigned int max_backlight_pwm; + + unsigned int min_abm_backlight; + unsigned int num_backlight_levels; + bool backlight_ramping_override; + unsigned int backlight_ramping_reduction; + unsigned int backlight_ramping_start; + bool def_varibright_enable; + unsigned int def_varibright_level; + unsigned int varibright_level; + unsigned int abm_config_setting; + + bool allow_psr_smu_optimizations; + + bool allow_psr_multi_disp_optimizations; + + bool use_custom_backlight_caps; + unsigned int custom_backlight_caps_config_no; + bool use_linear_backlight_curve; +}; + +struct mod_power { + int dummy; +}; + +/* VariBright settings structure */ +struct varibright_info { + unsigned int level; + bool enable; + bool activate; +}; + +struct mod_power_psr_context { + /* ddc line */ + unsigned int channel; + /* Transmitter id */ + unsigned int transmitter_id; + /* Engine Id is used for Dig Be source select */ + unsigned int engine_id; + /* Controller Id used for Dig Fe source select */ + unsigned int controller_id; + /* Pcie or Uniphy */ + unsigned int phy_type; + /* Physical PHY Id used by SMU interpretation */ + unsigned int smu_phy_id; + /* Vertical total pixels from crtc timing. + * This is used for static screen detection. + * ie. If we want to detect half a frame, + * we use this to determine the hyst lines. + */ + unsigned int crtc_timing_vertical_total; + /* PSR supported from panel capabilities and + * current display configuration + */ + bool psr_supported_display_config; + /* Whether fast link training is supported by the panel */ + bool psr_exit_link_training_required; + /* If RFB setup time is greater than the total VBLANK time, + * it is not possible for the sink to capture the video frame + * in the same frame the SDP is sent. In this case, + * the frame capture indication bit should be set and an extra + * static frame should be transmitted to the sink. + */ + bool psr_frame_capture_indication_req; + /* Set the last possible line SDP may be transmitted without violating + * the RFB setup time or entering the active video frame. + */ + unsigned int sdp_transmit_line_num_deadline; + /* The VSync rate in Hz used to calculate the + * step size for smooth brightness feature + */ + unsigned int vsync_rate_hz; + unsigned int skip_psr_wait_for_pll_lock; + unsigned int number_of_controllers; + /* Unused, for future use. To indicate that first changed frame from + * state3 shouldn't result in psr_inactive, but rather to perform + * an automatic single frame rfb_update. + */ + bool rfb_update_auto_en; + /* Number of frame before entering static screen */ + unsigned int timehyst_frames; + /* Partial frames before entering static screen */ + unsigned int hyst_lines; + /* # of repeated AUX transaction attempts to make before + * indicating failure to the driver + */ + unsigned int aux_repeats; + /* Controls hw blocks to power down during PSR active state */ + unsigned int psr_level; + /* Controls additional delay after remote frame capture before + * continuing powerd own + */ + unsigned int frame_delay; + bool allow_smu_optimizations; + bool allow_multi_disp_optimizations; + unsigned int line_time_in_us; + /* Panel self refresh 2 selective update granularity required */ + bool su_granularity_required; + /* psr2 selective update y granularity capability */ + uint8_t su_y_granularity; + uint8_t rate_control_caps; + bool os_request_force_ffu; +}; + +enum psr_event { + psr_event_invalid = 0x0, + psr_event_vsync = 0x1, + psr_event_full_screen = 0x2, + psr_event_defer_enable = 0x4, + psr_event_hw_programming = 0x8, + psr_event_test_harness_enable_psr = 0x10, + psr_event_test_harness_disable_psr = 0x20, + psr_event_mpo_video_selective_update = 0x40, + psr_event_edp_panel_off_disable_psr = 0x80, + psr_event_dynamic_display_switch = 0x100, + psr_event_big_screen_video = 0x200, + psr_event_dds_defer_stream_enable = 0x800, + psr_event_dynamic_link_rate_control = 0x1000, + psr_event_vrr_transition = 0x2000, + psr_event_pause = 0x4000, + psr_event_immediate_flip = 0x8000, + psr_event_os_request_disable = 0x10000, + psr_event_os_request_force_ffu = 0x20000, + psr_event_os_override_hold = 0x40000, + psr_event_crc_window_active = 0x80000, +}; + +enum replay_event { + replay_event_invalid = 0x0, + replay_event_vsync = 0x1, + replay_event_full_screen = 0x2, + replay_event_mpo_video_selective_update = 0x4, + replay_event_big_screen_video = 0x8, + replay_event_hw_programming = 0x10, + replay_event_edp_panel_off_disable_psr = 0x20, + replay_event_general_ui = 0x40, + replay_event_vrr = 0x80, + replay_event_prepare_vtotal = 0x100, + replay_event_test_harness_enable_replay = 0x200, + replay_event_test_harness_disable_replay = 0x400, + replay_event_test_harness_ultra_sleep = 0x800, + replay_event_immediate_flip = 0x1000, + replay_event_vrr_transition = 0x2000, + replay_event_pause = 0x4000, + replay_event_disable_replay_while_DPMS = 0x8000, + replay_event_test_harness_mode = 0x10000, + replay_event_cursor_updating = 0x20000, + replay_event_sleep_resume = 0x40000, + replay_event_disable_in_AC = 0x80000, + replay_event_disable_replay_while_detect_display = 0x100000, + replay_event_disable_replay_while_switching_mux = 0x400000, + replay_event_infopacket = 0x800000, + replay_event_os_request_disable = 0x1000000, + replay_event_os_request_force_ffu = 0x2000000, + replay_event_os_override_hold = 0x4000000, + replay_event_crc_window_active = 0x8000000, +}; + +enum replay_enable_option { + pr_enable_option_static_screen = 0x1, + pr_enable_option_mpo_video = 0x2, + pr_enable_option_full_screen_video = 0x4, + pr_enable_option_general_ui = 0x8, + pr_enable_option_full_screen = 0x10, + pr_enable_option_static_screen_coasting = 0x10000, + pr_enable_option_mpo_video_coasting = 0x20000, + pr_enable_option_full_screen_video_coasting = 0x40000, + pr_enable_option_full_screen_coasting = 0x100000, +}; + +struct mod_power *mod_power_create(struct dc *dc, + struct mod_power_init_params *init_params, + unsigned int edp_num); + +void mod_power_destroy(struct mod_power *mod_power); + +bool mod_power_hw_init(struct mod_power *mod_power); + +bool mod_power_add_stream(struct mod_power *mod_power, + struct dc_stream_state *stream, struct psr_caps *caps); + +bool mod_power_remove_stream(struct mod_power *mod_power, + const struct dc_stream_state *stream); + +bool mod_power_replace_stream(struct mod_power *mod_power, + const struct dc_stream_state *current_stream, + struct dc_stream_state *new_stream, + struct psr_caps *new_caps); + +bool mod_power_set_backlight_nits(struct mod_power *mod_power, + struct dc_stream_state *streams, + unsigned int backlight_millinit, + unsigned int transition_time_millisec, + bool skip_aux, + bool is_hdr); + +bool mod_power_set_backlight_percent(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millipercent, + unsigned int transition_time_millisec, + bool is_hdr); + +void mod_power_update_backlight(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millipercent); + +void mod_power_update_backlight_nits(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millinit); + +bool mod_power_get_backlight_pwm(struct mod_power *mod_power, + unsigned int *backlight_pwm, + unsigned int inst); + +bool mod_power_get_backlight_nits(struct mod_power *mod_power, + unsigned int *backlight_millinit, + unsigned int inst); + +bool mod_power_get_backlight_percent(struct mod_power *mod_power, + unsigned int *backlight_millipercent, + unsigned int inst); + +bool mod_power_get_hw_target_backlight_pwm_nits( + struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_millinit, + unsigned int inst); + +bool mod_power_get_hw_target_backlight_pwm_percent( + struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_millipercent, + unsigned int inst); + +bool mod_power_get_hw_target_backlight_pwm( + struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_u16_16); + +bool mod_power_get_hw_backlight_pwm( + struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight); + +bool mod_power_get_hw_backlight_pwm_nits( + struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_millinit, + unsigned int inst); + +bool mod_power_get_hw_backlight_aux_nits( + struct mod_power *mod_power, + struct dc_stream_state **streams, int num_streams, + unsigned int *backlight_millinit_avg, + unsigned int *backlight_millinit_peak); + +bool mod_power_get_hw_backlight_pwm_percent( + struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_millipercent, + unsigned int inst); + +void mod_power_initialize_backlight_caps + (struct mod_power *mod_power); + +bool mod_power_get_panel_backlight_boundaries + (struct mod_power *mod_power, + unsigned int *out_min_backlight, + unsigned int *out_max_backlight, + unsigned int *out_ac_backlight_percent, + unsigned int *out_dc_backlight_percent, + unsigned int inst); + +bool mod_power_set_smooth_brightness(struct mod_power *mod_power, + bool enable_brightness, + unsigned int inst); + +bool mod_power_notify_mode_change(struct mod_power *mod_power, + const struct dc_stream_state *stream, + bool is_hdr); + +bool mod_power_get_varibright_level(struct mod_power *mod_power, + unsigned int *varibright_level); + +bool mod_power_get_varibright_hw_level(struct mod_power *mod_power, + unsigned int *varibright_level); + +bool mod_power_get_varibright_default_level(struct mod_power *mod_power, + unsigned int *varibright_level); + +bool mod_power_get_varibright_enable(struct mod_power *mod_power, + bool *varibright_enable); + +bool mod_power_varibright_activate(struct mod_power *mod_power, + bool activate, struct dc_stream_update *stream_update); + +bool mod_power_varibright_feature_enable(struct mod_power *mod_power, + bool enable, struct dc_stream_update *stream_update); + + +bool mod_power_varibright_set_level(struct mod_power *mod_power, + unsigned int level, struct dc_stream_update *stream_update); + +bool mod_power_varibright_set_hw_level(struct mod_power *mod_power, + unsigned int level, struct dc_stream_update *stream_update); + +bool mod_power_is_abm_active(struct mod_power *mod_power, + const struct dc_link *link, + unsigned int inst); + + +bool mod_power_set_psr_event(struct mod_power *mod_power, + struct dc_stream_state *stream, bool set_event, + enum psr_event event, bool wait); + +bool mod_power_get_psr_event(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int *active_psr_events); + +bool mod_power_get_psr_state(struct mod_power *mod_power, + const struct dc_stream_state *stream, + enum dc_psr_state *state); + +bool mod_power_get_psr_enabled_status(struct mod_power *mod_power, + const struct dc_stream_state *stream, + bool *psr_enabled); + +bool mod_power_set_replay_event(struct mod_power *mod_power, + struct dc_stream_state *stream, bool set_event, + enum replay_event event, bool wait_for_disable); + +bool mod_power_get_replay_event(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int *active_replay_events); + +bool mod_power_get_replay_active_status(const struct dc_stream_state *stream, + bool *replay_active); + +bool mod_power_replay_set_coasting_vtotal(struct mod_power *mod_power, + const struct dc_stream_state *stream, + uint32_t coasting_vtotal, uint16_t frame_skip_number); + +void mod_power_replay_residency(const struct dc_stream_state *stream, + unsigned int *residency, const bool is_start, const bool is_alpm); + +bool mod_power_replay_set_power_opt_and_coasting_vtotal(struct mod_power *mod_power, + const struct dc_stream_state *stream, unsigned int active_replay_events, uint32_t coasting_vtotal, + bool is_ultra_sleep_mode, uint16_t frame_skip_number); + +void mod_power_replay_set_timing_sync_supported(struct mod_power *mod_power, + const struct dc_stream_state *stream); + +void mod_power_replay_disabled_adaptive_sync_sdp(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool force_disabled); + +void mod_power_replay_disabled_desync_error_detection(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool force_disabled); +void mod_power_set_low_rr_activate(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool low_rr_supported); + +void mod_power_set_video_conferencing_activate(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool video_conferencing_activate); + +void mod_power_set_live_capture_with_cvt_activate(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool live_capture_with_cvt_activate); + +void mod_power_set_replay_continuously_resync(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool enable); + +void mod_power_set_coasting_vtotal_without_frame_update(struct mod_power *mod_power, + const struct dc_stream_state *stream, uint32_t coasting_vtotal); + + + +void mod_power_psr_residency(struct mod_power *mod_power, + const struct dc_stream_state *stream, + unsigned int *residency, + const uint8_t mode); +bool mod_power_psr_get_active_psr_events(struct mod_power *mod_power, + const struct dc_stream_state *stream, unsigned int *active_psr_events); +bool mod_power_psr_set_sink_vtotal_in_psr_active(struct mod_power *mod_power, + const struct dc_stream_state *stream, + uint16_t psr_vtotal_idle, + uint16_t psr_vtotal_su); + + + +bool mod_power_backlight_percent_to_nits(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millipercent, + unsigned int *backlight_millinit); +bool mod_power_backlight_nits_to_percent(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millinit, + unsigned int *backlight_millipercent); + +#endif /* MODULES_INC_MOD_POWER_H_ */ diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c index 00473c6284d5..f5ac4bf32a78 100644 --- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c +++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c @@ -246,7 +246,7 @@ void set_vsc_packet_colorimetry_data( break; } - info_packet->sb[16] = (pixelEncoding << 4) | colorimetryFormat; + info_packet->sb[16] = (uint8_t)((pixelEncoding << 4) | colorimetryFormat); /* Set color depth */ switch (stream->timing.display_color_depth) { @@ -502,15 +502,20 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream, * * @stream: contains data we may need to construct VSIF (i.e. timing_3d_format, etc.) * @info_packet: output structure where to store VSIF + * @ALLMEnabled: indicates whether ALLM HF-VSIF should be generated + * @ALLMValue: ALLM bit value to advertise in HF-VSIF */ void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream, - struct dc_info_packet *info_packet) + struct dc_info_packet *info_packet, int ALLMEnabled, int ALLMValue) { unsigned int length = 5; bool hdmi_vic_mode = false; uint8_t checksum = 0; uint32_t i = 0; enum dc_timing_3d_format format; + bool bALLM = (bool)ALLMEnabled; + bool bALLMVal = (bool)ALLMValue; + int CCBPC = 0; info_packet->valid = false; format = stream->timing.timing_3d_format; @@ -523,47 +528,110 @@ void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream, && format == TIMING_3D_FORMAT_NONE) hdmi_vic_mode = true; - if ((format == TIMING_3D_FORMAT_NONE) && !hdmi_vic_mode) + if ((format == TIMING_3D_FORMAT_NONE) && !hdmi_vic_mode && !bALLM) return; - info_packet->sb[1] = 0x03; - info_packet->sb[2] = 0x0C; - info_packet->sb[3] = 0x00; - - if (format != TIMING_3D_FORMAT_NONE) - info_packet->sb[4] = (2 << 5); - - else if (hdmi_vic_mode) - info_packet->sb[4] = (1 << 5); - - switch (format) { - case TIMING_3D_FORMAT_HW_FRAME_PACKING: - case TIMING_3D_FORMAT_SW_FRAME_PACKING: - info_packet->sb[5] = (0x0 << 4); - break; - - case TIMING_3D_FORMAT_SIDE_BY_SIDE: - case TIMING_3D_FORMAT_SBS_SW_PACKED: - info_packet->sb[5] = (0x8 << 4); - length = 6; - break; - - case TIMING_3D_FORMAT_TOP_AND_BOTTOM: - case TIMING_3D_FORMAT_TB_SW_PACKED: - info_packet->sb[5] = (0x6 << 4); - break; - - default: - break; + if (!bALLM) { + info_packet->sb[1] = 0x03; + info_packet->sb[2] = 0x0C; + info_packet->sb[3] = 0x00; + + if (format != TIMING_3D_FORMAT_NONE) + info_packet->sb[4] = (2 << 5); + else if (hdmi_vic_mode) + info_packet->sb[4] = (1 << 5); + + switch (format) { + case TIMING_3D_FORMAT_HW_FRAME_PACKING: + case TIMING_3D_FORMAT_SW_FRAME_PACKING: + info_packet->sb[5] = (0x0 << 4); + break; + + case TIMING_3D_FORMAT_SIDE_BY_SIDE: + case TIMING_3D_FORMAT_SBS_SW_PACKED: + info_packet->sb[5] = (0x8 << 4); + length = 6; + break; + + case TIMING_3D_FORMAT_TOP_AND_BOTTOM: + case TIMING_3D_FORMAT_TB_SW_PACKED: + info_packet->sb[5] = (0x6 << 4); + break; + + default: + break; + } + + if (hdmi_vic_mode) { + ASSERT(stream->timing.hdmi_vic <= 0xFF); + info_packet->sb[5] = (uint8_t)stream->timing.hdmi_vic; + } + } else { + info_packet->sb[1] = 0xD8; + info_packet->sb[2] = 0x5D; + info_packet->sb[3] = 0xC4; + info_packet->sb[4] = HF_VSIF_VERSION; + + if (format != TIMING_3D_FORMAT_NONE) { + info_packet->sb[5] |= 0x01; + length = 6; + switch (format) { + case TIMING_3D_FORMAT_HW_FRAME_PACKING: + case TIMING_3D_FORMAT_SW_FRAME_PACKING: + info_packet->sb[6] = (0x0 << 4); + break; + + case TIMING_3D_FORMAT_SIDE_BY_SIDE: + case TIMING_3D_FORMAT_SBS_SW_PACKED: + info_packet->sb[6] = (0x8 << 4); + break; + + case TIMING_3D_FORMAT_TOP_AND_BOTTOM: + case TIMING_3D_FORMAT_TB_SW_PACKED: + info_packet->sb[6] = (0x6 << 4); + break; + + default: + break; + } + } + + info_packet->sb[5] = (info_packet->sb[5] & ~0x02) | (bALLMVal << 1); + + switch (stream->timing.display_color_depth) { + case COLOR_DEPTH_888: + CCBPC = 1; + break; + case COLOR_DEPTH_101010: + CCBPC = 3; + break; + case COLOR_DEPTH_121212: + CCBPC = 5; + break; + case COLOR_DEPTH_161616: + CCBPC = 9; + break; + + case COLOR_DEPTH_UNDEFINED: + case COLOR_DEPTH_666: +#ifdef CONFIG_DRM_AMD_DC_DCN2_0 + case COLOR_DEPTH_999: + case COLOR_DEPTH_111111: +#endif + case COLOR_DEPTH_141414: + default: + break; + } + + info_packet->sb[5] = (uint8_t)((info_packet->sb[5] & ~0xF0) | (CCBPC << 4)); } - if (hdmi_vic_mode) - info_packet->sb[5] = stream->timing.hdmi_vic; - info_packet->hb0 = HDMI_INFOFRAME_TYPE_VENDOR; info_packet->hb1 = 0x01; info_packet->hb2 = (uint8_t) (length); + + checksum += info_packet->hb0; checksum += info_packet->hb1; checksum += info_packet->hb2; diff --git a/drivers/gpu/drm/amd/display/modules/power/Makefile b/drivers/gpu/drm/amd/display/modules/power/Makefile index 9d1b22d35ece..3000f392bdbc 100644 --- a/drivers/gpu/drm/amd/display/modules/power/Makefile +++ b/drivers/gpu/drm/amd/display/modules/power/Makefile @@ -23,9 +23,9 @@ # Makefile for the 'power' sub-module of DAL. # -MOD_POWER = power_helpers.o +MOD_POWER = power_helpers.o power.o power_abm.o power_psr.o power_replay.o AMD_DAL_MOD_POWER = $(addprefix $(AMDDALPATH)/modules/power/,$(MOD_POWER)) #$(info ************ DAL POWER MODULE MAKEFILE ************) -AMD_DISPLAY_FILES += $(AMD_DAL_MOD_POWER) +AMD_DISPLAY_FILES += $(AMD_DAL_MOD_POWER)
\ No newline at end of file diff --git a/drivers/gpu/drm/amd/display/modules/power/power.c b/drivers/gpu/drm/amd/display/modules/power/power.c new file mode 100644 index 000000000000..5659a38b3366 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/power/power.c @@ -0,0 +1,503 @@ +/* + * Copyright 2016 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 "dm_services.h" +#include "dc.h" +#include "mod_power.h" +#include "core_types.h" +#include "dmcu.h" +#include "abm.h" +#include "power_helpers.h" +#include "dce/dmub_psr.h" +#include "dal_asic_id.h" +#include "link_service.h" +#include <linux/math.h> + +#define DC_TRACE_LEVEL_MESSAGE(...) /* do nothing */ +#define DC_TRACE_LEVEL_MESSAGEP(...) /* do nothing */ + +#define MOD_POWER_MAX_CONCURRENT_STREAMS 32 +#define SMOOTH_BRIGHTNESS_ADJUSTMENT_TIME_IN_MS 500 +#define LOW_REFRESH_RATE_DURATION_US_UPPER_BOUND 25000 + +/* If system or panel does not report some sort of brightness percent to nits + * mapping, we will use following default values so backlight control using + * nits based interfaces will still work, but might not describe panel + * correctly. In this case percentage based backlight control should ideally + * be used. + * Min = 5 nits + * Max = 300 nits + */ + +static const unsigned int pwr_default_min_brightness_millinits = 1000; +static const unsigned int pwr_default_sdr_brightness_millinits = 270000; + +static const unsigned int default_ac_backlight_percent = 100; +static const unsigned int default_dc_backlight_percent = 70; + +#define MOD_POWER_TO_CORE(mod_power)\ + container_of(mod_power, struct core_power, mod_public) + +/* Given a specific dc_stream* this function finds its equivalent + * on the core_freesync->map and returns the corresponding index + */ +unsigned int map_index_from_stream(struct core_power *core_power, + const struct dc_stream_state *stream) +{ + unsigned int index = 0; + + for (index = 0; index < core_power->num_entities; index++) { + if (core_power->map[index].stream == stream) + return index; + } + /* Could not find stream requested, this is not trivial, fix when hit*/ + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "map index from stream: ERROR: core_power=%p stream=%p", + core_power, + stream); + ASSERT(false); + /* We come here only when we can't map stream index. + * In good cases, this would happen when we attempt to change + * brightness before stream creation, in which case we create a + * dummy stream with index 0. + * With external monitor connected, the index passed from this return + * is 1. Passing anything greater than 0 from here would always point + * to bad memory. + */ + return 0; +} + +bool mod_power_hw_init(struct mod_power *mod_power) +{ + /* Call backlight initialization */ + return mod_power_hw_init_backlight(mod_power); + + /* Future: Add other HW init here */ +} + +struct mod_power *mod_power_create(struct dc *dc, + struct mod_power_init_params *init_params, + unsigned int edp_num) +{ + struct core_power *core_power = NULL; + int i = 0; + unsigned int abm_max_config = 0; + unsigned int inst = 0; + bool is_brightness_range_valid = false; + + if (dc == NULL) + goto fail_dc_null; + + core_power = kzalloc(sizeof(struct core_power), GFP_KERNEL); + + if (core_power == NULL) + goto fail_alloc_context; + + core_power->edp_num = edp_num; + core_power->map = kzalloc(sizeof(struct power_entity) * MOD_POWER_MAX_CONCURRENT_STREAMS, + GFP_KERNEL); + + if (core_power->map == NULL) + goto fail_alloc_map; + + for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++) { + core_power->map[i].stream = NULL; + } + + for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++) { + core_power->map[i].psr_context = + kzalloc(sizeof(struct mod_power_psr_context), + GFP_KERNEL); + if (core_power->map[i].psr_context == NULL) + goto fail_construct; + } + + core_power->psr_smu_optimizations_support = init_params->allow_psr_smu_optimizations; + core_power->multi_disp_optimizations_support = init_params->allow_psr_multi_disp_optimizations; + + for (inst = 0; inst < edp_num; inst++) { + core_power->bl_prop[inst].min_abm_backlight = + init_params[inst].min_abm_backlight; + core_power->bl_prop[inst].disable_fractional_pwm = + init_params[inst].disable_fractional_pwm; + core_power->bl_prop[inst].use_linear_backlight_curve = + init_params[inst].use_linear_backlight_curve; + core_power->bl_prop[inst].use_nits_based_brightness = + init_params[inst].use_nits_based_brightness; + core_power->bl_prop[inst].backlight_ramping_override = + init_params[inst].backlight_ramping_override; + core_power->bl_prop[inst].backlight_ramping_reduction = + init_params[inst].backlight_ramping_reduction; + core_power->bl_prop[inst].backlight_ramping_start = + init_params[inst].backlight_ramping_start; + core_power->bl_prop[inst].use_custom_backlight_caps = + init_params[inst].use_custom_backlight_caps; + core_power->bl_prop[inst].custom_backlight_caps_config_no = + init_params[inst].custom_backlight_caps_config_no; + + // Do not allow less than 101 backlight levels + if (init_params[inst].num_backlight_levels < 101) + core_power->bl_prop[inst].num_backlight_levels = 101; + else + core_power->bl_prop[inst].num_backlight_levels = + init_params[inst].num_backlight_levels; + + core_power->bl_prop[inst].backlight_lut = (unsigned int *) + (kzalloc(sizeof(unsigned int) * + core_power->bl_prop[inst].num_backlight_levels, GFP_KERNEL)); + if (core_power->bl_prop[inst].backlight_lut == NULL) + goto fail_alloc_backlight_array; + } + + core_power->varibright_prop.varibright_active = false; + + core_power->varibright_prop.varibright_user_enable = + init_params->def_varibright_enable; + + // Table of ABM levels here is 1-4, but level 0 also exists as 'off' + if (init_params->varibright_level <= abm_defines_max_level) { + core_power->varibright_prop.varibright_level = + init_params->varibright_level; + + } else { + core_power->varibright_prop.varibright_level = 3; + } + if (init_params->def_varibright_level <= abm_defines_max_level) { + core_power->varibright_prop.def_varibright_level = + init_params->def_varibright_level; + } else { + core_power->varibright_prop.def_varibright_level = 3; + } + + // ABM used to contain 4 different configs. There is only 3 since ABM 2.3. + if ((dc->res_pool->dmcu != NULL) && (dc->res_pool->dmcu->dmcu_version.abm_version < 0x23)) + abm_max_config = 4; + else + abm_max_config = 3; + + if (init_params->abm_config_setting < abm_max_config) + core_power->varibright_prop.varibright_config_setting = + init_params->abm_config_setting; + else + core_power->varibright_prop.varibright_config_setting = 0; + + for (inst = 0; inst < edp_num; inst++) { + core_power->bl_prop[inst].backlight_lut[0] = init_params[inst].min_backlight_pwm; + core_power->bl_prop[inst].backlight_lut[ + core_power->bl_prop[inst].num_backlight_levels-1] = + init_params[inst].max_backlight_pwm; + core_power->bl_prop[inst].min_backlight_pwm = init_params[inst].min_backlight_pwm; + core_power->bl_prop[inst].max_backlight_pwm = init_params[inst].max_backlight_pwm; + core_power->bl_prop[inst].ac_backlight_percent = + default_ac_backlight_percent; + core_power->bl_prop[inst].dc_backlight_percent = + default_dc_backlight_percent; + core_power->bl_prop[inst].backlight_caps_valid = false; + + if (core_power->bl_prop[inst].use_nits_based_brightness) { + core_power->bl_prop[inst].min_brightness_millinits = + init_params[inst].panel_min_millinits; + core_power->bl_prop[inst].max_brightness_millinits = + init_params[inst].panel_max_millinits; + } else { + + core_power->bl_prop[inst].min_brightness_millinits = + pwr_default_min_brightness_millinits; + core_power->bl_prop[inst].max_brightness_millinits = + pwr_default_sdr_brightness_millinits; + } + + core_power->bl_prop[inst].backlight_range = + core_power->bl_prop[inst].max_backlight_pwm- + core_power->bl_prop[inst].min_backlight_pwm; + + core_power->bl_prop[inst].nits_range = + core_power->bl_prop[inst].max_brightness_millinits - + core_power->bl_prop[inst].min_brightness_millinits; + + core_power->bl_state[inst].smooth_brightness_enabled = true; + } + + /* Check if at least 1 instance in core_power is populated before failing */ + for (inst = 0; inst < edp_num; inst++) { + if (core_power->bl_prop[inst].nits_range != 0 && core_power->bl_prop[inst].backlight_range != 0) { + is_brightness_range_valid = true; + break; + } + + } + if (!is_brightness_range_valid) + goto fail_bad_brightness_range; + + core_power->num_entities = 0; + + core_power->dc = dc; + for (inst = 0; inst < edp_num; inst++) { + initialize_backlight_caps(core_power, inst); + core_power->bl_state[inst].backlight_millipercent = + core_power->bl_prop[inst].dc_backlight_percent * 1000; + core_power->bl_state[inst].backlight_pwm = backlight_millipercent_to_pwm(core_power, + core_power->bl_state[inst].backlight_millipercent, inst); + core_power->bl_state[inst].backlight_millinit = backlight_millipercent_to_millinit(core_power, + core_power->bl_state[inst].backlight_millipercent, inst); + } + + return &core_power->mod_public; + +fail_bad_brightness_range: +fail_alloc_backlight_array: + for (inst = 0; inst < edp_num; inst++) + if (core_power->bl_prop[inst].backlight_lut) + kfree(core_power->bl_prop[inst].backlight_lut); +fail_construct: + for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++) { + if (core_power->map[i].psr_context) + kfree(core_power->map[i].psr_context); + } + kfree(core_power->map); + +fail_alloc_map: + kfree(core_power); + +fail_alloc_context: +fail_dc_null: + return NULL; +} + +void mod_power_destroy(struct mod_power *mod_power) +{ + if (mod_power != NULL) { + unsigned int i; + struct core_power *core_power = + MOD_POWER_TO_CORE(mod_power); + + for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++) + if (core_power->map[i].psr_context) + kfree(core_power->map[i].psr_context); + + for (i = 0; i < core_power->num_entities; i++) + if (core_power->map[i].stream) + dc_stream_release(core_power->map[i].stream); + + kfree(core_power->map); + + for (i = 0; i < MAX_NUM_EDP; i++) + if (core_power->bl_prop[i].backlight_lut) + kfree(core_power->bl_prop[i].backlight_lut); + + kfree(core_power); + } +} + +bool mod_power_add_stream(struct mod_power *mod_power, + struct dc_stream_state *stream, struct psr_caps *caps) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities < MOD_POWER_MAX_CONCURRENT_STREAMS) { + dc_stream_retain(stream); + + core_power->map[core_power->num_entities].stream = stream; + core_power->map[core_power->num_entities].caps = caps; + + // initialize cached PSR params to something "safe" (something that is + // consistent with disabled PSR state) + core_power->map[core_power->num_entities].psr_enabled = 0; + core_power->map[core_power->num_entities].psr_events = psr_event_vsync; + core_power->map[core_power->num_entities].psr_power_opt = 0; + core_power->num_entities++; + return true; + } + + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "mod_power: add_stream: ERROR: stream=%p num_entities=%u >= MOD_POWER_MAX_CONCURRENT_STREAMS", + stream, + core_power->num_entities); + + return false; +} + +bool mod_power_remove_stream(struct mod_power *mod_power, + const struct dc_stream_state *stream) +{ + unsigned int i = 0; + struct core_power *core_power = NULL; + unsigned int index = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + if (core_power->num_entities == 0) { + /* trying to remove a stream a second time or have not added yet */ + BREAK_TO_DEBUGGER(); + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "mod_power: remove_stream: ERROR: num_entities=0 stream=%p", + stream); + return false; + } + + index = map_index_from_stream(core_power, stream); + + if (index >= core_power->num_entities) { + /* trying to remove a stream a second time or have not added yet */ + BREAK_TO_DEBUGGER(); + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "mod_power: remove_stream: ERROR: index=%u >= num_entities=%u stream=%p", + index, + core_power->num_entities, + stream); + return false; + } + + dc_stream_release(core_power->map[index].stream); + core_power->map[index].stream = NULL; + /* To remove this entity, shift everything after down */ + for (i = index; i < core_power->num_entities - 1; i++) { + core_power->map[i].stream = core_power->map[i + 1].stream; + core_power->map[i].caps = core_power->map[i + 1].caps; + + // copy over cached parameters in case they map to PSR capable display + core_power->map[i].psr_enabled = core_power->map[i + 1].psr_enabled; + core_power->map[i].psr_events = core_power->map[i + 1].psr_events; + core_power->map[i].psr_power_opt = core_power->map[i + 1].psr_power_opt; + + memcpy(core_power->map[i].psr_context, core_power->map[i + 1].psr_context, sizeof(struct mod_power_psr_context)); + memset(core_power->map[i + 1].psr_context, 0, sizeof(struct mod_power_psr_context)); + } + core_power->num_entities--; + + return true; +} + +/* + * Replace_stream should be used when there is a mode set for existing + * display target with a valid stream. In this case might need to retain + * cached PSR state (events, power opt, en/dis) if we are dealing with PSR + * capable display. If mod_power_remove and mod_power_add are used instead, + * then stream may be assigned to a different slot and may end up with + * wrong cached PSR state. It is hard to tell which PSR events should + * persist through mode set or what psr_events should be initialized to, so + * it might be better just to retain them all. + */ +bool mod_power_replace_stream(struct mod_power *mod_power, + const struct dc_stream_state *current_stream, + struct dc_stream_state *new_stream, + struct psr_caps *new_caps) +{ + struct core_power *core_power = NULL; + unsigned int index = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + if (core_power->num_entities == 0) { + /* no streams exist in the table yet */ + BREAK_TO_DEBUGGER(); + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "mod_power: replace_stream: ERROR: num_entities=0 stream=%p", + current_stream); + return false; + } + + index = map_index_from_stream(core_power, current_stream); + + if (index >= core_power->num_entities) { + /* trying to replace a non-existent stream */ + BREAK_TO_DEBUGGER(); + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "mod_power: replace_stream: ERROR: index=%u >= num_entities=%u stream=%p", + index, + core_power->num_entities, + current_stream); + return false; + } + + dc_stream_release(core_power->map[index].stream); + dc_stream_retain(new_stream); + core_power->map[index].stream = new_stream; + core_power->map[index].caps = new_caps; + memset(core_power->map[index].psr_context, 0, sizeof(struct mod_power_psr_context)); + + return true; +} +bool mod_power_notify_mode_change(struct mod_power *mod_power, + const struct dc_stream_state *stream, + bool is_hdr) +{ + unsigned int stream_index = 0; + struct core_power *core_power = NULL; + struct dc_link *link = NULL; + struct dc *dc = NULL; + unsigned int panel_inst = 0; + uint8_t aux_inst = 0; + + if ((mod_power == NULL) || (stream == NULL)) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return false; + + stream_index = map_index_from_stream(core_power, stream); + + if (stream_index >= core_power->num_entities) + return false; + + dc = core_power->dc; + link = dc_stream_get_link(stream); + + if (link != NULL && dc_get_edp_link_panel_inst(dc, link, &panel_inst)) { + if (link->ctx->dc->config.dp_connector_no_native_i2c && link->no_ddc_pin) { + aux_inst = (uint8_t)link->aux_hw_inst; + } else { + ASSERT(link->ddc->ddc_pin->hw_info.ddc_channel <= 0xFF); + aux_inst = (uint8_t)link->ddc->ddc_pin->hw_info.ddc_channel; + } + + mod_power_update_backlight_on_mode_change(core_power, link, panel_inst, aux_inst, is_hdr); + + /* Handle PSR notification */ + mod_power_psr_notify_mode_change(mod_power, stream, link, stream_index); + + /* Handle Replay notification */ + mod_power_replay_notify_mode_change(mod_power, dc, link, stream, stream_index); + } + + return true; +} diff --git a/drivers/gpu/drm/amd/display/modules/power/power_abm.c b/drivers/gpu/drm/amd/display/modules/power/power_abm.c new file mode 100644 index 000000000000..a1a0563598b5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/power/power_abm.c @@ -0,0 +1,2070 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2026 Advanced Micro Devices, Inc. + +#include "dm_services.h" +#include "dc.h" +#include "mod_power.h" +#include "core_types.h" +#include "dmcu.h" +#include "abm.h" +#include "power_helpers.h" +#include "dce/dmub_psr.h" +#include "dal_asic_id.h" +#include "link_service.h" +#include <linux/math.h> + +#define DC_TRACE_LEVEL_MESSAGE(...) /* do nothing */ +#define DC_TRACE_LEVEL_MESSAGEP(...) /* do nothing */ + +#define DIV_ROUNDUP(a, b) (((a)+((b)/2))/(b)) +#define bswap16_based_on_endian(big_endian, value) \ + ((big_endian) ? cpu_to_be16(value) : cpu_to_le16(value)) + +/* Possible Min Reduction config from least aggressive to most aggressive + * 0 1 2 3 4 5 6 7 8 9 10 11 12 + * 100 98.0 94.1 94.1 85.1 80.3 75.3 69.4 60.0 57.6 50.2 49.8 40.0 % + */ +static const unsigned char min_reduction_table[13] = { +0xff, 0xfa, 0xf0, 0xf0, 0xd9, 0xcd, 0xc0, 0xb1, 0x99, 0x93, 0x80, 0x82, 0x66}; + +/* Possible Max Reduction configs from least aggressive to most aggressive + * 0 1 2 3 4 5 6 7 8 9 10 11 12 + * 96.1 89.8 85.1 80.3 69.4 64.7 64.7 50.2 39.6 30.2 30.2 30.2 19.6 % + */ +static const unsigned char max_reduction_table[13] = { +0xf5, 0xe5, 0xd9, 0xcd, 0xb1, 0xa5, 0xa5, 0x80, 0x65, 0x4d, 0x4d, 0x4d, 0x32}; + +/* Possible ABM 2.2 Min Reduction configs from least aggressive to most aggressive + * 0 1 2 3 4 5 6 7 8 9 10 11 12 + * 100 100 100 100 100 100 100 100 100 92.2 83.1 75.3 75.3 % + */ +static const unsigned char min_reduction_table_v_2_2[13] = { +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xeb, 0xd4, 0xc0, 0xc0}; + +/* Possible ABM 2.2 Max Reduction configs from least aggressive to most aggressive + * 0 1 2 3 4 5 6 7 8 9 10 11 12 + * 96.1 89.8 74.9 69.4 64.7 52.2 48.6 39.6 30.2 25.1 19.6 12.5 12.5 % + */ +static const unsigned char max_reduction_table_v_2_2[13] = { +0xf5, 0xe5, 0xbf, 0xb1, 0xa5, 0x85, 0x7c, 0x65, 0x4d, 0x40, 0x32, 0x20, 0x20}; + +/* Predefined ABM configuration sets. We may have different configuration sets + * in order to satisfy different power/quality requirements. + */ +static const unsigned char abm_config[abm_defines_max_config][abm_defines_max_level] = { +/* ABM Level 1, ABM Level 2, ABM Level 3, ABM Level 4 */ +{ 2, 5, 7, 8 }, /* Default - Medium aggressiveness */ +{ 2, 5, 8, 11 }, /* Alt #1 - Increased aggressiveness */ +{ 0, 2, 4, 8 }, /* Alt #2 - Minimal aggressiveness */ +{ 3, 6, 10, 12 }, /* Alt #3 - Super aggressiveness */ +}; + +struct abm_parameters { + unsigned char min_reduction; + unsigned char max_reduction; + unsigned char bright_pos_gain; + unsigned char dark_pos_gain; + unsigned char brightness_gain; + unsigned char contrast_factor; + unsigned char deviation_gain; + unsigned char min_knee; + unsigned char max_knee; + unsigned short blRampReduction; + unsigned short blRampStart; +}; + +static const struct abm_parameters abm_settings_config0[abm_defines_max_level] = { +// min_red max_red bright_pos dark_pos bright_gain contrast dev min_knee max_knee blRed blStart + {0xff, 0xbf, 0x20, 0x00, 0xff, 0x99, 0xb3, 0x40, 0xe0, 0xf777, 0xcccc}, + {0xde, 0x85, 0x20, 0x00, 0xe0, 0x90, 0xa8, 0x40, 0xc8, 0xf777, 0xcccc}, + {0xb0, 0x50, 0x20, 0x00, 0xc0, 0x88, 0x78, 0x70, 0xa0, 0xeeee, 0x9999}, + {0x82, 0x40, 0x20, 0x00, 0x00, 0xb8, 0xb3, 0x70, 0x70, 0xe333, 0xb333}, +}; + +static const struct abm_parameters abm_settings_config1[abm_defines_max_level] = { +// min_red max_red bright_pos dark_pos bright_gain contrast dev min_knee max_knee blRed blStart + {0xf0, 0xd9, 0x20, 0x00, 0x00, 0xff, 0xb3, 0x70, 0x70, 0xcccc, 0xcccc}, + {0xcd, 0xa5, 0x20, 0x00, 0x00, 0xff, 0xb3, 0x70, 0x70, 0xcccc, 0xcccc}, + {0x99, 0x65, 0x20, 0x00, 0x00, 0xff, 0xb3, 0x70, 0x70, 0xcccc, 0xcccc}, + {0x82, 0x4d, 0x20, 0x00, 0x00, 0xff, 0xb3, 0x70, 0x70, 0xcccc, 0xcccc}, +}; + +static const struct abm_parameters abm_settings_config2[abm_defines_max_level] = { +// min_red max_red bright_pos dark_pos bright_gain contrast dev min_knee max_knee blRed blStart + {0xf0, 0xbf, 0x20, 0x00, 0x88, 0x99, 0xb3, 0x40, 0xe0, 0x0000, 0xcccc}, + {0xd8, 0x85, 0x20, 0x00, 0x70, 0x90, 0xa8, 0x40, 0xc8, 0x0700, 0xb333}, + {0xb8, 0x58, 0x20, 0x00, 0x64, 0x88, 0x78, 0x70, 0xa0, 0x7000, 0x9999}, + {0x82, 0x40, 0x20, 0x00, 0x00, 0xb8, 0xb3, 0x70, 0x70, 0xc333, 0xb333}, +}; + +static const struct abm_parameters * const abm_settings[] = { + abm_settings_config0, + abm_settings_config1, + abm_settings_config2, +}; + +static const struct dm_bl_data_point custom_backlight_curve0[] = { + {2, 14}, {4, 16}, {6, 18}, {8, 21}, {10, 23}, {12, 26}, {14, 29}, {16, 32}, {18, 35}, + {20, 38}, {22, 41}, {24, 44}, {26, 48}, {28, 52}, {30, 55}, {32, 59}, {34, 62}, + {36, 67}, {38, 71}, {40, 75}, {42, 80}, {44, 84}, {46, 88}, {48, 93}, {50, 98}, + {52, 103}, {54, 108}, {56, 113}, {58, 118}, {60, 123}, {62, 129}, {64, 135}, {66, 140}, + {68, 146}, {70, 152}, {72, 158}, {74, 164}, {76, 171}, {78, 177}, {80, 183}, {82, 190}, + {84, 197}, {86, 204}, {88, 211}, {90, 218}, {92, 225}, {94, 232}, {96, 240}, {98, 247}}; + +struct custom_backlight_profile { + uint8_t ac_level_percentage; + uint8_t dc_level_percentage; + uint8_t min_input_signal; + uint8_t max_input_signal; + uint8_t num_data_points; + const struct dm_bl_data_point *data_points; +}; + +static const struct custom_backlight_profile custom_backlight_profiles[] = { + {100, 32, 12, 255, ARRAY_SIZE(custom_backlight_curve0), custom_backlight_curve0}, +}; + +#define NUM_AMBI_LEVEL 5 +#define NUM_AGGR_LEVEL 4 +#define NUM_POWER_FN_SEGS 8 +#define NUM_BL_CURVE_SEGS 16 +#define IRAM_SIZE 256 + +#define IRAM_RESERVE_AREA_START_V2 0xF0 // reserve 0xF0~0xF6 are write by DMCU only +#define IRAM_RESERVE_AREA_END_V2 0xF6 // reserve 0xF0~0xF6 are write by DMCU only + +#define IRAM_RESERVE_AREA_START_V2_2 0xF0 // reserve 0xF0~0xFF are write by DMCU only +#define IRAM_RESERVE_AREA_END_V2_2 0xFF // reserve 0xF0~0xFF are write by DMCU only + +#pragma pack(push, 1) +/* NOTE: iRAM is 256B in size */ +struct iram_table_v_2 { + /* flags */ + uint16_t min_abm_backlight; /* 0x00 U16 */ + + /* parameters for ABM2.0 algorithm */ + uint8_t min_reduction[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x02 U0.8 */ + uint8_t max_reduction[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x16 U0.8 */ + uint8_t bright_pos_gain[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x2a U2.6 */ + uint8_t bright_neg_gain[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x3e U2.6 */ + uint8_t dark_pos_gain[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x52 U2.6 */ + uint8_t dark_neg_gain[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x66 U2.6 */ + uint8_t iir_curve[NUM_AMBI_LEVEL]; /* 0x7a U0.8 */ + uint8_t deviation_gain; /* 0x7f U0.8 */ + + /* parameters for crgb conversion */ + uint16_t crgb_thresh[NUM_POWER_FN_SEGS]; /* 0x80 U3.13 */ + uint16_t crgb_offset[NUM_POWER_FN_SEGS]; /* 0x90 U1.15 */ + uint16_t crgb_slope[NUM_POWER_FN_SEGS]; /* 0xa0 U4.12 */ + + /* parameters for custom curve */ + /* thresholds for brightness --> backlight */ + uint16_t backlight_thresholds[NUM_BL_CURVE_SEGS]; /* 0xb0 U16.0 */ + /* offsets for brightness --> backlight */ + uint16_t backlight_offsets[NUM_BL_CURVE_SEGS]; /* 0xd0 U16.0 */ + + /* For reading PSR State directly from IRAM */ + uint8_t psr_state; /* 0xf0 */ + uint8_t dmcu_mcp_interface_version; /* 0xf1 */ + uint8_t dmcu_abm_feature_version; /* 0xf2 */ + uint8_t dmcu_psr_feature_version; /* 0xf3 */ + uint16_t dmcu_version; /* 0xf4 */ + uint8_t dmcu_state; /* 0xf6 */ + + uint16_t blRampReduction; /* 0xf7 */ + uint16_t blRampStart; /* 0xf9 */ + uint8_t dummy5; /* 0xfb */ + uint8_t dummy6; /* 0xfc */ + uint8_t dummy7; /* 0xfd */ + uint8_t dummy8; /* 0xfe */ + uint8_t dummy9; /* 0xff */ +}; + +struct iram_table_v_2_2 { + /* flags */ + uint16_t flags; /* 0x00 U16 */ + + /* parameters for ABM2.2 algorithm */ + uint8_t min_reduction[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x02 U0.8 */ + uint8_t max_reduction[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x16 U0.8 */ + uint8_t bright_pos_gain[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x2a U2.6 */ + uint8_t dark_pos_gain[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x3e U2.6 */ + uint8_t hybrid_factor[NUM_AGGR_LEVEL]; /* 0x52 U0.8 */ + uint8_t contrast_factor[NUM_AGGR_LEVEL]; /* 0x56 U0.8 */ + uint8_t deviation_gain[NUM_AGGR_LEVEL]; /* 0x5a U0.8 */ + uint8_t iir_curve[NUM_AMBI_LEVEL]; /* 0x5e U0.8 */ + uint8_t min_knee[NUM_AGGR_LEVEL]; /* 0x63 U0.8 */ + uint8_t max_knee[NUM_AGGR_LEVEL]; /* 0x67 U0.8 */ + uint16_t min_abm_backlight; /* 0x6b U16 */ + uint8_t pad[19]; /* 0x6d U0.8 */ + + /* parameters for crgb conversion */ + uint16_t crgb_thresh[NUM_POWER_FN_SEGS]; /* 0x80 U3.13 */ + uint16_t crgb_offset[NUM_POWER_FN_SEGS]; /* 0x90 U1.15 */ + uint16_t crgb_slope[NUM_POWER_FN_SEGS]; /* 0xa0 U4.12 */ + + /* parameters for custom curve */ + /* thresholds for brightness --> backlight */ + uint16_t backlight_thresholds[NUM_BL_CURVE_SEGS]; /* 0xb0 U16.0 */ + /* offsets for brightness --> backlight */ + uint16_t backlight_offsets[NUM_BL_CURVE_SEGS]; /* 0xd0 U16.0 */ + + /* For reading PSR State directly from IRAM */ + uint8_t psr_state; /* 0xf0 */ + uint8_t dmcu_mcp_interface_version; /* 0xf1 */ + uint8_t dmcu_abm_feature_version; /* 0xf2 */ + uint8_t dmcu_psr_feature_version; /* 0xf3 */ + uint16_t dmcu_version; /* 0xf4 */ + uint8_t dmcu_state; /* 0xf6 */ + + uint8_t dummy1; /* 0xf7 */ + uint8_t dummy2; /* 0xf8 */ + uint8_t dummy3; /* 0xf9 */ + uint8_t dummy4; /* 0xfa */ + uint8_t dummy5; /* 0xfb */ + uint8_t dummy6; /* 0xfc */ + uint8_t dummy7; /* 0xfd */ + uint8_t dummy8; /* 0xfe */ + uint8_t dummy9; /* 0xff */ +}; +#pragma pack(pop) + +#define MOD_POWER_MAX_CONCURRENT_STREAMS 32 +#define SMOOTH_BRIGHTNESS_ADJUSTMENT_TIME_IN_MS 500 + +/* If system or panel does not report some sort of brightness percent to nits + * mapping, we will use following default values so backlight control using + * nits based interfaces will still work, but might not describe panel + * correctly. In this case percentage based backlight control should ideally + * be used. + * Min = 5 nits + * Max = 300 nits + */ + +#define MOD_POWER_TO_CORE(mod_power)\ + container_of(mod_power, struct core_power, mod_public) + +static uint16_t backlight_8_to_16(unsigned int backlight_8bit) +{ + return (uint16_t)(backlight_8bit * 0x101); +} + +unsigned int backlight_millipercent_to_millinit( + struct core_power *core_power, unsigned int millipercent, unsigned int inst) +{ + unsigned int millinit = 0; + unsigned long long numerator = 0; + + if (core_power == NULL) + return 0; + + numerator = ((unsigned long long)millipercent) * + core_power->bl_prop[inst].nits_range; + millinit = ((unsigned int)div_u64(numerator, 100000)) + + core_power->bl_prop[inst].min_brightness_millinits; + + return millinit; +} + +static unsigned int backlight_millinit_to_millipercent( + struct core_power *core_power, unsigned int millinit, unsigned int inst) +{ + unsigned int millipercent = 0; + unsigned long long numerator = 0; + + if (core_power == NULL) + return 0; + + if (millinit <= core_power->bl_prop[inst].min_brightness_millinits) + return 0; + + if (millinit >= core_power->bl_prop[inst].max_brightness_millinits) + return (100 * 1000); + + numerator = (((unsigned long long)millinit) - + core_power->bl_prop[inst].min_brightness_millinits) * 100000; + millipercent = ((unsigned int)div_u64(numerator, + core_power->bl_prop[inst].nits_range)); + + return millipercent; +} + +static unsigned int backlight_pwm_to_millipercent( + struct core_power *core_power, unsigned int pwm, unsigned int inst) +{ + unsigned int millipercent = 0; + unsigned int max_index = 0; + + if (core_power == NULL) + return 0; + + if (!core_power->bl_prop[inst].backlight_caps_valid) + return 0; + + /* Doesn't really make sense to have one single backlight level + * possible... + */ + if (core_power->bl_prop[inst].num_backlight_levels < 2) + return 0; + + max_index = core_power->bl_prop[inst].num_backlight_levels - 1; + + if (pwm <= core_power->bl_prop[inst].backlight_lut[0]) + return 0; + + if (pwm > core_power->bl_prop[inst].backlight_lut[max_index]) + return (100 * 1000); + + /* We need to do a binary search over the array for where the pwm level + * is in the lut. Based on the index we can determine percentage. + */ + unsigned int min = 0; + unsigned int max = max_index; + unsigned int mid = 0; + + while (max >= min) { + mid = (min + max) / 2; /* floor of half range */ + + if (core_power->bl_prop[inst].backlight_lut[mid] < pwm) + min = mid + 1; + else if (core_power->bl_prop[inst].backlight_lut[mid] > pwm) + max = mid - 1; + else + break; + } + + /* In this case, exact match is not found. Check if mid/min/max + * value is actually closer. + */ + if (max < min) { + unsigned int min_delta; + unsigned int mid_delta; + unsigned int max_delta; + + min_delta = (core_power->bl_prop[inst].backlight_lut[min] > pwm) ? + core_power->bl_prop[inst].backlight_lut[min] - pwm : + pwm - core_power->bl_prop[inst].backlight_lut[min]; + + mid_delta = (core_power->bl_prop[inst].backlight_lut[mid] > pwm) ? + core_power->bl_prop[inst].backlight_lut[mid] - pwm : + pwm - core_power->bl_prop[inst].backlight_lut[mid]; + + max_delta = (core_power->bl_prop[inst].backlight_lut[max] > pwm) ? + core_power->bl_prop[inst].backlight_lut[max] - pwm : + pwm - core_power->bl_prop[inst].backlight_lut[max]; + + if ((min_delta < mid_delta) && (min_delta < max_delta)) + mid = min; + + if ((max_delta < mid_delta) && (max_delta < min_delta)) + mid = max; + } + + /* No interpolation, just take closest index */ + millipercent = 1000 * 100 * mid / max_index; + + return millipercent; +} + +static unsigned int backlight_pwm_to_millinit( + struct core_power *core_power, unsigned int pwm, unsigned int inst) +{ + unsigned int millinit = 0; + + if (core_power == NULL) + return 0; + + if (pwm <= core_power->bl_prop[inst].min_backlight_pwm) + return core_power->bl_prop[inst].min_brightness_millinits; + + if (pwm >= core_power->bl_prop[inst].max_backlight_pwm) + return core_power->bl_prop[inst].max_brightness_millinits; + + millinit = ((unsigned int)div_u64(((unsigned long long)pwm - + core_power->bl_prop[inst].min_backlight_pwm) * + core_power->bl_prop[inst].nits_range, + core_power->bl_prop[inst].backlight_range)); + + millinit += core_power->bl_prop[inst].min_brightness_millinits; + + if (millinit > core_power->bl_prop[inst].max_brightness_millinits) + millinit = core_power->bl_prop[inst].max_brightness_millinits; + + return millinit; +} + +unsigned int backlight_millipercent_to_pwm( + struct core_power *core_power, unsigned int millipercent, unsigned int inst) +{ + unsigned int pwm = (unsigned int)-1; + unsigned int index = 0; + + if (core_power == NULL) + return 0; + + // Bypass the brightness mapping LUT + if (core_power->bl_prop->use_linear_backlight_curve) { + pwm = core_power->bl_prop[inst].min_backlight_pwm + + (unsigned int) div_u64((unsigned long long) millipercent * + core_power->bl_prop[inst].backlight_range, + 100000); + + if (pwm > core_power->bl_prop[inst].max_backlight_pwm) + pwm = core_power->bl_prop[inst].max_backlight_pwm; + + return pwm; + } + + if (millipercent >= (100 * 1000)) + return core_power->bl_prop[inst].backlight_lut[core_power->bl_prop[inst].num_backlight_levels - 1]; + + /* This will give the floor index. */ + index = ((core_power->bl_prop[inst].num_backlight_levels - 1) * + millipercent) / 100000; + /* Null check otherwise eDP doesn't lightup when connected to DP1 */ + if (core_power->bl_prop[inst].backlight_lut == NULL) + return pwm; + + pwm = core_power->bl_prop[inst].backlight_lut[index]; + + return pwm; +} + +static unsigned int backlight_millinit_to_pwm( + struct core_power *core_power, unsigned int millinit, unsigned int inst) +{ + unsigned int pwm = 0; + + if (core_power == NULL) + return 0; + + /* For nits based brightness, the signal will be a value + * between the minimum and maximum value. + */ + if (millinit >= core_power->bl_prop[inst].max_brightness_millinits) + return core_power->bl_prop[inst].max_backlight_pwm; + else if (millinit <= core_power->bl_prop[inst].min_brightness_millinits) + return core_power->bl_prop[inst].min_backlight_pwm; + + pwm = ((unsigned int)div_u64(((unsigned long long)millinit - + core_power->bl_prop[inst].min_brightness_millinits) * + core_power->bl_prop[inst].backlight_range, + core_power->bl_prop[inst].nits_range)); + + pwm += core_power->bl_prop[inst].min_backlight_pwm; + + if (pwm > core_power->bl_prop[inst].max_backlight_pwm) + pwm = core_power->bl_prop[inst].max_backlight_pwm; + + return pwm; +} + +static bool validate_ext_backlight_caps( + struct dm_acpi_atif_backlight_caps *ext_backlight_caps) +{ + unsigned int i; + unsigned int num_of_data_points = 0; + unsigned int last_signal_level = 0; + unsigned int last_luminance = 0; + + num_of_data_points = ext_backlight_caps->num_data_points; + + /* Validation rules: + * 1. BIOS should carry customized data points and + * the number of data points should not be larger than 99. + * 2. The max_input_signal should be larger than min_input_signal. + * 3. For each data point: + * a. luminance should be in ascending order and + * should not be 0 or 100 since the corresponding signal_level + * are assigned by min_input_signal and max_input_signal. + * b. signal_level should be in ascending order and + * be within the range of min/max_input_signal. + */ + if (num_of_data_points > BL_DATA_POINTS) + return false; + + if (ext_backlight_caps->min_input_signal >= ext_backlight_caps->max_input_signal) + return false; + + last_signal_level = ext_backlight_caps->min_input_signal; + for (i = 0; i < num_of_data_points; i++) { + unsigned int luminance = ext_backlight_caps->data_points[i].luminance; + unsigned int signal_level = ext_backlight_caps->data_points[i].signal_level; + + if ((luminance <= last_luminance) || (luminance > BL_DATA_POINTS)) + return false; + + if ((signal_level <= last_signal_level) || (signal_level >= ext_backlight_caps->max_input_signal)) + return false; + + last_signal_level = signal_level; + last_luminance = luminance; + } + + return true; +} + +/* hard coded to default backlight curve. */ +void initialize_backlight_caps(struct core_power *core_power, unsigned int inst) +{ + unsigned int i; + struct dm_acpi_atif_backlight_caps *ext_backlight_caps = NULL; + bool custom_curve_present = false; + unsigned int num_levels = 0; + struct dc *dc = NULL; + enum dm_acpi_display_type acpi_display_type = + (inst == 0) ? AcpiDisplayType_LCD1 : AcpiDisplayType_LCD2; + + if (core_power == NULL) + return; + dc = core_power->dc; + + num_levels = core_power->bl_prop[inst].num_backlight_levels; + + /* Allocate memory for ATIF output + * (do not want to use 256 bytes on the stack) + */ + ext_backlight_caps = (struct dm_acpi_atif_backlight_caps *) + (kzalloc(sizeof(struct dm_acpi_atif_backlight_caps), + GFP_KERNEL)); + + if (ext_backlight_caps == NULL) + return; + + /* Retrieve ACPI extended brightness caps */ + if (dm_query_extended_brightness_caps + (dc->ctx, acpi_display_type, ext_backlight_caps)) { + custom_curve_present = validate_ext_backlight_caps(ext_backlight_caps); + } + + if (core_power->bl_prop[inst].use_custom_backlight_caps && + fill_custom_backlight_caps( + core_power->bl_prop[inst].custom_backlight_caps_config_no, + ext_backlight_caps)) { + custom_curve_present = validate_ext_backlight_caps(ext_backlight_caps); + } + + if (custom_curve_present) { + unsigned int index = 1; + unsigned int num_of_data_points = ext_backlight_caps->num_data_points; + + core_power->bl_prop[inst].ac_backlight_percent = + ext_backlight_caps->ac_level_percentage; + core_power->bl_prop[inst].dc_backlight_percent = + ext_backlight_caps->dc_level_percentage; + core_power->bl_prop[inst].backlight_lut[0] = + backlight_8_to_16( + ext_backlight_caps->min_input_signal); + core_power->bl_prop[inst].backlight_lut[num_levels - 1] = + backlight_8_to_16( + ext_backlight_caps->max_input_signal); + + /* Filling translation table from data points - + * between every two provided data points we + * lineary interpolate missing values + */ + for (i = 0; i < num_of_data_points; i++) { + unsigned int luminance = + ext_backlight_caps->data_points[i].luminance; + unsigned int signal_level = + backlight_8_to_16( + ext_backlight_caps->data_points[i].signal_level); + + /* Since luminance is a percentage, scale it by num_levels*/ + luminance = (luminance * num_levels) / 101; + + /* Lineary interpolate missing values */ + if (index < luminance) { + unsigned int base_value = + core_power->bl_prop[inst].backlight_lut[index-1]; + unsigned int delta_signal = + signal_level - base_value; + unsigned int delta_luma = + luminance - index + 1; + unsigned int step = delta_signal; + + for (; index < luminance; index++) { + core_power->bl_prop[inst].backlight_lut[index] = + base_value + (step / delta_luma); + step += delta_signal; + } + } + + /* Now [index == luminance], + * so we can add data point to the translation table + */ + core_power->bl_prop[inst].backlight_lut[index++] = signal_level; + } + + /* Complete the final segment of interpolation - + * between last datapoint and maximum value + */ + if (index < num_levels - 1) { + unsigned int base_value = + core_power->bl_prop[inst].backlight_lut[index-1]; + unsigned int delta_signal = + core_power->bl_prop[inst].backlight_lut[num_levels - 1] - + base_value; + unsigned int delta_luma = num_levels - index; + unsigned int step = delta_signal; + + for (; index < num_levels - 1; index++) { + core_power->bl_prop[inst].backlight_lut[index] = + base_value + (step / delta_luma); + step += delta_signal; + } + } + /* Build backlight translation table based on default curve */ + } else { + /* Defines default backlight curve F(x) = A(x*x) + Bx + C. + * + * Backlight curve should always satisfy: + * F(0) = min, F(100) = max, + * So polynom coefficients are: + * A is 0.0255 - B/100 - min/10000 - (255-max)/10000 = + * (max - min)/10000 - B/100 + * B is adjustable factor to modify the curve. + * Bigger B results in less concave curve. + * B range is [0..(max-min)/100] + * C is backlight minimum + */ + unsigned int backlight_curve_coeff_a_factor = + num_levels * num_levels; + unsigned int backlight_curve_coeff_b = num_levels; + unsigned int delta = + core_power->bl_prop[inst].backlight_lut[num_levels - 1] - + core_power->bl_prop[inst].backlight_lut[0]; + unsigned int coeffC = core_power->bl_prop[inst].backlight_lut[0]; + unsigned int coeffB = + (backlight_curve_coeff_b < delta ? + backlight_curve_coeff_b : delta); + unsigned long long coeffA = delta - coeffB; /* coeffB is B*100 */ + + for (i = 1; i < num_levels - 1; i++) { + uint64_t lut_val = div_u64(coeffA * i * i, backlight_curve_coeff_a_factor) + + div_u64((uint64_t)coeffB * i, backlight_curve_coeff_b) + coeffC; + + ASSERT(lut_val <= 0xFFFFFFFF); + core_power->bl_prop[inst].backlight_lut[i] = (unsigned int)lut_val; + } + } + + if (ext_backlight_caps != NULL) + kfree(ext_backlight_caps); + + /* Successfully initialized */ + core_power->bl_prop[inst].backlight_caps_valid = true; +} + +static void varibright_set_level(struct core_power *core_power) +{ + if (!core_power->varibright_prop.varibright_active || + !core_power->varibright_prop.varibright_user_enable) + core_power->varibright_prop.varibright_hw_level = 0; + else + core_power->varibright_prop.varibright_hw_level = + core_power->varibright_prop.varibright_level; +} + +bool mod_power_hw_init_backlight(struct mod_power *mod_power) +{ + struct core_power *core_power = NULL; + struct dc *dc = NULL; + struct dmcu *dmcu = NULL; + struct dmcu_iram_parameters params; + unsigned int i; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + dc = core_power->dc; + + for (i = 0; i < core_power->edp_num; i++) { + params.set = core_power->varibright_prop.varibright_config_setting; + params.backlight_ramping_override = core_power->bl_prop[i].backlight_ramping_override; + params.backlight_ramping_reduction = core_power->bl_prop[i].backlight_ramping_reduction; + params.backlight_ramping_start = core_power->bl_prop[i].backlight_ramping_start; + params.backlight_lut_array = core_power->bl_prop[i].backlight_lut; + params.backlight_lut_array_size = core_power->bl_prop[i].num_backlight_levels; + params.min_abm_backlight = core_power->bl_prop[i].min_abm_backlight; + + dmcu = dc->res_pool->dmcu; + + // In the case where abm is implemented on dmcub, + // dmcu object will be null. + // ABM 2.4 and up are implemented on dmcub. + if (dmcu) { + //DMCU does not support multiple eDP + return dmcu_load_iram(dmcu, params); + } else if (dc->ctx->dmub_srv) { + if (!dmub_init_abm_config(dc->res_pool, params, i)) + return false; + } else + return false; + } + return true; +} + +void mod_power_update_backlight_on_mode_change( + struct core_power *core_power, + struct dc_link *link, + unsigned int panel_inst, + uint8_t aux_inst, + bool is_hdr) +{ + struct set_backlight_level_params backlight_level_params = { 0 }; + + if (link->dpcd_sink_ext_caps.bits.hdr_aux_backlight_control == 1 || + link->dpcd_sink_ext_caps.bits.sdr_aux_backlight_control == 1) + dc_link_set_backlight_level_nits(link, core_power->bl_state[panel_inst].isHDR, + core_power->bl_state[panel_inst].backlight_millinit, 0); + + backlight_level_params.frame_ramp = 0; + + fill_backlight_level_params(core_power, &backlight_level_params, panel_inst, aux_inst, + core_power->bl_state[panel_inst].backlight_pwm, link->backlight_control_type, + core_power->bl_state[panel_inst].backlight_millinit, 0, is_hdr); + + dc_link_set_backlight_level(link, &backlight_level_params); +} + +static bool set_backlight_millinits_aux(struct core_power *core_power, + struct dc_stream_state *stream, + unsigned int backlight_millinits, + unsigned int transition_time_millisec, + unsigned int inst) +{ + struct dc_link *link = NULL; + + if (core_power == NULL) + return false; + + if (stream == NULL) + return true; + + link = dc_stream_get_link(stream); + + return dc_link_set_backlight_level_nits(link, core_power->bl_state[inst].isHDR, + backlight_millinits, transition_time_millisec); +} + +static bool set_backlight(struct core_power *core_power, + struct dc_stream_state *stream, + struct set_backlight_level_params *backlight_level_params, + unsigned int inst) +{ + bool retv = false; + unsigned int frame_ramp = 0; + unsigned int vsync_rate_hz; + union dmcu_abm_set_bl_params params; + const struct dc_link *link = NULL; + unsigned int backlight_pwm_u16_16 = backlight_level_params->backlight_pwm_u16_16; + unsigned int transition_time_millisec = backlight_level_params->transition_time_in_ms; + + if (core_power == NULL) + return false; + + core_power->bl_state[inst].backlight_pwm = backlight_pwm_u16_16; + + if (stream == NULL) + return true; + + if (stream->link->connector_signal != SIGNAL_TYPE_EDP) + return false; + + if (transition_time_millisec != 0) { + unsigned int v_total = + (stream->adjust.v_total_max == 0) ? stream->timing.v_total : stream->adjust.v_total_max; + + vsync_rate_hz = (unsigned int)div_u64(div_u64((stream-> + timing.pix_clk_100hz * 100), + v_total), + stream->timing.h_total); + + if (core_power->bl_state[inst].smooth_brightness_enabled) + frame_ramp = ((vsync_rate_hz * + transition_time_millisec) + 500) / 1000; + } + + core_power->bl_state[inst].frame_ramp = frame_ramp; + params.u32All = 0; + params.bits.gradual_change = (frame_ramp > 0); + params.bits.frame_ramp = frame_ramp; + link = dc_stream_get_link(stream); + + mod_power_set_psr_event(&core_power->mod_public, stream, true, psr_event_hw_programming, true); + mod_power_set_replay_event(&core_power->mod_public, stream, true, replay_event_hw_programming, true); + + backlight_level_params->frame_ramp = params.u32All; + retv = dc_link_set_backlight_level(link, backlight_level_params); + + mod_power_set_psr_event(&core_power->mod_public, stream, false, psr_event_hw_programming, false); + mod_power_set_replay_event(&core_power->mod_public, stream, false, replay_event_hw_programming, false); + + return retv; +} + +void fill_backlight_level_params(struct core_power *core_power, + struct set_backlight_level_params *backlight_level_params, + int panel_inst, uint8_t aux_inst, unsigned int backlight_pwm, + enum backlight_control_type backlight_control_type, + unsigned int backlight_millinit, unsigned int transition_time_millisec, + bool is_hdr) +{ + struct pwr_backlight_properties *bl_prop = &core_power->bl_prop[panel_inst]; + + backlight_level_params->aux_inst = aux_inst; + backlight_level_params->backlight_pwm_u16_16 = backlight_pwm; + backlight_level_params->control_type = backlight_control_type; + backlight_level_params->backlight_millinits = backlight_millinit; + backlight_level_params->transition_time_in_ms = transition_time_millisec; + backlight_level_params->min_luminance = bl_prop->min_brightness_millinits; + backlight_level_params->max_luminance = bl_prop->max_brightness_millinits; + backlight_level_params->min_backlight_pwm = bl_prop->min_backlight_pwm; + backlight_level_params->max_backlight_pwm = bl_prop->max_backlight_pwm; + + if (backlight_control_type == BACKLIGHT_CONTROL_AMD_AUX && !is_hdr) + backlight_level_params->control_type = BACKLIGHT_CONTROL_PWM; +} + +bool mod_power_set_backlight_nits(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millinit, + unsigned int transition_time_millisec, + bool skip_aux, + bool is_hdr) +{ + struct core_power *core_power = NULL; + unsigned int backlight_pwm; + unsigned int panel_inst = 0; + struct set_backlight_level_params backlight_level_params = { 0 }; + const struct dc_link *link = NULL; + uint8_t aux_inst = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + link = dc_stream_get_link(stream); + + if (link->ctx->dc->config.dp_connector_no_native_i2c && link->no_ddc_pin) { + aux_inst = (uint8_t)link->aux_hw_inst; + } else { + ASSERT(link->ddc->ddc_pin->hw_info.ddc_channel <= 0xFF); + aux_inst = (uint8_t)link->ddc->ddc_pin->hw_info.ddc_channel; + } + + if (!dc_get_edp_link_panel_inst(core_power->dc, stream->link, &panel_inst)) + return false; + + if (!skip_aux) { + if (!set_backlight_millinits_aux(core_power, stream, + backlight_millinit, transition_time_millisec, panel_inst)) + return false; + } +// always send both AUX (above) and PWM (below) + core_power->bl_state[panel_inst].backlight_millinit = backlight_millinit; + + core_power->bl_state[panel_inst].backlight_millipercent = + backlight_millinit_to_millipercent( + core_power, backlight_millinit, panel_inst); + + backlight_pwm = backlight_millinit_to_pwm( + core_power, backlight_millinit, panel_inst); + + fill_backlight_level_params(core_power, &backlight_level_params, panel_inst, aux_inst, backlight_pwm, + link->backlight_control_type, backlight_millinit, transition_time_millisec, is_hdr); + + return set_backlight(core_power, stream, + &backlight_level_params, panel_inst); +} + +bool mod_power_backlight_percent_to_nits(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millipercent, + unsigned int *backlight_millinit) +{ + struct core_power *core_power = NULL; + unsigned int inst = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (!dc_get_edp_link_panel_inst(core_power->dc, stream->link, &inst)) + return false; + + *backlight_millinit = backlight_millipercent_to_millinit( + core_power, backlight_millipercent, inst); + return true; +} + +bool mod_power_backlight_nits_to_percent(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millinit, + unsigned int *backlight_millipercent) +{ + struct core_power *core_power = NULL; + unsigned int inst = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (!dc_get_edp_link_panel_inst(core_power->dc, stream->link, &inst)) + return false; + + *backlight_millipercent = backlight_millinit_to_millipercent( + core_power, backlight_millinit, inst); + return true; +} + +bool mod_power_set_backlight_percent(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millipercent, + unsigned int transition_time_millisec, + bool is_hdr) +{ + struct core_power *core_power = NULL; + struct set_backlight_level_params backlight_level_params = { 0 }; + const struct dc_link *link = NULL; + unsigned int backlight_pwm; + unsigned int panel_inst = 0; + uint8_t aux_inst = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + link = dc_stream_get_link(stream); + if (link->ctx->dc->config.dp_connector_no_native_i2c && link->no_ddc_pin) { + aux_inst = (uint8_t)link->aux_hw_inst; + } else { + ASSERT(link->ddc->ddc_pin->hw_info.ddc_channel <= 0xFF); + aux_inst = (uint8_t)link->ddc->ddc_pin->hw_info.ddc_channel; + } + + if (!dc_get_edp_link_panel_inst(core_power->dc, stream->link, &panel_inst)) + return false; + core_power->bl_state[panel_inst].backlight_millipercent = backlight_millipercent; + + core_power->bl_state[panel_inst].backlight_millinit = + backlight_millipercent_to_millinit( + core_power, backlight_millipercent, panel_inst); + + backlight_pwm = backlight_millipercent_to_pwm( + core_power, backlight_millipercent, panel_inst); + + fill_backlight_level_params(core_power, &backlight_level_params, panel_inst, + aux_inst, backlight_pwm, link->backlight_control_type, + core_power->bl_state[panel_inst].backlight_millinit, transition_time_millisec, is_hdr); + + return set_backlight(core_power, stream, + &backlight_level_params, panel_inst); +} + +void mod_power_update_backlight(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millipercent) +{ + struct core_power *core_power = NULL; + unsigned int inst = 0; + + if (mod_power == NULL) + return; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (!dc_get_edp_link_panel_inst(core_power->dc, stream->link, &inst)) + return; + core_power->bl_state[inst].backlight_millipercent = backlight_millipercent; + + core_power->bl_state[inst].backlight_millinit = + backlight_millipercent_to_millinit( + core_power, backlight_millipercent, inst); + + core_power->bl_state[inst].backlight_pwm = backlight_millipercent_to_pwm( + core_power, backlight_millipercent, inst); +} + +void mod_power_update_backlight_nits(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millinit) +{ + struct core_power *core_power = NULL; + unsigned int inst = 0; + + if (mod_power == NULL) + return; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (!dc_get_edp_link_panel_inst(core_power->dc, stream->link, &inst)) + return; + + core_power->bl_state[inst].backlight_millinit = backlight_millinit; + + core_power->bl_state[inst].backlight_millipercent = backlight_millinit_to_millipercent( + core_power, backlight_millinit, inst); + core_power->bl_state[inst].backlight_pwm = backlight_millinit_to_pwm( + core_power, backlight_millinit, inst); +} + +bool mod_power_get_backlight_pwm(struct mod_power *mod_power, + unsigned int *backlight_pwm, + unsigned int inst) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + *backlight_pwm = core_power->bl_state[inst].backlight_pwm; + + return true; +} + +bool mod_power_get_backlight_nits(struct mod_power *mod_power, + unsigned int *backlight_millinit, + unsigned int inst) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + *backlight_millinit = core_power->bl_state[inst].backlight_millinit; + + return true; +} + +bool mod_power_get_backlight_percent(struct mod_power *mod_power, + unsigned int *backlight_millipercent, + unsigned int inst) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + *backlight_millipercent = core_power->bl_state[inst].backlight_millipercent; + + return true; +} + +bool mod_power_get_hw_target_backlight_pwm_nits(struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_millinit, + unsigned int inst) +{ + struct core_power *core_power = NULL; + unsigned int backlight_u16_16 = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (mod_power_get_hw_target_backlight_pwm(mod_power, link, + &backlight_u16_16)) { + *backlight_millinit = + backlight_pwm_to_millinit(core_power, + backlight_u16_16, inst); + return true; + } + return false; +} + +bool mod_power_get_hw_target_backlight_pwm_percent(struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_millipercent, + unsigned int inst) +{ + struct core_power *core_power = NULL; + unsigned int backlight_u16_16 = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (mod_power_get_hw_target_backlight_pwm(mod_power, link, + &backlight_u16_16)) { + *backlight_millipercent = + backlight_pwm_to_millipercent(core_power, + backlight_u16_16, inst); + return true; + } + return false; +} + +bool mod_power_get_hw_target_backlight_pwm(struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_u16_16) +{ + if (mod_power == NULL) + return false; + + *backlight_u16_16 = dc_link_get_target_backlight_pwm(link); + + return true; +} + +bool mod_power_get_hw_backlight_pwm_nits(struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_millinit, + unsigned int inst) +{ + struct core_power *core_power = NULL; + unsigned int backlight_u16_16 = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (mod_power_get_hw_backlight_pwm(mod_power, link, &backlight_u16_16)) { + *backlight_millinit = + backlight_pwm_to_millinit(core_power, + backlight_u16_16, inst); + return true; + } + return false; +} + +bool mod_power_get_hw_backlight_aux_nits(struct mod_power *mod_power, + struct dc_stream_state **streams, int num_streams, + unsigned int *backlight_millinit_avg, + unsigned int *backlight_millinit_peak) +{ + struct core_power *core_power = NULL; + struct dc_link *link = NULL; + int stream_index; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power == NULL) + return false; + + if (num_streams < 1) + return true; + + for (stream_index = 0; stream_index < num_streams; stream_index++) + if (streams[stream_index]->link->connector_signal == SIGNAL_TYPE_EDP || + streams[stream_index]->link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT) + break; + + if (stream_index == num_streams) + return false; + + link = dc_stream_get_link(streams[stream_index]); + if (link->dpcd_sink_ext_caps.bits.hdr_aux_backlight_control == 0) + return false; + + return dc_link_get_backlight_level_nits(link, backlight_millinit_avg, + backlight_millinit_peak); +} + +bool mod_power_get_hw_backlight_pwm_percent(struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_millipercent, + unsigned int inst) +{ + struct core_power *core_power = NULL; + unsigned int backlight_u16_16 = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (mod_power_get_hw_backlight_pwm(mod_power, link, &backlight_u16_16)) { + *backlight_millipercent = + backlight_pwm_to_millipercent(core_power, + backlight_u16_16, inst); + return true; + } + return false; +} + +bool mod_power_get_hw_backlight_pwm(struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_u16_16) +{ + if (mod_power == NULL) + return false; + + *backlight_u16_16 = dc_link_get_backlight_level(link); + + return true; +} + +bool mod_power_get_panel_backlight_boundaries( + struct mod_power *mod_power, + unsigned int *out_min_backlight, + unsigned int *out_max_backlight, + unsigned int *out_ac_backlight_percent, + unsigned int *out_dc_backlight_percent, + unsigned int inst) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + /* If cache was successfully updated, + * copy the values to output structure and return success + */ + if (core_power->bl_prop[inst].backlight_caps_valid) { + *out_min_backlight = core_power->bl_prop[inst].backlight_lut[0]; + *out_max_backlight = + core_power->bl_prop[inst].backlight_lut[ + core_power->bl_prop[inst].num_backlight_levels - 1]; + *out_ac_backlight_percent = + core_power->bl_prop[inst].ac_backlight_percent; + *out_dc_backlight_percent = + core_power->bl_prop[inst].dc_backlight_percent; + + return true; + } + + return false; +} + +bool mod_power_set_smooth_brightness(struct mod_power *mod_power, + bool enable_brightness, + unsigned int inst) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + core_power->bl_state[inst].smooth_brightness_enabled = enable_brightness; + + return true; +} + +bool mod_power_varibright_feature_enable(struct mod_power *mod_power, bool enable, + struct dc_stream_update *stream_update) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + core_power->varibright_prop.varibright_user_enable = enable; + + /* find abm hw level to program, and save in stream update */ + varibright_set_level(core_power); + *stream_update->abm_level = core_power->varibright_prop.varibright_hw_level; + + DC_TRACE_LEVEL_MESSAGEP(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Backlight_ABM, + ">ABM feature enable: enable=%u su->varibright_level=%u varibright_hw_level=%u", + (unsigned int) enable, + *stream_update->abm_level, + core_power->varibright_prop.varibright_hw_level); + return true; +} + +bool mod_power_varibright_activate(struct mod_power *mod_power, + bool activate, + struct dc_stream_update *stream_update) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + core_power->varibright_prop.varibright_active = activate; + + /* find abm hw level to program, and save in stream update */ + varibright_set_level(core_power); + *stream_update->abm_level = core_power->varibright_prop.varibright_hw_level; + + DC_TRACE_LEVEL_MESSAGEP(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Backlight_ABM, + ">ABM activate: activate=%u su->varibright_level=%u", + (unsigned int) activate, + *stream_update->abm_level); + return true; +} +bool mod_power_varibright_set_level(struct mod_power *mod_power, unsigned int level, + struct dc_stream_update *stream_update) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + core_power->varibright_prop.varibright_level = level; + core_power->varibright_prop.varibright_hw_level = level; + + /* find abm hw level to program, and save in stream update */ + varibright_set_level(core_power); + *stream_update->abm_level = core_power->varibright_prop.varibright_hw_level; + + DC_TRACE_LEVEL_MESSAGEP(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Backlight_ABM, + ">ABM set level: level=%u -> (varibright_level=%u varibright_hw_level=%u) -> su->varibright_level=%u", + level, + core_power->varibright_prop.varibright_level, + core_power->varibright_prop.varibright_hw_level, + *stream_update->abm_level); + return true; +} + +bool mod_power_varibright_set_hw_level(struct mod_power *mod_power, unsigned int level, + struct dc_stream_update *stream_update) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (level == 0 || level == ABM_LEVEL_IMMEDIATE_DISABLE) + core_power->varibright_prop.varibright_active = 0; + else + core_power->varibright_prop.varibright_active = 1; + core_power->varibright_prop.varibright_hw_level = level; + *stream_update->abm_level = core_power->varibright_prop.varibright_hw_level; + + DC_TRACE_LEVEL_MESSAGEP(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Backlight_ABM, + ">ABM set level: level=%u -> (varibright_level=%u varibright_hw_level=%u) -> su->varibright_level=%u", + level, + core_power->varibright_prop.varibright_level, + core_power->varibright_prop.varibright_hw_level, + *stream_update->abm_level); + return true; +} + +bool mod_power_get_varibright_level(struct mod_power *mod_power, + unsigned int *varibright_level) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + *varibright_level = core_power->varibright_prop.varibright_level; + + DC_TRACE_LEVEL_MESSAGEP(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Backlight_ABM, + ">get varibright level: cp->varibright_level=%u", + *varibright_level); + return true; + +} + +bool mod_power_get_varibright_hw_level(struct mod_power *mod_power, + unsigned int *varibright_level) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + *varibright_level = core_power->varibright_prop.varibright_hw_level; + DC_TRACE_LEVEL_MESSAGEP(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Backlight_ABM, + ">get varibright HW level: hw_level=%u", + *varibright_level); + return true; +} + +bool mod_power_get_varibright_default_level(struct mod_power *mod_power, + unsigned int *varibright_level) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + *varibright_level = core_power->varibright_prop.def_varibright_level; + DC_TRACE_LEVEL_MESSAGEP(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Backlight_ABM, + ">get varibright default level: def_varibright_level=%u", + *varibright_level); + return true; +} + +bool mod_power_get_varibright_enable(struct mod_power *mod_power, + bool *varibright_enable) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + *varibright_enable = core_power->varibright_prop.varibright_user_enable; + DC_TRACE_LEVEL_MESSAGEP(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Backlight_ABM, + ">get varibright enable state: varibright_user_enable=%u", + (unsigned int) (*varibright_enable)); + return true; +} + +bool mod_power_is_abm_active(struct mod_power *mod_power, + const struct dc_link *link, + unsigned int inst) +{ + unsigned int user_backlight = 0; + unsigned int current_backlight = 0; + bool is_active = false; + + if (mod_power == NULL) + return false; + + mod_power_get_backlight_pwm(mod_power, &user_backlight, inst); + mod_power_get_hw_backlight_pwm(mod_power, link, ¤t_backlight); + + if (user_backlight != current_backlight) + is_active = true; + else + is_active = false; + DC_TRACE_LEVEL_MESSAGEP(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Backlight_ABM, + ">get ABM active state: is_active=%u (user_backlight_pwm=%u, current_backlight_pwm=%u)", + (unsigned int)is_active, + user_backlight, + current_backlight); + return is_active; +} + +static void fill_backlight_transform_table(struct dmcu_iram_parameters params, + struct iram_table_v_2 *table) +{ + unsigned int i; + unsigned int num_entries = NUM_BL_CURVE_SEGS; + unsigned int lut_index; + + table->backlight_thresholds[0] = 0; + ASSERT(params.backlight_lut_array[0] <= 0xFFFF); + table->backlight_offsets[0] = (uint16_t)params.backlight_lut_array[0]; + table->backlight_thresholds[num_entries-1] = 0xFFFF; + ASSERT(params.backlight_lut_array[params.backlight_lut_array_size - 1] <= 0xFFFF); + table->backlight_offsets[num_entries-1] = + (uint16_t)params.backlight_lut_array[params.backlight_lut_array_size - 1]; + + /* Setup all brightness levels between 0% and 100% exclusive + * Fills brightness-to-backlight transform table. Backlight custom curve + * describes transform from brightness to backlight. It will be defined + * as set of thresholds and set of offsets, together, implying + * extrapolation of custom curve into 16 uniformly spanned linear + * segments. Each threshold/offset represented by 16 bit entry in + * format U4.10. + */ + for (i = 1; i+1 < num_entries; i++) { + lut_index = (params.backlight_lut_array_size - 1) * i / (num_entries - 1); + + ASSERT(lut_index < params.backlight_lut_array_size); + + unsigned int threshold_val = DIV_ROUNDUP((i * 65536), num_entries); + unsigned int offset_val = params.backlight_lut_array[lut_index]; + + ASSERT(threshold_val <= 0xFFFF); + ASSERT(offset_val <= 0xFFFF); + + table->backlight_thresholds[i] = cpu_to_be16((uint16_t)threshold_val); + table->backlight_offsets[i] = cpu_to_be16((uint16_t)offset_val); + } +} + +static void fill_backlight_transform_table_v_2_2(struct dmcu_iram_parameters params, + struct iram_table_v_2_2 *table, bool big_endian) +{ + unsigned int i; + unsigned int num_entries = NUM_BL_CURVE_SEGS; + unsigned int lut_index; + + table->backlight_thresholds[0] = 0; + ASSERT(params.backlight_lut_array[0] <= 0xFFFF); + table->backlight_offsets[0] = (uint16_t)params.backlight_lut_array[0]; + table->backlight_thresholds[num_entries-1] = 0xFFFF; + ASSERT(params.backlight_lut_array[params.backlight_lut_array_size - 1] <= 0xFFFF); + table->backlight_offsets[num_entries-1] = + (uint16_t)params.backlight_lut_array[params.backlight_lut_array_size - 1]; + + /* Setup all brightness levels between 0% and 100% exclusive + * Fills brightness-to-backlight transform table. Backlight custom curve + * describes transform from brightness to backlight. It will be defined + * as set of thresholds and set of offsets, together, implying + * extrapolation of custom curve into 16 uniformly spanned linear + * segments. Each threshold/offset represented by 16 bit entry in + * format U4.10. + */ + for (i = 1; i+1 < num_entries; i++) { + lut_index = DIV_ROUNDUP((i * params.backlight_lut_array_size), num_entries); + ASSERT(lut_index < params.backlight_lut_array_size); + + unsigned int threshold_val = DIV_ROUNDUP((i * 65536), num_entries); + unsigned int offset_val = params.backlight_lut_array[lut_index]; + + ASSERT(threshold_val <= 0xFFFF); + ASSERT(offset_val <= 0xFFFF); + + table->backlight_thresholds[i] = (big_endian) ? + cpu_to_be16((uint16_t)threshold_val) : cpu_to_le16((uint16_t)threshold_val); + table->backlight_offsets[i] = (big_endian) ? + cpu_to_be16((uint16_t)offset_val) : cpu_to_le16((uint16_t)offset_val); + } +} + +static void fill_iram_v_2(struct iram_table_v_2 *ram_table, struct dmcu_iram_parameters params) +{ + unsigned int set = params.set; + + ram_table->min_abm_backlight = + cpu_to_be16(params.min_abm_backlight); + ram_table->deviation_gain = 0xb3; + + ram_table->blRampReduction = + cpu_to_be16(params.backlight_ramping_reduction); + ram_table->blRampStart = + cpu_to_be16(params.backlight_ramping_start); + + ram_table->min_reduction[0][0] = min_reduction_table[abm_config[set][0]]; + ram_table->min_reduction[1][0] = min_reduction_table[abm_config[set][0]]; + ram_table->min_reduction[2][0] = min_reduction_table[abm_config[set][0]]; + ram_table->min_reduction[3][0] = min_reduction_table[abm_config[set][0]]; + ram_table->min_reduction[4][0] = min_reduction_table[abm_config[set][0]]; + ram_table->max_reduction[0][0] = max_reduction_table[abm_config[set][0]]; + ram_table->max_reduction[1][0] = max_reduction_table[abm_config[set][0]]; + ram_table->max_reduction[2][0] = max_reduction_table[abm_config[set][0]]; + ram_table->max_reduction[3][0] = max_reduction_table[abm_config[set][0]]; + ram_table->max_reduction[4][0] = max_reduction_table[abm_config[set][0]]; + + ram_table->min_reduction[0][1] = min_reduction_table[abm_config[set][1]]; + ram_table->min_reduction[1][1] = min_reduction_table[abm_config[set][1]]; + ram_table->min_reduction[2][1] = min_reduction_table[abm_config[set][1]]; + ram_table->min_reduction[3][1] = min_reduction_table[abm_config[set][1]]; + ram_table->min_reduction[4][1] = min_reduction_table[abm_config[set][1]]; + ram_table->max_reduction[0][1] = max_reduction_table[abm_config[set][1]]; + ram_table->max_reduction[1][1] = max_reduction_table[abm_config[set][1]]; + ram_table->max_reduction[2][1] = max_reduction_table[abm_config[set][1]]; + ram_table->max_reduction[3][1] = max_reduction_table[abm_config[set][1]]; + ram_table->max_reduction[4][1] = max_reduction_table[abm_config[set][1]]; + + ram_table->min_reduction[0][2] = min_reduction_table[abm_config[set][2]]; + ram_table->min_reduction[1][2] = min_reduction_table[abm_config[set][2]]; + ram_table->min_reduction[2][2] = min_reduction_table[abm_config[set][2]]; + ram_table->min_reduction[3][2] = min_reduction_table[abm_config[set][2]]; + ram_table->min_reduction[4][2] = min_reduction_table[abm_config[set][2]]; + ram_table->max_reduction[0][2] = max_reduction_table[abm_config[set][2]]; + ram_table->max_reduction[1][2] = max_reduction_table[abm_config[set][2]]; + ram_table->max_reduction[2][2] = max_reduction_table[abm_config[set][2]]; + ram_table->max_reduction[3][2] = max_reduction_table[abm_config[set][2]]; + ram_table->max_reduction[4][2] = max_reduction_table[abm_config[set][2]]; + + ram_table->min_reduction[0][3] = min_reduction_table[abm_config[set][3]]; + ram_table->min_reduction[1][3] = min_reduction_table[abm_config[set][3]]; + ram_table->min_reduction[2][3] = min_reduction_table[abm_config[set][3]]; + ram_table->min_reduction[3][3] = min_reduction_table[abm_config[set][3]]; + ram_table->min_reduction[4][3] = min_reduction_table[abm_config[set][3]]; + ram_table->max_reduction[0][3] = max_reduction_table[abm_config[set][3]]; + ram_table->max_reduction[1][3] = max_reduction_table[abm_config[set][3]]; + ram_table->max_reduction[2][3] = max_reduction_table[abm_config[set][3]]; + ram_table->max_reduction[3][3] = max_reduction_table[abm_config[set][3]]; + ram_table->max_reduction[4][3] = max_reduction_table[abm_config[set][3]]; + + ram_table->bright_pos_gain[0][0] = 0x20; + ram_table->bright_pos_gain[0][1] = 0x20; + ram_table->bright_pos_gain[0][2] = 0x20; + ram_table->bright_pos_gain[0][3] = 0x20; + ram_table->bright_pos_gain[1][0] = 0x20; + ram_table->bright_pos_gain[1][1] = 0x20; + ram_table->bright_pos_gain[1][2] = 0x20; + ram_table->bright_pos_gain[1][3] = 0x20; + ram_table->bright_pos_gain[2][0] = 0x20; + ram_table->bright_pos_gain[2][1] = 0x20; + ram_table->bright_pos_gain[2][2] = 0x20; + ram_table->bright_pos_gain[2][3] = 0x20; + ram_table->bright_pos_gain[3][0] = 0x20; + ram_table->bright_pos_gain[3][1] = 0x20; + ram_table->bright_pos_gain[3][2] = 0x20; + ram_table->bright_pos_gain[3][3] = 0x20; + ram_table->bright_pos_gain[4][0] = 0x20; + ram_table->bright_pos_gain[4][1] = 0x20; + ram_table->bright_pos_gain[4][2] = 0x20; + ram_table->bright_pos_gain[4][3] = 0x20; + ram_table->bright_neg_gain[0][0] = 0x00; + ram_table->bright_neg_gain[0][1] = 0x00; + ram_table->bright_neg_gain[0][2] = 0x00; + ram_table->bright_neg_gain[0][3] = 0x00; + ram_table->bright_neg_gain[1][0] = 0x00; + ram_table->bright_neg_gain[1][1] = 0x00; + ram_table->bright_neg_gain[1][2] = 0x00; + ram_table->bright_neg_gain[1][3] = 0x00; + ram_table->bright_neg_gain[2][0] = 0x00; + ram_table->bright_neg_gain[2][1] = 0x00; + ram_table->bright_neg_gain[2][2] = 0x00; + ram_table->bright_neg_gain[2][3] = 0x00; + ram_table->bright_neg_gain[3][0] = 0x00; + ram_table->bright_neg_gain[3][1] = 0x00; + ram_table->bright_neg_gain[3][2] = 0x00; + ram_table->bright_neg_gain[3][3] = 0x00; + ram_table->bright_neg_gain[4][0] = 0x00; + ram_table->bright_neg_gain[4][1] = 0x00; + ram_table->bright_neg_gain[4][2] = 0x00; + ram_table->bright_neg_gain[4][3] = 0x00; + ram_table->dark_pos_gain[0][0] = 0x00; + ram_table->dark_pos_gain[0][1] = 0x00; + ram_table->dark_pos_gain[0][2] = 0x00; + ram_table->dark_pos_gain[0][3] = 0x00; + ram_table->dark_pos_gain[1][0] = 0x00; + ram_table->dark_pos_gain[1][1] = 0x00; + ram_table->dark_pos_gain[1][2] = 0x00; + ram_table->dark_pos_gain[1][3] = 0x00; + ram_table->dark_pos_gain[2][0] = 0x00; + ram_table->dark_pos_gain[2][1] = 0x00; + ram_table->dark_pos_gain[2][2] = 0x00; + ram_table->dark_pos_gain[2][3] = 0x00; + ram_table->dark_pos_gain[3][0] = 0x00; + ram_table->dark_pos_gain[3][1] = 0x00; + ram_table->dark_pos_gain[3][2] = 0x00; + ram_table->dark_pos_gain[3][3] = 0x00; + ram_table->dark_pos_gain[4][0] = 0x00; + ram_table->dark_pos_gain[4][1] = 0x00; + ram_table->dark_pos_gain[4][2] = 0x00; + ram_table->dark_pos_gain[4][3] = 0x00; + ram_table->dark_neg_gain[0][0] = 0x00; + ram_table->dark_neg_gain[0][1] = 0x00; + ram_table->dark_neg_gain[0][2] = 0x00; + ram_table->dark_neg_gain[0][3] = 0x00; + ram_table->dark_neg_gain[1][0] = 0x00; + ram_table->dark_neg_gain[1][1] = 0x00; + ram_table->dark_neg_gain[1][2] = 0x00; + ram_table->dark_neg_gain[1][3] = 0x00; + ram_table->dark_neg_gain[2][0] = 0x00; + ram_table->dark_neg_gain[2][1] = 0x00; + ram_table->dark_neg_gain[2][2] = 0x00; + ram_table->dark_neg_gain[2][3] = 0x00; + ram_table->dark_neg_gain[3][0] = 0x00; + ram_table->dark_neg_gain[3][1] = 0x00; + ram_table->dark_neg_gain[3][2] = 0x00; + ram_table->dark_neg_gain[3][3] = 0x00; + ram_table->dark_neg_gain[4][0] = 0x00; + ram_table->dark_neg_gain[4][1] = 0x00; + ram_table->dark_neg_gain[4][2] = 0x00; + ram_table->dark_neg_gain[4][3] = 0x00; + + ram_table->iir_curve[0] = 0x65; + ram_table->iir_curve[1] = 0x65; + ram_table->iir_curve[2] = 0x65; + ram_table->iir_curve[3] = 0x65; + ram_table->iir_curve[4] = 0x65; + + //Gamma 2.4 + ram_table->crgb_thresh[0] = cpu_to_be16(0x13b6); + ram_table->crgb_thresh[1] = cpu_to_be16(0x1648); + ram_table->crgb_thresh[2] = cpu_to_be16(0x18e3); + ram_table->crgb_thresh[3] = cpu_to_be16(0x1b41); + ram_table->crgb_thresh[4] = cpu_to_be16(0x1d46); + ram_table->crgb_thresh[5] = cpu_to_be16(0x1f21); + ram_table->crgb_thresh[6] = cpu_to_be16(0x2167); + ram_table->crgb_thresh[7] = cpu_to_be16(0x2384); + ram_table->crgb_offset[0] = cpu_to_be16(0x2999); + ram_table->crgb_offset[1] = cpu_to_be16(0x3999); + ram_table->crgb_offset[2] = cpu_to_be16(0x4666); + ram_table->crgb_offset[3] = cpu_to_be16(0x5999); + ram_table->crgb_offset[4] = cpu_to_be16(0x6333); + ram_table->crgb_offset[5] = cpu_to_be16(0x7800); + ram_table->crgb_offset[6] = cpu_to_be16(0x8c00); + ram_table->crgb_offset[7] = cpu_to_be16(0xa000); + ram_table->crgb_slope[0] = cpu_to_be16(0x3147); + ram_table->crgb_slope[1] = cpu_to_be16(0x2978); + ram_table->crgb_slope[2] = cpu_to_be16(0x23a2); + ram_table->crgb_slope[3] = cpu_to_be16(0x1f55); + ram_table->crgb_slope[4] = cpu_to_be16(0x1c63); + ram_table->crgb_slope[5] = cpu_to_be16(0x1a0f); + ram_table->crgb_slope[6] = cpu_to_be16(0x178d); + ram_table->crgb_slope[7] = cpu_to_be16(0x15ab); + + fill_backlight_transform_table( + params, ram_table); +} + +static void fill_iram_v_2_2(struct iram_table_v_2_2 *ram_table, struct dmcu_iram_parameters params) +{ + unsigned int set = params.set; + + ram_table->flags = 0x0; + + ram_table->min_abm_backlight = + cpu_to_be16(params.min_abm_backlight); + + ram_table->deviation_gain[0] = 0xb3; + ram_table->deviation_gain[1] = 0xa8; + ram_table->deviation_gain[2] = 0x98; + ram_table->deviation_gain[3] = 0x68; + + ram_table->min_reduction[0][0] = min_reduction_table_v_2_2[abm_config[set][0]]; + ram_table->min_reduction[1][0] = min_reduction_table_v_2_2[abm_config[set][0]]; + ram_table->min_reduction[2][0] = min_reduction_table_v_2_2[abm_config[set][0]]; + ram_table->min_reduction[3][0] = min_reduction_table_v_2_2[abm_config[set][0]]; + ram_table->min_reduction[4][0] = min_reduction_table_v_2_2[abm_config[set][0]]; + ram_table->max_reduction[0][0] = max_reduction_table_v_2_2[abm_config[set][0]]; + ram_table->max_reduction[1][0] = max_reduction_table_v_2_2[abm_config[set][0]]; + ram_table->max_reduction[2][0] = max_reduction_table_v_2_2[abm_config[set][0]]; + ram_table->max_reduction[3][0] = max_reduction_table_v_2_2[abm_config[set][0]]; + ram_table->max_reduction[4][0] = max_reduction_table_v_2_2[abm_config[set][0]]; + + ram_table->min_reduction[0][1] = min_reduction_table_v_2_2[abm_config[set][1]]; + ram_table->min_reduction[1][1] = min_reduction_table_v_2_2[abm_config[set][1]]; + ram_table->min_reduction[2][1] = min_reduction_table_v_2_2[abm_config[set][1]]; + ram_table->min_reduction[3][1] = min_reduction_table_v_2_2[abm_config[set][1]]; + ram_table->min_reduction[4][1] = min_reduction_table_v_2_2[abm_config[set][1]]; + ram_table->max_reduction[0][1] = max_reduction_table_v_2_2[abm_config[set][1]]; + ram_table->max_reduction[1][1] = max_reduction_table_v_2_2[abm_config[set][1]]; + ram_table->max_reduction[2][1] = max_reduction_table_v_2_2[abm_config[set][1]]; + ram_table->max_reduction[3][1] = max_reduction_table_v_2_2[abm_config[set][1]]; + ram_table->max_reduction[4][1] = max_reduction_table_v_2_2[abm_config[set][1]]; + + ram_table->min_reduction[0][2] = min_reduction_table_v_2_2[abm_config[set][2]]; + ram_table->min_reduction[1][2] = min_reduction_table_v_2_2[abm_config[set][2]]; + ram_table->min_reduction[2][2] = min_reduction_table_v_2_2[abm_config[set][2]]; + ram_table->min_reduction[3][2] = min_reduction_table_v_2_2[abm_config[set][2]]; + ram_table->min_reduction[4][2] = min_reduction_table_v_2_2[abm_config[set][2]]; + ram_table->max_reduction[0][2] = max_reduction_table_v_2_2[abm_config[set][2]]; + ram_table->max_reduction[1][2] = max_reduction_table_v_2_2[abm_config[set][2]]; + ram_table->max_reduction[2][2] = max_reduction_table_v_2_2[abm_config[set][2]]; + ram_table->max_reduction[3][2] = max_reduction_table_v_2_2[abm_config[set][2]]; + ram_table->max_reduction[4][2] = max_reduction_table_v_2_2[abm_config[set][2]]; + + ram_table->min_reduction[0][3] = min_reduction_table_v_2_2[abm_config[set][3]]; + ram_table->min_reduction[1][3] = min_reduction_table_v_2_2[abm_config[set][3]]; + ram_table->min_reduction[2][3] = min_reduction_table_v_2_2[abm_config[set][3]]; + ram_table->min_reduction[3][3] = min_reduction_table_v_2_2[abm_config[set][3]]; + ram_table->min_reduction[4][3] = min_reduction_table_v_2_2[abm_config[set][3]]; + ram_table->max_reduction[0][3] = max_reduction_table_v_2_2[abm_config[set][3]]; + ram_table->max_reduction[1][3] = max_reduction_table_v_2_2[abm_config[set][3]]; + ram_table->max_reduction[2][3] = max_reduction_table_v_2_2[abm_config[set][3]]; + ram_table->max_reduction[3][3] = max_reduction_table_v_2_2[abm_config[set][3]]; + ram_table->max_reduction[4][3] = max_reduction_table_v_2_2[abm_config[set][3]]; + + ram_table->bright_pos_gain[0][0] = 0x20; + ram_table->bright_pos_gain[0][1] = 0x20; + ram_table->bright_pos_gain[0][2] = 0x20; + ram_table->bright_pos_gain[0][3] = 0x20; + ram_table->bright_pos_gain[1][0] = 0x20; + ram_table->bright_pos_gain[1][1] = 0x20; + ram_table->bright_pos_gain[1][2] = 0x20; + ram_table->bright_pos_gain[1][3] = 0x20; + ram_table->bright_pos_gain[2][0] = 0x20; + ram_table->bright_pos_gain[2][1] = 0x20; + ram_table->bright_pos_gain[2][2] = 0x20; + ram_table->bright_pos_gain[2][3] = 0x20; + ram_table->bright_pos_gain[3][0] = 0x20; + ram_table->bright_pos_gain[3][1] = 0x20; + ram_table->bright_pos_gain[3][2] = 0x20; + ram_table->bright_pos_gain[3][3] = 0x20; + ram_table->bright_pos_gain[4][0] = 0x20; + ram_table->bright_pos_gain[4][1] = 0x20; + ram_table->bright_pos_gain[4][2] = 0x20; + ram_table->bright_pos_gain[4][3] = 0x20; + + ram_table->dark_pos_gain[0][0] = 0x00; + ram_table->dark_pos_gain[0][1] = 0x00; + ram_table->dark_pos_gain[0][2] = 0x00; + ram_table->dark_pos_gain[0][3] = 0x00; + ram_table->dark_pos_gain[1][0] = 0x00; + ram_table->dark_pos_gain[1][1] = 0x00; + ram_table->dark_pos_gain[1][2] = 0x00; + ram_table->dark_pos_gain[1][3] = 0x00; + ram_table->dark_pos_gain[2][0] = 0x00; + ram_table->dark_pos_gain[2][1] = 0x00; + ram_table->dark_pos_gain[2][2] = 0x00; + ram_table->dark_pos_gain[2][3] = 0x00; + ram_table->dark_pos_gain[3][0] = 0x00; + ram_table->dark_pos_gain[3][1] = 0x00; + ram_table->dark_pos_gain[3][2] = 0x00; + ram_table->dark_pos_gain[3][3] = 0x00; + ram_table->dark_pos_gain[4][0] = 0x00; + ram_table->dark_pos_gain[4][1] = 0x00; + ram_table->dark_pos_gain[4][2] = 0x00; + ram_table->dark_pos_gain[4][3] = 0x00; + + ram_table->hybrid_factor[0] = 0xff; + ram_table->hybrid_factor[1] = 0xff; + ram_table->hybrid_factor[2] = 0xff; + ram_table->hybrid_factor[3] = 0xc0; + + ram_table->contrast_factor[0] = 0x99; + ram_table->contrast_factor[1] = 0x99; + ram_table->contrast_factor[2] = 0x90; + ram_table->contrast_factor[3] = 0x80; + + ram_table->iir_curve[0] = 0x65; + ram_table->iir_curve[1] = 0x65; + ram_table->iir_curve[2] = 0x65; + ram_table->iir_curve[3] = 0x65; + ram_table->iir_curve[4] = 0x65; + + //Gamma 2.2 + ram_table->crgb_thresh[0] = cpu_to_be16(0x127c); + ram_table->crgb_thresh[1] = cpu_to_be16(0x151b); + ram_table->crgb_thresh[2] = cpu_to_be16(0x17d5); + ram_table->crgb_thresh[3] = cpu_to_be16(0x1a56); + ram_table->crgb_thresh[4] = cpu_to_be16(0x1c83); + ram_table->crgb_thresh[5] = cpu_to_be16(0x1e72); + ram_table->crgb_thresh[6] = cpu_to_be16(0x20f0); + ram_table->crgb_thresh[7] = cpu_to_be16(0x232b); + ram_table->crgb_offset[0] = cpu_to_be16(0x2999); + ram_table->crgb_offset[1] = cpu_to_be16(0x3999); + ram_table->crgb_offset[2] = cpu_to_be16(0x4666); + ram_table->crgb_offset[3] = cpu_to_be16(0x5999); + ram_table->crgb_offset[4] = cpu_to_be16(0x6333); + ram_table->crgb_offset[5] = cpu_to_be16(0x7800); + ram_table->crgb_offset[6] = cpu_to_be16(0x8c00); + ram_table->crgb_offset[7] = cpu_to_be16(0xa000); + ram_table->crgb_slope[0] = cpu_to_be16(0x3609); + ram_table->crgb_slope[1] = cpu_to_be16(0x2dfa); + ram_table->crgb_slope[2] = cpu_to_be16(0x27ea); + ram_table->crgb_slope[3] = cpu_to_be16(0x235d); + ram_table->crgb_slope[4] = cpu_to_be16(0x2042); + ram_table->crgb_slope[5] = cpu_to_be16(0x1dc3); + ram_table->crgb_slope[6] = cpu_to_be16(0x1b1a); + ram_table->crgb_slope[7] = cpu_to_be16(0x1910); + + fill_backlight_transform_table_v_2_2( + params, ram_table, true); +} + +static void fill_iram_v_2_3(struct iram_table_v_2_2 *ram_table, struct dmcu_iram_parameters params, bool big_endian) +{ + unsigned int i, j; + unsigned int set = params.set; + + ram_table->flags = 0x0; + ram_table->min_abm_backlight = (uint16_t)((big_endian) ? + cpu_to_be16(params.min_abm_backlight) : + cpu_to_le16(params.min_abm_backlight)); + + for (i = 0; i < NUM_AGGR_LEVEL; i++) { + ram_table->hybrid_factor[i] = (uint8_t)abm_settings[set][i].brightness_gain; + ram_table->contrast_factor[i] = abm_settings[set][i].contrast_factor; + ram_table->deviation_gain[i] = abm_settings[set][i].deviation_gain; + ram_table->min_knee[i] = abm_settings[set][i].min_knee; + ram_table->max_knee[i] = abm_settings[set][i].max_knee; + + for (j = 0; j < NUM_AMBI_LEVEL; j++) { + ram_table->min_reduction[j][i] = abm_settings[set][i].min_reduction; + ram_table->max_reduction[j][i] = abm_settings[set][i].max_reduction; + ram_table->bright_pos_gain[j][i] = abm_settings[set][i].bright_pos_gain; + ram_table->dark_pos_gain[j][i] = abm_settings[set][i].dark_pos_gain; + } + } + + ram_table->iir_curve[0] = 0x65; + ram_table->iir_curve[1] = 0x65; + ram_table->iir_curve[2] = 0x65; + ram_table->iir_curve[3] = 0x65; + ram_table->iir_curve[4] = 0x65; + + //Gamma 2.2 + ram_table->crgb_thresh[0] = bswap16_based_on_endian(big_endian, 0x127c); + ram_table->crgb_thresh[1] = bswap16_based_on_endian(big_endian, 0x151b); + ram_table->crgb_thresh[2] = bswap16_based_on_endian(big_endian, 0x17d5); + ram_table->crgb_thresh[3] = bswap16_based_on_endian(big_endian, 0x1a56); + ram_table->crgb_thresh[4] = bswap16_based_on_endian(big_endian, 0x1c83); + ram_table->crgb_thresh[5] = bswap16_based_on_endian(big_endian, 0x1e72); + ram_table->crgb_thresh[6] = bswap16_based_on_endian(big_endian, 0x20f0); + ram_table->crgb_thresh[7] = bswap16_based_on_endian(big_endian, 0x232b); + ram_table->crgb_offset[0] = bswap16_based_on_endian(big_endian, 0x2999); + ram_table->crgb_offset[1] = bswap16_based_on_endian(big_endian, 0x3999); + ram_table->crgb_offset[2] = bswap16_based_on_endian(big_endian, 0x4666); + ram_table->crgb_offset[3] = bswap16_based_on_endian(big_endian, 0x5999); + ram_table->crgb_offset[4] = bswap16_based_on_endian(big_endian, 0x6333); + ram_table->crgb_offset[5] = bswap16_based_on_endian(big_endian, 0x7800); + ram_table->crgb_offset[6] = bswap16_based_on_endian(big_endian, 0x8c00); + ram_table->crgb_offset[7] = bswap16_based_on_endian(big_endian, 0xa000); + ram_table->crgb_slope[0] = bswap16_based_on_endian(big_endian, 0x3609); + ram_table->crgb_slope[1] = bswap16_based_on_endian(big_endian, 0x2dfa); + ram_table->crgb_slope[2] = bswap16_based_on_endian(big_endian, 0x27ea); + ram_table->crgb_slope[3] = bswap16_based_on_endian(big_endian, 0x235d); + ram_table->crgb_slope[4] = bswap16_based_on_endian(big_endian, 0x2042); + ram_table->crgb_slope[5] = bswap16_based_on_endian(big_endian, 0x1dc3); + ram_table->crgb_slope[6] = bswap16_based_on_endian(big_endian, 0x1b1a); + ram_table->crgb_slope[7] = bswap16_based_on_endian(big_endian, 0x1910); + + fill_backlight_transform_table_v_2_2( + params, ram_table, big_endian); +} + +bool dmub_init_abm_config(struct resource_pool *res_pool, + struct dmcu_iram_parameters params, + unsigned int inst) +{ + struct iram_table_v_2_2 ram_table; + struct abm_config_table config; + unsigned int set = params.set; + bool result = false; + uint32_t i, j = 0; + + if (res_pool->abm == NULL && res_pool->multiple_abms[inst] == NULL) + return false; + + memset(&ram_table, 0, sizeof(ram_table)); + memset(&config, 0, sizeof(config)); + + fill_iram_v_2_3(&ram_table, params, false); + + // We must copy to structure that is aligned to 32-bit + for (i = 0; i < NUM_POWER_FN_SEGS; i++) { + config.crgb_thresh[i] = ram_table.crgb_thresh[i]; + config.crgb_offset[i] = ram_table.crgb_offset[i]; + config.crgb_slope[i] = ram_table.crgb_slope[i]; + } + + for (i = 0; i < NUM_BL_CURVE_SEGS; i++) { + config.backlight_thresholds[i] = ram_table.backlight_thresholds[i]; + config.backlight_offsets[i] = ram_table.backlight_offsets[i]; + } + + for (i = 0; i < NUM_AMBI_LEVEL; i++) + config.iir_curve[i] = ram_table.iir_curve[i]; + + for (i = 0; i < NUM_AMBI_LEVEL; i++) { + for (j = 0; j < NUM_AGGR_LEVEL; j++) { + config.min_reduction[i][j] = ram_table.min_reduction[i][j]; + config.max_reduction[i][j] = ram_table.max_reduction[i][j]; + config.bright_pos_gain[i][j] = ram_table.bright_pos_gain[i][j]; + config.dark_pos_gain[i][j] = ram_table.dark_pos_gain[i][j]; + } + } + + for (i = 0; i < NUM_AGGR_LEVEL; i++) { + config.hybrid_factor[i] = ram_table.hybrid_factor[i]; + config.contrast_factor[i] = ram_table.contrast_factor[i]; + config.deviation_gain[i] = ram_table.deviation_gain[i]; + config.min_knee[i] = ram_table.min_knee[i]; + config.max_knee[i] = ram_table.max_knee[i]; + } + + if (params.backlight_ramping_override) { + + ASSERT(params.backlight_ramping_reduction <= 0xFFFF); + ASSERT(params.backlight_ramping_start <= 0xFFFF); + for (i = 0; i < NUM_AGGR_LEVEL; i++) { + config.blRampReduction[i] = (uint16_t)params.backlight_ramping_reduction; + config.blRampStart[i] = (uint16_t)params.backlight_ramping_start; + } + } else { + for (i = 0; i < NUM_AGGR_LEVEL; i++) { + config.blRampReduction[i] = abm_settings[set][i].blRampReduction; + config.blRampStart[i] = abm_settings[set][i].blRampStart; + } + } + + config.min_abm_backlight = ram_table.min_abm_backlight; + + if (res_pool->multiple_abms[inst]) { + result = res_pool->multiple_abms[inst]->funcs->init_abm_config( + res_pool->multiple_abms[inst], (char *)(&config), sizeof(struct abm_config_table), inst); + } else + result = res_pool->abm->funcs->init_abm_config( + res_pool->abm, (char *)(&config), sizeof(struct abm_config_table), 0); + + return result; +} + +bool dmcu_load_iram(struct dmcu *dmcu, + struct dmcu_iram_parameters params) +{ + unsigned char ram_table[IRAM_SIZE]; + bool result = false; + + if (dmcu == NULL) + return false; + + if (dmcu && !dmcu->funcs->is_dmcu_initialized(dmcu)) + return true; + + memset(&ram_table, 0, sizeof(ram_table)); + + if (dmcu->dmcu_version.abm_version == 0x24) { + fill_iram_v_2_3((struct iram_table_v_2_2 *)ram_table, params, true); + result = dmcu->funcs->load_iram(dmcu, 0, (char *)(&ram_table), + IRAM_RESERVE_AREA_START_V2_2); + } else if (dmcu->dmcu_version.abm_version == 0x23) { + fill_iram_v_2_3((struct iram_table_v_2_2 *)ram_table, params, true); + + result = dmcu->funcs->load_iram( + dmcu, 0, (char *)(&ram_table), IRAM_RESERVE_AREA_START_V2_2); + } else if (dmcu->dmcu_version.abm_version == 0x22) { + fill_iram_v_2_2((struct iram_table_v_2_2 *)ram_table, params); + + result = dmcu->funcs->load_iram( + dmcu, 0, (char *)(&ram_table), IRAM_RESERVE_AREA_START_V2_2); + } else { + fill_iram_v_2((struct iram_table_v_2 *)ram_table, params); + + result = dmcu->funcs->load_iram( + dmcu, 0, (char *)(&ram_table), IRAM_RESERVE_AREA_START_V2); + + if (result) + result = dmcu->funcs->load_iram( + dmcu, IRAM_RESERVE_AREA_END_V2 + 1, + (char *)(&ram_table) + IRAM_RESERVE_AREA_END_V2 + 1, + sizeof(ram_table) - IRAM_RESERVE_AREA_END_V2 - 1); + } + + return result; +} + +bool fill_custom_backlight_caps(unsigned int config_no, struct dm_acpi_atif_backlight_caps *caps) +{ + unsigned int data_points_size; + uint64_t caps_size; + + if (config_no >= ARRAY_SIZE(custom_backlight_profiles)) + return false; + + data_points_size = custom_backlight_profiles[config_no].num_data_points + * sizeof(custom_backlight_profiles[config_no].data_points[0]); + + caps_size = sizeof(struct dm_acpi_atif_backlight_caps) - sizeof(caps->data_points) + data_points_size; + ASSERT(caps_size <= 0xFFFF); + caps->size = (uint16_t)caps_size; + caps->flags = 0; + caps->error_code = 0; + caps->ac_level_percentage = custom_backlight_profiles[config_no].ac_level_percentage; + caps->dc_level_percentage = custom_backlight_profiles[config_no].dc_level_percentage; + caps->min_input_signal = custom_backlight_profiles[config_no].min_input_signal; + caps->max_input_signal = custom_backlight_profiles[config_no].max_input_signal; + caps->num_data_points = (uint8_t)custom_backlight_profiles[config_no].num_data_points; + memcpy(caps->data_points, custom_backlight_profiles[config_no].data_points, data_points_size); + return true; +} diff --git a/drivers/gpu/drm/amd/display/modules/power/power_helpers.c b/drivers/gpu/drm/amd/display/modules/power/power_helpers.c index 5d444e9eb38f..bf0c5901b4ee 100644 --- a/drivers/gpu/drm/amd/display/modules/power/power_helpers.c +++ b/drivers/gpu/drm/amd/display/modules/power/power_helpers.c @@ -33,1072 +33,7 @@ #define bswap16_based_on_endian(big_endian, value) \ ((big_endian) ? cpu_to_be16(value) : cpu_to_le16(value)) -/* Possible Min Reduction config from least aggressive to most aggressive - * 0 1 2 3 4 5 6 7 8 9 10 11 12 - * 100 98.0 94.1 94.1 85.1 80.3 75.3 69.4 60.0 57.6 50.2 49.8 40.0 % - */ -static const unsigned char min_reduction_table[13] = { -0xff, 0xfa, 0xf0, 0xf0, 0xd9, 0xcd, 0xc0, 0xb1, 0x99, 0x93, 0x80, 0x82, 0x66}; - -/* Possible Max Reduction configs from least aggressive to most aggressive - * 0 1 2 3 4 5 6 7 8 9 10 11 12 - * 96.1 89.8 85.1 80.3 69.4 64.7 64.7 50.2 39.6 30.2 30.2 30.2 19.6 % - */ -static const unsigned char max_reduction_table[13] = { -0xf5, 0xe5, 0xd9, 0xcd, 0xb1, 0xa5, 0xa5, 0x80, 0x65, 0x4d, 0x4d, 0x4d, 0x32}; - -/* Possible ABM 2.2 Min Reduction configs from least aggressive to most aggressive - * 0 1 2 3 4 5 6 7 8 9 10 11 12 - * 100 100 100 100 100 100 100 100 100 92.2 83.1 75.3 75.3 % - */ -static const unsigned char min_reduction_table_v_2_2[13] = { -0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xeb, 0xd4, 0xc0, 0xc0}; - -/* Possible ABM 2.2 Max Reduction configs from least aggressive to most aggressive - * 0 1 2 3 4 5 6 7 8 9 10 11 12 - * 96.1 89.8 74.9 69.4 64.7 52.2 48.6 39.6 30.2 25.1 19.6 12.5 12.5 % - */ -static const unsigned char max_reduction_table_v_2_2[13] = { -0xf5, 0xe5, 0xbf, 0xb1, 0xa5, 0x85, 0x7c, 0x65, 0x4d, 0x40, 0x32, 0x20, 0x20}; - -/* Predefined ABM configuration sets. We may have different configuration sets - * in order to satisfy different power/quality requirements. - */ -static const unsigned char abm_config[abm_defines_max_config][abm_defines_max_level] = { -/* ABM Level 1, ABM Level 2, ABM Level 3, ABM Level 4 */ -{ 2, 5, 7, 8 }, /* Default - Medium aggressiveness */ -{ 2, 5, 8, 11 }, /* Alt #1 - Increased aggressiveness */ -{ 0, 2, 4, 8 }, /* Alt #2 - Minimal aggressiveness */ -{ 3, 6, 10, 12 }, /* Alt #3 - Super aggressiveness */ -}; - -struct abm_parameters { - unsigned char min_reduction; - unsigned char max_reduction; - unsigned char bright_pos_gain; - unsigned char dark_pos_gain; - unsigned char brightness_gain; - unsigned char contrast_factor; - unsigned char deviation_gain; - unsigned char min_knee; - unsigned char max_knee; - unsigned short blRampReduction; - unsigned short blRampStart; -}; - -static const struct abm_parameters abm_settings_config0[abm_defines_max_level] = { -// min_red max_red bright_pos dark_pos bright_gain contrast dev min_knee max_knee blRed blStart - {0xff, 0xbf, 0x20, 0x00, 0xff, 0x99, 0xb3, 0x40, 0xe0, 0xf777, 0xcccc}, - {0xde, 0x85, 0x20, 0x00, 0xe0, 0x90, 0xa8, 0x40, 0xc8, 0xf777, 0xcccc}, - {0xb0, 0x50, 0x20, 0x00, 0xc0, 0x88, 0x78, 0x70, 0xa0, 0xeeee, 0x9999}, - {0x82, 0x40, 0x20, 0x00, 0x00, 0xb8, 0xb3, 0x70, 0x70, 0xe333, 0xb333}, -}; - -static const struct abm_parameters abm_settings_config1[abm_defines_max_level] = { -// min_red max_red bright_pos dark_pos bright_gain contrast dev min_knee max_knee blRed blStart - {0xf0, 0xd9, 0x20, 0x00, 0x00, 0xff, 0xb3, 0x70, 0x70, 0xcccc, 0xcccc}, - {0xcd, 0xa5, 0x20, 0x00, 0x00, 0xff, 0xb3, 0x70, 0x70, 0xcccc, 0xcccc}, - {0x99, 0x65, 0x20, 0x00, 0x00, 0xff, 0xb3, 0x70, 0x70, 0xcccc, 0xcccc}, - {0x82, 0x4d, 0x20, 0x00, 0x00, 0xff, 0xb3, 0x70, 0x70, 0xcccc, 0xcccc}, -}; - -static const struct abm_parameters abm_settings_config2[abm_defines_max_level] = { -// min_red max_red bright_pos dark_pos bright_gain contrast dev min_knee max_knee blRed blStart - {0xf0, 0xbf, 0x20, 0x00, 0x88, 0x99, 0xb3, 0x40, 0xe0, 0x0000, 0xcccc}, - {0xd8, 0x85, 0x20, 0x00, 0x70, 0x90, 0xa8, 0x40, 0xc8, 0x0700, 0xb333}, - {0xb8, 0x58, 0x20, 0x00, 0x64, 0x88, 0x78, 0x70, 0xa0, 0x7000, 0x9999}, - {0x82, 0x40, 0x20, 0x00, 0x00, 0xb8, 0xb3, 0x70, 0x70, 0xc333, 0xb333}, -}; - -static const struct abm_parameters * const abm_settings[] = { - abm_settings_config0, - abm_settings_config1, - abm_settings_config2, -}; - -static const struct dm_bl_data_point custom_backlight_curve0[] = { - {2, 14}, {4, 16}, {6, 18}, {8, 21}, {10, 23}, {12, 26}, {14, 29}, {16, 32}, {18, 35}, - {20, 38}, {22, 41}, {24, 44}, {26, 48}, {28, 52}, {30, 55}, {32, 59}, {34, 62}, - {36, 67}, {38, 71}, {40, 75}, {42, 80}, {44, 84}, {46, 88}, {48, 93}, {50, 98}, - {52, 103}, {54, 108}, {56, 113}, {58, 118}, {60, 123}, {62, 129}, {64, 135}, {66, 140}, - {68, 146}, {70, 152}, {72, 158}, {74, 164}, {76, 171}, {78, 177}, {80, 183}, {82, 190}, - {84, 197}, {86, 204}, {88, 211}, {90, 218}, {92, 225}, {94, 232}, {96, 240}, {98, 247}}; - -struct custom_backlight_profile { - uint8_t ac_level_percentage; - uint8_t dc_level_percentage; - uint8_t min_input_signal; - uint8_t max_input_signal; - uint8_t num_data_points; - const struct dm_bl_data_point *data_points; -}; - -static const struct custom_backlight_profile custom_backlight_profiles[] = { - {100, 32, 12, 255, ARRAY_SIZE(custom_backlight_curve0), custom_backlight_curve0}, -}; - -#define NUM_AMBI_LEVEL 5 -#define NUM_AGGR_LEVEL 4 -#define NUM_POWER_FN_SEGS 8 -#define NUM_BL_CURVE_SEGS 16 -#define IRAM_SIZE 256 - -#define IRAM_RESERVE_AREA_START_V2 0xF0 // reserve 0xF0~0xF6 are write by DMCU only -#define IRAM_RESERVE_AREA_END_V2 0xF6 // reserve 0xF0~0xF6 are write by DMCU only - -#define IRAM_RESERVE_AREA_START_V2_2 0xF0 // reserve 0xF0~0xFF are write by DMCU only -#define IRAM_RESERVE_AREA_END_V2_2 0xFF // reserve 0xF0~0xFF are write by DMCU only - -#pragma pack(push, 1) -/* NOTE: iRAM is 256B in size */ -struct iram_table_v_2 { - /* flags */ - uint16_t min_abm_backlight; /* 0x00 U16 */ - - /* parameters for ABM2.0 algorithm */ - uint8_t min_reduction[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x02 U0.8 */ - uint8_t max_reduction[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x16 U0.8 */ - uint8_t bright_pos_gain[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x2a U2.6 */ - uint8_t bright_neg_gain[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x3e U2.6 */ - uint8_t dark_pos_gain[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x52 U2.6 */ - uint8_t dark_neg_gain[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x66 U2.6 */ - uint8_t iir_curve[NUM_AMBI_LEVEL]; /* 0x7a U0.8 */ - uint8_t deviation_gain; /* 0x7f U0.8 */ - - /* parameters for crgb conversion */ - uint16_t crgb_thresh[NUM_POWER_FN_SEGS]; /* 0x80 U3.13 */ - uint16_t crgb_offset[NUM_POWER_FN_SEGS]; /* 0x90 U1.15 */ - uint16_t crgb_slope[NUM_POWER_FN_SEGS]; /* 0xa0 U4.12 */ - - /* parameters for custom curve */ - /* thresholds for brightness --> backlight */ - uint16_t backlight_thresholds[NUM_BL_CURVE_SEGS]; /* 0xb0 U16.0 */ - /* offsets for brightness --> backlight */ - uint16_t backlight_offsets[NUM_BL_CURVE_SEGS]; /* 0xd0 U16.0 */ - - /* For reading PSR State directly from IRAM */ - uint8_t psr_state; /* 0xf0 */ - uint8_t dmcu_mcp_interface_version; /* 0xf1 */ - uint8_t dmcu_abm_feature_version; /* 0xf2 */ - uint8_t dmcu_psr_feature_version; /* 0xf3 */ - uint16_t dmcu_version; /* 0xf4 */ - uint8_t dmcu_state; /* 0xf6 */ - - uint16_t blRampReduction; /* 0xf7 */ - uint16_t blRampStart; /* 0xf9 */ - uint8_t dummy5; /* 0xfb */ - uint8_t dummy6; /* 0xfc */ - uint8_t dummy7; /* 0xfd */ - uint8_t dummy8; /* 0xfe */ - uint8_t dummy9; /* 0xff */ -}; - -struct iram_table_v_2_2 { - /* flags */ - uint16_t flags; /* 0x00 U16 */ - - /* parameters for ABM2.2 algorithm */ - uint8_t min_reduction[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x02 U0.8 */ - uint8_t max_reduction[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x16 U0.8 */ - uint8_t bright_pos_gain[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x2a U2.6 */ - uint8_t dark_pos_gain[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x3e U2.6 */ - uint8_t hybrid_factor[NUM_AGGR_LEVEL]; /* 0x52 U0.8 */ - uint8_t contrast_factor[NUM_AGGR_LEVEL]; /* 0x56 U0.8 */ - uint8_t deviation_gain[NUM_AGGR_LEVEL]; /* 0x5a U0.8 */ - uint8_t iir_curve[NUM_AMBI_LEVEL]; /* 0x5e U0.8 */ - uint8_t min_knee[NUM_AGGR_LEVEL]; /* 0x63 U0.8 */ - uint8_t max_knee[NUM_AGGR_LEVEL]; /* 0x67 U0.8 */ - uint16_t min_abm_backlight; /* 0x6b U16 */ - uint8_t pad[19]; /* 0x6d U0.8 */ - - /* parameters for crgb conversion */ - uint16_t crgb_thresh[NUM_POWER_FN_SEGS]; /* 0x80 U3.13 */ - uint16_t crgb_offset[NUM_POWER_FN_SEGS]; /* 0x90 U1.15 */ - uint16_t crgb_slope[NUM_POWER_FN_SEGS]; /* 0xa0 U4.12 */ - - /* parameters for custom curve */ - /* thresholds for brightness --> backlight */ - uint16_t backlight_thresholds[NUM_BL_CURVE_SEGS]; /* 0xb0 U16.0 */ - /* offsets for brightness --> backlight */ - uint16_t backlight_offsets[NUM_BL_CURVE_SEGS]; /* 0xd0 U16.0 */ - - /* For reading PSR State directly from IRAM */ - uint8_t psr_state; /* 0xf0 */ - uint8_t dmcu_mcp_interface_version; /* 0xf1 */ - uint8_t dmcu_abm_feature_version; /* 0xf2 */ - uint8_t dmcu_psr_feature_version; /* 0xf3 */ - uint16_t dmcu_version; /* 0xf4 */ - uint8_t dmcu_state; /* 0xf6 */ - - uint8_t dummy1; /* 0xf7 */ - uint8_t dummy2; /* 0xf8 */ - uint8_t dummy3; /* 0xf9 */ - uint8_t dummy4; /* 0xfa */ - uint8_t dummy5; /* 0xfb */ - uint8_t dummy6; /* 0xfc */ - uint8_t dummy7; /* 0xfd */ - uint8_t dummy8; /* 0xfe */ - uint8_t dummy9; /* 0xff */ -}; -#pragma pack(pop) - -static void fill_backlight_transform_table(struct dmcu_iram_parameters params, - struct iram_table_v_2 *table) -{ - unsigned int i; - unsigned int num_entries = NUM_BL_CURVE_SEGS; - unsigned int lut_index; - - table->backlight_thresholds[0] = 0; - ASSERT(params.backlight_lut_array[0] <= 0xFFFF); - table->backlight_offsets[0] = (uint16_t)params.backlight_lut_array[0]; - table->backlight_thresholds[num_entries-1] = 0xFFFF; - ASSERT(params.backlight_lut_array[params.backlight_lut_array_size - 1] <= 0xFFFF); - table->backlight_offsets[num_entries-1] = - (uint16_t)params.backlight_lut_array[params.backlight_lut_array_size - 1]; - - /* Setup all brightness levels between 0% and 100% exclusive - * Fills brightness-to-backlight transform table. Backlight custom curve - * describes transform from brightness to backlight. It will be defined - * as set of thresholds and set of offsets, together, implying - * extrapolation of custom curve into 16 uniformly spanned linear - * segments. Each threshold/offset represented by 16 bit entry in - * format U4.10. - */ - for (i = 1; i+1 < num_entries; i++) { - lut_index = (params.backlight_lut_array_size - 1) * i / (num_entries - 1); - - ASSERT(lut_index < params.backlight_lut_array_size); - - unsigned int threshold_val = DIV_ROUNDUP((i * 65536), num_entries); - unsigned int offset_val = params.backlight_lut_array[lut_index]; - - ASSERT(threshold_val <= 0xFFFF); - ASSERT(offset_val <= 0xFFFF); - - table->backlight_thresholds[i] = cpu_to_be16((uint16_t)threshold_val); - table->backlight_offsets[i] = cpu_to_be16((uint16_t)offset_val); - } -} - -static void fill_backlight_transform_table_v_2_2(struct dmcu_iram_parameters params, - struct iram_table_v_2_2 *table, bool big_endian) -{ - unsigned int i; - unsigned int num_entries = NUM_BL_CURVE_SEGS; - unsigned int lut_index; - - table->backlight_thresholds[0] = 0; - ASSERT(params.backlight_lut_array[0] <= 0xFFFF); - table->backlight_offsets[0] = (uint16_t)params.backlight_lut_array[0]; - table->backlight_thresholds[num_entries-1] = 0xFFFF; - ASSERT(params.backlight_lut_array[params.backlight_lut_array_size - 1] <= 0xFFFF); - table->backlight_offsets[num_entries-1] = - (uint16_t)params.backlight_lut_array[params.backlight_lut_array_size - 1]; - - /* Setup all brightness levels between 0% and 100% exclusive - * Fills brightness-to-backlight transform table. Backlight custom curve - * describes transform from brightness to backlight. It will be defined - * as set of thresholds and set of offsets, together, implying - * extrapolation of custom curve into 16 uniformly spanned linear - * segments. Each threshold/offset represented by 16 bit entry in - * format U4.10. - */ - for (i = 1; i+1 < num_entries; i++) { - lut_index = DIV_ROUNDUP((i * params.backlight_lut_array_size), num_entries); - ASSERT(lut_index < params.backlight_lut_array_size); - - unsigned int threshold_val = DIV_ROUNDUP((i * 65536), num_entries); - unsigned int offset_val = params.backlight_lut_array[lut_index]; - - ASSERT(threshold_val <= 0xFFFF); - ASSERT(offset_val <= 0xFFFF); - - table->backlight_thresholds[i] = (big_endian) ? - cpu_to_be16((uint16_t)threshold_val) : cpu_to_le16((uint16_t)threshold_val); - table->backlight_offsets[i] = (big_endian) ? - cpu_to_be16((uint16_t)offset_val) : cpu_to_le16((uint16_t)offset_val); - } -} - -static void fill_iram_v_2(struct iram_table_v_2 *ram_table, struct dmcu_iram_parameters params) -{ - unsigned int set = params.set; - - ram_table->min_abm_backlight = - cpu_to_be16(params.min_abm_backlight); - ram_table->deviation_gain = 0xb3; - - ram_table->blRampReduction = - cpu_to_be16(params.backlight_ramping_reduction); - ram_table->blRampStart = - cpu_to_be16(params.backlight_ramping_start); - - ram_table->min_reduction[0][0] = min_reduction_table[abm_config[set][0]]; - ram_table->min_reduction[1][0] = min_reduction_table[abm_config[set][0]]; - ram_table->min_reduction[2][0] = min_reduction_table[abm_config[set][0]]; - ram_table->min_reduction[3][0] = min_reduction_table[abm_config[set][0]]; - ram_table->min_reduction[4][0] = min_reduction_table[abm_config[set][0]]; - ram_table->max_reduction[0][0] = max_reduction_table[abm_config[set][0]]; - ram_table->max_reduction[1][0] = max_reduction_table[abm_config[set][0]]; - ram_table->max_reduction[2][0] = max_reduction_table[abm_config[set][0]]; - ram_table->max_reduction[3][0] = max_reduction_table[abm_config[set][0]]; - ram_table->max_reduction[4][0] = max_reduction_table[abm_config[set][0]]; - - ram_table->min_reduction[0][1] = min_reduction_table[abm_config[set][1]]; - ram_table->min_reduction[1][1] = min_reduction_table[abm_config[set][1]]; - ram_table->min_reduction[2][1] = min_reduction_table[abm_config[set][1]]; - ram_table->min_reduction[3][1] = min_reduction_table[abm_config[set][1]]; - ram_table->min_reduction[4][1] = min_reduction_table[abm_config[set][1]]; - ram_table->max_reduction[0][1] = max_reduction_table[abm_config[set][1]]; - ram_table->max_reduction[1][1] = max_reduction_table[abm_config[set][1]]; - ram_table->max_reduction[2][1] = max_reduction_table[abm_config[set][1]]; - ram_table->max_reduction[3][1] = max_reduction_table[abm_config[set][1]]; - ram_table->max_reduction[4][1] = max_reduction_table[abm_config[set][1]]; - - ram_table->min_reduction[0][2] = min_reduction_table[abm_config[set][2]]; - ram_table->min_reduction[1][2] = min_reduction_table[abm_config[set][2]]; - ram_table->min_reduction[2][2] = min_reduction_table[abm_config[set][2]]; - ram_table->min_reduction[3][2] = min_reduction_table[abm_config[set][2]]; - ram_table->min_reduction[4][2] = min_reduction_table[abm_config[set][2]]; - ram_table->max_reduction[0][2] = max_reduction_table[abm_config[set][2]]; - ram_table->max_reduction[1][2] = max_reduction_table[abm_config[set][2]]; - ram_table->max_reduction[2][2] = max_reduction_table[abm_config[set][2]]; - ram_table->max_reduction[3][2] = max_reduction_table[abm_config[set][2]]; - ram_table->max_reduction[4][2] = max_reduction_table[abm_config[set][2]]; - - ram_table->min_reduction[0][3] = min_reduction_table[abm_config[set][3]]; - ram_table->min_reduction[1][3] = min_reduction_table[abm_config[set][3]]; - ram_table->min_reduction[2][3] = min_reduction_table[abm_config[set][3]]; - ram_table->min_reduction[3][3] = min_reduction_table[abm_config[set][3]]; - ram_table->min_reduction[4][3] = min_reduction_table[abm_config[set][3]]; - ram_table->max_reduction[0][3] = max_reduction_table[abm_config[set][3]]; - ram_table->max_reduction[1][3] = max_reduction_table[abm_config[set][3]]; - ram_table->max_reduction[2][3] = max_reduction_table[abm_config[set][3]]; - ram_table->max_reduction[3][3] = max_reduction_table[abm_config[set][3]]; - ram_table->max_reduction[4][3] = max_reduction_table[abm_config[set][3]]; - - ram_table->bright_pos_gain[0][0] = 0x20; - ram_table->bright_pos_gain[0][1] = 0x20; - ram_table->bright_pos_gain[0][2] = 0x20; - ram_table->bright_pos_gain[0][3] = 0x20; - ram_table->bright_pos_gain[1][0] = 0x20; - ram_table->bright_pos_gain[1][1] = 0x20; - ram_table->bright_pos_gain[1][2] = 0x20; - ram_table->bright_pos_gain[1][3] = 0x20; - ram_table->bright_pos_gain[2][0] = 0x20; - ram_table->bright_pos_gain[2][1] = 0x20; - ram_table->bright_pos_gain[2][2] = 0x20; - ram_table->bright_pos_gain[2][3] = 0x20; - ram_table->bright_pos_gain[3][0] = 0x20; - ram_table->bright_pos_gain[3][1] = 0x20; - ram_table->bright_pos_gain[3][2] = 0x20; - ram_table->bright_pos_gain[3][3] = 0x20; - ram_table->bright_pos_gain[4][0] = 0x20; - ram_table->bright_pos_gain[4][1] = 0x20; - ram_table->bright_pos_gain[4][2] = 0x20; - ram_table->bright_pos_gain[4][3] = 0x20; - ram_table->bright_neg_gain[0][0] = 0x00; - ram_table->bright_neg_gain[0][1] = 0x00; - ram_table->bright_neg_gain[0][2] = 0x00; - ram_table->bright_neg_gain[0][3] = 0x00; - ram_table->bright_neg_gain[1][0] = 0x00; - ram_table->bright_neg_gain[1][1] = 0x00; - ram_table->bright_neg_gain[1][2] = 0x00; - ram_table->bright_neg_gain[1][3] = 0x00; - ram_table->bright_neg_gain[2][0] = 0x00; - ram_table->bright_neg_gain[2][1] = 0x00; - ram_table->bright_neg_gain[2][2] = 0x00; - ram_table->bright_neg_gain[2][3] = 0x00; - ram_table->bright_neg_gain[3][0] = 0x00; - ram_table->bright_neg_gain[3][1] = 0x00; - ram_table->bright_neg_gain[3][2] = 0x00; - ram_table->bright_neg_gain[3][3] = 0x00; - ram_table->bright_neg_gain[4][0] = 0x00; - ram_table->bright_neg_gain[4][1] = 0x00; - ram_table->bright_neg_gain[4][2] = 0x00; - ram_table->bright_neg_gain[4][3] = 0x00; - ram_table->dark_pos_gain[0][0] = 0x00; - ram_table->dark_pos_gain[0][1] = 0x00; - ram_table->dark_pos_gain[0][2] = 0x00; - ram_table->dark_pos_gain[0][3] = 0x00; - ram_table->dark_pos_gain[1][0] = 0x00; - ram_table->dark_pos_gain[1][1] = 0x00; - ram_table->dark_pos_gain[1][2] = 0x00; - ram_table->dark_pos_gain[1][3] = 0x00; - ram_table->dark_pos_gain[2][0] = 0x00; - ram_table->dark_pos_gain[2][1] = 0x00; - ram_table->dark_pos_gain[2][2] = 0x00; - ram_table->dark_pos_gain[2][3] = 0x00; - ram_table->dark_pos_gain[3][0] = 0x00; - ram_table->dark_pos_gain[3][1] = 0x00; - ram_table->dark_pos_gain[3][2] = 0x00; - ram_table->dark_pos_gain[3][3] = 0x00; - ram_table->dark_pos_gain[4][0] = 0x00; - ram_table->dark_pos_gain[4][1] = 0x00; - ram_table->dark_pos_gain[4][2] = 0x00; - ram_table->dark_pos_gain[4][3] = 0x00; - ram_table->dark_neg_gain[0][0] = 0x00; - ram_table->dark_neg_gain[0][1] = 0x00; - ram_table->dark_neg_gain[0][2] = 0x00; - ram_table->dark_neg_gain[0][3] = 0x00; - ram_table->dark_neg_gain[1][0] = 0x00; - ram_table->dark_neg_gain[1][1] = 0x00; - ram_table->dark_neg_gain[1][2] = 0x00; - ram_table->dark_neg_gain[1][3] = 0x00; - ram_table->dark_neg_gain[2][0] = 0x00; - ram_table->dark_neg_gain[2][1] = 0x00; - ram_table->dark_neg_gain[2][2] = 0x00; - ram_table->dark_neg_gain[2][3] = 0x00; - ram_table->dark_neg_gain[3][0] = 0x00; - ram_table->dark_neg_gain[3][1] = 0x00; - ram_table->dark_neg_gain[3][2] = 0x00; - ram_table->dark_neg_gain[3][3] = 0x00; - ram_table->dark_neg_gain[4][0] = 0x00; - ram_table->dark_neg_gain[4][1] = 0x00; - ram_table->dark_neg_gain[4][2] = 0x00; - ram_table->dark_neg_gain[4][3] = 0x00; - - ram_table->iir_curve[0] = 0x65; - ram_table->iir_curve[1] = 0x65; - ram_table->iir_curve[2] = 0x65; - ram_table->iir_curve[3] = 0x65; - ram_table->iir_curve[4] = 0x65; - - //Gamma 2.4 - ram_table->crgb_thresh[0] = cpu_to_be16(0x13b6); - ram_table->crgb_thresh[1] = cpu_to_be16(0x1648); - ram_table->crgb_thresh[2] = cpu_to_be16(0x18e3); - ram_table->crgb_thresh[3] = cpu_to_be16(0x1b41); - ram_table->crgb_thresh[4] = cpu_to_be16(0x1d46); - ram_table->crgb_thresh[5] = cpu_to_be16(0x1f21); - ram_table->crgb_thresh[6] = cpu_to_be16(0x2167); - ram_table->crgb_thresh[7] = cpu_to_be16(0x2384); - ram_table->crgb_offset[0] = cpu_to_be16(0x2999); - ram_table->crgb_offset[1] = cpu_to_be16(0x3999); - ram_table->crgb_offset[2] = cpu_to_be16(0x4666); - ram_table->crgb_offset[3] = cpu_to_be16(0x5999); - ram_table->crgb_offset[4] = cpu_to_be16(0x6333); - ram_table->crgb_offset[5] = cpu_to_be16(0x7800); - ram_table->crgb_offset[6] = cpu_to_be16(0x8c00); - ram_table->crgb_offset[7] = cpu_to_be16(0xa000); - ram_table->crgb_slope[0] = cpu_to_be16(0x3147); - ram_table->crgb_slope[1] = cpu_to_be16(0x2978); - ram_table->crgb_slope[2] = cpu_to_be16(0x23a2); - ram_table->crgb_slope[3] = cpu_to_be16(0x1f55); - ram_table->crgb_slope[4] = cpu_to_be16(0x1c63); - ram_table->crgb_slope[5] = cpu_to_be16(0x1a0f); - ram_table->crgb_slope[6] = cpu_to_be16(0x178d); - ram_table->crgb_slope[7] = cpu_to_be16(0x15ab); - - fill_backlight_transform_table( - params, ram_table); -} - -static void fill_iram_v_2_2(struct iram_table_v_2_2 *ram_table, struct dmcu_iram_parameters params) -{ - unsigned int set = params.set; - - ram_table->flags = 0x0; - - ram_table->min_abm_backlight = - cpu_to_be16(params.min_abm_backlight); - - ram_table->deviation_gain[0] = 0xb3; - ram_table->deviation_gain[1] = 0xa8; - ram_table->deviation_gain[2] = 0x98; - ram_table->deviation_gain[3] = 0x68; - - ram_table->min_reduction[0][0] = min_reduction_table_v_2_2[abm_config[set][0]]; - ram_table->min_reduction[1][0] = min_reduction_table_v_2_2[abm_config[set][0]]; - ram_table->min_reduction[2][0] = min_reduction_table_v_2_2[abm_config[set][0]]; - ram_table->min_reduction[3][0] = min_reduction_table_v_2_2[abm_config[set][0]]; - ram_table->min_reduction[4][0] = min_reduction_table_v_2_2[abm_config[set][0]]; - ram_table->max_reduction[0][0] = max_reduction_table_v_2_2[abm_config[set][0]]; - ram_table->max_reduction[1][0] = max_reduction_table_v_2_2[abm_config[set][0]]; - ram_table->max_reduction[2][0] = max_reduction_table_v_2_2[abm_config[set][0]]; - ram_table->max_reduction[3][0] = max_reduction_table_v_2_2[abm_config[set][0]]; - ram_table->max_reduction[4][0] = max_reduction_table_v_2_2[abm_config[set][0]]; - - ram_table->min_reduction[0][1] = min_reduction_table_v_2_2[abm_config[set][1]]; - ram_table->min_reduction[1][1] = min_reduction_table_v_2_2[abm_config[set][1]]; - ram_table->min_reduction[2][1] = min_reduction_table_v_2_2[abm_config[set][1]]; - ram_table->min_reduction[3][1] = min_reduction_table_v_2_2[abm_config[set][1]]; - ram_table->min_reduction[4][1] = min_reduction_table_v_2_2[abm_config[set][1]]; - ram_table->max_reduction[0][1] = max_reduction_table_v_2_2[abm_config[set][1]]; - ram_table->max_reduction[1][1] = max_reduction_table_v_2_2[abm_config[set][1]]; - ram_table->max_reduction[2][1] = max_reduction_table_v_2_2[abm_config[set][1]]; - ram_table->max_reduction[3][1] = max_reduction_table_v_2_2[abm_config[set][1]]; - ram_table->max_reduction[4][1] = max_reduction_table_v_2_2[abm_config[set][1]]; - - ram_table->min_reduction[0][2] = min_reduction_table_v_2_2[abm_config[set][2]]; - ram_table->min_reduction[1][2] = min_reduction_table_v_2_2[abm_config[set][2]]; - ram_table->min_reduction[2][2] = min_reduction_table_v_2_2[abm_config[set][2]]; - ram_table->min_reduction[3][2] = min_reduction_table_v_2_2[abm_config[set][2]]; - ram_table->min_reduction[4][2] = min_reduction_table_v_2_2[abm_config[set][2]]; - ram_table->max_reduction[0][2] = max_reduction_table_v_2_2[abm_config[set][2]]; - ram_table->max_reduction[1][2] = max_reduction_table_v_2_2[abm_config[set][2]]; - ram_table->max_reduction[2][2] = max_reduction_table_v_2_2[abm_config[set][2]]; - ram_table->max_reduction[3][2] = max_reduction_table_v_2_2[abm_config[set][2]]; - ram_table->max_reduction[4][2] = max_reduction_table_v_2_2[abm_config[set][2]]; - - ram_table->min_reduction[0][3] = min_reduction_table_v_2_2[abm_config[set][3]]; - ram_table->min_reduction[1][3] = min_reduction_table_v_2_2[abm_config[set][3]]; - ram_table->min_reduction[2][3] = min_reduction_table_v_2_2[abm_config[set][3]]; - ram_table->min_reduction[3][3] = min_reduction_table_v_2_2[abm_config[set][3]]; - ram_table->min_reduction[4][3] = min_reduction_table_v_2_2[abm_config[set][3]]; - ram_table->max_reduction[0][3] = max_reduction_table_v_2_2[abm_config[set][3]]; - ram_table->max_reduction[1][3] = max_reduction_table_v_2_2[abm_config[set][3]]; - ram_table->max_reduction[2][3] = max_reduction_table_v_2_2[abm_config[set][3]]; - ram_table->max_reduction[3][3] = max_reduction_table_v_2_2[abm_config[set][3]]; - ram_table->max_reduction[4][3] = max_reduction_table_v_2_2[abm_config[set][3]]; - - ram_table->bright_pos_gain[0][0] = 0x20; - ram_table->bright_pos_gain[0][1] = 0x20; - ram_table->bright_pos_gain[0][2] = 0x20; - ram_table->bright_pos_gain[0][3] = 0x20; - ram_table->bright_pos_gain[1][0] = 0x20; - ram_table->bright_pos_gain[1][1] = 0x20; - ram_table->bright_pos_gain[1][2] = 0x20; - ram_table->bright_pos_gain[1][3] = 0x20; - ram_table->bright_pos_gain[2][0] = 0x20; - ram_table->bright_pos_gain[2][1] = 0x20; - ram_table->bright_pos_gain[2][2] = 0x20; - ram_table->bright_pos_gain[2][3] = 0x20; - ram_table->bright_pos_gain[3][0] = 0x20; - ram_table->bright_pos_gain[3][1] = 0x20; - ram_table->bright_pos_gain[3][2] = 0x20; - ram_table->bright_pos_gain[3][3] = 0x20; - ram_table->bright_pos_gain[4][0] = 0x20; - ram_table->bright_pos_gain[4][1] = 0x20; - ram_table->bright_pos_gain[4][2] = 0x20; - ram_table->bright_pos_gain[4][3] = 0x20; - - ram_table->dark_pos_gain[0][0] = 0x00; - ram_table->dark_pos_gain[0][1] = 0x00; - ram_table->dark_pos_gain[0][2] = 0x00; - ram_table->dark_pos_gain[0][3] = 0x00; - ram_table->dark_pos_gain[1][0] = 0x00; - ram_table->dark_pos_gain[1][1] = 0x00; - ram_table->dark_pos_gain[1][2] = 0x00; - ram_table->dark_pos_gain[1][3] = 0x00; - ram_table->dark_pos_gain[2][0] = 0x00; - ram_table->dark_pos_gain[2][1] = 0x00; - ram_table->dark_pos_gain[2][2] = 0x00; - ram_table->dark_pos_gain[2][3] = 0x00; - ram_table->dark_pos_gain[3][0] = 0x00; - ram_table->dark_pos_gain[3][1] = 0x00; - ram_table->dark_pos_gain[3][2] = 0x00; - ram_table->dark_pos_gain[3][3] = 0x00; - ram_table->dark_pos_gain[4][0] = 0x00; - ram_table->dark_pos_gain[4][1] = 0x00; - ram_table->dark_pos_gain[4][2] = 0x00; - ram_table->dark_pos_gain[4][3] = 0x00; - - ram_table->hybrid_factor[0] = 0xff; - ram_table->hybrid_factor[1] = 0xff; - ram_table->hybrid_factor[2] = 0xff; - ram_table->hybrid_factor[3] = 0xc0; - - ram_table->contrast_factor[0] = 0x99; - ram_table->contrast_factor[1] = 0x99; - ram_table->contrast_factor[2] = 0x90; - ram_table->contrast_factor[3] = 0x80; - - ram_table->iir_curve[0] = 0x65; - ram_table->iir_curve[1] = 0x65; - ram_table->iir_curve[2] = 0x65; - ram_table->iir_curve[3] = 0x65; - ram_table->iir_curve[4] = 0x65; - - //Gamma 2.2 - ram_table->crgb_thresh[0] = cpu_to_be16(0x127c); - ram_table->crgb_thresh[1] = cpu_to_be16(0x151b); - ram_table->crgb_thresh[2] = cpu_to_be16(0x17d5); - ram_table->crgb_thresh[3] = cpu_to_be16(0x1a56); - ram_table->crgb_thresh[4] = cpu_to_be16(0x1c83); - ram_table->crgb_thresh[5] = cpu_to_be16(0x1e72); - ram_table->crgb_thresh[6] = cpu_to_be16(0x20f0); - ram_table->crgb_thresh[7] = cpu_to_be16(0x232b); - ram_table->crgb_offset[0] = cpu_to_be16(0x2999); - ram_table->crgb_offset[1] = cpu_to_be16(0x3999); - ram_table->crgb_offset[2] = cpu_to_be16(0x4666); - ram_table->crgb_offset[3] = cpu_to_be16(0x5999); - ram_table->crgb_offset[4] = cpu_to_be16(0x6333); - ram_table->crgb_offset[5] = cpu_to_be16(0x7800); - ram_table->crgb_offset[6] = cpu_to_be16(0x8c00); - ram_table->crgb_offset[7] = cpu_to_be16(0xa000); - ram_table->crgb_slope[0] = cpu_to_be16(0x3609); - ram_table->crgb_slope[1] = cpu_to_be16(0x2dfa); - ram_table->crgb_slope[2] = cpu_to_be16(0x27ea); - ram_table->crgb_slope[3] = cpu_to_be16(0x235d); - ram_table->crgb_slope[4] = cpu_to_be16(0x2042); - ram_table->crgb_slope[5] = cpu_to_be16(0x1dc3); - ram_table->crgb_slope[6] = cpu_to_be16(0x1b1a); - ram_table->crgb_slope[7] = cpu_to_be16(0x1910); - - fill_backlight_transform_table_v_2_2( - params, ram_table, true); -} - -static void fill_iram_v_2_3(struct iram_table_v_2_2 *ram_table, struct dmcu_iram_parameters params, bool big_endian) -{ - unsigned int i, j; - unsigned int set = params.set; - - ram_table->flags = 0x0; - ram_table->min_abm_backlight = (big_endian) ? - cpu_to_be16(params.min_abm_backlight) : - cpu_to_le16(params.min_abm_backlight); - - for (i = 0; i < NUM_AGGR_LEVEL; i++) { - ram_table->hybrid_factor[i] = abm_settings[set][i].brightness_gain; - ram_table->contrast_factor[i] = abm_settings[set][i].contrast_factor; - ram_table->deviation_gain[i] = abm_settings[set][i].deviation_gain; - ram_table->min_knee[i] = abm_settings[set][i].min_knee; - ram_table->max_knee[i] = abm_settings[set][i].max_knee; - - for (j = 0; j < NUM_AMBI_LEVEL; j++) { - ram_table->min_reduction[j][i] = abm_settings[set][i].min_reduction; - ram_table->max_reduction[j][i] = abm_settings[set][i].max_reduction; - ram_table->bright_pos_gain[j][i] = abm_settings[set][i].bright_pos_gain; - ram_table->dark_pos_gain[j][i] = abm_settings[set][i].dark_pos_gain; - } - } - - ram_table->iir_curve[0] = 0x65; - ram_table->iir_curve[1] = 0x65; - ram_table->iir_curve[2] = 0x65; - ram_table->iir_curve[3] = 0x65; - ram_table->iir_curve[4] = 0x65; - - //Gamma 2.2 - ram_table->crgb_thresh[0] = bswap16_based_on_endian(big_endian, 0x127c); - ram_table->crgb_thresh[1] = bswap16_based_on_endian(big_endian, 0x151b); - ram_table->crgb_thresh[2] = bswap16_based_on_endian(big_endian, 0x17d5); - ram_table->crgb_thresh[3] = bswap16_based_on_endian(big_endian, 0x1a56); - ram_table->crgb_thresh[4] = bswap16_based_on_endian(big_endian, 0x1c83); - ram_table->crgb_thresh[5] = bswap16_based_on_endian(big_endian, 0x1e72); - ram_table->crgb_thresh[6] = bswap16_based_on_endian(big_endian, 0x20f0); - ram_table->crgb_thresh[7] = bswap16_based_on_endian(big_endian, 0x232b); - ram_table->crgb_offset[0] = bswap16_based_on_endian(big_endian, 0x2999); - ram_table->crgb_offset[1] = bswap16_based_on_endian(big_endian, 0x3999); - ram_table->crgb_offset[2] = bswap16_based_on_endian(big_endian, 0x4666); - ram_table->crgb_offset[3] = bswap16_based_on_endian(big_endian, 0x5999); - ram_table->crgb_offset[4] = bswap16_based_on_endian(big_endian, 0x6333); - ram_table->crgb_offset[5] = bswap16_based_on_endian(big_endian, 0x7800); - ram_table->crgb_offset[6] = bswap16_based_on_endian(big_endian, 0x8c00); - ram_table->crgb_offset[7] = bswap16_based_on_endian(big_endian, 0xa000); - ram_table->crgb_slope[0] = bswap16_based_on_endian(big_endian, 0x3609); - ram_table->crgb_slope[1] = bswap16_based_on_endian(big_endian, 0x2dfa); - ram_table->crgb_slope[2] = bswap16_based_on_endian(big_endian, 0x27ea); - ram_table->crgb_slope[3] = bswap16_based_on_endian(big_endian, 0x235d); - ram_table->crgb_slope[4] = bswap16_based_on_endian(big_endian, 0x2042); - ram_table->crgb_slope[5] = bswap16_based_on_endian(big_endian, 0x1dc3); - ram_table->crgb_slope[6] = bswap16_based_on_endian(big_endian, 0x1b1a); - ram_table->crgb_slope[7] = bswap16_based_on_endian(big_endian, 0x1910); - - fill_backlight_transform_table_v_2_2( - params, ram_table, big_endian); -} - -bool dmub_init_abm_config(struct resource_pool *res_pool, - struct dmcu_iram_parameters params, - unsigned int inst) -{ - struct iram_table_v_2_2 ram_table; - struct abm_config_table config; - unsigned int set = params.set; - bool result = false; - uint32_t i, j = 0; - - if (res_pool->abm == NULL && res_pool->multiple_abms[inst] == NULL) - return false; - - memset(&ram_table, 0, sizeof(ram_table)); - memset(&config, 0, sizeof(config)); - - fill_iram_v_2_3(&ram_table, params, false); - - // We must copy to structure that is aligned to 32-bit - for (i = 0; i < NUM_POWER_FN_SEGS; i++) { - config.crgb_thresh[i] = ram_table.crgb_thresh[i]; - config.crgb_offset[i] = ram_table.crgb_offset[i]; - config.crgb_slope[i] = ram_table.crgb_slope[i]; - } - - for (i = 0; i < NUM_BL_CURVE_SEGS; i++) { - config.backlight_thresholds[i] = ram_table.backlight_thresholds[i]; - config.backlight_offsets[i] = ram_table.backlight_offsets[i]; - } - - for (i = 0; i < NUM_AMBI_LEVEL; i++) - config.iir_curve[i] = ram_table.iir_curve[i]; - - for (i = 0; i < NUM_AMBI_LEVEL; i++) { - for (j = 0; j < NUM_AGGR_LEVEL; j++) { - config.min_reduction[i][j] = ram_table.min_reduction[i][j]; - config.max_reduction[i][j] = ram_table.max_reduction[i][j]; - config.bright_pos_gain[i][j] = ram_table.bright_pos_gain[i][j]; - config.dark_pos_gain[i][j] = ram_table.dark_pos_gain[i][j]; - } - } - - for (i = 0; i < NUM_AGGR_LEVEL; i++) { - config.hybrid_factor[i] = ram_table.hybrid_factor[i]; - config.contrast_factor[i] = ram_table.contrast_factor[i]; - config.deviation_gain[i] = ram_table.deviation_gain[i]; - config.min_knee[i] = ram_table.min_knee[i]; - config.max_knee[i] = ram_table.max_knee[i]; - } - - if (params.backlight_ramping_override) { - - ASSERT(params.backlight_ramping_reduction <= 0xFFFF); - ASSERT(params.backlight_ramping_start <= 0xFFFF); - for (i = 0; i < NUM_AGGR_LEVEL; i++) { - config.blRampReduction[i] = (uint16_t)params.backlight_ramping_reduction; - config.blRampStart[i] = (uint16_t)params.backlight_ramping_start; - } - } else { - for (i = 0; i < NUM_AGGR_LEVEL; i++) { - config.blRampReduction[i] = abm_settings[set][i].blRampReduction; - config.blRampStart[i] = abm_settings[set][i].blRampStart; - } - } - - config.min_abm_backlight = ram_table.min_abm_backlight; - - if (res_pool->multiple_abms[inst]) { - result = res_pool->multiple_abms[inst]->funcs->init_abm_config( - res_pool->multiple_abms[inst], (char *)(&config), sizeof(struct abm_config_table), inst); - } else - result = res_pool->abm->funcs->init_abm_config( - res_pool->abm, (char *)(&config), sizeof(struct abm_config_table), 0); - - return result; -} - -bool dmcu_load_iram(struct dmcu *dmcu, - struct dmcu_iram_parameters params) -{ - unsigned char ram_table[IRAM_SIZE]; - bool result = false; - - if (dmcu == NULL) - return false; - - if (dmcu && !dmcu->funcs->is_dmcu_initialized(dmcu)) - return true; - - memset(&ram_table, 0, sizeof(ram_table)); - - if (dmcu->dmcu_version.abm_version == 0x24) { - fill_iram_v_2_3((struct iram_table_v_2_2 *)ram_table, params, true); - result = dmcu->funcs->load_iram(dmcu, 0, (char *)(&ram_table), - IRAM_RESERVE_AREA_START_V2_2); - } else if (dmcu->dmcu_version.abm_version == 0x23) { - fill_iram_v_2_3((struct iram_table_v_2_2 *)ram_table, params, true); - - result = dmcu->funcs->load_iram( - dmcu, 0, (char *)(&ram_table), IRAM_RESERVE_AREA_START_V2_2); - } else if (dmcu->dmcu_version.abm_version == 0x22) { - fill_iram_v_2_2((struct iram_table_v_2_2 *)ram_table, params); - - result = dmcu->funcs->load_iram( - dmcu, 0, (char *)(&ram_table), IRAM_RESERVE_AREA_START_V2_2); - } else { - fill_iram_v_2((struct iram_table_v_2 *)ram_table, params); - - result = dmcu->funcs->load_iram( - dmcu, 0, (char *)(&ram_table), IRAM_RESERVE_AREA_START_V2); - - if (result) - result = dmcu->funcs->load_iram( - dmcu, IRAM_RESERVE_AREA_END_V2 + 1, - (char *)(&ram_table) + IRAM_RESERVE_AREA_END_V2 + 1, - sizeof(ram_table) - IRAM_RESERVE_AREA_END_V2 - 1); - } - - return result; -} - -/* - * is_psr_su_specific_panel() - check if sink is AMD vendor-specific PSR-SU - * supported eDP device. - * - * @link: dc link pointer - * - * Return: true if AMDGPU vendor specific PSR-SU eDP panel - */ -bool is_psr_su_specific_panel(struct dc_link *link) -{ - bool isPSRSUSupported = false; - struct dpcd_caps *dpcd_caps = &link->dpcd_caps; - - if (dpcd_caps->edp_rev >= DP_EDP_14) { - if (dpcd_caps->psr_info.psr_version >= DP_PSR2_WITH_Y_COORD_ET_SUPPORTED) - isPSRSUSupported = true; - /* - * Some panels will report PSR capabilities over additional DPCD bits. - * Such panels are approved despite reporting only PSR v3, as long as - * the additional bits are reported. - */ - if (dpcd_caps->sink_dev_id == DP_BRANCH_DEVICE_ID_001CF8) { - /* - * This is the temporary workaround to disable PSRSU when system turned on - * DSC function on the sepcific sink. - */ - if (dpcd_caps->psr_info.psr_version < DP_PSR2_WITH_Y_COORD_IS_SUPPORTED) - isPSRSUSupported = false; - else if (dpcd_caps->dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT && - ((dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x08) || - (dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x07))) - isPSRSUSupported = false; - else if (dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x03) - isPSRSUSupported = false; - else if (dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x01) - isPSRSUSupported = false; - else if (dpcd_caps->psr_info.force_psrsu_cap == 0x1) - isPSRSUSupported = true; - } - } - - return isPSRSUSupported; -} - -/** - * mod_power_calc_psr_configs() - calculate/update generic psr configuration fields. - * @psr_config: [output], psr configuration structure to be updated - * @link: [input] dc link pointer - * @stream: [input] dc stream state pointer - * - * calculate and update the psr configuration fields that are not DM specific, i.e. such - * fields which are based on DPCD caps or timing information. To setup PSR in DMUB FW, - * this helper is assumed to be called before the call of the DC helper dc_link_setup_psr(). - * - * PSR config fields to be updated within the helper: - * - psr_rfb_setup_time - * - psr_sdp_transmit_line_num_deadline - * - line_time_in_us - * - su_y_granularity - * - su_granularity_required - * - psr_frame_capture_indication_req - * - psr_exit_link_training_required - * - * PSR config fields that are DM specific and NOT updated within the helper: - * - allow_smu_optimizations - * - allow_multi_disp_optimizations - */ -void mod_power_calc_psr_configs(struct psr_config *psr_config, - struct dc_link *link, - const struct dc_stream_state *stream) -{ - unsigned int num_vblank_lines = 0; - unsigned int vblank_time_in_us = 0; - unsigned int sdp_tx_deadline_in_us = 0; - unsigned int line_time_in_us = 0; - struct dpcd_caps *dpcd_caps = &link->dpcd_caps; - const int psr_setup_time_step_in_us = 55; /* refer to eDP spec DPCD 0x071h */ - - /* timing parameters */ - num_vblank_lines = stream->timing.v_total - - stream->timing.v_addressable - - stream->timing.v_border_top - - stream->timing.v_border_bottom; - - vblank_time_in_us = (stream->timing.h_total * num_vblank_lines * 1000) / (stream->timing.pix_clk_100hz / 10); - - line_time_in_us = ((stream->timing.h_total * 1000) / (stream->timing.pix_clk_100hz / 10)) + 1; - - /** - * psr configuration fields - * - * as per eDP 1.5 pg. 377 of 459, DPCD 0x071h bits [3:1], psr setup time bits interpreted as below - * 000b <--> 330 us (default) - * 001b <--> 275 us - * 010b <--> 220 us - * 011b <--> 165 us - * 100b <--> 110 us - * 101b <--> 055 us - * 110b <--> 000 us - */ - psr_config->psr_rfb_setup_time = - (6 - dpcd_caps->psr_info.psr_dpcd_caps.bits.PSR_SETUP_TIME) * psr_setup_time_step_in_us; - - if (psr_config->psr_rfb_setup_time > vblank_time_in_us) { - link->psr_settings.psr_frame_capture_indication_req = true; - link->psr_settings.psr_sdp_transmit_line_num_deadline = num_vblank_lines; - } else { - sdp_tx_deadline_in_us = vblank_time_in_us - psr_config->psr_rfb_setup_time; - - /* Set the last possible line SDP may be transmitted without violating the RFB setup time */ - link->psr_settings.psr_frame_capture_indication_req = false; - link->psr_settings.psr_sdp_transmit_line_num_deadline = sdp_tx_deadline_in_us / line_time_in_us; - } - - psr_config->psr_sdp_transmit_line_num_deadline = link->psr_settings.psr_sdp_transmit_line_num_deadline; - psr_config->line_time_in_us = line_time_in_us; - psr_config->su_y_granularity = dpcd_caps->psr_info.psr2_su_y_granularity_cap; - psr_config->su_granularity_required = dpcd_caps->psr_info.psr_dpcd_caps.bits.SU_GRANULARITY_REQUIRED; - psr_config->psr_frame_capture_indication_req = link->psr_settings.psr_frame_capture_indication_req; - psr_config->psr_exit_link_training_required = - !link->dpcd_caps.psr_info.psr_dpcd_caps.bits.LINK_TRAINING_ON_EXIT_NOT_REQUIRED; -} - -void init_replay_config(struct dc_link *link, struct replay_config *pr_config) -{ - link->replay_settings.config = *pr_config; -} - bool mod_power_only_edp(const struct dc_state *context, const struct dc_stream_state *stream) { return context && context->stream_count == 1 && dc_is_embedded_signal(stream->signal); } - -bool psr_su_set_dsc_slice_height(struct dc *dc, struct dc_link *link, - struct dc_stream_state *stream, - struct psr_config *config) -{ - uint16_t pic_height; - uint16_t slice_height; - - config->dsc_slice_height = 0; - if (!(link->connector_signal & SIGNAL_TYPE_EDP) || - !dc->caps.edp_dsc_support || - link->panel_config.dsc.disable_dsc_edp || - !link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT || - !stream->timing.dsc_cfg.num_slices_v) - return true; - - pic_height = stream->timing.v_addressable + - stream->timing.v_border_top + stream->timing.v_border_bottom; - - if (stream->timing.dsc_cfg.num_slices_v == 0) - return false; - - slice_height = pic_height / stream->timing.dsc_cfg.num_slices_v; - config->dsc_slice_height = slice_height; - - if (slice_height) { - if (config->su_y_granularity && - (slice_height % config->su_y_granularity)) { - ASSERT(0); - return false; - } - } - - return true; -} - -void set_replay_frame_skip_number(struct dc_link *link, - enum replay_coasting_vtotal_type type, - uint32_t coasting_vtotal_refresh_rate_mhz, - uint32_t flicker_free_refresh_rate_mhz, - bool is_defer) -{ - uint32_t *frame_skip_number_array = NULL; - uint32_t frame_skip_number = 0; - - if (link == NULL) - return; - - if (false == link->replay_settings.config.frame_skip_supported) - return; - - if (flicker_free_refresh_rate_mhz == 0 || coasting_vtotal_refresh_rate_mhz == 0) - return; - - if (is_defer) - frame_skip_number_array = link->replay_settings.defer_frame_skip_number_table; - else - frame_skip_number_array = link->replay_settings.frame_skip_number_table; - - if (frame_skip_number_array == NULL) - return; - - frame_skip_number = coasting_vtotal_refresh_rate_mhz / flicker_free_refresh_rate_mhz; - - if (frame_skip_number >= 1) - frame_skip_number_array[type] = frame_skip_number - 1; - else - frame_skip_number_array[type] = 0; -} - -void set_replay_defer_update_coasting_vtotal(struct dc_link *link, - enum replay_coasting_vtotal_type type, - uint32_t vtotal) -{ - link->replay_settings.defer_update_coasting_vtotal_table[type] = vtotal; -} - -void update_replay_coasting_vtotal_from_defer(struct dc_link *link, - enum replay_coasting_vtotal_type type) -{ - link->replay_settings.coasting_vtotal_table[type] = - link->replay_settings.defer_update_coasting_vtotal_table[type]; - link->replay_settings.frame_skip_number_table[type] = - link->replay_settings.defer_frame_skip_number_table[type]; -} - -void set_replay_coasting_vtotal(struct dc_link *link, - enum replay_coasting_vtotal_type type, - uint32_t vtotal) -{ - link->replay_settings.coasting_vtotal_table[type] = vtotal; -} - -void set_replay_low_rr_full_screen_video_src_vtotal(struct dc_link *link, uint16_t vtotal) -{ - link->replay_settings.low_rr_full_screen_video_pseudo_vtotal = vtotal; -} - -void calculate_replay_link_off_frame_count(struct dc_link *link, - uint16_t vtotal, uint16_t htotal) -{ - uint8_t max_link_off_frame_count = 0; - uint16_t max_deviation_line = 0, pixel_deviation_per_line = 0; - - if (!link || link->replay_settings.config.replay_version != DC_FREESYNC_REPLAY) - return; - - max_deviation_line = link->dpcd_caps.pr_info.max_deviation_line; - pixel_deviation_per_line = link->dpcd_caps.pr_info.pixel_deviation_per_line; - - if (htotal != 0 && vtotal != 0 && pixel_deviation_per_line != 0) - max_link_off_frame_count = htotal * max_deviation_line / (pixel_deviation_per_line * vtotal); - else - ASSERT(0); - - link->replay_settings.link_off_frame_count = max_link_off_frame_count; -} - -bool fill_custom_backlight_caps(unsigned int config_no, struct dm_acpi_atif_backlight_caps *caps) -{ - unsigned int data_points_size; - uint64_t caps_size; - - if (config_no >= ARRAY_SIZE(custom_backlight_profiles)) - return false; - - data_points_size = custom_backlight_profiles[config_no].num_data_points - * sizeof(custom_backlight_profiles[config_no].data_points[0]); - - caps_size = sizeof(struct dm_acpi_atif_backlight_caps) - sizeof(caps->data_points) + data_points_size; - ASSERT(caps_size <= 0xFFFF); - caps->size = (uint16_t)caps_size; - caps->flags = 0; - caps->error_code = 0; - caps->ac_level_percentage = custom_backlight_profiles[config_no].ac_level_percentage; - caps->dc_level_percentage = custom_backlight_profiles[config_no].dc_level_percentage; - caps->min_input_signal = custom_backlight_profiles[config_no].min_input_signal; - caps->max_input_signal = custom_backlight_profiles[config_no].max_input_signal; - caps->num_data_points = custom_backlight_profiles[config_no].num_data_points; - memcpy(caps->data_points, custom_backlight_profiles[config_no].data_points, data_points_size); - return true; -} - -void reset_replay_dsync_error_count(struct dc_link *link) -{ - link->replay_settings.replay_desync_error_fail_count = 0; -} diff --git a/drivers/gpu/drm/amd/display/modules/power/power_helpers.h b/drivers/gpu/drm/amd/display/modules/power/power_helpers.h index 87d31d9dce5a..600da3e33126 100644 --- a/drivers/gpu/drm/amd/display/modules/power/power_helpers.h +++ b/drivers/gpu/drm/amd/display/modules/power/power_helpers.h @@ -28,6 +28,7 @@ #include "dc/inc/hw/dmcu.h" #include "dc/inc/hw/abm.h" #include "dc/inc/core_types.h" +#include "mod_power.h" struct resource_pool; @@ -46,7 +47,99 @@ struct dmcu_iram_parameters { unsigned int min_abm_backlight; unsigned int set; }; +struct backlight_state { + /* HW uses u16.16 format for backlight PWM */ + unsigned int backlight_pwm; + /* DM may call power module to set backlight + * targeting percent brightness + */ + unsigned int backlight_millipercent; + /* DM may call power module to set backlight based on an explicit + * nits value. + */ + unsigned int backlight_millinit; + unsigned int frame_ramp; + bool smooth_brightness_enabled; + bool isHDR; +}; +struct power_entity { + struct dc_stream_state *stream; + struct psr_caps *caps; + struct mod_power_psr_context *psr_context; + + /*PSR cached properties*/ + bool psr_enabled; + unsigned int psr_events; + unsigned int psr_power_opt; + unsigned int replay_events; +}; + +struct pwr_backlight_properties { + bool use_nits_based_brightness; + bool disable_fractional_pwm; + + unsigned int min_abm_backlight; + unsigned int num_backlight_levels; + + bool backlight_ramping_override; + unsigned int backlight_ramping_reduction; + unsigned int backlight_ramping_start; + + /* Backlight cached properties */ + unsigned int ac_backlight_percent; + unsigned int dc_backlight_percent; + + /* backlight LUT stored in HW u16.16 format*/ + unsigned int *backlight_lut; + unsigned int min_backlight_pwm; + unsigned int max_backlight_pwm; + unsigned int backlight_range; + /* Describes the panel's min and max luminance in millinits measured + * on full white screen, in min and max backlight settings. + */ + unsigned int min_brightness_millinits; + unsigned int max_brightness_millinits; + unsigned int nits_range; + + bool backlight_caps_valid; + bool use_custom_backlight_caps; + unsigned int custom_backlight_caps_config_no; + bool use_linear_backlight_curve; +}; + +struct dmcu_varibright_cached_properties { + unsigned int varibright_config_setting; + unsigned int varibright_level; + unsigned int varibright_hw_level; + unsigned int def_varibright_level; + bool varibright_user_enable; + bool varibright_active; +}; + +struct core_power { + struct mod_power mod_public; + struct dc *dc; + struct power_entity *map; + struct dmcu_varibright_cached_properties varibright_prop; + struct pwr_backlight_properties bl_prop[MAX_NUM_EDP]; + struct backlight_state bl_state[MAX_NUM_EDP]; + unsigned int edp_num; + + bool psr_smu_optimizations_support; + bool multi_disp_optimizations_support; + + unsigned int num_entities; +}; + +union dmcu_abm_set_bl_params { + struct { + unsigned int gradual_change : 1; /* [0:0] */ + unsigned int reserved : 15; /* [15:1] */ + unsigned int frame_ramp : 16; /* [31:16] */ + } bits; + unsigned int u32All; +}; bool dmcu_load_iram(struct dmcu *dmcu, struct dmcu_iram_parameters params); bool dmub_init_abm_config(struct resource_pool *res_pool, @@ -86,4 +179,33 @@ bool fill_custom_backlight_caps(unsigned int config_no, void reset_replay_dsync_error_count(struct dc_link *link); void change_replay_to_psr(struct dc_link *link); void change_psr_to_replay(struct dc_link *link); +void initialize_backlight_caps(struct core_power *core_power, unsigned int inst); +unsigned int backlight_millipercent_to_pwm( + struct core_power *core_power, unsigned int millipercent, unsigned int inst); +unsigned int backlight_millipercent_to_millinit( + struct core_power *core_power, unsigned int millipercent, unsigned int inst); +void fill_backlight_level_params(struct core_power *core_power, + struct set_backlight_level_params *backlight_level_params, + int panel_inst, uint8_t aux_inst, unsigned int backlight_pwm, + enum backlight_control_type backlight_control_type, + unsigned int backlight_millinit, unsigned int transition_time_millisec, + bool is_hdr); +bool mod_power_hw_init_backlight(struct mod_power *mod_power); +void mod_power_update_backlight_on_mode_change( + struct core_power *core_power, + struct dc_link *link, + unsigned int panel_inst, + uint8_t aux_inst, + bool is_hdr); +unsigned int map_index_from_stream(struct core_power *core_power, + const struct dc_stream_state *stream); +bool mod_power_psr_notify_mode_change(struct mod_power *mod_power, + const struct dc_stream_state *stream, + struct dc_link *link, + unsigned int stream_index); +void mod_power_replay_notify_mode_change(struct mod_power *mod_power, + struct dc *dc, + struct dc_link *link, + const struct dc_stream_state *stream, + unsigned int stream_index); #endif /* MODULES_POWER_POWER_HELPERS_H_ */ diff --git a/drivers/gpu/drm/amd/display/modules/power/power_psr.c b/drivers/gpu/drm/amd/display/modules/power/power_psr.c new file mode 100644 index 000000000000..5ecb570c204e --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/power/power_psr.c @@ -0,0 +1,665 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2026 Advanced Micro Devices, Inc. + +#include "dm_services.h" +#include "dc.h" +#include "mod_power.h" +#include "core_types.h" +#include "dmcu.h" +#include "abm.h" +#include "power_helpers.h" +#include "dce/dmub_psr.h" +#include "dal_asic_id.h" +#include "link_service.h" +#include <linux/math.h> + +#define DC_TRACE_LEVEL_MESSAGE(...) /* do nothing */ +#define DC_TRACE_LEVEL_MESSAGEP(...) /* do nothing */ +#include "dc/inc/hw/dmcu.h" +#include "dc/inc/hw/abm.h" +#include "dmub_cmd.h" + +#define MOD_POWER_TO_CORE(mod_power)\ + container_of(mod_power, struct core_power, mod_public) + +static unsigned int calc_psr_num_static_frames(unsigned int vsync_rate_hz) +{ + /* Initialize fail-safe to 2 static frames. */ + unsigned int num_frames_static = 2; + + /* Calculate number of frames such that at least 30 ms has passed. + * Round up to ensure the static period is not shorter than 30 ms. + */ + if (vsync_rate_hz != 0) + num_frames_static = DIV_ROUND_UP(30000 * vsync_rate_hz, 1000000); + + return num_frames_static; +} + +bool mod_power_psr_notify_mode_change(struct mod_power *mod_power, + const struct dc_stream_state *stream, + struct dc_link *link, + unsigned int stream_index) +{ + struct core_power *core_power = NULL; + struct dc *dc = NULL; + struct psr_config psr_config = {0}; + struct psr_context psr_context = {0}; + int active_psr_events = 0; + + if ((mod_power == NULL) || (stream == NULL) || (link == NULL)) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + dc = core_power->dc; + + // NO num_entities check here - already validated by caller + // stream_index is passed as validated parameter + active_psr_events = core_power->map[stream_index].psr_events; + + /* Calculate PSR configurations */ + mod_power_calc_psr_configs(&psr_config, link, stream); + + psr_config.psr_exit_link_training_required = + core_power->map[stream_index].caps->psr_exit_link_training_required; + if (dc->ctx->asic_id.chip_family >= AMDGPU_FAMILY_GC_11_0_1) + psr_config.allow_smu_optimizations = + core_power->psr_smu_optimizations_support && dc_is_embedded_signal(stream->signal); + else + psr_config.allow_smu_optimizations = + core_power->psr_smu_optimizations_support && + mod_power_only_edp(dc->current_state, stream); + + psr_config.allow_multi_disp_optimizations = core_power->multi_disp_optimizations_support; + + psr_config.rate_control_caps = core_power->map[stream_index].caps->rate_control_caps; + + if (active_psr_events & psr_event_os_request_force_ffu) + psr_config.os_request_force_ffu = true; + + /* + * DSC support: + * DSC slice height value must be 'mod' by su_y_granularity. + * According to Panel Vendor, there might be varied conditions to fulfill. + * Right now, DSC slice height value must be multiple of su_y_granularity. + * + * The value of DSC slice height is determined in DSC Driver but it does not + * propagated out here, so we need to calculate it as below 'slice_height'. + */ + psr_su_set_dsc_slice_height(dc, link, + (struct dc_stream_state *) stream, + &psr_config); + + dc_link_setup_psr(link, stream, &psr_config, &psr_context); + + return true; +} + +static void mod_power_psr_set_power_opt(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int active_psr_events, + bool psr_enable_request) +{ + (void)psr_enable_request; + struct core_power *core_power = NULL; + struct dc_link *link = NULL; + unsigned int stream_index = 0; + unsigned int power_opt = 0; + + if (!stream) + return; + + core_power = MOD_POWER_TO_CORE(mod_power); + stream_index = map_index_from_stream(core_power, stream); + if (!core_power->map[stream_index].caps->psr_version) + return; + + link = dc_stream_get_link(stream); + + if (active_psr_events == 0) { + /* Static Screen */ + power_opt |= (psr_power_opt_smu_opt_static_screen | psr_power_opt_z10_static_screen | + psr_power_opt_ds_disable_allow); + } + + /* psr_power_opt_flag is a configuration parameter into the module that determines + * which optimizations to enable during psr + */ + power_opt &= core_power->map[stream_index].caps->psr_power_opt_flag; + if (core_power->map[stream_index].psr_power_opt != power_opt) { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_VERBOSE, + WPP_BIT_FLAG_Firmware_PsrState, + "mod_power set_power_opt: psr_power_opt=0x%04x, power_opt=0x%04x active_psr_events=0x%04x, psr_power_opt_flag=0x%04x", + core_power->map[stream_index].psr_power_opt, + power_opt, + active_psr_events, + core_power->map[stream_index].caps->psr_power_opt_flag); + dc_link_set_psr_allow_active(link, NULL, false, false, &power_opt); + core_power->map[stream_index].psr_power_opt = power_opt; + } +} + +static bool set_psr_enable(struct mod_power *mod_power, + struct dc_stream_state *stream, + bool psr_enable, + bool wait, + bool force_static) +{ + struct core_power *core_power = NULL; + enum dc_psr_state state = PSR_STATE0; + unsigned int retry_count; + const unsigned int max_retry = 1000; + struct dc_link *link = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "set psr enable: ERROR: stream=%p num_entities=%u", + stream, + core_power->num_entities); + return false; + } + + if (psr_enable) { + unsigned int vsync_rate_hz; + struct dc_static_screen_params params = {0}; + + vsync_rate_hz = (unsigned int)div_u64(div_u64(( + stream->timing.pix_clk_100hz * 100), + stream->timing.v_total), + stream->timing.h_total); + + params.triggers.cursor_update = true; + params.triggers.overlay_update = true; + params.triggers.surface_update = true; + params.num_frames = calc_psr_num_static_frames(vsync_rate_hz); + + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Firmware_PsrState, + "set psr enable: CALCS: pix_clk_100hz=%u v_total=%u h_total=%u vsync_rate_hz=%u num_frames=%u", + stream->timing.pix_clk_100hz, + stream->timing.v_total, + stream->timing.h_total, + vsync_rate_hz, + params.num_frames); + + dc_stream_set_static_screen_params(core_power->dc, + &stream, 1, + ¶ms); + } + + link = dc_stream_get_link(stream); + + if (!dc_link_set_psr_allow_active(link, &psr_enable, false, force_static, NULL)) { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "set psr enable: ERROR: stream=%p link=%p psr_enable=%d", + stream, + link, + psr_enable); + return false; + } + + if (wait == true) { + + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Firmware_PsrState, + "set psr enable: BEGIN WAIT: psr_enable=%d", + (int)psr_enable); + + for (retry_count = 0; retry_count <= max_retry; retry_count++) { + dc_link_get_psr_state(link, &state); + if (psr_enable) { + if (state != PSR_STATE0 && + (!force_static || state == PSR_STATE3)) + break; + } else { + if (state == PSR_STATE0) + break; + } + udelay(500); + } + + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Firmware_PsrState, + "set psr enable: END WAIT: psr_enable=%d", + (int)psr_enable); + + /* assert if max retry hit */ + if (retry_count >= max_retry) { + ASSERT(0); + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "set psr enable: ERROR: retry_count=%u: Unexpectedly long wait for PSR state change.", + retry_count); + } + } else { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Firmware_PsrState, + "set psr enable: PSR state change initiated (wait=false): psr_enable=%d", + (int)psr_enable); + } + + return true; +} + +bool mod_power_get_psr_event(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int *active_psr_events) +{ + struct core_power *core_power = NULL; + unsigned int stream_index = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return false; + + stream_index = map_index_from_stream(core_power, stream); + + if (!core_power->map[stream_index].caps->psr_version) + return false; + + *active_psr_events = core_power->map[stream_index].psr_events; + + return true; +} + +bool mod_power_set_psr_event(struct mod_power *mod_power, + struct dc_stream_state *stream, bool set_event, + enum psr_event event, bool wait) +{ + struct core_power *core_power = NULL; + unsigned int stream_index = 0; + unsigned int active_psr_events = 0; + bool psr_enable_request = false; + bool force_static = false; + + if (mod_power == NULL || stream == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + stream_index = map_index_from_stream(core_power, stream); + + if (core_power->num_entities == 0) { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "mod_power set_psr_event: ERROR: stream=%p event=%d num_entities=%u", + stream, + (int)event, + core_power->num_entities); + return false; + } + + if (!core_power->map[stream_index].caps->psr_version) + return false; + + if (set_event) + core_power->map[stream_index].psr_events |= event; + else + core_power->map[stream_index].psr_events &= ~event; + + active_psr_events = core_power->map[stream_index].psr_events; + + // ignore other events when we're in forced psr enabled state + if (active_psr_events & psr_event_dynamic_display_switch && + event != psr_event_dynamic_display_switch) + return false; + + // ignore other events when we're in forced psr enabled state + if (active_psr_events & psr_event_os_override_hold && + event != psr_event_os_override_hold) + return false; + + // ignore other events when we're in forced psr enabled state + // dds events need to be processed while in dynamic_link_rate_control + if (active_psr_events & psr_event_dynamic_link_rate_control && + event != psr_event_dynamic_link_rate_control && + event != psr_event_dds_defer_stream_enable && + event != psr_event_dynamic_display_switch) + return false; + + if (active_psr_events & (psr_event_test_harness_disable_psr | psr_event_os_request_disable)) + psr_enable_request = false; + else if (active_psr_events & psr_event_pause) + psr_enable_request = false; + else if (active_psr_events & psr_event_test_harness_enable_psr) + psr_enable_request = true; + else if (active_psr_events & psr_event_dynamic_display_switch) { + psr_enable_request = true; + force_static = true; + } else if (active_psr_events & psr_event_dynamic_link_rate_control) { + psr_enable_request = true; + force_static = true; + } else if (active_psr_events & psr_event_edp_panel_off_disable_psr) + psr_enable_request = false; + else if (active_psr_events & (psr_event_hw_programming | + psr_event_defer_enable | + psr_event_dds_defer_stream_enable | + psr_event_vrr_transition | + psr_event_immediate_flip)) + psr_enable_request = false; + else if (active_psr_events & psr_event_big_screen_video) + psr_enable_request = true; + else if (active_psr_events & psr_event_full_screen) + psr_enable_request = false; + else if (active_psr_events & psr_event_mpo_video_selective_update) + psr_enable_request = true; + else if (active_psr_events & psr_event_vsync) + psr_enable_request = false; + else if (active_psr_events & psr_event_crc_window_active) + psr_enable_request = false; + else + psr_enable_request = true; + + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_VERBOSE, + WPP_BIT_FLAG_Firmware_PsrState, + "mod_power set_psr_event: before: psr_enabled=%d -> request: set_event=%d event=0x%04x -> result: psr_events=0x%04x psr_enable_request=%d", + (int)core_power->map[stream_index].psr_enabled, + (int)set_event, + (unsigned int)event, + (unsigned int)core_power->map[stream_index].psr_events, + (int)psr_enable_request); + mod_power_psr_set_power_opt(mod_power, stream, active_psr_events, psr_enable_request); + + if (core_power->map[stream_index].psr_enabled != psr_enable_request || force_static) { + if (set_psr_enable(mod_power, stream, psr_enable_request, wait, force_static)) + core_power->map[stream_index].psr_enabled = psr_enable_request; + } + + return true; +} + +bool mod_power_get_psr_state(struct mod_power *mod_power, + const struct dc_stream_state *stream, + enum dc_psr_state *state) +{ + struct core_power *core_power = NULL; + const struct dc_link *link = NULL; + + if (!stream) + return false; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return false; + + link = dc_stream_get_link(stream); + return dc_link_get_psr_state(link, state); +} + +bool mod_power_get_psr_enabled_status(struct mod_power *mod_power, + const struct dc_stream_state *stream, + bool *psr_enabled) +{ + struct core_power *core_power = NULL; + unsigned int stream_index = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return false; + + stream_index = map_index_from_stream(core_power, stream); + + if (!core_power->map[stream_index].caps->psr_version) + return false; + + *psr_enabled = core_power->map[stream_index].psr_enabled; + + return true; +} + +void mod_power_psr_residency(struct mod_power *mod_power, + const struct dc_stream_state *stream, + unsigned int *residency, + const uint8_t mode) +{ + struct core_power *core_power = NULL; + const struct dc_link *link = NULL; + + if (!stream) + return; + + if (mod_power == NULL) + return; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return; + + link = dc_stream_get_link(stream); + + if (link != NULL) + link->dc->link_srv->edp_get_psr_residency(link, residency, mode); +} +bool mod_power_psr_get_active_psr_events(struct mod_power *mod_power, + const struct dc_stream_state *stream, unsigned int *active_psr_events) +{ + struct core_power *core_power = NULL; + unsigned int stream_index = 0; + + if (!stream) + return false; + + if (mod_power == NULL) + return false; + + if (active_psr_events == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return false; + + stream_index = map_index_from_stream(core_power, stream); + + *active_psr_events = core_power->map[stream_index].psr_events; + return true; +} + +bool mod_power_psr_set_sink_vtotal_in_psr_active(struct mod_power *mod_power, + const struct dc_stream_state *stream, + uint16_t psr_vtotal_idle, + uint16_t psr_vtotal_su) +{ + struct core_power *core_power = NULL; + unsigned int stream_index = 0; + const struct dc_link *link = NULL; + + if (!stream) + return false; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return false; + + stream_index = map_index_from_stream(core_power, stream); + + if (!core_power->map[stream_index].caps->psr_version) + return false; + + link = dc_stream_get_link(stream); + + return link->dc->link_srv->edp_set_sink_vtotal_in_psr_active( + link, psr_vtotal_idle, psr_vtotal_su); +} +/* + * is_psr_su_specific_panel() - check if sink is AMD vendor-specific PSR-SU + * supported eDP device. + * + * @link: dc link pointer + * + * Return: true if AMDGPU vendor specific PSR-SU eDP panel + */ +bool is_psr_su_specific_panel(struct dc_link *link) +{ + bool isPSRSUSupported = false; + struct dpcd_caps *dpcd_caps = &link->dpcd_caps; + + if (dpcd_caps->edp_rev >= DP_EDP_14) { + if (dpcd_caps->psr_info.psr_version >= DP_PSR2_WITH_Y_COORD_ET_SUPPORTED) + isPSRSUSupported = true; + /* + * Some panels will report PSR capabilities over additional DPCD bits. + * Such panels are approved despite reporting only PSR v3, as long as + * the additional bits are reported. + */ + if (dpcd_caps->sink_dev_id == DP_BRANCH_DEVICE_ID_001CF8) { + /* + * This is the temporary workaround to disable PSRSU when system turned on + * DSC function on the sepcific sink. + */ + if (dpcd_caps->psr_info.psr_version < DP_PSR2_WITH_Y_COORD_IS_SUPPORTED) + isPSRSUSupported = false; + else if (dpcd_caps->dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT && + ((dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x08) || + (dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x07))) + isPSRSUSupported = false; + else if (dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x03) + isPSRSUSupported = false; + else if (dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x01) + isPSRSUSupported = false; + else if (dpcd_caps->psr_info.force_psrsu_cap == 0x1) + isPSRSUSupported = true; + } + } + + return isPSRSUSupported; +} + +/** + * mod_power_calc_psr_configs() - calculate/update generic psr configuration fields. + * @psr_config: [output], psr configuration structure to be updated + * @link: [input] dc link pointer + * @stream: [input] dc stream state pointer + * + * calculate and update the psr configuration fields that are not DM specific, i.e. such + * fields which are based on DPCD caps or timing information. To setup PSR in DMUB FW, + * this helper is assumed to be called before the call of the DC helper dc_link_setup_psr(). + * + * PSR config fields to be updated within the helper: + * - psr_rfb_setup_time + * - psr_sdp_transmit_line_num_deadline + * - line_time_in_us + * - su_y_granularity + * - su_granularity_required + * - psr_frame_capture_indication_req + * - psr_exit_link_training_required + * + * PSR config fields that are DM specific and NOT updated within the helper: + * - allow_smu_optimizations + * - allow_multi_disp_optimizations + */ +void mod_power_calc_psr_configs(struct psr_config *psr_config, + struct dc_link *link, + const struct dc_stream_state *stream) +{ + unsigned int num_vblank_lines = 0; + unsigned int vblank_time_in_us = 0; + unsigned int sdp_tx_deadline_in_us = 0; + unsigned int line_time_in_us = 0; + struct dpcd_caps *dpcd_caps = &link->dpcd_caps; + const int psr_setup_time_step_in_us = 55; /* refer to eDP spec DPCD 0x071h */ + + /* timing parameters */ + num_vblank_lines = stream->timing.v_total - + stream->timing.v_addressable - + stream->timing.v_border_top - + stream->timing.v_border_bottom; + + vblank_time_in_us = (stream->timing.h_total * num_vblank_lines * 1000) / (stream->timing.pix_clk_100hz / 10); + + line_time_in_us = ((stream->timing.h_total * 1000) / (stream->timing.pix_clk_100hz / 10)) + 1; + + /** + * psr configuration fields + * + * as per eDP 1.5 pg. 377 of 459, DPCD 0x071h bits [3:1], psr setup time bits interpreted as below + * 000b <--> 330 us (default) + * 001b <--> 275 us + * 010b <--> 220 us + * 011b <--> 165 us + * 100b <--> 110 us + * 101b <--> 055 us + * 110b <--> 000 us + */ + psr_config->psr_rfb_setup_time = + (6 - dpcd_caps->psr_info.psr_dpcd_caps.bits.PSR_SETUP_TIME) * psr_setup_time_step_in_us; + + if (psr_config->psr_rfb_setup_time > vblank_time_in_us) { + link->psr_settings.psr_frame_capture_indication_req = true; + link->psr_settings.psr_sdp_transmit_line_num_deadline = num_vblank_lines; + } else { + sdp_tx_deadline_in_us = vblank_time_in_us - psr_config->psr_rfb_setup_time; + + /* Set the last possible line SDP may be transmitted without violating the RFB setup time */ + link->psr_settings.psr_frame_capture_indication_req = false; + link->psr_settings.psr_sdp_transmit_line_num_deadline = sdp_tx_deadline_in_us / line_time_in_us; + } + + psr_config->psr_sdp_transmit_line_num_deadline = link->psr_settings.psr_sdp_transmit_line_num_deadline; + psr_config->line_time_in_us = line_time_in_us; + psr_config->su_y_granularity = dpcd_caps->psr_info.psr2_su_y_granularity_cap; + psr_config->su_granularity_required = dpcd_caps->psr_info.psr_dpcd_caps.bits.SU_GRANULARITY_REQUIRED; + psr_config->psr_frame_capture_indication_req = link->psr_settings.psr_frame_capture_indication_req; + psr_config->psr_exit_link_training_required = + !link->dpcd_caps.psr_info.psr_dpcd_caps.bits.LINK_TRAINING_ON_EXIT_NOT_REQUIRED; +} + +bool psr_su_set_dsc_slice_height(struct dc *dc, struct dc_link *link, + struct dc_stream_state *stream, + struct psr_config *config) +{ + uint32_t pic_height; + uint32_t slice_height; + + config->dsc_slice_height = 0; + if (!(link->connector_signal & SIGNAL_TYPE_EDP) || + !dc->caps.edp_dsc_support || + link->panel_config.dsc.disable_dsc_edp || + !link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT || + !stream->timing.dsc_cfg.num_slices_v) + return true; + + pic_height = stream->timing.v_addressable + + stream->timing.v_border_top + stream->timing.v_border_bottom; + + if (stream->timing.dsc_cfg.num_slices_v == 0) + return false; + + slice_height = pic_height / stream->timing.dsc_cfg.num_slices_v; + config->dsc_slice_height = (uint16_t)slice_height; + + if (slice_height) { + if (config->su_y_granularity && + (slice_height % config->su_y_granularity)) { + ASSERT(0); + return false; + } + } + + return true; +} diff --git a/drivers/gpu/drm/amd/display/modules/power/power_replay.c b/drivers/gpu/drm/amd/display/modules/power/power_replay.c new file mode 100644 index 000000000000..983be9759e74 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/power/power_replay.c @@ -0,0 +1,911 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2026 Advanced Micro Devices, Inc. + +#include "dm_services.h" +#include "dc.h" +#include "mod_power.h" +#include "core_types.h" +#include "dmcu.h" +#include "abm.h" +#include "power_helpers.h" +#include "dce/dmub_psr.h" +#include "dal_asic_id.h" +#include "link_service.h" +#include <linux/math.h> + +#define DC_TRACE_LEVEL_MESSAGE(...) /* do nothing */ +#define DC_TRACE_LEVEL_MESSAGEP(...) /* do nothing */ +#include "power_helpers.h" +#include "dc/inc/hw/dmcu.h" +#include "dc/inc/hw/abm.h" +#include "dc.h" +#include "core_types.h" +#include "dmub_cmd.h" + +#define MOD_POWER_TO_CORE(mod_power)\ + container_of(mod_power, struct core_power, mod_public) + +#define LOW_REFRESH_RATE_DURATION_US_UPPER_BOUND 25000 + +static bool mod_power_set_replay_active(struct dc_stream_state *stream, + bool replay_active, + bool wait, + bool force_static) +{ + uint64_t state; + unsigned int retry_count; + const unsigned int max_retry = 1000; + struct dc_link *link = NULL; + + if (!stream) + return false; + + link = dc_stream_get_link(stream); + + if (!link) + return false; + + if (!dc_link_set_replay_allow_active(link, &replay_active, false, force_static, NULL)) + return false; + + if (wait == true) { + + for (retry_count = 0; retry_count <= max_retry; retry_count++) { + dc_link_get_replay_state(link, &state); + if (replay_active) { + if (state != REPLAY_STATE_0 && + (!force_static || state == REPLAY_STATE_3)) + break; + } else { + if (state == REPLAY_STATE_0) + break; + } + udelay(500); + } + + /* assert if max retry hit */ + if (retry_count >= max_retry) + ASSERT(0); + } else { + /* To-do: Add trace log */ + } + + return true; +} + +static unsigned int mod_power_replay_setup_power_opt(struct dc_link *link, + unsigned int active_replay_events, bool is_ultra_sleep_mode) +{ + unsigned int power_opt = 0; + + if (is_ultra_sleep_mode) { + /* Static Screen */ + power_opt |= (replay_power_opt_smu_opt_static_screen | replay_power_opt_z10_static_screen); + } else if (active_replay_events & replay_event_test_harness_ultra_sleep) { + power_opt |= replay_power_opt_z10_static_screen; + } + + /* replay_power_opt_flag is a configuration parameter into the module that determines + * which optimizations to enable during replay + */ + power_opt &= link->replay_settings.config.replay_power_opt_supported; + + return power_opt; +} + +static bool mod_power_replay_set_power_opt(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int active_replay_events, + bool is_ultra_sleep_mode) +{ + (void)mod_power; + struct dc_link *link = NULL; + unsigned int power_opt = 0; + + if (!stream) + return false; + + link = dc_stream_get_link(stream); + + if (!link || !link->replay_settings.replay_feature_enabled) + return false; + + power_opt = mod_power_replay_setup_power_opt(link, active_replay_events, is_ultra_sleep_mode); + + if (!dc_link_set_replay_allow_active(link, NULL, false, false, &power_opt)) + return false; + + return true; +} + +bool mod_power_get_replay_event(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int *active_replay_events) +{ + struct core_power *core_power = NULL; + unsigned int stream_index = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return false; + + stream_index = map_index_from_stream(core_power, stream); + + *active_replay_events = core_power->map[stream_index].replay_events; + + return true; +} + +static bool mod_power_update_replay_active_status(unsigned int active_replay_events, + struct dc_link *link, uint32_t *coasting_vtotal, bool *is_full_screen_video, + bool *is_ultra_sleep_mode, uint16_t *frame_skip_number, bool *is_video_playback) +{ + if (!link || !coasting_vtotal || !is_full_screen_video || !is_video_playback) + return false; + + // Check coasting_vtotal_table has been updated. + if (!link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_STATIC] + || !link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM]) + return false; + + unsigned int replay_enable_option = + link->replay_settings.config.replay_enable_option; + + /* TODO: To support test harness and DDS event */ + + *coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM]; + ASSERT(link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM] <= 0xFFFF); + *frame_skip_number = (uint16_t)link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM]; + + link->replay_settings.config.replay_timing_sync_supported = false; + + *is_full_screen_video = false; + + *is_ultra_sleep_mode = false; + + *is_video_playback = false; + + /* DSAT test scenario */ + if (active_replay_events & replay_event_test_harness_mode) { + if (link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS]) + *coasting_vtotal = + link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS]; + if (link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]) { + ASSERT(link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS] <= 0xFFFF); + *frame_skip_number = + (uint16_t)link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]; + } + + /* During the ultra sleep mode testing, disable the timing sync in short vblank mode */ + if (active_replay_events & (replay_event_test_harness_enable_replay)) { + if ((active_replay_events & replay_event_test_harness_ultra_sleep) && + !link->replay_settings.config.replay_support_fast_resync_in_ultra_sleep_mode) + link->replay_settings.config.replay_timing_sync_supported = false; + return true; + } else + return false; + } else if (active_replay_events & (replay_event_test_harness_enable_replay)) { + if (link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS]) + *coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS]; + if (link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]) { + uint32_t frame_skip_val = + link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]; + + ASSERT(frame_skip_val <= 0xFFFF); + *frame_skip_number = (uint16_t)frame_skip_val; + } + + /* During the ultra sleep mode testing, disable the timing sync in short vblank mode */ + if ((active_replay_events & replay_event_test_harness_ultra_sleep) && + !link->replay_settings.config.replay_support_fast_resync_in_ultra_sleep_mode) { + link->replay_settings.config.replay_timing_sync_supported = false; + } + return true; + } else if (active_replay_events & + (replay_event_test_harness_disable_replay | replay_event_os_request_disable)) { + // set last set coasting vtotal + if (link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS]) + *coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS]; + if (link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]) { + uint32_t frame_skip_val = + link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]; + + ASSERT(frame_skip_val <= 0xFFFF); + *frame_skip_number = (uint16_t)frame_skip_val; + } + return false; + } + + /* Inactive conditions */ + if (active_replay_events & (replay_event_edp_panel_off_disable_psr | + replay_event_hw_programming | + replay_event_vrr | + replay_event_immediate_flip | + replay_event_prepare_vtotal | + replay_event_vrr_transition | + replay_event_pause | + replay_event_disable_replay_while_DPMS | + replay_event_sleep_resume | + replay_event_disable_in_AC | + replay_event_disable_replay_while_detect_display | + replay_event_infopacket | + replay_event_crc_window_active)) + return false; + + // Full screen scenario + if (active_replay_events & replay_event_full_screen) { + if (!(replay_enable_option & pr_enable_option_full_screen)) + return false; + } + + /* Full screen video scenario */ + if (active_replay_events & replay_event_big_screen_video) { + + link->replay_settings.config.replay_timing_sync_supported = false; + + if (replay_enable_option & pr_enable_option_full_screen_video_coasting) { + unsigned int fsn_vid = + link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_FULL_SCREEN_VIDEO]; + + *coasting_vtotal = + link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_FULL_SCREEN_VIDEO]; + ASSERT(fsn_vid <= 0xFFFF); + *frame_skip_number = (uint16_t)fsn_vid; + } + + *is_video_playback = true; + + if ((replay_enable_option & pr_enable_option_full_screen_video) && + (replay_enable_option & pr_enable_option_full_screen_video_coasting)) { + *is_full_screen_video = true; + return true; + } else + return false; + } + + /* MPO video scenario + * Some of the cases may contain a full screen UI layer in MPO video scenario which is + * not the expected case to enable Replay. + */ + if ((active_replay_events & replay_event_mpo_video_selective_update) && + !(active_replay_events & replay_event_full_screen)) { + + link->replay_settings.config.replay_timing_sync_supported = false; + + if (replay_enable_option & pr_enable_option_mpo_video_coasting) { + *coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM]; + { + uint32_t frame_skip_val = + link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM]; + + ASSERT(frame_skip_val <= 0xFFFF); + *frame_skip_number = (uint16_t)frame_skip_val; + } + } + + *is_video_playback = true; + + if (replay_enable_option & pr_enable_option_mpo_video) + return true; + else + return false; + } + + /* Static screen scenario */ + if (!(active_replay_events & replay_event_vsync)) { + + if (replay_enable_option & pr_enable_option_static_screen_coasting) { + // Do not adjust eDP refresh rate if static screen + normal sleep mode + if ((!(link->replay_settings.config.replay_power_opt_supported & + replay_power_opt_z10_static_screen)) || + (active_replay_events & replay_event_cursor_updating)) { + // normal sleep mode + *coasting_vtotal = + link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM]; + { + uint32_t frame_skip_val = + link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM]; + + ASSERT(frame_skip_val <= 0xFFFF); + *frame_skip_number = (uint16_t)frame_skip_val; + } + } else { + // ultra sleep mode + *coasting_vtotal = + link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_STATIC]; + { + uint32_t frame_skip_val = + link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_STATIC]; + + ASSERT(frame_skip_val <= 0xFFFF); + *frame_skip_number = (uint16_t)frame_skip_val; + } + *is_ultra_sleep_mode = true; + } + } + + if (replay_enable_option & pr_enable_option_static_screen) { + if (!link->replay_settings.config.replay_support_fast_resync_in_ultra_sleep_mode) + link->replay_settings.config.replay_timing_sync_supported = false; + return true; + } else + return false; + } + + /* General UI scenario */ + if (active_replay_events & replay_event_general_ui) { + if (replay_enable_option & pr_enable_option_general_ui) + return true; + else + return false; + } + + return false; +} + +bool mod_power_replay_set_coasting_vtotal(struct mod_power *mod_power, + const struct dc_stream_state *stream, + uint32_t coasting_vtotal, + uint16_t frame_skip_number) +{ + struct core_power *core_power = NULL; + struct dc_link *link = NULL; + + if (!stream) + return false; + + link = dc_stream_get_link(stream); + if (!link || !link->replay_settings.replay_feature_enabled) + return false; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return false; + + return link->dc->link_srv->edp_set_coasting_vtotal(link, coasting_vtotal, frame_skip_number); +} + +void mod_power_replay_set_timing_sync_supported(struct mod_power *mod_power, + const struct dc_stream_state *stream) +{ + struct core_power *core_power = NULL; + struct dc_link *link = NULL; + unsigned int stream_index = 0; + union dmub_replay_cmd_set cmd_data = { 0 }; + + if (!stream || mod_power == NULL) + return; + + core_power = MOD_POWER_TO_CORE(mod_power); + if (core_power->num_entities == 0) + return; + + stream_index = map_index_from_stream(core_power, stream); + if (stream_index > core_power->num_entities) //invalid index + return; + + link = dc_stream_get_link(stream); + if (!link || !link->replay_settings.replay_feature_enabled) + return; + + cmd_data.sync_data.timing_sync_supported = link->replay_settings.config.replay_timing_sync_supported; + + link->dc->link_srv->edp_send_replay_cmd(link, Replay_Set_Timing_Sync_Supported, + &cmd_data); +} + +void mod_power_replay_disabled_adaptive_sync_sdp(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool force_disabled) +{ + struct core_power *core_power = NULL; + struct dc_link *link = NULL; + unsigned int stream_index = 0; + union dmub_replay_cmd_set cmd_data = { 0 }; + + if (!stream || mod_power == NULL) + return; + + core_power = MOD_POWER_TO_CORE(mod_power); + if (core_power->num_entities == 0) + return; + + stream_index = map_index_from_stream(core_power, stream); + if (stream_index > core_power->num_entities) //invalid index + return; + + link = dc_stream_get_link(stream); + if (!link || !link->replay_settings.replay_feature_enabled) + return; + + cmd_data.disabled_adaptive_sync_sdp_data.force_disabled = force_disabled; + + link->dc->link_srv->edp_send_replay_cmd(link, Replay_Disabled_Adaptive_Sync_SDP, + &cmd_data); +} + +static void mod_power_replay_set_general_cmd(struct mod_power *mod_power, + const struct dc_stream_state *stream, + const enum dmub_cmd_replay_general_subtype general_cmd_type, + const uint32_t param1, const uint32_t param2) +{ + struct core_power *core_power = NULL; + struct dc_link *link = NULL; + unsigned int stream_index = 0; + union dmub_replay_cmd_set cmd_data = { 0 }; + + if (!stream || mod_power == NULL) + return; + + core_power = MOD_POWER_TO_CORE(mod_power); + if (core_power->num_entities == 0) + return; + + stream_index = map_index_from_stream(core_power, stream); + if (stream_index > core_power->num_entities) //invalid index + return; + + link = dc_stream_get_link(stream); + if (!link || !link->replay_settings.replay_feature_enabled) + return; + + cmd_data.set_general_cmd_data.subtype = general_cmd_type; + cmd_data.set_general_cmd_data.param1 = param1; + cmd_data.set_general_cmd_data.param2 = param2; + link->dc->link_srv->edp_send_replay_cmd(link, Replay_Set_General_Cmd, + &cmd_data); +} + +void mod_power_replay_disabled_desync_error_detection(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool force_disabled) +{ + mod_power_replay_set_general_cmd(mod_power, stream, + REPLAY_GENERAL_CMD_DISABLED_DESYNC_ERROR_DETECTION, + force_disabled, 0); +} + +static void mod_power_replay_set_pseudo_vtotal(struct mod_power *mod_power, + const struct dc_stream_state *stream, uint16_t vtotal) +{ + struct core_power *core_power = NULL; + struct dc_link *link = NULL; + unsigned int stream_index = 0; + union dmub_replay_cmd_set cmd_data = { 0 }; + + if (!stream || mod_power == NULL) + return; + + core_power = MOD_POWER_TO_CORE(mod_power); + if (core_power->num_entities == 0) + return; + + stream_index = map_index_from_stream(core_power, stream); + if (stream_index > core_power->num_entities) //invalid index + return; + + link = dc_stream_get_link(stream); + if (!link || !link->replay_settings.replay_feature_enabled) + return; + + cmd_data.pseudo_vtotal_data.vtotal = vtotal; + + if (link->replay_settings.last_pseudo_vtotal != vtotal) { + link->replay_settings.last_pseudo_vtotal = vtotal; + link->dc->link_srv->edp_send_replay_cmd(link, Replay_Set_Pseudo_VTotal, &cmd_data); + } +} + +static void mod_power_update_error_status(struct mod_power *mod_power, + const struct dc_stream_state *stream) +{ + struct dc_link *link = NULL; + union replay_debug_flags *pDebug = NULL; + + if (mod_power == NULL || stream == NULL) + return; + + link = dc_stream_get_link(stream); + + if (!link) + return; + + pDebug = (union replay_debug_flags *)&link->replay_settings.config.debug_flags; + + if (pDebug->bitfields.enable_visual_confirm_debug == 0) + return; + + mod_power_replay_set_general_cmd(mod_power, stream, + REPLAY_GENERAL_CMD_UPDATE_ERROR_STATUS, + link->replay_settings.config.replay_error_status.raw, 0); +} + +void mod_power_set_low_rr_activate(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool low_rr_supported) +{ + struct dc_link *link = NULL; + + if (mod_power == NULL || stream == NULL) + return; + + link = dc_stream_get_link(stream); + + if (!link) + return; + + mod_power_replay_set_general_cmd(mod_power, stream, + REPLAY_GENERAL_CMD_SET_LOW_RR_ACTIVATE, + low_rr_supported, 0); +} + +void mod_power_set_video_conferencing_activate(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool video_conferencing_activate) +{ + struct dc_link *link = NULL; + + if (mod_power == NULL || stream == NULL) + return; + + link = dc_stream_get_link(stream); + if (!link || !link->replay_settings.replay_feature_enabled) + return; + + mod_power_replay_set_general_cmd(mod_power, stream, + REPLAY_GENERAL_CMD_VIDEO_CONFERENCING, + video_conferencing_activate, 0); +} + +void mod_power_set_coasting_vtotal_without_frame_update(struct mod_power *mod_power, + const struct dc_stream_state *stream, uint32_t coasting_vtotal) +{ + struct dc_link *link = NULL; + + if (mod_power == NULL || stream == NULL) + return; + + link = dc_stream_get_link(stream); + if (!link || !link->replay_settings.replay_feature_enabled) + return; + + mod_power_replay_set_general_cmd(mod_power, stream, + REPLAY_GENERAL_CMD_SET_COASTING_VTOTAL_WITHOUT_FRAME_UPDATE, + coasting_vtotal, 0); +} + +void mod_power_set_replay_continuously_resync(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool enable) +{ + struct dc_link *link = NULL; + + if (mod_power == NULL || stream == NULL) + return; + + link = dc_stream_get_link(stream); + if (!link || !link->replay_settings.replay_feature_enabled) + return; + + mod_power_replay_set_general_cmd(mod_power, stream, + REPLAY_GENERAL_CMD_SET_CONTINUOUSLY_RESYNC, + enable, 0); +} + +void mod_power_set_live_capture_with_cvt_activate(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool live_capture_with_cvt_activate) +{ + struct dc_link *link = NULL; + + if (mod_power == NULL || stream == NULL) + return; + + link = dc_stream_get_link(stream); + if (!link || !link->replay_settings.replay_feature_enabled) + return; + + // Check if LIVE_CAPTURE_WITH_CVT bit is enabled in replay optimization config + if (!link->replay_settings.config.replay_optimization.bits.LIVE_CAPTURE_WITH_CVT) + return; + + if (link->replay_settings.config.live_capture_with_cvt_activated != live_capture_with_cvt_activate) { + link->replay_settings.config.live_capture_with_cvt_activated = live_capture_with_cvt_activate; + mod_power_replay_set_general_cmd(mod_power, stream, + REPLAY_GENERAL_CMD_LIVE_CAPTURE_WITH_CVT, + live_capture_with_cvt_activate, 0); + } +} + +bool mod_power_set_replay_event(struct mod_power *mod_power, + struct dc_stream_state *stream, bool set_event, + enum replay_event event, bool wait_for_disable) +{ + struct core_power *core_power = NULL; + struct dc_link *link = NULL; + unsigned int stream_index = 0; + unsigned int active_replay_events = 0; + bool replay_active_request = false; + bool force_static = false; + uint32_t coasting_vtotal = 0; + bool current_timing_sync_status = false; + bool is_full_screen_video = false; + bool is_ultra_sleep_mode = false; + unsigned int sink_duration_us = 0; + bool low_rr_active = false; + uint16_t frame_skip_number = 0; + bool is_video_playback = false; + + if (!stream) + return false; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return false; + + stream_index = map_index_from_stream(core_power, stream); + + if (set_event) + core_power->map[stream_index].replay_events |= event; + else + core_power->map[stream_index].replay_events &= ~event; + + link = dc_stream_get_link(stream); + if (!link || !link->replay_settings.replay_feature_enabled) + return false; + + if ((core_power->map[stream_index].replay_events & replay_event_disable_replay_while_switching_mux) != 0) + return false; + + if ((core_power->map[stream_index].replay_events & replay_event_os_override_hold) != 0) + return false; + + active_replay_events = core_power->map[stream_index].replay_events; + + current_timing_sync_status = + link->replay_settings.config.replay_timing_sync_supported; + + replay_active_request = mod_power_update_replay_active_status(active_replay_events, + link, &coasting_vtotal, &is_full_screen_video, &is_ultra_sleep_mode, &frame_skip_number, &is_video_playback); + + if (is_full_screen_video) + mod_power_replay_set_pseudo_vtotal(mod_power, stream, + link->replay_settings.low_rr_full_screen_video_pseudo_vtotal); + else + mod_power_replay_set_pseudo_vtotal(mod_power, stream, 0); + + //If timing_sync_status change, then re-enabled set timing_sync_supported value and re-enabled replay + if (current_timing_sync_status != link->replay_settings.config.replay_timing_sync_supported) + mod_power_replay_set_timing_sync_supported(mod_power, stream); + + if (link->replay_settings.config.low_rr_supported) { + sink_duration_us = + (unsigned int)(div_u64(((unsigned long long)(coasting_vtotal) + * 10000) * stream->timing.h_total, + stream->timing.pix_clk_100hz)); + low_rr_active = sink_duration_us < LOW_REFRESH_RATE_DURATION_US_UPPER_BOUND ? false : true; + if (low_rr_active != link->replay_settings.config.low_rr_activated) { + mod_power_set_low_rr_activate(mod_power, stream, low_rr_active); + link->replay_settings.config.low_rr_activated = low_rr_active; + } + } + + // The function return fail when + // 1. DMUB function is not support (for backward compatible). + // 2. active_replay_events or coasting_vtotal is not updated in the same time + if (!mod_power_replay_set_power_opt_and_coasting_vtotal(mod_power, + stream, active_replay_events, coasting_vtotal, is_ultra_sleep_mode, frame_skip_number)) { + if (!mod_power_replay_set_power_opt(mod_power, stream, active_replay_events, is_ultra_sleep_mode)) + return false; + + if (!mod_power_replay_set_coasting_vtotal(mod_power, stream, coasting_vtotal, frame_skip_number)) + return false; + } + + mod_power_set_live_capture_with_cvt_activate(mod_power, stream, is_video_playback); + + mod_power_update_error_status(mod_power, stream); + + // If Replay is going to be enable (No matter is disable -> enable or enable -> enable), we don't need to wait. + // If Replay is going to be disable + // if disable -> disable + // -> Replay DMUB state should be state 0. + // So no matter wait_for_disable is true or not, it should makes no difference. + // if enable -> disable -> We should wait if wait_for_disable is true. + if (replay_active_request) + wait_for_disable = false; + + if (!mod_power_set_replay_active(stream, replay_active_request, wait_for_disable, force_static)) + return false; + + return true; +} + +bool mod_power_get_replay_active_status(const struct dc_stream_state *stream, + bool *replay_active) +{ + const struct dc_link *link = NULL; + + if (!stream) + return false; + + link = dc_stream_get_link(stream); + *replay_active = link->replay_settings.replay_allow_active; + + return true; +} + +void mod_power_replay_residency(const struct dc_stream_state *stream, + unsigned int *residency, const bool is_start, const bool is_alpm) +{ + const struct dc_link *link = NULL; + enum pr_residency_mode mode; + + if (!stream) + return; + + link = dc_stream_get_link(stream); + + if (is_alpm) + mode = PR_RESIDENCY_MODE_ALPM; + else + mode = PR_RESIDENCY_MODE_PHY; + + if (link && link->dc && link->dc->link_srv) + link->dc->link_srv->edp_replay_residency(link, residency, is_start, mode); +} + +bool mod_power_replay_set_power_opt_and_coasting_vtotal(struct mod_power *mod_power, + const struct dc_stream_state *stream, unsigned int active_replay_events, uint32_t coasting_vtotal, + bool is_ultra_sleep_mode, uint16_t frame_skip_number) +{ + struct core_power *core_power = NULL; + struct dc_link *link = NULL; + unsigned int power_opt = 0; + + if (!stream) + return false; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return false; + + link = dc_stream_get_link(stream); + + if (!link || !link->replay_settings.replay_feature_enabled) + return false; + + power_opt = mod_power_replay_setup_power_opt(link, active_replay_events, is_ultra_sleep_mode); + + return link->dc->link_srv->edp_set_replay_power_opt_and_coasting_vtotal(link, &power_opt, coasting_vtotal, frame_skip_number); +} + +void mod_power_replay_notify_mode_change(struct mod_power *mod_power, + struct dc *dc, + struct dc_link *link, + const struct dc_stream_state *stream, + unsigned int stream_index) +{ + struct core_power *core_power = NULL; + int active_replay_events = 0; + + if (!mod_power || !dc || !link || !stream) + return; + + core_power = MOD_POWER_TO_CORE(mod_power); + active_replay_events = core_power->map[stream_index].replay_events; + + link->replay_settings.replay_smu_opt_enable = + (link->replay_settings.config.replay_smu_opt_supported && + mod_power_only_edp(dc->current_state, stream)); + + if (active_replay_events & replay_event_os_request_force_ffu) { + link->replay_settings.config.os_request_force_ffu = true; + } + + if (dc_is_embedded_signal(stream->signal)) + dc->link_srv->dp_setup_replay(link, stream); +} + +void init_replay_config(struct dc_link *link, struct replay_config *pr_config) +{ + link->replay_settings.config = *pr_config; +} + +void set_replay_frame_skip_number(struct dc_link *link, + enum replay_coasting_vtotal_type type, + uint32_t coasting_vtotal_refresh_rate_uhz, + uint32_t flicker_free_refresh_rate_uhz, + bool is_defer) +{ + uint32_t *frame_skip_number_array = NULL; + uint32_t frame_skip_number = 0; + + if (link == NULL) + return; + + if (link->replay_settings.config.frame_skip_supported == false) + return; + + if (flicker_free_refresh_rate_uhz == 0 || coasting_vtotal_refresh_rate_uhz == 0) + return; + + if (is_defer) + frame_skip_number_array = link->replay_settings.defer_frame_skip_number_table; + else + frame_skip_number_array = link->replay_settings.frame_skip_number_table; + + if (frame_skip_number_array == NULL) + return; + + frame_skip_number = (coasting_vtotal_refresh_rate_uhz + 500000) / flicker_free_refresh_rate_uhz; + + if (frame_skip_number >= 1) + frame_skip_number_array[type] = frame_skip_number - 1; + else + frame_skip_number_array[type] = 0; +} + +void set_replay_defer_update_coasting_vtotal(struct dc_link *link, + enum replay_coasting_vtotal_type type, + uint32_t vtotal) +{ + link->replay_settings.defer_update_coasting_vtotal_table[type] = vtotal; +} + +void update_replay_coasting_vtotal_from_defer(struct dc_link *link, + enum replay_coasting_vtotal_type type) +{ + link->replay_settings.coasting_vtotal_table[type] = + link->replay_settings.defer_update_coasting_vtotal_table[type]; + link->replay_settings.frame_skip_number_table[type] = + link->replay_settings.defer_frame_skip_number_table[type]; +} + +void set_replay_coasting_vtotal(struct dc_link *link, + enum replay_coasting_vtotal_type type, + uint32_t vtotal) +{ + link->replay_settings.coasting_vtotal_table[type] = vtotal; +} + +void set_replay_low_rr_full_screen_video_src_vtotal(struct dc_link *link, uint16_t vtotal) +{ + link->replay_settings.low_rr_full_screen_video_pseudo_vtotal = vtotal; +} + +void calculate_replay_link_off_frame_count(struct dc_link *link, + uint16_t vtotal, uint16_t htotal) +{ + uint32_t max_link_off_frame_count = 0; + uint16_t max_deviation_line = 0, pixel_deviation_per_line = 0; + + if (!link || link->replay_settings.config.replay_version != DC_FREESYNC_REPLAY) + return; + + max_deviation_line = link->dpcd_caps.pr_info.max_deviation_line; + pixel_deviation_per_line = link->dpcd_caps.pr_info.pixel_deviation_per_line; + + if (htotal != 0 && vtotal != 0 && pixel_deviation_per_line != 0) + max_link_off_frame_count = htotal * max_deviation_line / (pixel_deviation_per_line * vtotal); + else + ASSERT(0); + + link->replay_settings.link_off_frame_count = max_link_off_frame_count; +} + +void reset_replay_dsync_error_count(struct dc_link *link) +{ + link->replay_settings.replay_desync_error_fail_count = 0; +} diff --git a/drivers/gpu/drm/amd/display/modules/vmid/vmid.c b/drivers/gpu/drm/amd/display/modules/vmid/vmid.c index 179b505f7777..32617893ffb2 100644 --- a/drivers/gpu/drm/amd/display/modules/vmid/vmid.c +++ b/drivers/gpu/drm/amd/display/modules/vmid/vmid.c @@ -56,7 +56,7 @@ static void clear_entry_from_vmid_table(struct core_vmid *core_vmid, unsigned in static void evict_vmids(struct core_vmid *core_vmid) { - int i; + unsigned int i; int ord_int = dc_get_vmid_use_vector(core_vmid->dc); ASSERT(ord_int >= 0 && ord_int <= 0xFFFF); @@ -72,7 +72,7 @@ static void evict_vmids(struct core_vmid *core_vmid) // Return value of -1 indicates vmid table uninitialized or ptb dne in the table static int get_existing_vmid_for_ptb(struct core_vmid *core_vmid, uint64_t ptb) { - int i; + unsigned int i; for (i = 0; i < core_vmid->num_vmid; i++) { if (core_vmid->ptb_assigned_to_vmid[i] == ptb) @@ -85,7 +85,7 @@ static int get_existing_vmid_for_ptb(struct core_vmid *core_vmid, uint64_t ptb) // Expected to be called only when there's an available vmid static int get_next_available_vmid(struct core_vmid *core_vmid) { - int i; + unsigned int i; for (i = 1; i < core_vmid->num_vmid; i++) { if (core_vmid->ptb_assigned_to_vmid[i] == 0) |
