drm/amd/display: HLG support
authorVitaly Prosyak <vitaly.prosyak@amd.com>
Thu, 10 May 2018 17:37:35 +0000 (12:37 -0500)
committerAlex Deucher <alexander.deucher@amd.com>
Fri, 15 Jun 2018 17:20:26 +0000 (12:20 -0500)
Low level calculation methods.

Signed-off-by: Vitaly Prosyak <vitaly.prosyak@amd.com>
Reviewed-by: Tony Cheng <Tony.Cheng@amd.com>
Acked-by: Harry Wentland <harry.wentland@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/dc/dc.h
drivers/gpu/drm/amd/display/modules/color/color_gamma.c

index 22d113e..7a70a24 100644 (file)
@@ -378,6 +378,8 @@ enum dc_transfer_func_predefined {
        TRANSFER_FUNCTION_PQ,
        TRANSFER_FUNCTION_LINEAR,
        TRANSFER_FUNCTION_UNITY,
+       TRANSFER_FUNCTION_HLG,
+       TRANSFER_FUNCTION_HLG12
 };
 
 struct dc_transfer_func {
index 0cd111d..b422fa2 100644 (file)
@@ -131,6 +131,63 @@ static void compute_de_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y)
                        dc_fixpt_div(dc_fixpt_one, m1));
 
 }
+
+/*de gamma, none linear to linear*/
+static void compute_hlg_oetf(struct fixed31_32 in_x, bool is_light0_12, struct fixed31_32 *out_y)
+{
+       struct fixed31_32 a;
+       struct fixed31_32 b;
+       struct fixed31_32 c;
+       struct fixed31_32 threshold;
+       struct fixed31_32 reference_white_level;
+
+       a = dc_fixpt_from_fraction(17883277, 100000000);
+       if (is_light0_12) {
+               /*light 0-12*/
+               b = dc_fixpt_from_fraction(28466892, 100000000);
+               c = dc_fixpt_from_fraction(55991073, 100000000);
+               threshold = dc_fixpt_one;
+               reference_white_level = dc_fixpt_half;
+       } else {
+               /*light 0-1*/
+               b = dc_fixpt_from_fraction(2372241, 100000000);
+               c = dc_fixpt_add(dc_fixpt_one, dc_fixpt_from_fraction(429347, 100000000));
+               threshold = dc_fixpt_from_fraction(1, 12);
+               reference_white_level = dc_fixpt_pow(dc_fixpt_from_fraction(3, 1), dc_fixpt_half);
+       }
+       if (dc_fixpt_lt(threshold, in_x))
+               *out_y = dc_fixpt_add(c, dc_fixpt_mul(a, dc_fixpt_log(dc_fixpt_sub(in_x, b))));
+       else
+               *out_y = dc_fixpt_mul(dc_fixpt_pow(in_x, dc_fixpt_half), reference_white_level);
+}
+
+/*re gamma, linear to none linear*/
+static void compute_hlg_eotf(struct fixed31_32 in_x, bool is_light0_12, struct fixed31_32 *out_y)
+{
+       struct fixed31_32 a;
+       struct fixed31_32 b;
+       struct fixed31_32 c;
+       struct fixed31_32 reference_white_level;
+
+       a = dc_fixpt_from_fraction(17883277, 100000000);
+       if (is_light0_12) {
+               /*light 0-12*/
+               b = dc_fixpt_from_fraction(28466892, 100000000);
+               c = dc_fixpt_from_fraction(55991073, 100000000);
+               reference_white_level = dc_fixpt_from_fraction(4, 1);
+       } else {
+               /*light 0-1*/
+               b = dc_fixpt_from_fraction(2372241, 100000000);
+               c = dc_fixpt_add(dc_fixpt_one, dc_fixpt_from_fraction(429347, 100000000));
+               reference_white_level = dc_fixpt_from_fraction(1, 3);
+       }
+       if (dc_fixpt_lt(dc_fixpt_half, in_x))
+               *out_y = dc_fixpt_add(dc_fixpt_exp(dc_fixpt_div(dc_fixpt_sub(in_x, c), a)), b);
+       else
+               *out_y = dc_fixpt_mul(dc_fixpt_pow(in_x, dc_fixpt_from_fraction(2, 1)), reference_white_level);
+}
+
+
 /* one-time pre-compute PQ values - only for sdr_white_level 80 */
 void precompute_pq(void)
 {
@@ -691,6 +748,48 @@ static void build_degamma(struct pwl_float_data_ex *curve,
        }
 }
 
