Commit 55a01d40 authored by Krunoslav Kovac's avatar Krunoslav Kovac Committed by Alex Deucher
Browse files

drm/amd/display: Add user_regamma to color module

parent 8a79593d
Loading
Loading
Loading
Loading
+301 −13
Original line number Diff line number Diff line
@@ -691,7 +691,7 @@ static void build_degamma(struct pwl_float_data_ex *curve,
	}
}

static bool scale_gamma(struct pwl_float_data *pwl_rgb,
static void scale_gamma(struct pwl_float_data *pwl_rgb,
		const struct dc_gamma *ramp,
		struct dividers dividers)
{
@@ -752,11 +752,9 @@ static bool scale_gamma(struct pwl_float_data *pwl_rgb,
			dividers.divider3);
	rgb->b = dal_fixed31_32_mul(rgb_last->b,
			dividers.divider3);

	return true;
}

static bool scale_gamma_dx(struct pwl_float_data *pwl_rgb,
static void scale_gamma_dx(struct pwl_float_data *pwl_rgb,
		const struct dc_gamma *ramp,
		struct dividers dividers)
{
@@ -818,8 +816,71 @@ static bool scale_gamma_dx(struct pwl_float_data *pwl_rgb,
				pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g);
	pwl_rgb[i].b =  dal_fixed31_32_sub(dal_fixed31_32_mul_int(
				pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b);
}

	return true;
/* todo: all these scale_gamma functions are inherently the same but
 *  take different structures as params or different format for ramp
 *  values. We could probably implement it in a more generic fashion
 */
static void scale_user_regamma_ramp(struct pwl_float_data *pwl_rgb,
		const struct regamma_ramp *ramp,
		struct dividers dividers)
{
	unsigned short max_driver = 0xFFFF;
	unsigned short max_os = 0xFF00;
	unsigned short scaler = max_os;
	uint32_t i;
	struct pwl_float_data *rgb = pwl_rgb;
	struct pwl_float_data *rgb_last = rgb + GAMMA_RGB_256_ENTRIES - 1;

	i = 0;
	do {
		if (ramp->gamma[i] > max_os ||
				ramp->gamma[i + 256] > max_os ||
				ramp->gamma[i + 512] > max_os) {
			scaler = max_driver;
			break;
		}
		i++;
	} while (i != GAMMA_RGB_256_ENTRIES);

	i = 0;
	do {
		rgb->r = dal_fixed31_32_from_fraction(
				ramp->gamma[i], scaler);
		rgb->g = dal_fixed31_32_from_fraction(
				ramp->gamma[i + 256], scaler);
		rgb->b = dal_fixed31_32_from_fraction(
				ramp->gamma[i + 512], scaler);

		++rgb;
		++i;
	} while (i != GAMMA_RGB_256_ENTRIES);

	rgb->r = dal_fixed31_32_mul(rgb_last->r,
			dividers.divider1);
	rgb->g = dal_fixed31_32_mul(rgb_last->g,
			dividers.divider1);
	rgb->b = dal_fixed31_32_mul(rgb_last->b,
			dividers.divider1);

	++rgb;

	rgb->r = dal_fixed31_32_mul(rgb_last->r,
			dividers.divider2);
	rgb->g = dal_fixed31_32_mul(rgb_last->g,
			dividers.divider2);
	rgb->b = dal_fixed31_32_mul(rgb_last->b,
			dividers.divider2);

	++rgb;

	rgb->r = dal_fixed31_32_mul(rgb_last->r,
			dividers.divider3);
	rgb->g = dal_fixed31_32_mul(rgb_last->g,
			dividers.divider3);
	rgb->b = dal_fixed31_32_mul(rgb_last->b,
			dividers.divider3);
}

/*
@@ -949,7 +1010,7 @@ static inline void copy_rgb_regamma_to_coordinates_x(
	uint32_t i = 0;
	const struct pwl_float_data_ex *rgb_regamma = rgb_ex;

	while (i <= hw_points_num) {
	while (i <= hw_points_num + 1) {
		coords->regamma_y_red = rgb_regamma->r;
		coords->regamma_y_green = rgb_regamma->g;
		coords->regamma_y_blue = rgb_regamma->b;
@@ -1002,6 +1063,102 @@ static bool calculate_interpolated_hardware_curve(
	return true;
}

/* The "old" interpolation uses a complicated scheme to build an array of
 * coefficients while also using an array of 0-255 normalized to 0-1
 * Then there's another loop using both of the above + new scaled user ramp
 * and we concatenate them. It also searches for points of interpolation and
 * uses enums for positions.
 *
 * This function uses a different approach:
 * user ramp is always applied on X with 0/255, 1/255, 2/255, ..., 255/255
 * To find index for hwX , we notice the following:
 * i/255 <= hwX < (i+1)/255  <=> i <= 255*hwX < i+1
 * See apply_lut_1d which is the same principle, but on 4K entry 1D LUT
 *
 * Once the index is known, combined Y is simply:
 * user_ramp(index) + (hwX-index/255)*(user_ramp(index+1) - user_ramp(index)
 *
 * We should switch to this method in all cases, it's simpler and faster
 * ToDo one day - for now this only applies to ADL regamma to avoid regression
 * for regular use cases (sRGB and PQ)
 */
static void interpolate_user_regamma(uint32_t hw_points_num,
		struct pwl_float_data *rgb_user,
		bool apply_degamma,
		struct dc_transfer_func_distributed_points *tf_pts)
{
	uint32_t i;
	uint32_t color = 0;
	int32_t index;
	int32_t index_next;
	struct fixed31_32 *tf_point;
	struct fixed31_32 hw_x;
	struct fixed31_32 norm_factor =
			dal_fixed31_32_from_int_nonconst(255);
	struct fixed31_32 norm_x;
	struct fixed31_32 index_f;
	struct fixed31_32 lut1;
	struct fixed31_32 lut2;
	struct fixed31_32 delta_lut;
	struct fixed31_32 delta_index;

	i = 0;
	/* fixed_pt library has problems handling too small values */
	while (i != 32) {
		tf_pts->red[i] = dal_fixed31_32_zero;
		tf_pts->green[i] = dal_fixed31_32_zero;
		tf_pts->blue[i] = dal_fixed31_32_zero;
		++i;
	}
	while (i <= hw_points_num + 1) {
		for (color = 0; color < 3; color++) {
			if (color == 0)
				tf_point = &tf_pts->red[i];
			else if (color == 1)
				tf_point = &tf_pts->green[i];
			else
				tf_point = &tf_pts->blue[i];

			if (apply_degamma) {
				if (color == 0)
					hw_x = coordinates_x[i].regamma_y_red;
				else if (color == 1)
					hw_x = coordinates_x[i].regamma_y_green;
				else
					hw_x = coordinates_x[i].regamma_y_blue;
			} else
				hw_x = coordinates_x[i].x;

			norm_x = dal_fixed31_32_mul(norm_factor, hw_x);
			index = dal_fixed31_32_floor(norm_x);
			if (index < 0 || index > 255)
				continue;

			index_f = dal_fixed31_32_from_int_nonconst(index);
			index_next = (index == 255) ? index : index + 1;

			if (color == 0) {
				lut1 = rgb_user[index].r;
				lut2 = rgb_user[index_next].r;
			} else if (color == 1) {
				lut1 = rgb_user[index].g;
				lut2 = rgb_user[index_next].g;
			} else {
				lut1 = rgb_user[index].b;
				lut2 = rgb_user[index_next].b;
			}

			// we have everything now, so interpolate
			delta_lut = dal_fixed31_32_sub(lut2, lut1);
			delta_index = dal_fixed31_32_sub(norm_x, index_f);

			*tf_point = dal_fixed31_32_add(lut1,
				dal_fixed31_32_mul(delta_index, delta_lut));
		}
		++i;
	}
}

static void build_new_custom_resulted_curve(
	uint32_t hw_points_num,
	struct dc_transfer_func_distributed_points *tf_pts)
@@ -1025,6 +1182,29 @@ static void build_new_custom_resulted_curve(
	}
}

static void apply_degamma_for_user_regamma(struct pwl_float_data_ex *rgb_regamma,
		uint32_t hw_points_num)
{
	uint32_t i;

	struct gamma_coefficients coeff;
	struct pwl_float_data_ex *rgb = rgb_regamma;
	const struct hw_x_point *coord_x = coordinates_x;

	build_coefficients(&coeff, true);

	i = 0;
	while (i != hw_points_num + 1) {
		rgb->r = translate_from_linear_space_ex(
				coord_x->x, &coeff, 0);
		rgb->g = rgb->r;
		rgb->b = rgb->r;
		++coord_x;
		++rgb;
		++i;
	}
}

static bool map_regamma_hw_to_x_user(
	const struct dc_gamma *ramp,
	struct pixel_gamma_point *coeff128,
@@ -1062,6 +1242,7 @@ static bool map_regamma_hw_to_x_user(
		}
	}

	/* this should be named differently, all it does is clamp to 0-1 */
	build_new_custom_resulted_curve(hw_points_num, tf_pts);

	return true;
@@ -1168,6 +1349,113 @@ bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf,
	return ret;
}

bool calculate_user_regamma_coeff(struct dc_transfer_func *output_tf,
		const struct regamma_lut *regamma)
{
	struct gamma_coefficients coeff;
	const struct hw_x_point *coord_x = coordinates_x;
	uint32_t i = 0;

	do {
		coeff.a0[i] = dal_fixed31_32_from_fraction(
				regamma->coeff.A0[i], 10000000);
		coeff.a1[i] = dal_fixed31_32_from_fraction(
				regamma->coeff.A1[i], 1000);
		coeff.a2[i] = dal_fixed31_32_from_fraction(
				regamma->coeff.A2[i], 1000);
		coeff.a3[i] = dal_fixed31_32_from_fraction(
				regamma->coeff.A3[i], 1000);
		coeff.user_gamma[i] = dal_fixed31_32_from_fraction(
				regamma->coeff.gamma[i], 1000);

		++i;
	} while (i != 3);

	i = 0;
	/* fixed_pt library has problems handling too small values */
	while (i != 32) {
		output_tf->tf_pts.red[i] = dal_fixed31_32_zero;
		output_tf->tf_pts.green[i] = dal_fixed31_32_zero;
		output_tf->tf_pts.blue[i] = dal_fixed31_32_zero;
		++coord_x;
		++i;
	}
	while (i != MAX_HW_POINTS + 1) {
		output_tf->tf_pts.red[i] = translate_from_linear_space_ex(
				coord_x->x, &coeff, 0);
		output_tf->tf_pts.green[i] = translate_from_linear_space_ex(
				coord_x->x, &coeff, 1);
		output_tf->tf_pts.blue[i] = translate_from_linear_space_ex(
				coord_x->x, &coeff, 2);
		++coord_x;
		++i;
	}

	// this function just clamps output to 0-1
	build_new_custom_resulted_curve(MAX_HW_POINTS, &output_tf->tf_pts);
	output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;

	return true;
}

bool calculate_user_regamma_ramp(struct dc_transfer_func *output_tf,
		const struct regamma_lut *regamma)
{
	struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts;
	struct dividers dividers;

	struct pwl_float_data *rgb_user = NULL;
	struct pwl_float_data_ex *rgb_regamma = NULL;
	bool ret = false;

	if (regamma == NULL)
		return false;

	output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;

	rgb_user = kzalloc(sizeof(*rgb_user) * (GAMMA_RGB_256_ENTRIES + _EXTRA_POINTS),
			GFP_KERNEL);
	if (!rgb_user)
		goto rgb_user_alloc_fail;

	rgb_regamma = kzalloc(sizeof(*rgb_regamma) * (MAX_HW_POINTS + _EXTRA_POINTS),
			GFP_KERNEL);
	if (!rgb_regamma)
		goto rgb_regamma_alloc_fail;

	dividers.divider1 = dal_fixed31_32_from_fraction(3, 2);
	dividers.divider2 = dal_fixed31_32_from_int(2);
	dividers.divider3 = dal_fixed31_32_from_fraction(5, 2);

	scale_user_regamma_ramp(rgb_user, &regamma->ramp, dividers);

	if (regamma->flags.bits.applyDegamma == 1) {
		apply_degamma_for_user_regamma(rgb_regamma, MAX_HW_POINTS);
		copy_rgb_regamma_to_coordinates_x(coordinates_x,
				MAX_HW_POINTS, rgb_regamma);
	}

	interpolate_user_regamma(MAX_HW_POINTS, rgb_user,
			regamma->flags.bits.applyDegamma, tf_pts);

	// no custom HDR curves!
	tf_pts->end_exponent = 0;
	tf_pts->x_point_at_y1_red = 1;
	tf_pts->x_point_at_y1_green = 1;
	tf_pts->x_point_at_y1_blue = 1;

	// this function just clamps output to 0-1
	build_new_custom_resulted_curve(MAX_HW_POINTS, tf_pts);

	ret = true;

	kfree(rgb_regamma);
rgb_regamma_alloc_fail:
	kfree(rgb_user);
rgb_user_alloc_fail:
	return ret;
}

bool mod_color_calculate_degamma_params(struct dc_transfer_func *input_tf,
		const struct dc_gamma *ramp, bool mapUserRamp)
{
+47 −1
Original line number Diff line number Diff line
@@ -32,6 +32,47 @@ struct dc_transfer_func_distributed_points;
struct dc_rgb_fixed;
enum dc_transfer_func_predefined;

/* For SetRegamma ADL interface support
 * Must match escape type
 */
union regamma_flags {
	unsigned int raw;
	struct {
		unsigned int gammaRampArray       :1;    // RegammaRamp is in use
		unsigned int gammaFromEdid        :1;    //gamma from edid is in use
		unsigned int gammaFromEdidEx      :1;    //gamma from edid is in use , but only for Display Id 1.2
		unsigned int gammaFromUser        :1;    //user custom gamma is used
		unsigned int coeffFromUser        :1;    //coeff. A0-A3 from user is in use
		unsigned int coeffFromEdid        :1;    //coeff. A0-A3 from edid is in use
		unsigned int applyDegamma         :1;    //flag for additional degamma correction in driver
		unsigned int gammaPredefinedSRGB  :1;    //flag for SRGB gamma
		unsigned int gammaPredefinedPQ    :1;    //flag for PQ gamma
		unsigned int gammaPredefinedPQ2084Interim :1;    //flag for PQ gamma, lower max nits
		unsigned int gammaPredefined36    :1;    //flag for 3.6 gamma
		unsigned int gammaPredefinedReset :1;    //flag to return to previous gamma
	} bits;
};

struct regamma_ramp {
	unsigned short gamma[256*3];  // gamma ramp packed  in same way as OS windows ,r , g & b
};

struct regamma_coeff {
	int    gamma[3];
	int    A0[3];
	int    A1[3];
	int    A2[3];
	int    A3[3];
};

struct regamma_lut {
	union regamma_flags flags;
	union {
		struct regamma_ramp ramp;
		struct regamma_coeff coeff;
	};
};

void setup_x_points_distribution(void);
void precompute_pq(void);
void precompute_de_pq(void);
@@ -48,6 +89,11 @@ bool mod_color_calculate_curve(enum dc_transfer_func_predefined trans,
bool mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans,
				struct dc_transfer_func_distributed_points *points);

bool calculate_user_regamma_coeff(struct dc_transfer_func *output_tf,
		const struct regamma_lut *regamma);

bool calculate_user_regamma_ramp(struct dc_transfer_func *output_tf,
		const struct regamma_lut *regamma);


#endif /* COLOR_MOD_COLOR_GAMMA_H_ */