Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 43610a9b authored by Vitaly Prosyak's avatar Vitaly Prosyak Committed by Alex Deucher
Browse files

drm/amd/display: HLG support



Low level calculation methods.

Signed-off-by: default avatarVitaly Prosyak <vitaly.prosyak@amd.com>
Reviewed-by: default avatarTony Cheng <Tony.Cheng@amd.com>
Acked-by: default avatarHarry Wentland <harry.wentland@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 1a05873f
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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 {
+137 −0
Original line number Diff line number Diff line
@@ -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)
@@ -1614,6 +1713,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:
@@ -1674,6 +1792,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;