aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpu/drm
diff options
context:
space:
mode:
authorRay Wu <ray.wu@amd.com>2026-02-27 16:09:19 -0500
committerAlex Deucher <alexander.deucher@amd.com>2026-04-17 15:41:15 -0400
commit4cef2ac4c795e4fffd6d35ea110f1273e3cb74bf (patch)
tree4df37b9e37a03b5a75e20edbe86b76d578aef598 /drivers/gpu/drm
parentf67790a0715edd292d64a104e6e294290243d16d (diff)
drm/amd/display: Introduce power module on Linux
[Why] Other OS supported by DC uses the power module to manage panel power features such as backlight and self-refresh. It contains enhancements on top what amdgpu_dm is doing today that can benefit power. [How] Introduce the power module. It's currently not being used anywhere, a future change will incorporate it into amdgpu_dm. Reviewed-by: Leo Li <sunpeng.li@amd.com> Signed-off-by: Ray Wu <ray.wu@amd.com> Signed-off-by: Leo Li <sunpeng.li@amd.com> Signed-off-by: Chenyu Chen <chen-yu.chen@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c11
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_stream.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_stream.h3
-rw-r--r--drivers/gpu/drm/amd/display/modules/inc/mod_power.h415
-rw-r--r--drivers/gpu/drm/amd/display/modules/power/Makefile2
-rw-r--r--drivers/gpu/drm/amd/display/modules/power/power.c3030
6 files changed, 3466 insertions, 1 deletions
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c
index 8550d5e8b753..0ef7435ffda9 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c
@@ -62,3 +62,14 @@ void dm_trace_smu_exit(bool success, uint32_t response, struct dc_context *ctx)
}
/**** power component interfaces ****/
+
+bool dm_query_extended_brightness_caps(struct dc_context *ctx,
+ enum dm_acpi_display_type display,
+ struct dm_acpi_atif_backlight_caps *pCaps)
+{
+ /*
+ * TODO: Implement query for extended backlight caps.
+ * Some plumbing required, see amdgpu_atif_query_backlight_caps()
+ */
+ return false;
+}
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c
index 49e4f68ff397..16ff43b58e00 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c
@@ -259,6 +259,12 @@ const struct dc_stream_status *dc_stream_get_status_const(
return dc_state_get_stream_status(dc->current_state, stream);
}
+struct dc_link *dc_stream_get_link(
+ const struct dc_stream_state *stream)
+{
+ return stream->link;
+}
+
void program_cursor_attributes(
struct dc *dc,
struct dc_stream_state *stream)
diff --git a/drivers/gpu/drm/amd/display/dc/dc_stream.h b/drivers/gpu/drm/amd/display/dc/dc_stream.h
index 88f70a9b64b1..6a8c1390b85f 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_stream.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_stream.h
@@ -494,6 +494,9 @@ struct surface_update_descriptor dc_check_update_surfaces_for_stream(
int surface_count,
struct dc_stream_update *stream_update);
+struct dc_link *dc_stream_get_link(
+ const struct dc_stream_state *dc_stream);
+
/**
* Create a new default stream for the requested sink
*/
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..89037f7b7961
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/modules/inc/mod_power.h
@@ -0,0 +1,415 @@
+/* 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 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/power/Makefile b/drivers/gpu/drm/amd/display/modules/power/Makefile
index 9d1b22d35ece..b27a1ff3d86b 100644
--- a/drivers/gpu/drm/amd/display/modules/power/Makefile
+++ b/drivers/gpu/drm/amd/display/modules/power/Makefile
@@ -23,7 +23,7 @@
# Makefile for the 'power' sub-module of DAL.
#
-MOD_POWER = power_helpers.o
+MOD_POWER = power_helpers.o power.o
AMD_DAL_MOD_POWER = $(addprefix $(AMDDALPATH)/modules/power/,$(MOD_POWER))
#$(info ************ DAL POWER MODULE MAKEFILE ************)
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..6c73fecf57d5
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/modules/power/power.c
@@ -0,0 +1,3030 @@
+/*
+ * 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
+
+
+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 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 public;
+ struct dc *dc;
+ struct power_entity *map;
+ struct dmcu_varibright_cached_properties varibright_prop;
+ struct 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;
+
+ 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;
+};
+
+/* 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, public)
+
+static unsigned int calc_psr_num_static_frames(unsigned int vsync_rate_hz)
+{
+ /* Calculate number of static frames before generating interrupt to
+ * enter PSR.
+ */
+ unsigned int frame_time_microsec = 1000000 / vsync_rate_hz;
+
+ // Init fail safe of 2 frames static
+ unsigned int num_frames_static = 2;
+
+ /* Round up
+ * Calculate number of frames such that at least 30 ms of time has
+ * passed.
+ */
+ if (vsync_rate_hz != 0)
+ num_frames_static = (30000 / frame_time_microsec) + 1;
+
+ return num_frames_static;
+}
+
+/* Given a specific dc_stream* this function finds its equivalent
+ * on the core_freesync->map and returns the corresponding index
+ */
+static 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;
+}
+
+static uint16_t backlight_8_to_16(unsigned int backlight_8bit)
+{
+ return (uint16_t)(backlight_8bit * 0x101);
+}
+
+
+static 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;
+}
+
+static 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. */
+static void initialize_backlight_caps(struct core_power *core_power, unsigned int inst)
+{
+ unsigned int i;
+ struc