diff options
Diffstat (limited to 'drivers/gpu/drm/amd/display/modules')
3 files changed, 203 insertions, 31 deletions
diff --git a/drivers/gpu/drm/amd/display/modules/color/color_gamma.c b/drivers/gpu/drm/amd/display/modules/color/color_gamma.c index cdcefd087487..7480f072c375 100644 --- a/drivers/gpu/drm/amd/display/modules/color/color_gamma.c +++ b/drivers/gpu/drm/amd/display/modules/color/color_gamma.c | |||
@@ -306,6 +306,18 @@ static struct fixed31_32 translate_from_linear_space( | |||
306 | a1); | 306 | a1); |
307 | } | 307 | } |
308 | 308 | ||
309 | static struct fixed31_32 calculate_gamma22(struct fixed31_32 arg) | ||
310 | { | ||
311 | struct fixed31_32 gamma = dc_fixpt_from_fraction(22, 10); | ||
312 | |||
313 | return translate_from_linear_space(arg, | ||
314 | dc_fixpt_zero, | ||
315 | dc_fixpt_zero, | ||
316 | dc_fixpt_zero, | ||
317 | dc_fixpt_zero, | ||
318 | gamma); | ||
319 | } | ||
320 | |||
309 | static struct fixed31_32 translate_to_linear_space( | 321 | static struct fixed31_32 translate_to_linear_space( |
310 | struct fixed31_32 arg, | 322 | struct fixed31_32 arg, |
311 | struct fixed31_32 a0, | 323 | struct fixed31_32 a0, |
@@ -709,6 +721,169 @@ static void build_regamma(struct pwl_float_data_ex *rgb_regamma, | |||
709 | } | 721 | } |
710 | } | 722 | } |
711 | 723 | ||
724 | static void hermite_spline_eetf(struct fixed31_32 input_x, | ||
725 | struct fixed31_32 max_display, | ||
726 | struct fixed31_32 min_display, | ||
727 | struct fixed31_32 max_content, | ||
728 | struct fixed31_32 *out_x) | ||
729 | { | ||
730 | struct fixed31_32 min_lum_pq; | ||
731 | struct fixed31_32 max_lum_pq; | ||
732 | struct fixed31_32 max_content_pq; | ||
733 | struct fixed31_32 ks; | ||
734 | struct fixed31_32 E1; | ||
735 | struct fixed31_32 E2; | ||
736 | struct fixed31_32 E3; | ||
737 | struct fixed31_32 t; | ||
738 | struct fixed31_32 t2; | ||
739 | struct fixed31_32 t3; | ||
740 | struct fixed31_32 two; | ||
741 | struct fixed31_32 three; | ||
742 | struct fixed31_32 temp1; | ||
743 | struct fixed31_32 temp2; | ||
744 | struct fixed31_32 a = dc_fixpt_from_fraction(15, 10); | ||
745 | struct fixed31_32 b = dc_fixpt_from_fraction(5, 10); | ||
746 | struct fixed31_32 epsilon = dc_fixpt_from_fraction(1, 1000000); // dc_fixpt_epsilon is a bit too small | ||
747 | |||
748 | if (dc_fixpt_eq(max_content, dc_fixpt_zero)) { | ||
749 | *out_x = dc_fixpt_zero; | ||
750 | return; | ||
751 | } | ||
752 | |||
753 | compute_pq(input_x, &E1); | ||
754 | compute_pq(dc_fixpt_div(min_display, max_content), &min_lum_pq); | ||
755 | compute_pq(dc_fixpt_div(max_display, max_content), &max_lum_pq); | ||
756 | compute_pq(dc_fixpt_one, &max_content_pq); // always 1? DAL2 code is weird | ||
757 | a = dc_fixpt_div(dc_fixpt_add(dc_fixpt_one, b), max_content_pq); // (1+b)/maxContent | ||
758 | ks = dc_fixpt_sub(dc_fixpt_mul(a, max_lum_pq), b); // a * max_lum_pq - b | ||
759 | |||
760 | if (dc_fixpt_lt(E1, ks)) | ||
761 | E2 = E1; | ||
762 | else if (dc_fixpt_le(ks, E1) && dc_fixpt_le(E1, dc_fixpt_one)) { | ||
763 | if (dc_fixpt_lt(epsilon, dc_fixpt_sub(dc_fixpt_one, ks))) | ||
764 | // t = (E1 - ks) / (1 - ks) | ||
765 | t = dc_fixpt_div(dc_fixpt_sub(E1, ks), | ||
766 | dc_fixpt_sub(dc_fixpt_one, ks)); | ||
767 | else | ||
768 | t = dc_fixpt_zero; | ||
769 | |||
770 | two = dc_fixpt_from_int(2); | ||
771 | three = dc_fixpt_from_int(3); | ||
772 | |||
773 | t2 = dc_fixpt_mul(t, t); | ||
774 | t3 = dc_fixpt_mul(t2, t); | ||
775 | temp1 = dc_fixpt_mul(two, t3); | ||
776 | temp2 = dc_fixpt_mul(three, t2); | ||
777 | |||
778 | // (2t^3 - 3t^2 + 1) * ks | ||
779 | E2 = dc_fixpt_mul(ks, dc_fixpt_add(dc_fixpt_one, | ||
780 | dc_fixpt_sub(temp1, temp2))); | ||
781 | |||
782 | // (-2t^3 + 3t^2) * max_lum_pq | ||
783 | E2 = dc_fixpt_add(E2, dc_fixpt_mul(max_lum_pq, | ||
784 | dc_fixpt_sub(temp2, temp1))); | ||
785 | |||
786 | temp1 = dc_fixpt_mul(two, t2); | ||
787 | temp2 = dc_fixpt_sub(dc_fixpt_one, ks); | ||
788 | |||
789 | // (t^3 - 2t^2 + t) * (1-ks) | ||
790 | E2 = dc_fixpt_add(E2, dc_fixpt_mul(temp2, | ||
791 | dc_fixpt_add(t, dc_fixpt_sub(t3, temp1)))); | ||
792 | } else | ||
793 | E2 = dc_fixpt_one; | ||
794 | |||
795 | temp1 = dc_fixpt_sub(dc_fixpt_one, E2); | ||
796 | temp2 = dc_fixpt_mul(temp1, temp1); | ||
797 | temp2 = dc_fixpt_mul(temp2, temp2); | ||
798 | // temp2 = (1-E2)^4 | ||
799 | |||
800 | E3 = dc_fixpt_add(E2, dc_fixpt_mul(min_lum_pq, temp2)); | ||
801 | compute_de_pq(E3, out_x); | ||
802 | |||
803 | *out_x = dc_fixpt_div(*out_x, dc_fixpt_div(max_display, max_content)); | ||
804 | } | ||
805 | |||
806 | static bool build_freesync_hdr(struct pwl_float_data_ex *rgb_regamma, | ||
807 | uint32_t hw_points_num, | ||
808 | const struct hw_x_point *coordinate_x, | ||
809 | const struct freesync_hdr_tf_params *fs_params) | ||
810 | { | ||
811 | uint32_t i; | ||
812 | struct pwl_float_data_ex *rgb = rgb_regamma; | ||
813 | const struct hw_x_point *coord_x = coordinate_x; | ||
814 | struct fixed31_32 scaledX = dc_fixpt_zero; | ||
815 | struct fixed31_32 scaledX1 = dc_fixpt_zero; | ||
816 | struct fixed31_32 max_display = dc_fixpt_from_int(fs_params->max_display); | ||
817 | struct fixed31_32 min_display = dc_fixpt_from_fraction(fs_params->min_display, 10000); | ||
818 | struct fixed31_32 max_content = dc_fixpt_from_int(fs_params->max_content); | ||
819 | struct fixed31_32 min_content = dc_fixpt_from_fraction(fs_params->min_content, 10000); | ||
820 | struct fixed31_32 clip = dc_fixpt_one; | ||
821 | struct fixed31_32 output; | ||
822 | bool use_eetf = false; | ||
823 | bool is_clipped = false; | ||
824 | struct fixed31_32 sdr_white_level = dc_fixpt_from_int(fs_params->sdr_white_level); | ||
825 | |||
826 | if (fs_params == NULL || fs_params->max_content == 0 || | ||
827 | fs_params->max_display == 0) | ||
828 | return false; | ||
829 | |||
830 | if (fs_params->min_display > 1000) // cap at 0.1 at the bottom | ||
831 | min_display = dc_fixpt_from_fraction(1, 10); | ||
832 | if (fs_params->max_display < 100) // cap at 100 at the top | ||
833 | max_display = dc_fixpt_from_int(100); | ||
834 | |||
835 | if (fs_params->min_content < fs_params->min_display) | ||
836 | use_eetf = true; | ||
837 | else | ||
838 | min_content = min_display; | ||
839 | |||
840 | if (fs_params->max_content > fs_params->max_display) | ||
841 | use_eetf = true; | ||
842 | else | ||
843 | max_content = max_display; | ||
844 | |||
845 | rgb += 32; // first 32 points have problems with fixed point, too small | ||
846 | coord_x += 32; | ||
847 | for (i = 32; i <= hw_points_num; i++) { | ||
848 | if (!is_clipped) { | ||
849 | if (use_eetf) { | ||
850 | /*max content is equal 1 */ | ||
851 | scaledX1 = dc_fixpt_div(coord_x->x, | ||
852 | dc_fixpt_div(max_content, sdr_white_level)); | ||
853 | hermite_spline_eetf(scaledX1, max_display, min_display, | ||
854 | max_content, &scaledX); | ||
855 | } else | ||
856 | scaledX = dc_fixpt_div(coord_x->x, | ||
857 | dc_fixpt_div(max_display, sdr_white_level)); | ||
858 | |||
859 | if (dc_fixpt_lt(scaledX, clip)) { | ||
860 | if (dc_fixpt_lt(scaledX, dc_fixpt_zero)) | ||
861 | output = dc_fixpt_zero; | ||
862 | else | ||
863 | output = calculate_gamma22(scaledX); | ||
864 | |||
865 | rgb->r = output; | ||
866 | rgb->g = output; | ||
867 | rgb->b = output; | ||
868 | } else { | ||
869 | is_clipped = true; | ||
870 | rgb->r = clip; | ||
871 | rgb->g = clip; | ||
872 | rgb->b = clip; | ||
873 | } | ||
874 | } else { | ||
875 | rgb->r = clip; | ||
876 | rgb->g = clip; | ||
877 | rgb->b = clip; | ||
878 | } | ||
879 | |||
880 | ++coord_x; | ||
881 | ++rgb; | ||
882 | } | ||
883 | |||
884 | return true; | ||
885 | } | ||
886 | |||
712 | static void build_degamma(struct pwl_float_data_ex *curve, | 887 | static void build_degamma(struct pwl_float_data_ex *curve, |
713 | uint32_t hw_points_num, | 888 | uint32_t hw_points_num, |
714 | const struct hw_x_point *coordinate_x, bool is_2_4) | 889 | const struct hw_x_point *coordinate_x, bool is_2_4) |
@@ -1356,7 +1531,8 @@ static bool map_regamma_hw_to_x_user( | |||
1356 | #define _EXTRA_POINTS 3 | 1531 | #define _EXTRA_POINTS 3 |
1357 | 1532 | ||
1358 | bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf, | 1533 | bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf, |
1359 | const struct dc_gamma *ramp, bool mapUserRamp, bool canRomBeUsed) | 1534 | const struct dc_gamma *ramp, bool mapUserRamp, bool canRomBeUsed, |
1535 | const struct freesync_hdr_tf_params *fs_params) | ||
1360 | { | 1536 | { |
1361 | struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts; | 1537 | struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts; |
1362 | struct dividers dividers; | 1538 | struct dividers dividers; |
@@ -1374,7 +1550,7 @@ bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf, | |||
1374 | /* we can use hardcoded curve for plain SRGB TF */ | 1550 | /* we can use hardcoded curve for plain SRGB TF */ |
1375 | if (output_tf->type == TF_TYPE_PREDEFINED && canRomBeUsed == true && | 1551 | if (output_tf->type == TF_TYPE_PREDEFINED && canRomBeUsed == true && |
1376 | output_tf->tf == TRANSFER_FUNCTION_SRGB && | 1552 | output_tf->tf == TRANSFER_FUNCTION_SRGB && |
1377 | (!mapUserRamp && ramp->type == GAMMA_RGB_256)) | 1553 | (ramp->is_identity || (!mapUserRamp && ramp->type == GAMMA_RGB_256))) |
1378 | return true; | 1554 | return true; |
1379 | 1555 | ||
1380 | output_tf->type = TF_TYPE_DISTRIBUTED_POINTS; | 1556 | output_tf->type = TF_TYPE_DISTRIBUTED_POINTS; |
@@ -1424,6 +1600,12 @@ bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf, | |||
1424 | MAX_HW_POINTS, | 1600 | MAX_HW_POINTS, |
1425 | coordinates_x, | 1601 | coordinates_x, |
1426 | output_tf->sdr_ref_white_level); | 1602 | output_tf->sdr_ref_white_level); |
1603 | } else if (tf == TRANSFER_FUNCTION_GAMMA22 && | ||
1604 | fs_params != NULL) { | ||
1605 | build_freesync_hdr(rgb_regamma, | ||
1606 | MAX_HW_POINTS, | ||
1607 | coordinates_x, | ||
1608 | fs_params); | ||
1427 | } else { | 1609 | } else { |
1428 | tf_pts->end_exponent = 0; | 1610 | tf_pts->end_exponent = 0; |
1429 | tf_pts->x_point_at_y1_red = 1; | 1611 | tf_pts->x_point_at_y1_red = 1; |
diff --git a/drivers/gpu/drm/amd/display/modules/color/color_gamma.h b/drivers/gpu/drm/amd/display/modules/color/color_gamma.h index 63ccb9c91224..a6e164df090a 100644 --- a/drivers/gpu/drm/amd/display/modules/color/color_gamma.h +++ b/drivers/gpu/drm/amd/display/modules/color/color_gamma.h | |||
@@ -73,12 +73,21 @@ struct regamma_lut { | |||
73 | }; | 73 | }; |
74 | }; | 74 | }; |
75 | 75 | ||
76 | struct freesync_hdr_tf_params { | ||
77 | unsigned int sdr_white_level; | ||
78 | unsigned int min_content; // luminance in 1/10000 nits | ||
79 | unsigned int max_content; // luminance in nits | ||
80 | unsigned int min_display; // luminance in 1/10000 nits | ||
81 | unsigned int max_display; // luminance in nits | ||
82 | }; | ||
83 | |||
76 | void setup_x_points_distribution(void); | 84 | void setup_x_points_distribution(void); |
77 | void precompute_pq(void); | 85 | void precompute_pq(void); |
78 | void precompute_de_pq(void); | 86 | void precompute_de_pq(void); |
79 | 87 | ||
80 | bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf, | 88 | bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf, |
81 | const struct dc_gamma *ramp, bool mapUserRamp, bool canRomBeUsed); | 89 | const struct dc_gamma *ramp, bool mapUserRamp, bool canRomBeUsed, |
90 | const struct freesync_hdr_tf_params *fs_params); | ||
82 | 91 | ||
83 | bool mod_color_calculate_degamma_params(struct dc_transfer_func *output_tf, | 92 | bool mod_color_calculate_degamma_params(struct dc_transfer_func *output_tf, |
84 | const struct dc_gamma *ramp, bool mapUserRamp); | 93 | const struct dc_gamma *ramp, bool mapUserRamp); |
diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c index 4018c7180d00..620a171620ee 100644 --- a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c +++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c | |||
@@ -37,6 +37,8 @@ | |||
37 | #define RENDER_TIMES_MAX_COUNT 10 | 37 | #define RENDER_TIMES_MAX_COUNT 10 |
38 | /* Threshold to exit BTR (to avoid frequent enter-exits at the lower limit) */ | 38 | /* Threshold to exit BTR (to avoid frequent enter-exits at the lower limit) */ |
39 | #define BTR_EXIT_MARGIN 2000 | 39 | #define BTR_EXIT_MARGIN 2000 |
40 | /*Threshold to exit fixed refresh rate*/ | ||
41 | #define FIXED_REFRESH_EXIT_MARGIN_IN_HZ 4 | ||
40 | /* Number of consecutive frames to check before entering/exiting fixed refresh*/ | 42 | /* Number of consecutive frames to check before entering/exiting fixed refresh*/ |
41 | #define FIXED_REFRESH_ENTER_FRAME_COUNT 5 | 43 | #define FIXED_REFRESH_ENTER_FRAME_COUNT 5 |
42 | #define FIXED_REFRESH_EXIT_FRAME_COUNT 5 | 44 | #define FIXED_REFRESH_EXIT_FRAME_COUNT 5 |
@@ -257,40 +259,14 @@ static void apply_below_the_range(struct core_freesync *core_freesync, | |||
257 | if (in_out_vrr->btr.btr_active) { | 259 | if (in_out_vrr->btr.btr_active) { |
258 | in_out_vrr->btr.frame_counter = 0; | 260 | in_out_vrr->btr.frame_counter = 0; |
259 | in_out_vrr->btr.btr_active = false; | 261 | in_out_vrr->btr.btr_active = false; |
260 | |||
261 | /* Exit Fixed Refresh mode */ | ||
262 | } else if (in_out_vrr->fixed.fixed_active) { | ||
263 | |||
264 | in_out_vrr->fixed.frame_counter++; | ||
265 | |||
266 | if (in_out_vrr->fixed.frame_counter > | ||
267 | FIXED_REFRESH_EXIT_FRAME_COUNT) { | ||
268 | in_out_vrr->fixed.frame_counter = 0; | ||
269 | in_out_vrr->fixed.fixed_active = false; | ||
270 | } | ||
271 | } | 262 | } |
272 | } else if (last_render_time_in_us > max_render_time_in_us) { | 263 | } else if (last_render_time_in_us > max_render_time_in_us) { |
273 | /* Enter Below the Range */ | 264 | /* Enter Below the Range */ |
274 | if (!in_out_vrr->btr.btr_active && | 265 | in_out_vrr->btr.btr_active = true; |
275 | in_out_vrr->btr.btr_enabled) { | ||
276 | in_out_vrr->btr.btr_active = true; | ||
277 | |||
278 | /* Enter Fixed Refresh mode */ | ||
279 | } else if (!in_out_vrr->fixed.fixed_active && | ||
280 | !in_out_vrr->btr.btr_enabled) { | ||
281 | in_out_vrr->fixed.frame_counter++; | ||
282 | |||
283 | if (in_out_vrr->fixed.frame_counter > | ||
284 | FIXED_REFRESH_ENTER_FRAME_COUNT) { | ||
285 | in_out_vrr->fixed.frame_counter = 0; | ||
286 | in_out_vrr->fixed.fixed_active = true; | ||
287 | } | ||
288 | } | ||
289 | } | 266 | } |
290 | 267 | ||
291 | /* BTR set to "not active" so disengage */ | 268 | /* BTR set to "not active" so disengage */ |
292 | if (!in_out_vrr->btr.btr_active) { | 269 | if (!in_out_vrr->btr.btr_active) { |
293 | in_out_vrr->btr.btr_active = false; | ||
294 | in_out_vrr->btr.inserted_duration_in_us = 0; | 270 | in_out_vrr->btr.inserted_duration_in_us = 0; |
295 | in_out_vrr->btr.frames_to_insert = 0; | 271 | in_out_vrr->btr.frames_to_insert = 0; |
296 | in_out_vrr->btr.frame_counter = 0; | 272 | in_out_vrr->btr.frame_counter = 0; |
@@ -375,7 +351,12 @@ static void apply_fixed_refresh(struct core_freesync *core_freesync, | |||
375 | bool update = false; | 351 | bool update = false; |
376 | unsigned int max_render_time_in_us = in_out_vrr->max_duration_in_us; | 352 | unsigned int max_render_time_in_us = in_out_vrr->max_duration_in_us; |
377 | 353 | ||
378 | if (last_render_time_in_us + BTR_EXIT_MARGIN < max_render_time_in_us) { | 354 | //Compute the exit refresh rate and exit frame duration |
355 | unsigned int exit_refresh_rate_in_milli_hz = ((1000000000/max_render_time_in_us) | ||
356 | + (1000*FIXED_REFRESH_EXIT_MARGIN_IN_HZ)); | ||
357 | unsigned int exit_frame_duration_in_us = 1000000000/exit_refresh_rate_in_milli_hz; | ||
358 | |||
359 | if (last_render_time_in_us < exit_frame_duration_in_us) { | ||
379 | /* Exit Fixed Refresh mode */ | 360 | /* Exit Fixed Refresh mode */ |
380 | if (in_out_vrr->fixed.fixed_active) { | 361 | if (in_out_vrr->fixed.fixed_active) { |
381 | in_out_vrr->fixed.frame_counter++; | 362 | in_out_vrr->fixed.frame_counter++; |