|
@@ -185,14 +185,14 @@ struct dividers {
|
|
|
|
|
|
static void build_coefficients(struct gamma_coefficients *coefficients, bool is_2_4)
|
|
|
{
|
|
|
- static const int32_t numerator01[] = { 31308, 180000};
|
|
|
- static const int32_t numerator02[] = { 12920, 4500};
|
|
|
- static const int32_t numerator03[] = { 55, 99};
|
|
|
- static const int32_t numerator04[] = { 55, 99};
|
|
|
- static const int32_t numerator05[] = { 2400, 2200};
|
|
|
+ static const int32_t numerator01[] = { 31308, 180000};
|
|
|
+ static const int32_t numerator02[] = { 12920, 4500};
|
|
|
+ static const int32_t numerator03[] = { 55, 99};
|
|
|
+ static const int32_t numerator04[] = { 55, 99};
|
|
|
+ static const int32_t numerator05[] = { 2400, 2200};
|
|
|
|
|
|
- uint32_t i = 0;
|
|
|
- uint32_t index = is_2_4 == true ? 0:1;
|
|
|
+ uint32_t i = 0;
|
|
|
+ uint32_t index = is_2_4 == true ? 0:1;
|
|
|
|
|
|
do {
|
|
|
coefficients->a0[i] = dal_fixed31_32_from_fraction(
|
|
@@ -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 @@ rgb_user_alloc_fail:
|
|
|
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, ®amma->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)
|
|
|
{
|