+static void build_hlg_degamma(struct pwl_float_data_ex *degamma,
+               uint32_t hw_points_num,
+               const struct hw_x_point *coordinate_x, bool is_light0_12)
+{
+       uint32_t i;
+
+       struct pwl_float_data_ex *rgb = degamma;
+       const struct hw_x_point *coord_x = coordinate_x;
+
+       i = 0;
+
+       while (i != hw_points_num + 1) {
+               compute_hlg_oetf(coord_x->x, is_light0_12, &rgb->r);
+               rgb->g = rgb->r;
+               rgb->b = rgb->r;
+               ++coord_x;
+               ++rgb;
+               ++i;
+       }
+}
+
+static void build_hlg_regamma(struct pwl_float_data_ex *regamma,
+               uint32_t hw_points_num,
+               const struct hw_x_point *coordinate_x, bool is_light0_12)
+{
+       uint32_t i;
+
+       struct pwl_float_data_ex *rgb = regamma;
+       const struct hw_x_point *coord_x = coordinate_x;
+
+       i = 0;
+
+       while (i != hw_points_num + 1) {
+               compute_hlg_eotf(coord_x->x, is_light0_12, &rgb->r);
+               rgb->g = rgb->r;
+               rgb->b = rgb->r;
+               ++coord_x;
+               ++rgb;
+               ++i;
+       }
+}
+
 static void scale_gamma(struct pwl_float_data *pwl_rgb,
                const struct dc_gamma *ramp,
                struct dividers dividers)
@@ -1615,6 +1714,25 @@ bool  mod_color_calculate_curve(enum dc_transfer_func_predefined trans,
                ret = true;
 
                kvfree(rgb_regamma);
+       } else if (trans == TRANSFER_FUNCTION_HLG ||
+               trans == TRANSFER_FUNCTION_HLG12) {
+               rgb_regamma = kvzalloc(sizeof(*rgb_regamma) *
+                                      (MAX_HW_POINTS + _EXTRA_POINTS),
+                                      GFP_KERNEL);
+               if (!rgb_regamma)
+                       goto rgb_regamma_alloc_fail;
+
+               build_hlg_regamma(rgb_regamma,
+                               MAX_HW_POINTS,
+                               coordinates_x,
+                               trans == TRANSFER_FUNCTION_HLG12 ? true:false);
+               for (i = 0; i <= MAX_HW_POINTS ; i++) {
+                       points->red[i]    = rgb_regamma[i].r;
+                       points->green[i]  = rgb_regamma[i].g;
+                       points->blue[i]   = rgb_regamma[i].b;
+               }
+               ret = true;
+               kvfree(rgb_regamma);
        }
 rgb_regamma_alloc_fail:
        return ret;
@@ -1675,6 +1793,25 @@ bool  mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans,
                ret = true;
 
                kvfree(rgb_degamma);
+       } else if (trans == TRANSFER_FUNCTION_HLG ||
+               trans == TRANSFER_FUNCTION_HLG12) {
+               rgb_degamma = kvzalloc(sizeof(*rgb_degamma) *
+                                      (MAX_HW_POINTS + _EXTRA_POINTS),
+                                      GFP_KERNEL);
+               if (!rgb_degamma)
+                       goto rgb_degamma_alloc_fail;
+
+               build_hlg_degamma(rgb_degamma,
+                               MAX_HW_POINTS,
+                               coordinates_x,
+                               trans == TRANSFER_FUNCTION_HLG12 ? true:false);
+               for (i = 0; i <= MAX_HW_POINTS ; i++) {
+                       points->red[i]    = rgb_degamma[i].r;
+                       points->green[i]  = rgb_degamma[i].g;
+                       points->blue[i]   = rgb_degamma[i].b;
+               }
+               ret = true;
+               kvfree(rgb_degamma);
        }
        points->end_exponent = 0;
        points->x_point_at_y1_red = 1;