From 3fe5739db488434bc0368577615ea7275b0f43a5 Mon Sep 17 00:00:00 2001 From: Angus Wang Date: Tue, 22 Mar 2022 15:37:15 -0400 Subject: drm/amd/display: Add flip interval workaround [WHY] Some games experience low FPS issues when FreeSync is on and VSync is toggled to half refresh rate. [HOW] First create a function to determine workaround conditions, which is when we detect 2 or more VSync interrupts between flips and a very short VSync to flip interval. We do the workaround during VSync interrupts and set the v_total_max and min to nominal. We also cleanup after we exit the game. Tested-by: Daniel Wheeler Reviewed-by: Anthony Koo Reviewed-by: Aric Cyr Acked-by: Tom Chung Signed-off-by: Angus Wang Signed-off-by: Alex Deucher --- .../drm/amd/display/modules/freesync/freesync.c | 74 +++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/amd/display/modules/freesync/freesync.c') diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c index bd1d1dc93629..d72566c6928a 100644 --- a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c +++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c @@ -29,6 +29,7 @@ #include "dc.h" #include "mod_freesync.h" #include "core_types.h" +#include "dm_services.h" #define MOD_FREESYNC_MAX_CONCURRENT_STREAMS 32 @@ -46,6 +47,10 @@ /* Number of consecutive frames to check before entering/exiting fixed refresh */ #define FIXED_REFRESH_ENTER_FRAME_COUNT 5 #define FIXED_REFRESH_EXIT_FRAME_COUNT 10 +/* Flip interval workaround constants */ +#define VSYNCS_BETWEEN_FLIP_THRESHOLD 2 +#define FREESYNC_CONSEC_FLIP_AFTER_VSYNC 5 +#define FREESYNC_VSYNC_TO_FLIP_DELTA_IN_US 500 struct core_freesync { struct mod_freesync public; @@ -466,6 +471,41 @@ static void apply_fixed_refresh(struct core_freesync *core_freesync, } } +static void determine_flip_interval_workaround_req(struct mod_vrr_params *in_vrr, + unsigned int curr_time_stamp_in_us) +{ + in_vrr->flip_interval.vsync_to_flip_in_us = curr_time_stamp_in_us - + in_vrr->flip_interval.v_update_timestamp_in_us; + + /* Determine conditions for stopping workaround */ + if (in_vrr->flip_interval.flip_interval_workaround_active && + in_vrr->flip_interval.vsyncs_between_flip < VSYNCS_BETWEEN_FLIP_THRESHOLD && + in_vrr->flip_interval.vsync_to_flip_in_us > FREESYNC_VSYNC_TO_FLIP_DELTA_IN_US) { + in_vrr->flip_interval.flip_interval_detect_counter = 0; + in_vrr->flip_interval.program_flip_interval_workaround = true; + in_vrr->flip_interval.flip_interval_workaround_active = false; + } else { + /* Determine conditions for starting workaround */ + if (in_vrr->flip_interval.vsyncs_between_flip >= VSYNCS_BETWEEN_FLIP_THRESHOLD && + in_vrr->flip_interval.vsync_to_flip_in_us < FREESYNC_VSYNC_TO_FLIP_DELTA_IN_US) { + /* Increase flip interval counter we have 2 vsyncs between flips and + * vsync to flip interval is less than 500us + */ + in_vrr->flip_interval.flip_interval_detect_counter++; + if (in_vrr->flip_interval.flip_interval_detect_counter > FREESYNC_CONSEC_FLIP_AFTER_VSYNC) { + /* Start workaround if we detect 5 consecutive instances of the above case */ + in_vrr->flip_interval.program_flip_interval_workaround = true; + in_vrr->flip_interval.flip_interval_workaround_active = true; + } + } else { + /* Reset the flip interval counter if we condition is no longer met */ + in_vrr->flip_interval.flip_interval_detect_counter = 0; + } + } + + in_vrr->flip_interval.vsyncs_between_flip = 0; +} + static bool vrr_settings_require_update(struct core_freesync *core_freesync, struct mod_freesync_config *in_config, unsigned int min_refresh_in_uhz, @@ -1179,6 +1219,9 @@ void mod_freesync_handle_preflip(struct mod_freesync *mod_freesync, in_out_vrr); } + determine_flip_interval_workaround_req(in_out_vrr, + curr_time_stamp_in_us); + } } @@ -1187,6 +1230,7 @@ void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync, struct mod_vrr_params *in_out_vrr) { struct core_freesync *core_freesync = NULL; + unsigned int cur_timestamp_in_us; if ((mod_freesync == NULL) || (stream == NULL) || (in_out_vrr == NULL)) return; @@ -1196,6 +1240,34 @@ void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync, if (in_out_vrr->supported == false) return; + cur_timestamp_in_us = dm_get_timestamp(core_freesync->dc->ctx)/10; + + in_out_vrr->flip_interval.vsyncs_between_flip++; + in_out_vrr->flip_interval.v_update_timestamp_in_us = cur_timestamp_in_us; + + if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE && + (in_out_vrr->flip_interval.flip_interval_workaround_active || + (!in_out_vrr->flip_interval.flip_interval_workaround_active && + in_out_vrr->flip_interval.program_flip_interval_workaround))) { + // set freesync vmin vmax to nominal for workaround + in_out_vrr->adjust.v_total_min = + mod_freesync_calc_v_total_from_refresh( + stream, in_out_vrr->max_refresh_in_uhz); + in_out_vrr->adjust.v_total_max = + in_out_vrr->adjust.v_total_min; + in_out_vrr->flip_interval.program_flip_interval_workaround = false; + in_out_vrr->flip_interval.do_flip_interval_workaround_cleanup = true; + return; + } + + if (in_out_vrr->state != VRR_STATE_ACTIVE_VARIABLE && + in_out_vrr->flip_interval.do_flip_interval_workaround_cleanup) { + in_out_vrr->flip_interval.do_flip_interval_workaround_cleanup = false; + in_out_vrr->flip_interval.flip_interval_detect_counter = 0; + in_out_vrr->flip_interval.vsyncs_between_flip = 0; + in_out_vrr->flip_interval.vsync_to_flip_in_us = 0; + } + /* Below the Range Logic */ /* Only execute if in fullscreen mode */ @@ -1302,7 +1374,7 @@ unsigned long long mod_freesync_calc_field_rate_from_timing( bool mod_freesync_is_valid_range(uint32_t min_refresh_cap_in_uhz, uint32_t max_refresh_cap_in_uhz, - uint32_t nominal_field_rate_in_uhz) + uint32_t nominal_field_rate_in_uhz) { /* Typically nominal refresh calculated can have some fractional part. -- cgit From 8d2aad983de2a332bf8c22798ab6799f06864fed Mon Sep 17 00:00:00 2001 From: Lv Ruyi Date: Wed, 6 Apr 2022 07:28:40 +0000 Subject: drm/amd/dc: remove duplicate include 'dm_services.h' included in 'freesync,c' is duplicated, so remove one. Reported-by: Zeal Robot Signed-off-by: Lv Ruyi Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/modules/freesync/freesync.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/gpu/drm/amd/display/modules/freesync/freesync.c') diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c index d72566c6928a..0130f1879116 100644 --- a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c +++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c @@ -29,7 +29,6 @@ #include "dc.h" #include "mod_freesync.h" #include "core_types.h" -#include "dm_services.h" #define MOD_FREESYNC_MAX_CONCURRENT_STREAMS 32 -- cgit From 1754cea1763e2bdc6a2153220440fe9aa9e0f2c9 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 7 Apr 2022 15:46:06 -0400 Subject: drm/amd/display: fix 64 bit divide in freesync code Use div_u64() rather than a a 64 bit divide. Fixes: 3fe5739db48843 ("drm/amd/display: Add flip interval workaround") Reviewed-by: Nathan Chancellor Reported-by: kernel test robot Signed-off-by: Alex Deucher Cc: Angus Wang Cc: Anthony Koo Cc: Aric Cyr Cc: Nathan Chancellor --- drivers/gpu/drm/amd/display/modules/freesync/freesync.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/amd/display/modules/freesync/freesync.c') diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c index 0130f1879116..d2d76ce56f89 100644 --- a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c +++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c @@ -1239,7 +1239,7 @@ void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync, if (in_out_vrr->supported == false) return; - cur_timestamp_in_us = dm_get_timestamp(core_freesync->dc->ctx)/10; + cur_timestamp_in_us = div_u64(dm_get_timestamp(core_freesync->dc->ctx), 10); in_out_vrr->flip_interval.vsyncs_between_flip++; in_out_vrr->flip_interval.v_update_timestamp_in_us = cur_timestamp_in_us; -- cgit From 19a2e1e36a2b14d7549a6d9584be131f4286f757 Mon Sep 17 00:00:00 2001 From: Angus Wang Date: Thu, 31 Mar 2022 09:33:10 -0400 Subject: drm/amd/display: Fix inconsistent timestamp type [WHY] An unsigned int timestamp variable is assigned with an unsigned long long value. Also, the assignment directly converts the tick value to us without using built-in get elapsed time function. [HOW] Cast the assigned value correctly and also use built-in function to get the timestamp in the unit we want. v2: squash in 64 bit division fix Reviewed-by: Aric Cyr Acked-by: Pavle Kotarac Signed-off-by: Angus Wang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/modules/freesync/freesync.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/amd/display/modules/freesync/freesync.c') diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c index d2d76ce56f89..03fa63d56fa6 100644 --- a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c +++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c @@ -1230,6 +1230,7 @@ void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync, { struct core_freesync *core_freesync = NULL; unsigned int cur_timestamp_in_us; + unsigned long long cur_tick; if ((mod_freesync == NULL) || (stream == NULL) || (in_out_vrr == NULL)) return; @@ -1239,7 +1240,9 @@ void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync, if (in_out_vrr->supported == false) return; - cur_timestamp_in_us = div_u64(dm_get_timestamp(core_freesync->dc->ctx), 10); + cur_tick = dm_get_timestamp(core_freesync->dc->ctx); + cur_timestamp_in_us = (unsigned int) + div_u64(dm_get_elapse_time_in_ns(core_freesync->dc->ctx, cur_tick, 0), 1000); in_out_vrr->flip_interval.vsyncs_between_flip++; in_out_vrr->flip_interval.v_update_timestamp_in_us = cur_timestamp_in_us; -- cgit