drm/amd/display: Add support for Freesync 2 HDR and Content to Display Mapping
authorSivapiriyanKumarasamy <sivapiriyan.kumarasamy@amd.com>
Tue, 11 Sep 2018 21:48:07 +0000 (17:48 -0400)
committerAlex Deucher <alexander.deucher@amd.com>
Mon, 5 Nov 2018 19:20:43 +0000 (14:20 -0500)
[Why]
Freesync 2 HDR and support for HDR content
outside the range of the HDR display
require implementation on Dal 3 to better match
Dal2.

[How]
Add support for Freesync HDR and mapping
of source content to display ranges for better
representation of HDR content.

Signed-off-by: SivapiriyanKumarasamy <sivapiriyan.kumarasamy@amd.com>
Reviewed-by: Anthony Koo <Anthony.Koo@amd.com>
Acked-by: Bhawanpreet Lakha <Bhawanpreet.Lakha@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c
drivers/gpu/drm/amd/display/modules/color/color_gamma.c
drivers/gpu/drm/amd/display/modules/color/color_gamma.h

index be19e6861189c63e0fe85d16ad1a2bedc14cf58c..216e48cec71664f67eb7325b3dd0fc876d228a6a 100644 (file)
@@ -164,7 +164,7 @@ int amdgpu_dm_set_regamma_lut(struct dm_crtc_state *crtc)
         */
        stream->out_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS;
        ret = mod_color_calculate_regamma_params(stream->out_transfer_func,
-                                                gamma, true, adev->asic_type <= CHIP_RAVEN);
+                                                gamma, true, adev->asic_type <= CHIP_RAVEN, NULL);
        dc_gamma_release(&gamma);
        if (!ret) {
                stream->out_transfer_func->type = old_type;
index 5d95a997fd9f96e5539cf6e91fa3d05483cf9a8f..97c059934febf5a4b84c16edf5e4050022c7b52d 100644 (file)
@@ -268,7 +268,7 @@ bool cm_helper_translate_curve_to_hw_format(
        memset(lut_params, 0, sizeof(struct pwl_params));
        memset(seg_distr, 0, sizeof(seg_distr));
 
-       if (output_tf->tf == TRANSFER_FUNCTION_PQ) {
+       if (output_tf->tf == TRANSFER_FUNCTION_PQ || output_tf->tf == TRANSFER_FUNCTION_GAMMA22) {
                /* 32 segments
                 * segments are from 2^-25 to 2^7
                 */
index cdcefd08748784b353bed088aab4ed642213b8c9..2e215c9e54453e1232ab652af35ef93a87f311b1 100644 (file)
@@ -306,6 +306,18 @@ static struct fixed31_32 translate_from_linear_space(
                        a1);
 }
 
+static struct fixed31_32 calculate_gamma22(struct fixed31_32 arg)
+{
+       struct fixed31_32 gamma = dc_fixpt_from_fraction(22, 10);
+
+       return translate_from_linear_space(arg,
+                       dc_fixpt_zero,
+                       dc_fixpt_zero,
+                       dc_fixpt_zero,
+                       dc_fixpt_zero,
+                       gamma);
+}
+
 static struct fixed31_32 translate_to_linear_space(
        struct fixed31_32 arg,
        struct fixed31_32 a0,
@@ -709,6 +721,160 @@ static void build_regamma(struct pwl_float_data_ex *rgb_regamma,
        }
 }
 
+static void hermite_spline_eetf(struct fixed31_32 input_x,
+                               struct fixed31_32 max_display,
+                               struct fixed31_32 min_display,
+                               struct fixed31_32 max_content,
+                               struct fixed31_32 *out_x)
+{
+       struct fixed31_32 min_lum_pq;
+       struct fixed31_32 max_lum_pq;
+       struct fixed31_32 max_content_pq;
+       struct fixed31_32 ks;
+       struct fixed31_32 E1;
+       struct fixed31_32 E2;
+       struct fixed31_32 E3;
+       struct fixed31_32 t;
+       struct fixed31_32 t2;
+       struct fixed31_32 t3;
+       struct fixed31_32 two;
+       struct fixed31_32 three;
+       struct fixed31_32 temp1;
+       struct fixed31_32 temp2;
+       struct fixed31_32 a = dc_fixpt_from_fraction(15, 10);
+       struct fixed31_32 b = dc_fixpt_from_fraction(5, 10);
+       struct fixed31_32 epsilon = dc_fixpt_from_fraction(1, 1000000); // dc_fixpt_epsilon is a bit too small
+
+       if (dc_fixpt_eq(max_content, dc_fixpt_zero)) {
+               *out_x = dc_fixpt_zero;
+               return;
+       }
+
+       compute_pq(input_x, &E1);
+       compute_pq(dc_fixpt_div(min_display, max_content), &min_lum_pq);
+       compute_pq(dc_fixpt_div(max_display, max_content), &max_lum_pq);
+       compute_pq(dc_fixpt_one, &max_content_pq); // always 1? DAL2 code is weird
+       a = dc_fixpt_div(dc_fixpt_add(dc_fixpt_one, b), max_content_pq); // (1+b)/maxContent
+       ks = dc_fixpt_sub(dc_fixpt_mul(a, max_lum_pq), b); // a * max_lum_pq - b
+
+       if (dc_fixpt_lt(E1, ks))
+               E2 = E1;
+       else if (dc_fixpt_le(ks, E1) && dc_fixpt_le(E1, dc_fixpt_one)) {
+               if (dc_fixpt_lt(epsilon, dc_fixpt_sub(dc_fixpt_one, ks)))
+                       // t = (E1 - ks) / (1 - ks)
+                       t = dc_fixpt_div(dc_fixpt_sub(E1, ks),
+                                       dc_fixpt_sub(dc_fixpt_one, ks));
+               else
+                       t = dc_fixpt_zero;
+
+               two = dc_fixpt_from_int(2);
+               three = dc_fixpt_from_int(3);
+
+               t2 = dc_fixpt_mul(t, t);
+               t3 = dc_fixpt_mul(t2, t);
+               temp1 = dc_fixpt_mul(two, t3);
+               temp2 = dc_fixpt_mul(three, t2);
+
+               // (2t^3 - 3t^2 + 1) * ks
+               E2 = dc_fixpt_mul(ks, dc_fixpt_add(dc_fixpt_one,
+                               dc_fixpt_sub(temp1, temp2)));
+
+               // (-2t^3 + 3t^2) * max_lum_pq
+               E2 = dc_fixpt_add(E2, dc_fixpt_mul(max_lum_pq,
+                               dc_fixpt_sub(temp2, temp1)));
+
+               temp1 = dc_fixpt_mul(two, t2);
+               temp2 = dc_fixpt_sub(dc_fixpt_one, ks);
+
+               // (t^3 - 2t^2 + t) * (1-ks)
+               E2 = dc_fixpt_add(E2, dc_fixpt_mul(temp2,
+                               dc_fixpt_add(t, dc_fixpt_sub(t3, temp1))));
+       }
+
+       temp1 = dc_fixpt_sub(dc_fixpt_one, E2);
+       temp2 = dc_fixpt_mul(temp1, temp1);
+       temp2 = dc_fixpt_mul(temp2, temp2);
+       // temp2 = (1-E2)^4
+
+       E3 =  dc_fixpt_add(E2, dc_fixpt_mul(min_lum_pq, temp2));
+       compute_de_pq(E3, out_x);
+
+       *out_x = dc_fixpt_div(*out_x, dc_fixpt_div(max_display, max_content));
+}
+
+static bool build_freesync_hdr(struct pwl_float_data_ex *rgb_regamma,
+               uint32_t hw_points_num,
+               const struct hw_x_point *coordinate_x,
+               const struct freesync_hdr_tf_params *fs_params)
+{
+       uint32_t i;
+       struct pwl_float_data_ex *rgb = rgb_regamma;
+       const struct hw_x_point *coord_x = coordinate_x;
+       struct fixed31_32 scaledX = dc_fixpt_zero;
+       struct fixed31_32 scaledX1 = dc_fixpt_zero;
+       struct fixed31_32 max_display = dc_fixpt_from_int(fs_params->max_display);
+       struct fixed31_32 min_display = dc_fixpt_from_fraction(fs_params->min_display, 10000);
+       struct fixed31_32 max_content = dc_fixpt_from_int(fs_params->max_content);
+       struct fixed31_32 min_content = dc_fixpt_from_fraction(fs_params->min_content, 10000);
+       struct fixed31_32 clip = dc_fixpt_one;
+       struct fixed31_32 output;
+       bool use_eetf = false;
+       struct fixed31_32 sdr_white_level = dc_fixpt_from_int(fs_params->sdr_white_level);
+
+       if (fs_params == NULL || fs_params->max_content == 0 ||
+                       fs_params->max_display == 0)
+               return false;
+
+       if (fs_params->min_display > 1000) // cap at 0.1 at the bottom
+               min_display = dc_fixpt_from_fraction(1, 10);
+       if (fs_params->max_display < 100) // cap at 100 at the top
+               max_display = dc_fixpt_from_int(100);
+
+       if (fs_params->min_content < fs_params->min_display)
+               use_eetf = true;
+       else
+               min_content = min_display;
+
+       if (fs_params->max_content > fs_params->max_display)
+               use_eetf = true;
+       else
+               max_content = max_display;
+
+       rgb += 32; // first 32 points have problems with fixed point, too small
+       coord_x += 32;
+       for (i = 32; i <= hw_points_num; i++) {
+               if (use_eetf) {
+                       /*max content is equal 1 */
+                       scaledX1 = dc_fixpt_div(coord_x->x,
+                                       dc_fixpt_div(max_content, sdr_white_level));
+                       hermite_spline_eetf(scaledX1, max_display, min_display,
+                                       max_content, &scaledX);
+               } else
+                       scaledX = dc_fixpt_div(coord_x->x,
+                                       dc_fixpt_div(max_display, sdr_white_level));
+
+               if (dc_fixpt_lt(scaledX, clip)) {
+                       if (dc_fixpt_lt(scaledX, dc_fixpt_zero))
+                               output = dc_fixpt_zero;
+                       else
+                               output = calculate_gamma22(scaledX);
+
+                       rgb->r = output;
+                       rgb->g = output;
+                       rgb->b = output;
+               } else {
+                       rgb->r = clip;
+                       rgb->g = clip;
+                       rgb->b = clip;
+               }
+
+               ++coord_x;
+               ++rgb;
+       }
+
+       return true;
+}
+
 static void build_degamma(struct pwl_float_data_ex *curve,
                uint32_t hw_points_num,
                const struct hw_x_point *coordinate_x, bool is_2_4)
@@ -1356,7 +1522,8 @@ static bool map_regamma_hw_to_x_user(
 #define _EXTRA_POINTS 3
 
 bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf,
-               const struct dc_gamma *ramp, bool mapUserRamp, bool canRomBeUsed)
+               const struct dc_gamma *ramp, bool mapUserRamp, bool canRomBeUsed,
+               const struct freesync_hdr_tf_params *fs_params)
 {
        struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts;
        struct dividers dividers;
@@ -1424,6 +1591,12 @@ bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf,
                                MAX_HW_POINTS,
                                coordinates_x,
                                output_tf->sdr_ref_white_level);
+       } else if (tf == TRANSFER_FUNCTION_GAMMA22 &&
+                       fs_params != NULL) {
+               build_freesync_hdr(rgb_regamma,
+                               MAX_HW_POINTS,
+                               coordinates_x,
+                               fs_params);
        } else {
                tf_pts->end_exponent = 0;
                tf_pts->x_point_at_y1_red = 1;
index 63ccb9c91224796fbce9e78ab2b241f28c64514d..a6e164df090a669542cb195ee43552cffab2c23d 100644 (file)
@@ -73,12 +73,21 @@ struct regamma_lut {
        };
 };
 
+struct freesync_hdr_tf_params {
+       unsigned int sdr_white_level;
+       unsigned int min_content; // luminance in 1/10000 nits
+       unsigned int max_content; // luminance in nits
+       unsigned int min_display; // luminance in 1/10000 nits
+       unsigned int max_display; // luminance in nits
+};
+
 void setup_x_points_distribution(void);
 void precompute_pq(void);
 void precompute_de_pq(void);
 
 bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf,
-               const struct dc_gamma *ramp, bool mapUserRamp, bool canRomBeUsed);
+               const struct dc_gamma *ramp, bool mapUserRamp, bool canRomBeUsed,
+               const struct freesync_hdr_tf_params *fs_params);
 
 bool mod_color_calculate_degamma_params(struct dc_transfer_func *output_tf,
                const struct dc_gamma *ramp, bool mapUserRamp);