summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/amd/display/modules/freesync
diff options
context:
space:
mode:
authorAngus Wang <Angus.Wang@amd.com>2022-03-22 15:37:15 -0400
committerAlex Deucher <alexander.deucher@amd.com>2022-04-05 10:29:47 -0400
commit3fe5739db488434bc0368577615ea7275b0f43a5 (patch)
tree84297b3b7c63817f3a0463c61afb7f815748082a /drivers/gpu/drm/amd/display/modules/freesync
parent35b2186b13b66075035fa008e9390d14b331e1b3 (diff)
downloadlinux-stable-3fe5739db488434bc0368577615ea7275b0f43a5.tar.gz
linux-stable-3fe5739db488434bc0368577615ea7275b0f43a5.tar.bz2
linux-stable-3fe5739db488434bc0368577615ea7275b0f43a5.zip
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 <daniel.wheeler@amd.com> Reviewed-by: Anthony Koo <Anthony.Koo@amd.com> Reviewed-by: Aric Cyr <Aric.Cyr@amd.com> Acked-by: Tom Chung <chiahsuan.chung@amd.com> Signed-off-by: Angus Wang <Angus.Wang@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm/amd/display/modules/freesync')
-rw-r--r--drivers/gpu/drm/amd/display/modules/freesync/freesync.c74
1 files changed, 73 insertions, 1 deletions
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.