Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Ethan Bitnun
Ethan Bitnun
and committed by
Alex Deucher
dc2be9c6 ec426766

+274 -2
+228
drivers/gpu/drm/amd/display/dc/core/dc_stream.c
··· 35 35 #include "dc_stream_priv.h" 36 36 37 37 #define DC_LOGGER dc->ctx->logger 38 + #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) 39 + #define MAX(x, y) ((x > y) ? x : y) 38 40 39 41 /******************************************************************************* 40 42 * Private functions ··· 783 781 } 784 782 } 785 783 784 + /* 785 + * Finds the greatest index in refresh_rate_hz that contains a value <= refresh 786 + */ 787 + static int dc_stream_get_nearest_smallest_index(struct dc_stream_state *stream, int refresh) 788 + { 789 + for (int i = 0; i < (LUMINANCE_DATA_TABLE_SIZE - 1); ++i) { 790 + if ((stream->lumin_data.refresh_rate_hz[i] <= refresh) && (refresh < stream->lumin_data.refresh_rate_hz[i + 1])) { 791 + return i; 792 + } 793 + } 794 + return 9; 795 + } 796 + 797 + /* 798 + * Finds a corresponding brightness for a given refresh rate between 2 given indices, where index1 < index2 799 + */ 800 + static int dc_stream_get_brightness_millinits_linear_interpolation (struct dc_stream_state *stream, 801 + int index1, 802 + int index2, 803 + int refresh_hz) 804 + { 805 + int slope = 0; 806 + if (stream->lumin_data.refresh_rate_hz[index2] != stream->lumin_data.refresh_rate_hz[index1]) { 807 + slope = (stream->lumin_data.luminance_millinits[index2] - stream->lumin_data.luminance_millinits[index1]) / 808 + (stream->lumin_data.refresh_rate_hz[index2] - stream->lumin_data.refresh_rate_hz[index1]); 809 + } 810 + 811 + int y_intercept = stream->lumin_data.luminance_millinits[index2] - slope * stream->lumin_data.refresh_rate_hz[index2]; 812 + 813 + return (y_intercept + refresh_hz * slope); 814 + } 815 + 816 + /* 817 + * Finds a corresponding refresh rate for a given brightness between 2 given indices, where index1 < index2 818 + */ 819 + static int dc_stream_get_refresh_hz_linear_interpolation (struct dc_stream_state *stream, 820 + int index1, 821 + int index2, 822 + int brightness_millinits) 823 + { 824 + int slope = 1; 825 + if (stream->lumin_data.refresh_rate_hz[index2] != stream->lumin_data.refresh_rate_hz[index1]) { 826 + slope = (stream->lumin_data.luminance_millinits[index2] - stream->lumin_data.luminance_millinits[index1]) / 827 + (stream->lumin_data.refresh_rate_hz[index2] - stream->lumin_data.refresh_rate_hz[index1]); 828 + } 829 + 830 + int y_intercept = stream->lumin_data.luminance_millinits[index2] - slope * stream->lumin_data.refresh_rate_hz[index2]; 831 + 832 + return ((brightness_millinits - y_intercept) / slope); 833 + } 834 + 835 + /* 836 + * Finds the current brightness in millinits given a refresh rate 837 + */ 838 + static int dc_stream_get_brightness_millinits_from_refresh (struct dc_stream_state *stream, int refresh_hz) 839 + { 840 + int nearest_smallest_index = dc_stream_get_nearest_smallest_index(stream, refresh_hz); 841 + int nearest_smallest_value = stream->lumin_data.refresh_rate_hz[nearest_smallest_index]; 842 + 843 + if (nearest_smallest_value == refresh_hz) 844 + return stream->lumin_data.luminance_millinits[nearest_smallest_index]; 845 + 846 + if (nearest_smallest_index >= 9) 847 + return dc_stream_get_brightness_millinits_linear_interpolation(stream, nearest_smallest_index - 1, nearest_smallest_index, refresh_hz); 848 + 849 + if (nearest_smallest_value == stream->lumin_data.refresh_rate_hz[nearest_smallest_index + 1]) 850 + return stream->lumin_data.luminance_millinits[nearest_smallest_index]; 851 + 852 + return dc_stream_get_brightness_millinits_linear_interpolation(stream, nearest_smallest_index, nearest_smallest_index + 1, refresh_hz); 853 + } 854 + 855 + /* 856 + * Finds the lowest refresh rate that can be achieved 857 + * from starting_refresh_hz while staying within flicker criteria 858 + */ 859 + static int dc_stream_calculate_flickerless_refresh_rate(struct dc_stream_state *stream, 860 + int current_brightness, 861 + int starting_refresh_hz, 862 + bool is_gaming, 863 + bool search_for_max_increase) 864 + { 865 + int nearest_smallest_index = dc_stream_get_nearest_smallest_index(stream, starting_refresh_hz); 866 + 867 + int flicker_criteria_millinits = is_gaming ? 868 + stream->lumin_data.flicker_criteria_milli_nits_GAMING : 869 + stream->lumin_data.flicker_criteria_milli_nits_STATIC; 870 + 871 + int safe_upper_bound = current_brightness + flicker_criteria_millinits; 872 + int safe_lower_bound = current_brightness - flicker_criteria_millinits; 873 + int lumin_millinits_temp = 0; 874 + 875 + int offset = -1; 876 + if (search_for_max_increase) { 877 + offset = 1; 878 + } 879 + 880 + /* 881 + * Increments up or down by 1 depending on search_for_max_increase 882 + */ 883 + for (int i = nearest_smallest_index; (i > 0 && !search_for_max_increase) || (i < (LUMINANCE_DATA_TABLE_SIZE - 1) && search_for_max_increase); i += offset) { 884 + 885 + lumin_millinits_temp = stream->lumin_data.luminance_millinits[i + offset]; 886 + 887 + if ((lumin_millinits_temp >= safe_upper_bound) || (lumin_millinits_temp <= safe_lower_bound)) { 888 + 889 + if (stream->lumin_data.refresh_rate_hz[i + offset] == stream->lumin_data.refresh_rate_hz[i]) 890 + return stream->lumin_data.refresh_rate_hz[i]; 891 + 892 + int target_brightness = (stream->lumin_data.luminance_millinits[i + offset] >= (current_brightness + flicker_criteria_millinits)) ? 893 + current_brightness + flicker_criteria_millinits : 894 + current_brightness - flicker_criteria_millinits; 895 + 896 + int refresh = 0; 897 + 898 + /* 899 + * Need the second input to be < third input for dc_stream_get_refresh_hz_linear_interpolation 900 + */ 901 + if (search_for_max_increase) 902 + refresh = dc_stream_get_refresh_hz_linear_interpolation(stream, i, i + offset, target_brightness); 903 + else 904 + refresh = dc_stream_get_refresh_hz_linear_interpolation(stream, i + offset, i, target_brightness); 905 + 906 + if (refresh == stream->lumin_data.refresh_rate_hz[i + offset]) 907 + return stream->lumin_data.refresh_rate_hz[i + offset]; 908 + 909 + return refresh; 910 + } 911 + } 912 + 913 + if (search_for_max_increase) 914 + return stream->lumin_data.refresh_rate_hz[LUMINANCE_DATA_TABLE_SIZE - 1]; 915 + else 916 + return stream->lumin_data.refresh_rate_hz[0]; 917 + } 918 + 919 + /* 920 + * Gets the max delta luminance within a specified refresh range 921 + */ 922 + static int dc_stream_get_max_delta_lumin_millinits(struct dc_stream_state *stream, int hz1, int hz2, bool isGaming) 923 + { 924 + int lower_refresh_brightness = dc_stream_get_brightness_millinits_from_refresh (stream, hz1); 925 + int higher_refresh_brightness = dc_stream_get_brightness_millinits_from_refresh (stream, hz2); 926 + 927 + int min = lower_refresh_brightness; 928 + int max = higher_refresh_brightness; 929 + 930 + /* 931 + * Static screen, therefore no need to scan through array 932 + */ 933 + if (!isGaming) { 934 + if (lower_refresh_brightness >= higher_refresh_brightness) { 935 + return lower_refresh_brightness - higher_refresh_brightness; 936 + } 937 + return higher_refresh_brightness - lower_refresh_brightness; 938 + } 939 + 940 + min = MIN(lower_refresh_brightness, higher_refresh_brightness); 941 + max = MAX(lower_refresh_brightness, higher_refresh_brightness); 942 + 943 + int nearest_smallest_index = dc_stream_get_nearest_smallest_index(stream, hz1); 944 + 945 + for (; nearest_smallest_index < (LUMINANCE_DATA_TABLE_SIZE - 1) && 946 + stream->lumin_data.refresh_rate_hz[nearest_smallest_index + 1] <= hz2 ; nearest_smallest_index++) { 947 + min = MIN(min, stream->lumin_data.luminance_millinits[nearest_smallest_index + 1]); 948 + max = MAX(max, stream->lumin_data.luminance_millinits[nearest_smallest_index + 1]); 949 + } 950 + 951 + return (max - min); 952 + } 953 + 954 + /* 955 + * Finds the highest refresh rate that can be achieved 956 + * from starting_refresh_hz while staying within flicker criteria 957 + */ 958 + int dc_stream_calculate_max_flickerless_refresh_rate(struct dc_stream_state *stream, int starting_refresh_hz, bool is_gaming) 959 + { 960 + if (!stream->lumin_data.is_valid) 961 + return 0; 962 + 963 + int current_brightness = dc_stream_get_brightness_millinits_from_refresh(stream, starting_refresh_hz); 964 + 965 + return dc_stream_calculate_flickerless_refresh_rate(stream, 966 + current_brightness, 967 + starting_refresh_hz, 968 + is_gaming, 969 + true); 970 + } 971 + 972 + /* 973 + * Finds the lowest refresh rate that can be achieved 974 + * from starting_refresh_hz while staying within flicker criteria 975 + */ 976 + int dc_stream_calculate_min_flickerless_refresh_rate(struct dc_stream_state *stream, int starting_refresh_hz, bool is_gaming) 977 + { 978 + if (!stream->lumin_data.is_valid) 979 + return 0; 980 + 981 + int current_brightness = dc_stream_get_brightness_millinits_from_refresh(stream, starting_refresh_hz); 982 + 983 + return dc_stream_calculate_flickerless_refresh_rate(stream, 984 + current_brightness, 985 + starting_refresh_hz, 986 + is_gaming, 987 + false); 988 + } 989 + 990 + /* 991 + * Determines if there will be a flicker when moving between 2 refresh rates 992 + */ 993 + bool dc_stream_is_refresh_rate_range_flickerless(struct dc_stream_state *stream, int hz1, int hz2, bool is_gaming) 994 + { 995 + 996 + /* 997 + * Assume that we wont flicker if there is invalid data 998 + */ 999 + if (!stream->lumin_data.is_valid) 1000 + return false; 1001 + 1002 + int dl = dc_stream_get_max_delta_lumin_millinits(stream, hz1, hz2, is_gaming); 1003 + 1004 + int flicker_criteria_millinits = (is_gaming) ? 1005 + stream->lumin_data.flicker_criteria_milli_nits_GAMING : 1006 + stream->lumin_data.flicker_criteria_milli_nits_STATIC; 1007 + 1008 + return (dl <= flicker_criteria_millinits); 1009 + }
+1
drivers/gpu/drm/amd/display/dc/dc.h
··· 456 456 bool allow_0_dtb_clk; 457 457 bool use_assr_psp_message; 458 458 bool support_edp0_on_dp1; 459 + unsigned int enable_fpo_flicker_detection; 459 460 }; 460 461 461 462 enum visual_confirm {
+14
drivers/gpu/drm/amd/display/dc/dc_stream.h
··· 160 160 char force_odm_combine_segments; 161 161 }; 162 162 163 + #define LUMINANCE_DATA_TABLE_SIZE 10 164 + 165 + struct luminance_data { 166 + bool is_valid; 167 + int refresh_rate_hz[LUMINANCE_DATA_TABLE_SIZE]; 168 + int luminance_millinits[LUMINANCE_DATA_TABLE_SIZE]; 169 + int flicker_criteria_milli_nits_GAMING; 170 + int flicker_criteria_milli_nits_STATIC; 171 + int nominal_refresh_rate; 172 + int dm_max_decrease_from_nominal; 173 + }; 174 + 163 175 struct dc_stream_state { 164 176 // sink is deprecated, new code should not reference 165 177 // this pointer ··· 298 286 bool vblank_synchronized; 299 287 bool fpo_in_use; 300 288 bool is_phantom; 289 + 290 + struct luminance_data lumin_data; 301 291 }; 302 292 303 293 #define ABM_LEVEL_IMMEDIATE_DISABLE 255
+24
drivers/gpu/drm/amd/display/dc/dc_stream_priv.h
··· 34 34 35 35 void dc_stream_assign_stream_id(struct dc_stream_state *stream); 36 36 37 + /* 38 + * Finds the highest refresh rate that can be achieved 39 + * from starting_freq while staying within flicker criteria 40 + */ 41 + int dc_stream_calculate_max_flickerless_refresh_rate(struct dc_stream_state *stream, 42 + int starting_refresh_hz, 43 + bool is_gaming); 44 + 45 + /* 46 + * Finds the lowest refresh rate that can be achieved 47 + * from starting_freq while staying within flicker criteria 48 + */ 49 + int dc_stream_calculate_min_flickerless_refresh_rate(struct dc_stream_state *stream, 50 + int starting_refresh_hz, 51 + bool is_gaming); 52 + 53 + /* 54 + * Determines if there will be a flicker when moving between 2 refresh rates 55 + */ 56 + bool dc_stream_is_refresh_rate_range_flickerless(struct dc_stream_state *stream, 57 + int hz1, 58 + int hz2, 59 + bool is_gaming); 60 + 37 61 #endif // _DC_STREAM_PRIV_H_
+7 -2
drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource_helpers.c
··· 29 29 #include "dml/dcn32/display_mode_vba_util_32.h" 30 30 #include "dml/dcn32/dcn32_fpu.h" 31 31 #include "dc_state_priv.h" 32 + #include "dc_stream_priv.h" 32 33 33 34 static bool is_dual_plane(enum surface_pixel_format format) 34 35 { ··· 460 459 } 461 460 462 461 static bool is_refresh_rate_support_mclk_switch_using_fw_based_vblank_stretch( 463 - struct dc_stream_state *fpo_candidate_stream, uint32_t fpo_vactive_margin_us) 462 + struct dc_stream_state *fpo_candidate_stream, uint32_t fpo_vactive_margin_us, int current_refresh_rate) 464 463 { 465 464 int refresh_rate_max_stretch_100hz; 466 465 int min_refresh_100hz; ··· 472 471 min_refresh_100hz = fpo_candidate_stream->timing.min_refresh_in_uhz / 10000; 473 472 474 473 if (refresh_rate_max_stretch_100hz < min_refresh_100hz) 474 + return false; 475 + 476 + if (fpo_candidate_stream->ctx->dc->config.enable_fpo_flicker_detection > 0 && 477 + !dc_stream_is_refresh_rate_range_flickerless(fpo_candidate_stream, (refresh_rate_max_stretch_100hz / 100), current_refresh_rate, false)) 475 478 return false; 476 479 477 480 return true; ··· 574 569 return NULL; 575 570 576 571 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 577 - if (!is_refresh_rate_support_mclk_switch_using_fw_based_vblank_stretch(fpo_candidate_stream, fpo_vactive_margin_us)) 572 + if (!is_refresh_rate_support_mclk_switch_using_fw_based_vblank_stretch(fpo_candidate_stream, fpo_vactive_margin_us, refresh_rate)) 578 573 return NULL; 579 574 580 575 if (!fpo_candidate_stream->allow_freesync)