mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 17:13:52 +02:00
drm/amd/display: Block FPO According to Luminance Delta
[Description] - Block FPO if the max stretch refresh rate is low enough to cause a flicker by storing the maximum safe refresh decrease from nominal in stream. - Brought over various Freesync Luminance functions to dc. Use these new functions to block fpo if we will flicker. - Generalized increase/reduce dependent functions to reduce code clutter and allow for easier use. - Added a debug option to enable the feature. Disabled by default. Co-authored-by: Ethan Bitnun <etbitnun@amd.com> Reviewed-by: Dillon Varone <dillon.varone@amd.com> Acked-by: Aurabindo Pillai <aurabindo.pillai@amd.com> Signed-off-by: Ethan Bitnun <etbitnun@amd.com> Tested-by: Daniel Wheeler <daniel.wheeler@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
parent
ec426766a4
commit
dc2be9c68f
|
|
@ -35,6 +35,8 @@
|
|||
#include "dc_stream_priv.h"
|
||||
|
||||
#define DC_LOGGER dc->ctx->logger
|
||||
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
|
||||
#define MAX(x, y) ((x > y) ? x : y)
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
|
|
@ -781,3 +783,229 @@ void dc_stream_log(const struct dc *dc, const struct dc_stream_state *stream)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the greatest index in refresh_rate_hz that contains a value <= refresh
|
||||
*/
|
||||
static int dc_stream_get_nearest_smallest_index(struct dc_stream_state *stream, int refresh)
|
||||
{
|
||||
for (int i = 0; i < (LUMINANCE_DATA_TABLE_SIZE - 1); ++i) {
|
||||
if ((stream->lumin_data.refresh_rate_hz[i] <= refresh) && (refresh < stream->lumin_data.refresh_rate_hz[i + 1])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 9;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds a corresponding brightness for a given refresh rate between 2 given indices, where index1 < index2
|
||||
*/
|
||||
static int dc_stream_get_brightness_millinits_linear_interpolation (struct dc_stream_state *stream,
|
||||
int index1,
|
||||
int index2,
|
||||
int refresh_hz)
|
||||
{
|
||||
int slope = 0;
|
||||
if (stream->lumin_data.refresh_rate_hz[index2] != stream->lumin_data.refresh_rate_hz[index1]) {
|
||||
slope = (stream->lumin_data.luminance_millinits[index2] - stream->lumin_data.luminance_millinits[index1]) /
|
||||
(stream->lumin_data.refresh_rate_hz[index2] - stream->lumin_data.refresh_rate_hz[index1]);
|
||||
}
|
||||
|
||||
int y_intercept = stream->lumin_data.luminance_millinits[index2] - slope * stream->lumin_data.refresh_rate_hz[index2];
|
||||
|
||||
return (y_intercept + refresh_hz * slope);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds a corresponding refresh rate for a given brightness between 2 given indices, where index1 < index2
|
||||
*/
|
||||
static int dc_stream_get_refresh_hz_linear_interpolation (struct dc_stream_state *stream,
|
||||
int index1,
|
||||
int index2,
|
||||
int brightness_millinits)
|
||||
{
|
||||
int slope = 1;
|
||||
if (stream->lumin_data.refresh_rate_hz[index2] != stream->lumin_data.refresh_rate_hz[index1]) {
|
||||
slope = (stream->lumin_data.luminance_millinits[index2] - stream->lumin_data.luminance_millinits[index1]) /
|
||||
(stream->lumin_data.refresh_rate_hz[index2] - stream->lumin_data.refresh_rate_hz[index1]);
|
||||
}
|
||||
|
||||
int y_intercept = stream->lumin_data.luminance_millinits[index2] - slope * stream->lumin_data.refresh_rate_hz[index2];
|
||||
|
||||
return ((brightness_millinits - y_intercept) / slope);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the current brightness in millinits given a refresh rate
|
||||
*/
|
||||
static int dc_stream_get_brightness_millinits_from_refresh (struct dc_stream_state *stream, int refresh_hz)
|
||||
{
|
||||
int nearest_smallest_index = dc_stream_get_nearest_smallest_index(stream, refresh_hz);
|
||||
int nearest_smallest_value = stream->lumin_data.refresh_rate_hz[nearest_smallest_index];
|
||||
|
||||
if (nearest_smallest_value == refresh_hz)
|
||||
return stream->lumin_data.luminance_millinits[nearest_smallest_index];
|
||||
|
||||
if (nearest_smallest_index >= 9)
|
||||
return dc_stream_get_brightness_millinits_linear_interpolation(stream, nearest_smallest_index - 1, nearest_smallest_index, refresh_hz);
|
||||
|
||||
if (nearest_smallest_value == stream->lumin_data.refresh_rate_hz[nearest_smallest_index + 1])
|
||||
return stream->lumin_data.luminance_millinits[nearest_smallest_index];
|
||||
|
||||
return dc_stream_get_brightness_millinits_linear_interpolation(stream, nearest_smallest_index, nearest_smallest_index + 1, refresh_hz);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the lowest refresh rate that can be achieved
|
||||
* from starting_refresh_hz while staying within flicker criteria
|
||||
*/
|
||||
static int dc_stream_calculate_flickerless_refresh_rate(struct dc_stream_state *stream,
|
||||
int current_brightness,
|
||||
int starting_refresh_hz,
|
||||
bool is_gaming,
|
||||
bool search_for_max_increase)
|
||||
{
|
||||
int nearest_smallest_index = dc_stream_get_nearest_smallest_index(stream, starting_refresh_hz);
|
||||
|
||||
int flicker_criteria_millinits = is_gaming ?
|
||||
stream->lumin_data.flicker_criteria_milli_nits_GAMING :
|
||||
stream->lumin_data.flicker_criteria_milli_nits_STATIC;
|
||||
|
||||
int safe_upper_bound = current_brightness + flicker_criteria_millinits;
|
||||
int safe_lower_bound = current_brightness - flicker_criteria_millinits;
|
||||
int lumin_millinits_temp = 0;
|
||||
|
||||
int offset = -1;
|
||||
if (search_for_max_increase) {
|
||||
offset = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Increments up or down by 1 depending on search_for_max_increase
|
||||
*/
|
||||
for (int i = nearest_smallest_index; (i > 0 && !search_for_max_increase) || (i < (LUMINANCE_DATA_TABLE_SIZE - 1) && search_for_max_increase); i += offset) {
|
||||
|
||||
lumin_millinits_temp = stream->lumin_data.luminance_millinits[i + offset];
|
||||
|
||||
if ((lumin_millinits_temp >= safe_upper_bound) || (lumin_millinits_temp <= safe_lower_bound)) {
|
||||
|
||||
if (stream->lumin_data.refresh_rate_hz[i + offset] == stream->lumin_data.refresh_rate_hz[i])
|
||||
return stream->lumin_data.refresh_rate_hz[i];
|
||||
|
||||
int target_brightness = (stream->lumin_data.luminance_millinits[i + offset] >= (current_brightness + flicker_criteria_millinits)) ?
|
||||
current_brightness + flicker_criteria_millinits :
|
||||
current_brightness - flicker_criteria_millinits;
|
||||
|
||||
int refresh = 0;
|
||||
|
||||
/*
|
||||
* Need the second input to be < third input for dc_stream_get_refresh_hz_linear_interpolation
|
||||
*/
|
||||
if (search_for_max_increase)
|
||||
refresh = dc_stream_get_refresh_hz_linear_interpolation(stream, i, i + offset, target_brightness);
|
||||
else
|
||||
refresh = dc_stream_get_refresh_hz_linear_interpolation(stream, i + offset, i, target_brightness);
|
||||
|
||||
if (refresh == stream->lumin_data.refresh_rate_hz[i + offset])
|
||||
return stream->lumin_data.refresh_rate_hz[i + offset];
|
||||
|
||||
return refresh;
|
||||
}
|
||||
}
|
||||
|
||||
if (search_for_max_increase)
|
||||
return stream->lumin_data.refresh_rate_hz[LUMINANCE_DATA_TABLE_SIZE - 1];
|
||||
else
|
||||
return stream->lumin_data.refresh_rate_hz[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets the max delta luminance within a specified refresh range
|
||||
*/
|
||||
static int dc_stream_get_max_delta_lumin_millinits(struct dc_stream_state *stream, int hz1, int hz2, bool isGaming)
|
||||
{
|
||||
int lower_refresh_brightness = dc_stream_get_brightness_millinits_from_refresh (stream, hz1);
|
||||
int higher_refresh_brightness = dc_stream_get_brightness_millinits_from_refresh (stream, hz2);
|
||||
|
||||
int min = lower_refresh_brightness;
|
||||
int max = higher_refresh_brightness;
|
||||
|
||||
/*
|
||||
* Static screen, therefore no need to scan through array
|
||||
*/
|
||||
if (!isGaming) {
|
||||
if (lower_refresh_brightness >= higher_refresh_brightness) {
|
||||
return lower_refresh_brightness - higher_refresh_brightness;
|
||||
}
|
||||
return higher_refresh_brightness - lower_refresh_brightness;
|
||||
}
|
||||
|
||||
min = MIN(lower_refresh_brightness, higher_refresh_brightness);
|
||||
max = MAX(lower_refresh_brightness, higher_refresh_brightness);
|
||||
|
||||
int nearest_smallest_index = dc_stream_get_nearest_smallest_index(stream, hz1);
|
||||
|
||||
for (; nearest_smallest_index < (LUMINANCE_DATA_TABLE_SIZE - 1) &&
|
||||
stream->lumin_data.refresh_rate_hz[nearest_smallest_index + 1] <= hz2 ; nearest_smallest_index++) {
|
||||
min = MIN(min, stream->lumin_data.luminance_millinits[nearest_smallest_index + 1]);
|
||||
max = MAX(max, stream->lumin_data.luminance_millinits[nearest_smallest_index + 1]);
|
||||
}
|
||||
|
||||
return (max - min);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the highest refresh rate that can be achieved
|
||||
* from starting_refresh_hz while staying within flicker criteria
|
||||
*/
|
||||
int dc_stream_calculate_max_flickerless_refresh_rate(struct dc_stream_state *stream, int starting_refresh_hz, bool is_gaming)
|
||||
{
|
||||
if (!stream->lumin_data.is_valid)
|
||||
return 0;
|
||||
|
||||
int current_brightness = dc_stream_get_brightness_millinits_from_refresh(stream, starting_refresh_hz);
|
||||
|
||||
return dc_stream_calculate_flickerless_refresh_rate(stream,
|
||||
current_brightness,
|
||||
starting_refresh_hz,
|
||||
is_gaming,
|
||||
true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the lowest refresh rate that can be achieved
|
||||
* from starting_refresh_hz while staying within flicker criteria
|
||||
*/
|
||||
int dc_stream_calculate_min_flickerless_refresh_rate(struct dc_stream_state *stream, int starting_refresh_hz, bool is_gaming)
|
||||
{
|
||||
if (!stream->lumin_data.is_valid)
|
||||
return 0;
|
||||
|
||||
int current_brightness = dc_stream_get_brightness_millinits_from_refresh(stream, starting_refresh_hz);
|
||||
|
||||
return dc_stream_calculate_flickerless_refresh_rate(stream,
|
||||
current_brightness,
|
||||
starting_refresh_hz,
|
||||
is_gaming,
|
||||
false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines if there will be a flicker when moving between 2 refresh rates
|
||||
*/
|
||||
bool dc_stream_is_refresh_rate_range_flickerless(struct dc_stream_state *stream, int hz1, int hz2, bool is_gaming)
|
||||
{
|
||||
|
||||
/*
|
||||
* Assume that we wont flicker if there is invalid data
|
||||
*/
|
||||
if (!stream->lumin_data.is_valid)
|
||||
return false;
|
||||
|
||||
int dl = dc_stream_get_max_delta_lumin_millinits(stream, hz1, hz2, is_gaming);
|
||||
|
||||
int flicker_criteria_millinits = (is_gaming) ?
|
||||
stream->lumin_data.flicker_criteria_milli_nits_GAMING :
|
||||
stream->lumin_data.flicker_criteria_milli_nits_STATIC;
|
||||
|
||||
return (dl <= flicker_criteria_millinits);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -456,6 +456,7 @@ struct dc_config {
|
|||
bool allow_0_dtb_clk;
|
||||
bool use_assr_psp_message;
|
||||
bool support_edp0_on_dp1;
|
||||
unsigned int enable_fpo_flicker_detection;
|
||||
};
|
||||
|
||||
enum visual_confirm {
|
||||
|
|
|
|||
|
|
@ -160,6 +160,18 @@ struct dc_stream_debug_options {
|
|||
char force_odm_combine_segments;
|
||||
};
|
||||
|
||||
#define LUMINANCE_DATA_TABLE_SIZE 10
|
||||
|
||||
struct luminance_data {
|
||||
bool is_valid;
|
||||
int refresh_rate_hz[LUMINANCE_DATA_TABLE_SIZE];
|
||||
int luminance_millinits[LUMINANCE_DATA_TABLE_SIZE];
|
||||
int flicker_criteria_milli_nits_GAMING;
|
||||
int flicker_criteria_milli_nits_STATIC;
|
||||
int nominal_refresh_rate;
|
||||
int dm_max_decrease_from_nominal;
|
||||
};
|
||||
|
||||
struct dc_stream_state {
|
||||
// sink is deprecated, new code should not reference
|
||||
// this pointer
|
||||
|
|
@ -286,6 +298,8 @@ struct dc_stream_state {
|
|||
bool vblank_synchronized;
|
||||
bool fpo_in_use;
|
||||
bool is_phantom;
|
||||
|
||||
struct luminance_data lumin_data;
|
||||
};
|
||||
|
||||
#define ABM_LEVEL_IMMEDIATE_DISABLE 255
|
||||
|
|
|
|||
|
|
@ -34,4 +34,28 @@ void dc_stream_destruct(struct dc_stream_state *stream);
|
|||
|
||||
void dc_stream_assign_stream_id(struct dc_stream_state *stream);
|
||||
|
||||
/*
|
||||
* Finds the highest refresh rate that can be achieved
|
||||
* from starting_freq while staying within flicker criteria
|
||||
*/
|
||||
int dc_stream_calculate_max_flickerless_refresh_rate(struct dc_stream_state *stream,
|
||||
int starting_refresh_hz,
|
||||
bool is_gaming);
|
||||
|
||||
/*
|
||||
* Finds the lowest refresh rate that can be achieved
|
||||
* from starting_freq while staying within flicker criteria
|
||||
*/
|
||||
int dc_stream_calculate_min_flickerless_refresh_rate(struct dc_stream_state *stream,
|
||||
int starting_refresh_hz,
|
||||
bool is_gaming);
|
||||
|
||||
/*
|
||||
* Determines if there will be a flicker when moving between 2 refresh rates
|
||||
*/
|
||||
bool dc_stream_is_refresh_rate_range_flickerless(struct dc_stream_state *stream,
|
||||
int hz1,
|
||||
int hz2,
|
||||
bool is_gaming);
|
||||
|
||||
#endif // _DC_STREAM_PRIV_H_
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "dml/dcn32/display_mode_vba_util_32.h"
|
||||
#include "dml/dcn32/dcn32_fpu.h"
|
||||
#include "dc_state_priv.h"
|
||||
#include "dc_stream_priv.h"
|
||||
|
||||
static bool is_dual_plane(enum surface_pixel_format format)
|
||||
{
|
||||
|
|
@ -459,7 +460,7 @@ static int get_frame_rate_at_max_stretch_100hz(
|
|||
}
|
||||
|
||||
static bool is_refresh_rate_support_mclk_switch_using_fw_based_vblank_stretch(
|
||||
struct dc_stream_state *fpo_candidate_stream, uint32_t fpo_vactive_margin_us)
|
||||
struct dc_stream_state *fpo_candidate_stream, uint32_t fpo_vactive_margin_us, int current_refresh_rate)
|
||||
{
|
||||
int refresh_rate_max_stretch_100hz;
|
||||
int min_refresh_100hz;
|
||||
|
|
@ -473,6 +474,10 @@ static bool is_refresh_rate_support_mclk_switch_using_fw_based_vblank_stretch(
|
|||
if (refresh_rate_max_stretch_100hz < min_refresh_100hz)
|
||||
return false;
|
||||
|
||||
if (fpo_candidate_stream->ctx->dc->config.enable_fpo_flicker_detection > 0 &&
|
||||
!dc_stream_is_refresh_rate_range_flickerless(fpo_candidate_stream, (refresh_rate_max_stretch_100hz / 100), current_refresh_rate, false))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -569,7 +574,7 @@ struct dc_stream_state *dcn32_can_support_mclk_switch_using_fw_based_vblank_stre
|
|||
return NULL;
|
||||
|
||||
fpo_vactive_margin_us = is_fpo_vactive ? dc->debug.fpo_vactive_margin_us : 0; // For now hardcode the FPO + Vactive stretch margin to be 2000us
|
||||
if (!is_refresh_rate_support_mclk_switch_using_fw_based_vblank_stretch(fpo_candidate_stream, fpo_vactive_margin_us))
|
||||
if (!is_refresh_rate_support_mclk_switch_using_fw_based_vblank_stretch(fpo_candidate_stream, fpo_vactive_margin_us, refresh_rate))
|
||||
return NULL;
|
||||
|
||||
if (!fpo_candidate_stream->allow_freesync)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user