summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/amd/display/modules
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/amd/display/modules')
-rw-r--r--drivers/gpu/drm/amd/display/modules/color/color_gamma.c40
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c2
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h6
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c30
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.c2
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.h2
-rw-r--r--drivers/gpu/drm/amd/display/modules/inc/mod_color_types.h47
-rw-r--r--drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h3
-rw-r--r--drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h1
-rw-r--r--drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h11
-rw-r--r--drivers/gpu/drm/amd/display/modules/inc/mod_info_packet_types.h37
-rw-r--r--drivers/gpu/drm/amd/display/modules/inc/mod_power.h417
-rw-r--r--drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c138
-rw-r--r--drivers/gpu/drm/amd/display/modules/power/Makefile4
-rw-r--r--drivers/gpu/drm/amd/display/modules/power/power.c503
-rw-r--r--drivers/gpu/drm/amd/display/modules/power/power_abm.c2070
-rw-r--r--drivers/gpu/drm/amd/display/modules/power/power_helpers.c1065
-rw-r--r--drivers/gpu/drm/amd/display/modules/power/power_helpers.h122
-rw-r--r--drivers/gpu/drm/amd/display/modules/power/power_psr.c665
-rw-r--r--drivers/gpu/drm/amd/display/modules/power/power_replay.c911
-rw-r--r--drivers/gpu/drm/amd/display/modules/vmid/vmid.c6
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, &current_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,
+ &params);
+ }
+
+ 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)