Home | History | Annotate | Line # | Download | only in color
      1 /*	$NetBSD: amdgpu_color_gamma.c,v 1.2 2021/12/18 23:45:07 riastradh Exp $	*/
      2 
      3 /*
      4  * Copyright 2016 Advanced Micro Devices, Inc.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included in
     14  * all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     19  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
     20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     22  * OTHER DEALINGS IN THE SOFTWARE.
     23  *
     24  * Authors: AMD
     25  *
     26  */
     27 
     28 #include <sys/cdefs.h>
     29 __KERNEL_RCSID(0, "$NetBSD: amdgpu_color_gamma.c,v 1.2 2021/12/18 23:45:07 riastradh Exp $");
     30 
     31 #include <linux/mm.h>
     32 #include <linux/slab.h>
     33 
     34 #include "dc.h"
     35 #include "opp.h"
     36 #include "color_gamma.h"
     37 
     38 #define NUM_PTS_IN_REGION 16
     39 #define NUM_REGIONS 32
     40 #define MAX_HW_POINTS (NUM_PTS_IN_REGION*NUM_REGIONS)
     41 
     42 static struct hw_x_point coordinates_x[MAX_HW_POINTS + 2];
     43 
     44 static struct fixed31_32 pq_table[MAX_HW_POINTS + 2];
     45 static struct fixed31_32 de_pq_table[MAX_HW_POINTS + 2];
     46 
     47 // these are helpers for calculations to reduce stack usage
     48 // do not depend on these being preserved across calls
     49 static struct fixed31_32 scratch_1;
     50 static struct fixed31_32 scratch_2;
     51 static struct translate_from_linear_space_args scratch_gamma_args;
     52 
     53 /* Helper to optimize gamma calculation, only use in translate_from_linear, in
     54  * particular the dc_fixpt_pow function which is very expensive
     55  * The idea is that our regions for X points are exponential and currently they all use
     56  * the same number of points (NUM_PTS_IN_REGION) and in each region every point
     57  * is exactly 2x the one at the same index in the previous region. In other words
     58  * X[i] = 2 * X[i-NUM_PTS_IN_REGION] for i>=16
     59  * The other fact is that (2x)^gamma = 2^gamma * x^gamma
     60  * So we compute and save x^gamma for the first 16 regions, and for every next region
     61  * just multiply with 2^gamma which can be computed once, and save the result so we
     62  * recursively compute all the values.
     63  */
     64 static struct fixed31_32 pow_buffer[NUM_PTS_IN_REGION];
     65 static struct fixed31_32 gamma_of_2; // 2^gamma
     66 int pow_buffer_ptr = -1;
     67 										/*sRGB	 709 2.2 2.4 P3*/
     68 static const int32_t gamma_numerator01[] = { 31308,	180000,	0,	0,	0};
     69 static const int32_t gamma_numerator02[] = { 12920,	4500,	0,	0,	0};
     70 static const int32_t gamma_numerator03[] = { 55,	99,		0,	0,	0};
     71 static const int32_t gamma_numerator04[] = { 55,	99,		0,	0,	0};
     72 static const int32_t gamma_numerator05[] = { 2400,	2200,	2200, 2400, 2600};
     73 
     74 static bool pq_initialized; /* = false; */
     75 static bool de_pq_initialized; /* = false; */
     76 
     77 /* one-time setup of X points */
     78 void setup_x_points_distribution(void)
     79 {
     80 	struct fixed31_32 region_size = dc_fixpt_from_int(128);
     81 	int32_t segment;
     82 	uint32_t seg_offset;
     83 	uint32_t index;
     84 	struct fixed31_32 increment;
     85 
     86 	coordinates_x[MAX_HW_POINTS].x = region_size;
     87 	coordinates_x[MAX_HW_POINTS + 1].x = region_size;
     88 
     89 	for (segment = 6; segment > (6 - NUM_REGIONS); segment--) {
     90 		region_size = dc_fixpt_div_int(region_size, 2);
     91 		increment = dc_fixpt_div_int(region_size,
     92 						NUM_PTS_IN_REGION);
     93 		seg_offset = (segment + (NUM_REGIONS - 7)) * NUM_PTS_IN_REGION;
     94 		coordinates_x[seg_offset].x = region_size;
     95 
     96 		for (index = seg_offset + 1;
     97 				index < seg_offset + NUM_PTS_IN_REGION;
     98 				index++) {
     99 			coordinates_x[index].x = dc_fixpt_add
    100 					(coordinates_x[index-1].x, increment);
    101 		}
    102 	}
    103 }
    104 
    105 void log_x_points_distribution(struct dal_logger *logger)
    106 {
    107 	int i = 0;
    108 
    109 	if (logger != NULL) {
    110 		LOG_GAMMA_WRITE("Log X Distribution\n");
    111 
    112 		for (i = 0; i < MAX_HW_POINTS; i++)
    113 			LOG_GAMMA_WRITE("%llu\n", coordinates_x[i].x.value);
    114 	}
    115 }
    116 
    117 static void compute_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y)
    118 {
    119 	/* consts for PQ gamma formula. */
    120 	const struct fixed31_32 m1 =
    121 		dc_fixpt_from_fraction(159301758, 1000000000);
    122 	const struct fixed31_32 m2 =
    123 		dc_fixpt_from_fraction(7884375, 100000);
    124 	const struct fixed31_32 c1 =
    125 		dc_fixpt_from_fraction(8359375, 10000000);
    126 	const struct fixed31_32 c2 =
    127 		dc_fixpt_from_fraction(188515625, 10000000);
    128 	const struct fixed31_32 c3 =
    129 		dc_fixpt_from_fraction(186875, 10000);
    130 
    131 	struct fixed31_32 l_pow_m1;
    132 	struct fixed31_32 base;
    133 
    134 	if (dc_fixpt_lt(in_x, dc_fixpt_zero))
    135 		in_x = dc_fixpt_zero;
    136 
    137 	l_pow_m1 = dc_fixpt_pow(in_x, m1);
    138 	base = dc_fixpt_div(
    139 			dc_fixpt_add(c1,
    140 					(dc_fixpt_mul(c2, l_pow_m1))),
    141 			dc_fixpt_add(dc_fixpt_one,
    142 					(dc_fixpt_mul(c3, l_pow_m1))));
    143 	*out_y = dc_fixpt_pow(base, m2);
    144 }
    145 
    146 static void compute_de_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y)
    147 {
    148 	/* consts for dePQ gamma formula. */
    149 	const struct fixed31_32 m1 =
    150 		dc_fixpt_from_fraction(159301758, 1000000000);
    151 	const struct fixed31_32 m2 =
    152 		dc_fixpt_from_fraction(7884375, 100000);
    153 	const struct fixed31_32 c1 =
    154 		dc_fixpt_from_fraction(8359375, 10000000);
    155 	const struct fixed31_32 c2 =
    156 		dc_fixpt_from_fraction(188515625, 10000000);
    157 	const struct fixed31_32 c3 =
    158 		dc_fixpt_from_fraction(186875, 10000);
    159 
    160 	struct fixed31_32 l_pow_m1;
    161 	struct fixed31_32 base, div;
    162 	struct fixed31_32 base2;
    163 
    164 
    165 	if (dc_fixpt_lt(in_x, dc_fixpt_zero))
    166 		in_x = dc_fixpt_zero;
    167 
    168 	l_pow_m1 = dc_fixpt_pow(in_x,
    169 			dc_fixpt_div(dc_fixpt_one, m2));
    170 	base = dc_fixpt_sub(l_pow_m1, c1);
    171 
    172 	div = dc_fixpt_sub(c2, dc_fixpt_mul(c3, l_pow_m1));
    173 
    174 	base2 = dc_fixpt_div(base, div);
    175 	//avoid complex numbers
    176 	if (dc_fixpt_lt(base2, dc_fixpt_zero))
    177 		base2 = dc_fixpt_sub(dc_fixpt_zero, base2);
    178 
    179 
    180 	*out_y = dc_fixpt_pow(base2, dc_fixpt_div(dc_fixpt_one, m1));
    181 
    182 }
    183 
    184 
    185 /*de gamma, none linear to linear*/
    186 static void compute_hlg_eotf(struct fixed31_32 in_x,
    187 		struct fixed31_32 *out_y,
    188 		uint32_t sdr_white_level, uint32_t max_luminance_nits)
    189 {
    190 	struct fixed31_32 a;
    191 	struct fixed31_32 b;
    192 	struct fixed31_32 c;
    193 	struct fixed31_32 threshold;
    194 	struct fixed31_32 x;
    195 
    196 	struct fixed31_32 scaling_factor =
    197 			dc_fixpt_from_fraction(max_luminance_nits, sdr_white_level);
    198 	a = dc_fixpt_from_fraction(17883277, 100000000);
    199 	b = dc_fixpt_from_fraction(28466892, 100000000);
    200 	c = dc_fixpt_from_fraction(55991073, 100000000);
    201 	threshold = dc_fixpt_from_fraction(1, 2);
    202 
    203 	if (dc_fixpt_lt(in_x, threshold)) {
    204 		x = dc_fixpt_mul(in_x, in_x);
    205 		x = dc_fixpt_div_int(x, 3);
    206 	} else {
    207 		x = dc_fixpt_sub(in_x, c);
    208 		x = dc_fixpt_div(x, a);
    209 		x = dc_fixpt_exp(x);
    210 		x = dc_fixpt_add(x, b);
    211 		x = dc_fixpt_div_int(x, 12);
    212 	}
    213 	*out_y = dc_fixpt_mul(x, scaling_factor);
    214 
    215 }
    216 
    217 /*re gamma, linear to none linear*/
    218 static void compute_hlg_oetf(struct fixed31_32 in_x, struct fixed31_32 *out_y,
    219 		uint32_t sdr_white_level, uint32_t max_luminance_nits)
    220 {
    221 	struct fixed31_32 a;
    222 	struct fixed31_32 b;
    223 	struct fixed31_32 c;
    224 	struct fixed31_32 threshold;
    225 	struct fixed31_32 x;
    226 
    227 	struct fixed31_32 scaling_factor =
    228 			dc_fixpt_from_fraction(sdr_white_level, max_luminance_nits);
    229 	a = dc_fixpt_from_fraction(17883277, 100000000);
    230 	b = dc_fixpt_from_fraction(28466892, 100000000);
    231 	c = dc_fixpt_from_fraction(55991073, 100000000);
    232 	threshold = dc_fixpt_from_fraction(1, 12);
    233 	x = dc_fixpt_mul(in_x, scaling_factor);
    234 
    235 
    236 	if (dc_fixpt_lt(x, threshold)) {
    237 		x = dc_fixpt_mul(x, dc_fixpt_from_fraction(3, 1));
    238 		*out_y = dc_fixpt_pow(x, dc_fixpt_half);
    239 	} else {
    240 		x = dc_fixpt_mul(x, dc_fixpt_from_fraction(12, 1));
    241 		x = dc_fixpt_sub(x, b);
    242 		x = dc_fixpt_log(x);
    243 		x = dc_fixpt_mul(a, x);
    244 		*out_y = dc_fixpt_add(x, c);
    245 	}
    246 }
    247 
    248 
    249 /* one-time pre-compute PQ values - only for sdr_white_level 80 */
    250 void precompute_pq(void)
    251 {
    252 	int i;
    253 	struct fixed31_32 x;
    254 	const struct hw_x_point *coord_x = coordinates_x + 32;
    255 	struct fixed31_32 scaling_factor =
    256 			dc_fixpt_from_fraction(80, 10000);
    257 
    258 	/* pow function has problems with arguments too small */
    259 	for (i = 0; i < 32; i++)
    260 		pq_table[i] = dc_fixpt_zero;
    261 
    262 	for (i = 32; i <= MAX_HW_POINTS; i++) {
    263 		x = dc_fixpt_mul(coord_x->x, scaling_factor);
    264 		compute_pq(x, &pq_table[i]);
    265 		++coord_x;
    266 	}
    267 }
    268 
    269 /* one-time pre-compute dePQ values - only for max pixel value 125 FP16 */
    270 void precompute_de_pq(void)
    271 {
    272 	int i;
    273 	struct fixed31_32  y;
    274 	uint32_t begin_index, end_index;
    275 
    276 	struct fixed31_32 scaling_factor = dc_fixpt_from_int(125);
    277 
    278 	/* X points is 2^-25 to 2^7
    279 	 * De-gamma X is 2^-12 to 2^0  we are skipping first -12-(-25) = 13 regions
    280 	 */
    281 	begin_index = 13 * NUM_PTS_IN_REGION;
    282 	end_index = begin_index + 12 * NUM_PTS_IN_REGION;
    283 
    284 	for (i = 0; i <= begin_index; i++)
    285 		de_pq_table[i] = dc_fixpt_zero;
    286 
    287 	for (; i <= end_index; i++) {
    288 		compute_de_pq(coordinates_x[i].x, &y);
    289 		de_pq_table[i] = dc_fixpt_mul(y, scaling_factor);
    290 	}
    291 
    292 	for (; i <= MAX_HW_POINTS; i++)
    293 		de_pq_table[i] = de_pq_table[i-1];
    294 }
    295 struct dividers {
    296 	struct fixed31_32 divider1;
    297 	struct fixed31_32 divider2;
    298 	struct fixed31_32 divider3;
    299 };
    300 
    301 
    302 static bool build_coefficients(struct gamma_coefficients *coefficients, enum dc_transfer_func_predefined type)
    303 {
    304 
    305 	uint32_t i = 0;
    306 	uint32_t index = 0;
    307 	bool ret = true;
    308 
    309 	if (type == TRANSFER_FUNCTION_SRGB)
    310 		index = 0;
    311 	else if (type == TRANSFER_FUNCTION_BT709)
    312 		index = 1;
    313 	else if (type == TRANSFER_FUNCTION_GAMMA22)
    314 		index = 2;
    315 	else if (type == TRANSFER_FUNCTION_GAMMA24)
    316 		index = 3;
    317 	else if (type == TRANSFER_FUNCTION_GAMMA26)
    318 		index = 4;
    319 	else {
    320 		ret = false;
    321 		goto release;
    322 	}
    323 
    324 	do {
    325 		coefficients->a0[i] = dc_fixpt_from_fraction(
    326 			gamma_numerator01[index], 10000000);
    327 		coefficients->a1[i] = dc_fixpt_from_fraction(
    328 			gamma_numerator02[index], 1000);
    329 		coefficients->a2[i] = dc_fixpt_from_fraction(
    330 			gamma_numerator03[index], 1000);
    331 		coefficients->a3[i] = dc_fixpt_from_fraction(
    332 			gamma_numerator04[index], 1000);
    333 		coefficients->user_gamma[i] = dc_fixpt_from_fraction(
    334 			gamma_numerator05[index], 1000);
    335 
    336 		++i;
    337 	} while (i != ARRAY_SIZE(coefficients->a0));
    338 release:
    339 	return ret;
    340 }
    341 
    342 static struct fixed31_32 translate_from_linear_space(
    343 		struct translate_from_linear_space_args *args)
    344 {
    345 	const struct fixed31_32 one = dc_fixpt_from_int(1);
    346 
    347 	if (dc_fixpt_le(one, args->arg))
    348 		return one;
    349 
    350 	if (dc_fixpt_le(args->arg, dc_fixpt_neg(args->a0))) {
    351 		scratch_1 = dc_fixpt_add(one, args->a3);
    352 		scratch_2 = dc_fixpt_pow(
    353 				dc_fixpt_neg(args->arg),
    354 				dc_fixpt_recip(args->gamma));
    355 		scratch_1 = dc_fixpt_mul(scratch_1, scratch_2);
    356 		scratch_1 = dc_fixpt_sub(args->a2, scratch_1);
    357 
    358 		return scratch_1;
    359 	} else if (dc_fixpt_le(args->a0, args->arg)) {
    360 		if (pow_buffer_ptr == 0) {
    361 			gamma_of_2 = dc_fixpt_pow(dc_fixpt_from_int(2),
    362 					dc_fixpt_recip(args->gamma));
    363 		}
    364 		scratch_1 = dc_fixpt_add(one, args->a3);
    365 		if (pow_buffer_ptr < 16)
    366 			scratch_2 = dc_fixpt_pow(args->arg,
    367 					dc_fixpt_recip(args->gamma));
    368 		else
    369 			scratch_2 = dc_fixpt_mul(gamma_of_2,
    370 					pow_buffer[pow_buffer_ptr%16]);
    371 
    372 		if (pow_buffer_ptr != -1) {
    373 			pow_buffer[pow_buffer_ptr%16] = scratch_2;
    374 			pow_buffer_ptr++;
    375 		}
    376 
    377 		scratch_1 = dc_fixpt_mul(scratch_1, scratch_2);
    378 		scratch_1 = dc_fixpt_sub(scratch_1, args->a2);
    379 
    380 		return scratch_1;
    381 	}
    382 	else
    383 		return dc_fixpt_mul(args->arg, args->a1);
    384 }
    385 
    386 
    387 static struct fixed31_32 translate_from_linear_space_long(
    388 		struct translate_from_linear_space_args *args)
    389 {
    390 	const struct fixed31_32 one = dc_fixpt_from_int(1);
    391 
    392 	if (dc_fixpt_lt(one, args->arg))
    393 		return one;
    394 
    395 	if (dc_fixpt_le(args->arg, dc_fixpt_neg(args->a0)))
    396 		return dc_fixpt_sub(
    397 			args->a2,
    398 			dc_fixpt_mul(
    399 				dc_fixpt_add(
    400 					one,
    401 					args->a3),
    402 				dc_fixpt_pow(
    403 					dc_fixpt_neg(args->arg),
    404 					dc_fixpt_recip(args->gamma))));
    405 	else if (dc_fixpt_le(args->a0, args->arg))
    406 		return dc_fixpt_sub(
    407 			dc_fixpt_mul(
    408 				dc_fixpt_add(
    409 					one,
    410 					args->a3),
    411 				dc_fixpt_pow(
    412 						args->arg,
    413 					dc_fixpt_recip(args->gamma))),
    414 					args->a2);
    415 	else
    416 		return dc_fixpt_mul(
    417 			args->arg,
    418 			args->a1);
    419 }
    420 
    421 static struct fixed31_32 calculate_gamma22(struct fixed31_32 arg, bool use_eetf)
    422 {
    423 	struct fixed31_32 gamma = dc_fixpt_from_fraction(22, 10);
    424 
    425 	scratch_gamma_args.arg = arg;
    426 	scratch_gamma_args.a0 = dc_fixpt_zero;
    427 	scratch_gamma_args.a1 = dc_fixpt_zero;
    428 	scratch_gamma_args.a2 = dc_fixpt_zero;
    429 	scratch_gamma_args.a3 = dc_fixpt_zero;
    430 	scratch_gamma_args.gamma = gamma;
    431 
    432 	if (use_eetf)
    433 		return translate_from_linear_space_long(&scratch_gamma_args);
    434 
    435 	return translate_from_linear_space(&scratch_gamma_args);
    436 }
    437 
    438 
    439 static struct fixed31_32 translate_to_linear_space(
    440 	struct fixed31_32 arg,
    441 	struct fixed31_32 a0,
    442 	struct fixed31_32 a1,
    443 	struct fixed31_32 a2,
    444 	struct fixed31_32 a3,
    445 	struct fixed31_32 gamma)
    446 {
    447 	struct fixed31_32 linear;
    448 
    449 	a0 = dc_fixpt_mul(a0, a1);
    450 	if (dc_fixpt_le(arg, dc_fixpt_neg(a0)))
    451 
    452 		linear = dc_fixpt_neg(
    453 				 dc_fixpt_pow(
    454 				 dc_fixpt_div(
    455 				 dc_fixpt_sub(a2, arg),
    456 				 dc_fixpt_add(
    457 				 dc_fixpt_one, a3)), gamma));
    458 
    459 	else if (dc_fixpt_le(dc_fixpt_neg(a0), arg) &&
    460 			 dc_fixpt_le(arg, a0))
    461 		linear = dc_fixpt_div(arg, a1);
    462 	else
    463 		linear =  dc_fixpt_pow(
    464 					dc_fixpt_div(
    465 					dc_fixpt_add(a2, arg),
    466 					dc_fixpt_add(
    467 					dc_fixpt_one, a3)), gamma);
    468 
    469 	return linear;
    470 }
    471 
    472 static struct fixed31_32 translate_from_linear_space_ex(
    473 	struct fixed31_32 arg,
    474 	struct gamma_coefficients *coeff,
    475 	uint32_t color_index)
    476 {
    477 	scratch_gamma_args.arg = arg;
    478 	scratch_gamma_args.a0 = coeff->a0[color_index];
    479 	scratch_gamma_args.a1 = coeff->a1[color_index];
    480 	scratch_gamma_args.a2 = coeff->a2[color_index];
    481 	scratch_gamma_args.a3 = coeff->a3[color_index];
    482 	scratch_gamma_args.gamma = coeff->user_gamma[color_index];
    483 
    484 	return translate_from_linear_space(&scratch_gamma_args);
    485 }
    486 
    487 
    488 static inline struct fixed31_32 translate_to_linear_space_ex(
    489 	struct fixed31_32 arg,
    490 	struct gamma_coefficients *coeff,
    491 	uint32_t color_index)
    492 {
    493 	return translate_to_linear_space(
    494 		arg,
    495 		coeff->a0[color_index],
    496 		coeff->a1[color_index],
    497 		coeff->a2[color_index],
    498 		coeff->a3[color_index],
    499 		coeff->user_gamma[color_index]);
    500 }
    501 
    502 
    503 static bool find_software_points(
    504 	const struct dc_gamma *ramp,
    505 	const struct gamma_pixel *axis_x,
    506 	struct fixed31_32 hw_point,
    507 	enum channel_name channel,
    508 	uint32_t *index_to_start,
    509 	uint32_t *index_left,
    510 	uint32_t *index_right,
    511 	enum hw_point_position *pos)
    512 {
    513 	const uint32_t max_number = ramp->num_entries + 3;
    514 
    515 	struct fixed31_32 left, right;
    516 
    517 	uint32_t i = *index_to_start;
    518 
    519 	while (i < max_number) {
    520 		if (channel == CHANNEL_NAME_RED) {
    521 			left = axis_x[i].r;
    522 
    523 			if (i < max_number - 1)
    524 				right = axis_x[i + 1].r;
    525 			else
    526 				right = axis_x[max_number - 1].r;
    527 		} else if (channel == CHANNEL_NAME_GREEN) {
    528 			left = axis_x[i].g;
    529 
    530 			if (i < max_number - 1)
    531 				right = axis_x[i + 1].g;
    532 			else
    533 				right = axis_x[max_number - 1].g;
    534 		} else {
    535 			left = axis_x[i].b;
    536 
    537 			if (i < max_number - 1)
    538 				right = axis_x[i + 1].b;
    539 			else
    540 				right = axis_x[max_number - 1].b;
    541 		}
    542 
    543 		if (dc_fixpt_le(left, hw_point) &&
    544 			dc_fixpt_le(hw_point, right)) {
    545 			*index_to_start = i;
    546 			*index_left = i;
    547 
    548 			if (i < max_number - 1)
    549 				*index_right = i + 1;
    550 			else
    551 				*index_right = max_number - 1;
    552 
    553 			*pos = HW_POINT_POSITION_MIDDLE;
    554 
    555 			return true;
    556 		} else if ((i == *index_to_start) &&
    557 			dc_fixpt_le(hw_point, left)) {
    558 			*index_to_start = i;
    559 			*index_left = i;
    560 			*index_right = i;
    561 
    562 			*pos = HW_POINT_POSITION_LEFT;
    563 
    564 			return true;
    565 		} else if ((i == max_number - 1) &&
    566 			dc_fixpt_le(right, hw_point)) {
    567 			*index_to_start = i;
    568 			*index_left = i;
    569 			*index_right = i;
    570 
    571 			*pos = HW_POINT_POSITION_RIGHT;
    572 
    573 			return true;
    574 		}
    575 
    576 		++i;
    577 	}
    578 
    579 	return false;
    580 }
    581 
    582 static bool build_custom_gamma_mapping_coefficients_worker(
    583 	const struct dc_gamma *ramp,
    584 	struct pixel_gamma_point *coeff,
    585 	const struct hw_x_point *coordinates_x,
    586 	const struct gamma_pixel *axis_x,
    587 	enum channel_name channel,
    588 	uint32_t number_of_points)
    589 {
    590 	uint32_t i = 0;
    591 
    592 	while (i <= number_of_points) {
    593 		struct fixed31_32 coord_x;
    594 
    595 		uint32_t index_to_start = 0;
    596 		uint32_t index_left = 0;
    597 		uint32_t index_right = 0;
    598 
    599 		enum hw_point_position hw_pos;
    600 
    601 		struct gamma_point *point;
    602 
    603 		struct fixed31_32 left_pos;
    604 		struct fixed31_32 right_pos;
    605 
    606 		if (channel == CHANNEL_NAME_RED)
    607 			coord_x = coordinates_x[i].regamma_y_red;
    608 		else if (channel == CHANNEL_NAME_GREEN)
    609 			coord_x = coordinates_x[i].regamma_y_green;
    610 		else
    611 			coord_x = coordinates_x[i].regamma_y_blue;
    612 
    613 		if (!find_software_points(
    614 			ramp, axis_x, coord_x, channel,
    615 			&index_to_start, &index_left, &index_right, &hw_pos)) {
    616 			BREAK_TO_DEBUGGER();
    617 			return false;
    618 		}
    619 
    620 		if (index_left >= ramp->num_entries + 3) {
    621 			BREAK_TO_DEBUGGER();
    622 			return false;
    623 		}
    624 
    625 		if (index_right >= ramp->num_entries + 3) {
    626 			BREAK_TO_DEBUGGER();
    627 			return false;
    628 		}
    629 
    630 		if (channel == CHANNEL_NAME_RED) {
    631 			point = &coeff[i].r;
    632 
    633 			left_pos = axis_x[index_left].r;
    634 			right_pos = axis_x[index_right].r;
    635 		} else if (channel == CHANNEL_NAME_GREEN) {
    636 			point = &coeff[i].g;
    637 
    638 			left_pos = axis_x[index_left].g;
    639 			right_pos = axis_x[index_right].g;
    640 		} else {
    641 			point = &coeff[i].b;
    642 
    643 			left_pos = axis_x[index_left].b;
    644 			right_pos = axis_x[index_right].b;
    645 		}
    646 
    647 		if (hw_pos == HW_POINT_POSITION_MIDDLE)
    648 			point->coeff = dc_fixpt_div(
    649 				dc_fixpt_sub(
    650 					coord_x,
    651 					left_pos),
    652 				dc_fixpt_sub(
    653 					right_pos,
    654 					left_pos));
    655 		else if (hw_pos == HW_POINT_POSITION_LEFT)
    656 			point->coeff = dc_fixpt_zero;
    657 		else if (hw_pos == HW_POINT_POSITION_RIGHT)
    658 			point->coeff = dc_fixpt_from_int(2);
    659 		else {
    660 			BREAK_TO_DEBUGGER();
    661 			return false;
    662 		}
    663 
    664 		point->left_index = index_left;
    665 		point->right_index = index_right;
    666 		point->pos = hw_pos;
    667 
    668 		++i;
    669 	}
    670 
    671 	return true;
    672 }
    673 
    674 static struct fixed31_32 calculate_mapped_value(
    675 	struct pwl_float_data *rgb,
    676 	const struct pixel_gamma_point *coeff,
    677 	enum channel_name channel,
    678 	uint32_t max_index)
    679 {
    680 	const struct gamma_point *point;
    681 
    682 	struct fixed31_32 result;
    683 
    684 	if (channel == CHANNEL_NAME_RED)
    685 		point = &coeff->r;
    686 	else if (channel == CHANNEL_NAME_GREEN)
    687 		point = &coeff->g;
    688 	else
    689 		point = &coeff->b;
    690 
    691 	if ((point->left_index < 0) || (point->left_index > max_index)) {
    692 		BREAK_TO_DEBUGGER();
    693 		return dc_fixpt_zero;
    694 	}
    695 
    696 	if ((point->right_index < 0) || (point->right_index > max_index)) {
    697 		BREAK_TO_DEBUGGER();
    698 		return dc_fixpt_zero;
    699 	}
    700 
    701 	if (point->pos == HW_POINT_POSITION_MIDDLE)
    702 		if (channel == CHANNEL_NAME_RED)
    703 			result = dc_fixpt_add(
    704 				dc_fixpt_mul(
    705 					point->coeff,
    706 					dc_fixpt_sub(
    707 						rgb[point->right_index].r,
    708 						rgb[point->left_index].r)),
    709 				rgb[point->left_index].r);
    710 		else if (channel == CHANNEL_NAME_GREEN)
    711 			result = dc_fixpt_add(
    712 				dc_fixpt_mul(
    713 					point->coeff,
    714 					dc_fixpt_sub(
    715 						rgb[point->right_index].g,
    716 						rgb[point->left_index].g)),
    717 				rgb[point->left_index].g);
    718 		else
    719 			result = dc_fixpt_add(
    720 				dc_fixpt_mul(
    721 					point->coeff,
    722 					dc_fixpt_sub(
    723 						rgb[point->right_index].b,
    724 						rgb[point->left_index].b)),
    725 				rgb[point->left_index].b);
    726 	else if (point->pos == HW_POINT_POSITION_LEFT) {
    727 		BREAK_TO_DEBUGGER();
    728 		result = dc_fixpt_zero;
    729 	} else {
    730 		BREAK_TO_DEBUGGER();
    731 		result = dc_fixpt_one;
    732 	}
    733 
    734 	return result;
    735 }
    736 
    737 static void build_pq(struct pwl_float_data_ex *rgb_regamma,
    738 		uint32_t hw_points_num,
    739 		const struct hw_x_point *coordinate_x,
    740 		uint32_t sdr_white_level)
    741 {
    742 	uint32_t i, start_index;
    743 
    744 	struct pwl_float_data_ex *rgb = rgb_regamma;
    745 	const struct hw_x_point *coord_x = coordinate_x;
    746 	struct fixed31_32 x;
    747 	struct fixed31_32 output;
    748 	struct fixed31_32 scaling_factor =
    749 			dc_fixpt_from_fraction(sdr_white_level, 10000);
    750 
    751 	if (!pq_initialized && sdr_white_level == 80) {
    752 		precompute_pq();
    753 		pq_initialized = true;
    754 	}
    755 
    756 	/* TODO: start index is from segment 2^-24, skipping first segment
    757 	 * due to x values too small for power calculations
    758 	 */
    759 	start_index = 32;
    760 	rgb += start_index;
    761 	coord_x += start_index;
    762 
    763 	for (i = start_index; i <= hw_points_num; i++) {
    764 		/* Multiply 0.008 as regamma is 0-1 and FP16 input is 0-125.
    765 		 * FP 1.0 = 80nits
    766 		 */
    767 		if (sdr_white_level == 80) {
    768 			output = pq_table[i];
    769 		} else {
    770 			x = dc_fixpt_mul(coord_x->x, scaling_factor);
    771 			compute_pq(x, &output);
    772 		}
    773 
    774 		/* should really not happen? */
    775 		if (dc_fixpt_lt(output, dc_fixpt_zero))
    776 			output = dc_fixpt_zero;
    777 		else if (dc_fixpt_lt(dc_fixpt_one, output))
    778 			output = dc_fixpt_one;
    779 
    780 		rgb->r = output;
    781 		rgb->g = output;
    782 		rgb->b = output;
    783 
    784 		++coord_x;
    785 		++rgb;
    786 	}
    787 }
    788 
    789 static void build_de_pq(struct pwl_float_data_ex *de_pq,
    790 		uint32_t hw_points_num,
    791 		const struct hw_x_point *coordinate_x)
    792 {
    793 	uint32_t i;
    794 	struct fixed31_32 output;
    795 
    796 	struct fixed31_32 scaling_factor = dc_fixpt_from_int(125);
    797 
    798 	if (!de_pq_initialized) {
    799 		precompute_de_pq();
    800 		de_pq_initialized = true;
    801 	}
    802 
    803 
    804 	for (i = 0; i <= hw_points_num; i++) {
    805 		output = de_pq_table[i];
    806 		/* should really not happen? */
    807 		if (dc_fixpt_lt(output, dc_fixpt_zero))
    808 			output = dc_fixpt_zero;
    809 		else if (dc_fixpt_lt(scaling_factor, output))
    810 			output = scaling_factor;
    811 		de_pq[i].r = output;
    812 		de_pq[i].g = output;
    813 		de_pq[i].b = output;
    814 	}
    815 }
    816 
    817 static bool build_regamma(struct pwl_float_data_ex *rgb_regamma,
    818 		uint32_t hw_points_num,
    819 		const struct hw_x_point *coordinate_x, enum dc_transfer_func_predefined type)
    820 {
    821 	uint32_t i;
    822 	bool ret = false;
    823 
    824 	struct gamma_coefficients *coeff;
    825 	struct pwl_float_data_ex *rgb = rgb_regamma;
    826 	const struct hw_x_point *coord_x = coordinate_x;
    827 
    828 	coeff = kvzalloc(sizeof(*coeff), GFP_KERNEL);
    829 	if (!coeff)
    830 		goto release;
    831 
    832 	if (!build_coefficients(coeff, type))
    833 		goto release;
    834 
    835 	memset(pow_buffer, 0, NUM_PTS_IN_REGION * sizeof(struct fixed31_32));
    836 	pow_buffer_ptr = 0; // see variable definition for more info
    837 	i = 0;
    838 	while (i <= hw_points_num) {
    839 		/*TODO use y vs r,g,b*/
    840 		rgb->r = translate_from_linear_space_ex(
    841 			coord_x->x, coeff, 0);
    842 		rgb->g = rgb->r;
    843 		rgb->b = rgb->r;
    844 		++coord_x;
    845 		++rgb;
    846 		++i;
    847 	}
    848 	pow_buffer_ptr = -1; // reset back to no optimize
    849 	ret = true;
    850 release:
    851 	kfree(coeff);
    852 	return ret;
    853 }
    854 
    855 static void hermite_spline_eetf(struct fixed31_32 input_x,
    856 				struct fixed31_32 max_display,
    857 				struct fixed31_32 min_display,
    858 				struct fixed31_32 max_content,
    859 				struct fixed31_32 *out_x)
    860 {
    861 	struct fixed31_32 min_lum_pq;
    862 	struct fixed31_32 max_lum_pq;
    863 	struct fixed31_32 max_content_pq;
    864 	struct fixed31_32 ks;
    865 	struct fixed31_32 E1;
    866 	struct fixed31_32 E2;
    867 	struct fixed31_32 E3;
    868 	struct fixed31_32 t;
    869 	struct fixed31_32 t2;
    870 	struct fixed31_32 t3;
    871 	struct fixed31_32 two;
    872 	struct fixed31_32 three;
    873 	struct fixed31_32 temp1;
    874 	struct fixed31_32 temp2;
    875 	struct fixed31_32 a = dc_fixpt_from_fraction(15, 10);
    876 	struct fixed31_32 b = dc_fixpt_from_fraction(5, 10);
    877 	struct fixed31_32 epsilon = dc_fixpt_from_fraction(1, 1000000); // dc_fixpt_epsilon is a bit too small
    878 
    879 	if (dc_fixpt_eq(max_content, dc_fixpt_zero)) {
    880 		*out_x = dc_fixpt_zero;
    881 		return;
    882 	}
    883 
    884 	compute_pq(input_x, &E1);
    885 	compute_pq(dc_fixpt_div(min_display, max_content), &min_lum_pq);
    886 	compute_pq(dc_fixpt_div(max_display, max_content), &max_lum_pq);
    887 	compute_pq(dc_fixpt_one, &max_content_pq); // always 1? DAL2 code is weird
    888 	a = dc_fixpt_div(dc_fixpt_add(dc_fixpt_one, b), max_content_pq); // (1+b)/maxContent
    889 	ks = dc_fixpt_sub(dc_fixpt_mul(a, max_lum_pq), b); // a * max_lum_pq - b
    890 
    891 	if (dc_fixpt_lt(E1, ks))
    892 		E2 = E1;
    893 	else if (dc_fixpt_le(ks, E1) && dc_fixpt_le(E1, dc_fixpt_one)) {
    894 		if (dc_fixpt_lt(epsilon, dc_fixpt_sub(dc_fixpt_one, ks)))
    895 			// t = (E1 - ks) / (1 - ks)
    896 			t = dc_fixpt_div(dc_fixpt_sub(E1, ks),
    897 					dc_fixpt_sub(dc_fixpt_one, ks));
    898 		else
    899 			t = dc_fixpt_zero;
    900 
    901 		two = dc_fixpt_from_int(2);
    902 		three = dc_fixpt_from_int(3);
    903 
    904 		t2 = dc_fixpt_mul(t, t);
    905 		t3 = dc_fixpt_mul(t2, t);
    906 		temp1 = dc_fixpt_mul(two, t3);
    907 		temp2 = dc_fixpt_mul(three, t2);
    908 
    909 		// (2t^3 - 3t^2 + 1) * ks
    910 		E2 = dc_fixpt_mul(ks, dc_fixpt_add(dc_fixpt_one,
    911 				dc_fixpt_sub(temp1, temp2)));
    912 
    913 		// (-2t^3 + 3t^2) * max_lum_pq
    914 		E2 = dc_fixpt_add(E2, dc_fixpt_mul(max_lum_pq,
    915 				dc_fixpt_sub(temp2, temp1)));
    916 
    917 		temp1 = dc_fixpt_mul(two, t2);
    918 		temp2 = dc_fixpt_sub(dc_fixpt_one, ks);
    919 
    920 		// (t^3 - 2t^2 + t) * (1-ks)
    921 		E2 = dc_fixpt_add(E2, dc_fixpt_mul(temp2,
    922 				dc_fixpt_add(t, dc_fixpt_sub(t3, temp1))));
    923 	} else
    924 		E2 = dc_fixpt_one;
    925 
    926 	temp1 = dc_fixpt_sub(dc_fixpt_one, E2);
    927 	temp2 = dc_fixpt_mul(temp1, temp1);
    928 	temp2 = dc_fixpt_mul(temp2, temp2);
    929 	// temp2 = (1-E2)^4
    930 
    931 	E3 =  dc_fixpt_add(E2, dc_fixpt_mul(min_lum_pq, temp2));
    932 	compute_de_pq(E3, out_x);
    933 
    934 	*out_x = dc_fixpt_div(*out_x, dc_fixpt_div(max_display, max_content));
    935 }
    936 
    937 static bool build_freesync_hdr(struct pwl_float_data_ex *rgb_regamma,
    938 		uint32_t hw_points_num,
    939 		const struct hw_x_point *coordinate_x,
    940 		const struct freesync_hdr_tf_params *fs_params)
    941 {
    942 	uint32_t i;
    943 	struct pwl_float_data_ex *rgb = rgb_regamma;
    944 	const struct hw_x_point *coord_x = coordinate_x;
    945 	struct fixed31_32 scaledX = dc_fixpt_zero;
    946 	struct fixed31_32 scaledX1 = dc_fixpt_zero;
    947 	struct fixed31_32 max_display;
    948 	struct fixed31_32 min_display;
    949 	struct fixed31_32 max_content;
    950 	struct fixed31_32 clip = dc_fixpt_one;
    951 	struct fixed31_32 output;
    952 	bool use_eetf = false;
    953 	bool is_clipped = false;
    954 	struct fixed31_32 sdr_white_level;
    955 
    956 	if (fs_params->max_content == 0 ||
    957 			fs_params->max_display == 0)
    958 		return false;
    959 
    960 	max_display = dc_fixpt_from_int(fs_params->max_display);
    961 	min_display = dc_fixpt_from_fraction(fs_params->min_display, 10000);
    962 	max_content = dc_fixpt_from_int(fs_params->max_content);
    963 	sdr_white_level = dc_fixpt_from_int(fs_params->sdr_white_level);
    964 
    965 	if (fs_params->min_display > 1000) // cap at 0.1 at the bottom
    966 		min_display = dc_fixpt_from_fraction(1, 10);
    967 	if (fs_params->max_display < 100) // cap at 100 at the top
    968 		max_display = dc_fixpt_from_int(100);
    969 
    970 	// only max used, we don't adjust min luminance
    971 	if (fs_params->max_content > fs_params->max_display)
    972 		use_eetf = true;
    973 	else
    974 		max_content = max_display;
    975 
    976 	if (!use_eetf)
    977 		pow_buffer_ptr = 0; // see var definition for more info
    978 	rgb += 32; // first 32 points have problems with fixed point, too small
    979 	coord_x += 32;
    980 	for (i = 32; i <= hw_points_num; i++) {
    981 		if (!is_clipped) {
    982 			if (use_eetf) {
    983 				/*max content is equal 1 */
    984 				scaledX1 = dc_fixpt_div(coord_x->x,
    985 						dc_fixpt_div(max_content, sdr_white_level));
    986 				hermite_spline_eetf(scaledX1, max_display, min_display,
    987 						max_content, &scaledX);
    988 			} else
    989 				scaledX = dc_fixpt_div(coord_x->x,
    990 						dc_fixpt_div(max_display, sdr_white_level));
    991 
    992 			if (dc_fixpt_lt(scaledX, clip)) {
    993 				if (dc_fixpt_lt(scaledX, dc_fixpt_zero))
    994 					output = dc_fixpt_zero;
    995 				else
    996 					output = calculate_gamma22(scaledX, use_eetf);
    997 
    998 				rgb->r = output;
    999 				rgb->g = output;
   1000 				rgb->b = output;
   1001 			} else {
   1002 				is_clipped = true;
   1003 				rgb->r = clip;
   1004 				rgb->g = clip;
   1005 				rgb->b = clip;
   1006 			}
   1007 		} else {
   1008 			rgb->r = clip;
   1009 			rgb->g = clip;
   1010 			rgb->b = clip;
   1011 		}
   1012 
   1013 		++coord_x;
   1014 		++rgb;
   1015 	}
   1016 	pow_buffer_ptr = -1;
   1017 
   1018 	return true;
   1019 }
   1020 
   1021 static bool build_degamma(struct pwl_float_data_ex *curve,
   1022 		uint32_t hw_points_num,
   1023 		const struct hw_x_point *coordinate_x, enum dc_transfer_func_predefined type)
   1024 {
   1025 	uint32_t i;
   1026 	struct gamma_coefficients coeff;
   1027 	uint32_t begin_index, end_index;
   1028 	bool ret = false;
   1029 
   1030 	if (!build_coefficients(&coeff, type))
   1031 		goto release;
   1032 
   1033 	i = 0;
   1034 
   1035 	/* X points is 2^-25 to 2^7
   1036 	 * De-gamma X is 2^-12 to 2^0  we are skipping first -12-(-25) = 13 regions
   1037 	 */
   1038 	begin_index = 13 * NUM_PTS_IN_REGION;
   1039 	end_index = begin_index + 12 * NUM_PTS_IN_REGION;
   1040 
   1041 	while (i != begin_index) {
   1042 		curve[i].r = dc_fixpt_zero;
   1043 		curve[i].g = dc_fixpt_zero;
   1044 		curve[i].b = dc_fixpt_zero;
   1045 		i++;
   1046 	}
   1047 
   1048 	while (i != end_index) {
   1049 		curve[i].r = translate_to_linear_space_ex(
   1050 				coordinate_x[i].x, &coeff, 0);
   1051 		curve[i].g = curve[i].r;
   1052 		curve[i].b = curve[i].r;
   1053 		i++;
   1054 	}
   1055 	while (i != hw_points_num + 1) {
   1056 		curve[i].r = dc_fixpt_one;
   1057 		curve[i].g = dc_fixpt_one;
   1058 		curve[i].b = dc_fixpt_one;
   1059 		i++;
   1060 	}
   1061 	ret = true;
   1062 release:
   1063 	return ret;
   1064 }
   1065 
   1066 
   1067 
   1068 
   1069 
   1070 static void build_hlg_degamma(struct pwl_float_data_ex *degamma,
   1071 		uint32_t hw_points_num,
   1072 		const struct hw_x_point *coordinate_x,
   1073 		uint32_t sdr_white_level, uint32_t max_luminance_nits)
   1074 {
   1075 	uint32_t i;
   1076 
   1077 	struct pwl_float_data_ex *rgb = degamma;
   1078 	const struct hw_x_point *coord_x = coordinate_x;
   1079 
   1080 	i = 0;
   1081 	//check when i == 434
   1082 	while (i != hw_points_num + 1) {
   1083 		compute_hlg_eotf(coord_x->x, &rgb->r, sdr_white_level, max_luminance_nits);
   1084 		rgb->g = rgb->r;
   1085 		rgb->b = rgb->r;
   1086 		++coord_x;
   1087 		++rgb;
   1088 		++i;
   1089 	}
   1090 }
   1091 
   1092 
   1093 static void build_hlg_regamma(struct pwl_float_data_ex *regamma,
   1094 		uint32_t hw_points_num,
   1095 		const struct hw_x_point *coordinate_x,
   1096 		uint32_t sdr_white_level, uint32_t max_luminance_nits)
   1097 {
   1098 	uint32_t i;
   1099 
   1100 	struct pwl_float_data_ex *rgb = regamma;
   1101 	const struct hw_x_point *coord_x = coordinate_x;
   1102 
   1103 	i = 0;
   1104 
   1105 	//when i == 471
   1106 	while (i != hw_points_num + 1) {
   1107 		compute_hlg_oetf(coord_x->x, &rgb->r, sdr_white_level, max_luminance_nits);
   1108 		rgb->g = rgb->r;
   1109 		rgb->b = rgb->r;
   1110 		++coord_x;
   1111 		++rgb;
   1112 		++i;
   1113 	}
   1114 }
   1115 
   1116 static void scale_gamma(struct pwl_float_data *pwl_rgb,
   1117 		const struct dc_gamma *ramp,
   1118 		struct dividers dividers)
   1119 {
   1120 	const struct fixed31_32 max_driver = dc_fixpt_from_int(0xFFFF);
   1121 	const struct fixed31_32 max_os = dc_fixpt_from_int(0xFF00);
   1122 	struct fixed31_32 scaler = max_os;
   1123 	uint32_t i;
   1124 	struct pwl_float_data *rgb = pwl_rgb;
   1125 	struct pwl_float_data *rgb_last = rgb + ramp->num_entries - 1;
   1126 
   1127 	i = 0;
   1128 
   1129 	do {
   1130 		if (dc_fixpt_lt(max_os, ramp->entries.red[i]) ||
   1131 			dc_fixpt_lt(max_os, ramp->entries.green[i]) ||
   1132 			dc_fixpt_lt(max_os, ramp->entries.blue[i])) {
   1133 			scaler = max_driver;
   1134 			break;
   1135 		}
   1136 		++i;
   1137 	} while (i != ramp->num_entries);
   1138 
   1139 	i = 0;
   1140 
   1141 	do {
   1142 		rgb->r = dc_fixpt_div(
   1143 			ramp->entries.red[i], scaler);
   1144 		rgb->g = dc_fixpt_div(
   1145 			ramp->entries.green[i], scaler);
   1146 		rgb->b = dc_fixpt_div(
   1147 			ramp->entries.blue[i], scaler);
   1148 
   1149 		++rgb;
   1150 		++i;
   1151 	} while (i != ramp->num_entries);
   1152 
   1153 	rgb->r = dc_fixpt_mul(rgb_last->r,
   1154 			dividers.divider1);
   1155 	rgb->g = dc_fixpt_mul(rgb_last->g,
   1156 			dividers.divider1);
   1157 	rgb->b = dc_fixpt_mul(rgb_last->b,
   1158 			dividers.divider1);
   1159 
   1160 	++rgb;
   1161 
   1162 	rgb->r = dc_fixpt_mul(rgb_last->r,
   1163 			dividers.divider2);
   1164 	rgb->g = dc_fixpt_mul(rgb_last->g,
   1165 			dividers.divider2);
   1166 	rgb->b = dc_fixpt_mul(rgb_last->b,
   1167 			dividers.divider2);
   1168 
   1169 	++rgb;
   1170 
   1171 	rgb->r = dc_fixpt_mul(rgb_last->r,
   1172 			dividers.divider3);
   1173 	rgb->g = dc_fixpt_mul(rgb_last->g,
   1174 			dividers.divider3);
   1175 	rgb->b = dc_fixpt_mul(rgb_last->b,
   1176 			dividers.divider3);
   1177 }
   1178 
   1179 static void scale_gamma_dx(struct pwl_float_data *pwl_rgb,
   1180 		const struct dc_gamma *ramp,
   1181 		struct dividers dividers)
   1182 {
   1183 	uint32_t i;
   1184 	struct fixed31_32 min = dc_fixpt_zero;
   1185 	struct fixed31_32 max = dc_fixpt_one;
   1186 
   1187 	struct fixed31_32 delta = dc_fixpt_zero;
   1188 	struct fixed31_32 offset = dc_fixpt_zero;
   1189 
   1190 	for (i = 0 ; i < ramp->num_entries; i++) {
   1191 		if (dc_fixpt_lt(ramp->entries.red[i], min))
   1192 			min = ramp->entries.red[i];
   1193 
   1194 		if (dc_fixpt_lt(ramp->entries.green[i], min))
   1195 			min = ramp->entries.green[i];
   1196 
   1197 		if (dc_fixpt_lt(ramp->entries.blue[i], min))
   1198 			min = ramp->entries.blue[i];
   1199 
   1200 		if (dc_fixpt_lt(max, ramp->entries.red[i]))
   1201 			max = ramp->entries.red[i];
   1202 
   1203 		if (dc_fixpt_lt(max, ramp->entries.green[i]))
   1204 			max = ramp->entries.green[i];
   1205 
   1206 		if (dc_fixpt_lt(max, ramp->entries.blue[i]))
   1207 			max = ramp->entries.blue[i];
   1208 	}
   1209 
   1210 	if (dc_fixpt_lt(min, dc_fixpt_zero))
   1211 		delta = dc_fixpt_neg(min);
   1212 
   1213 	offset = dc_fixpt_add(min, max);
   1214 
   1215 	for (i = 0 ; i < ramp->num_entries; i++) {
   1216 		pwl_rgb[i].r = dc_fixpt_div(
   1217 			dc_fixpt_add(
   1218 				ramp->entries.red[i], delta), offset);
   1219 		pwl_rgb[i].g = dc_fixpt_div(
   1220 			dc_fixpt_add(
   1221 				ramp->entries.green[i], delta), offset);
   1222 		pwl_rgb[i].b = dc_fixpt_div(
   1223 			dc_fixpt_add(
   1224 				ramp->entries.blue[i], delta), offset);
   1225 
   1226 	}
   1227 
   1228 	pwl_rgb[i].r =  dc_fixpt_sub(dc_fixpt_mul_int(
   1229 				pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r);
   1230 	pwl_rgb[i].g =  dc_fixpt_sub(dc_fixpt_mul_int(
   1231 				pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g);
   1232 	pwl_rgb[i].b =  dc_fixpt_sub(dc_fixpt_mul_int(
   1233 				pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b);
   1234 	++i;
   1235 	pwl_rgb[i].r =  dc_fixpt_sub(dc_fixpt_mul_int(
   1236 				pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r);
   1237 	pwl_rgb[i].g =  dc_fixpt_sub(dc_fixpt_mul_int(
   1238 				pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g);
   1239 	pwl_rgb[i].b =  dc_fixpt_sub(dc_fixpt_mul_int(
   1240 				pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b);
   1241 }
   1242 
   1243 /* todo: all these scale_gamma functions are inherently the same but
   1244  *  take different structures as params or different format for ramp
   1245  *  values. We could probably implement it in a more generic fashion
   1246  */
   1247 static void scale_user_regamma_ramp(struct pwl_float_data *pwl_rgb,
   1248 		const struct regamma_ramp *ramp,
   1249 		struct dividers dividers)
   1250 {
   1251 	unsigned short max_driver = 0xFFFF;
   1252 	unsigned short max_os = 0xFF00;
   1253 	unsigned short scaler = max_os;
   1254 	uint32_t i;
   1255 	struct pwl_float_data *rgb = pwl_rgb;
   1256 	struct pwl_float_data *rgb_last = rgb + GAMMA_RGB_256_ENTRIES - 1;
   1257 
   1258 	i = 0;
   1259 	do {
   1260 		if (ramp->gamma[i] > max_os ||
   1261 				ramp->gamma[i + 256] > max_os ||
   1262 				ramp->gamma[i + 512] > max_os) {
   1263 			scaler = max_driver;
   1264 			break;
   1265 		}
   1266 		i++;
   1267 	} while (i != GAMMA_RGB_256_ENTRIES);
   1268 
   1269 	i = 0;
   1270 	do {
   1271 		rgb->r = dc_fixpt_from_fraction(
   1272 				ramp->gamma[i], scaler);
   1273 		rgb->g = dc_fixpt_from_fraction(
   1274 				ramp->gamma[i + 256], scaler);
   1275 		rgb->b = dc_fixpt_from_fraction(
   1276 				ramp->gamma[i + 512], scaler);
   1277 
   1278 		++rgb;
   1279 		++i;
   1280 	} while (i != GAMMA_RGB_256_ENTRIES);
   1281 
   1282 	rgb->r = dc_fixpt_mul(rgb_last->r,
   1283 			dividers.divider1);
   1284 	rgb->g = dc_fixpt_mul(rgb_last->g,
   1285 			dividers.divider1);
   1286 	rgb->b = dc_fixpt_mul(rgb_last->b,
   1287 			dividers.divider1);
   1288 
   1289 	++rgb;
   1290 
   1291 	rgb->r = dc_fixpt_mul(rgb_last->r,
   1292 			dividers.divider2);
   1293 	rgb->g = dc_fixpt_mul(rgb_last->g,
   1294 			dividers.divider2);
   1295 	rgb->b = dc_fixpt_mul(rgb_last->b,
   1296 			dividers.divider2);
   1297 
   1298 	++rgb;
   1299 
   1300 	rgb->r = dc_fixpt_mul(rgb_last->r,
   1301 			dividers.divider3);
   1302 	rgb->g = dc_fixpt_mul(rgb_last->g,
   1303 			dividers.divider3);
   1304 	rgb->b = dc_fixpt_mul(rgb_last->b,
   1305 			dividers.divider3);
   1306 }
   1307 
   1308 /*
   1309  * RS3+ color transform DDI - 1D LUT adjustment is composed with regamma here
   1310  * Input is evenly distributed in the output color space as specified in
   1311  * SetTimings
   1312  *
   1313  * Interpolation details:
   1314  * 1D LUT has 4096 values which give curve correction in 0-1 float range
   1315  * for evenly spaced points in 0-1 range. lut1D[index] gives correction
   1316  * for index/4095.
   1317  * First we find index for which:
   1318  *	index/4095 < regamma_y < (index+1)/4095 =>
   1319  *	index < 4095*regamma_y < index + 1
   1320  * norm_y = 4095*regamma_y, and index is just truncating to nearest integer
   1321  * lut1 = lut1D[index], lut2 = lut1D[index+1]
   1322  *
   1323  * adjustedY is then linearly interpolating regamma Y between lut1 and lut2
   1324  *
   1325  * Custom degamma on Linux uses the same interpolation math, so is handled here
   1326  */
   1327 static void apply_lut_1d(
   1328 		const struct dc_gamma *ramp,
   1329 		uint32_t num_hw_points,
   1330 		struct dc_transfer_func_distributed_points *tf_pts)
   1331 {
   1332 	int i = 0;
   1333 	int color = 0;
   1334 	struct fixed31_32 *regamma_y;
   1335 	struct fixed31_32 norm_y;
   1336 	struct fixed31_32 lut1;
   1337 	struct fixed31_32 lut2;
   1338 	const int max_lut_index = 4095;
   1339 	const struct fixed31_32 max_lut_index_f =
   1340 			dc_fixpt_from_int(max_lut_index);
   1341 	int32_t index = 0, index_next = 0;
   1342 	struct fixed31_32 index_f;
   1343 	struct fixed31_32 delta_lut;
   1344 	struct fixed31_32 delta_index;
   1345 
   1346 	if (ramp->type != GAMMA_CS_TFM_1D && ramp->type != GAMMA_CUSTOM)
   1347 		return; // this is not expected
   1348 
   1349 	for (i = 0; i < num_hw_points; i++) {
   1350 		for (color = 0; color < 3; color++) {
   1351 			if (color == 0)
   1352 				regamma_y = &tf_pts->red[i];
   1353 			else if (color == 1)
   1354 				regamma_y = &tf_pts->green[i];
   1355 			else
   1356 				regamma_y = &tf_pts->blue[i];
   1357 
   1358 			norm_y = dc_fixpt_mul(max_lut_index_f,
   1359 						   *regamma_y);
   1360 			index = dc_fixpt_floor(norm_y);
   1361 			index_f = dc_fixpt_from_int(index);
   1362 
   1363 			if (index < 0 || index > max_lut_index)
   1364 				continue;
   1365 
   1366 			index_next = (index == max_lut_index) ? index : index+1;
   1367 
   1368 			if (color == 0) {
   1369 				lut1 = ramp->entries.red[index];
   1370 				lut2 = ramp->entries.red[index_next];
   1371 			} else if (color == 1) {
   1372 				lut1 = ramp->entries.green[index];
   1373 				lut2 = ramp->entries.green[index_next];
   1374 			} else {
   1375 				lut1 = ramp->entries.blue[index];
   1376 				lut2 = ramp->entries.blue[index_next];
   1377 			}
   1378 
   1379 			// we have everything now, so interpolate
   1380 			delta_lut = dc_fixpt_sub(lut2, lut1);
   1381 			delta_index = dc_fixpt_sub(norm_y, index_f);
   1382 
   1383 			*regamma_y = dc_fixpt_add(lut1,
   1384 				dc_fixpt_mul(delta_index, delta_lut));
   1385 		}
   1386 	}
   1387 }
   1388 
   1389 static void build_evenly_distributed_points(
   1390 	struct gamma_pixel *points,
   1391 	uint32_t numberof_points,
   1392 	struct dividers dividers)
   1393 {
   1394 	struct gamma_pixel *p = points;
   1395 	struct gamma_pixel *p_last;
   1396 
   1397 	uint32_t i = 0;
   1398 
   1399 	// This function should not gets called with 0 as a parameter
   1400 	ASSERT(numberof_points > 0);
   1401 	p_last = p + numberof_points - 1;
   1402 
   1403 	do {
   1404 		struct fixed31_32 value = dc_fixpt_from_fraction(i,
   1405 			numberof_points - 1);
   1406 
   1407 		p->r = value;
   1408 		p->g = value;
   1409 		p->b = value;
   1410 
   1411 		++p;
   1412 		++i;
   1413 	} while (i < numberof_points);
   1414 
   1415 	p->r = dc_fixpt_div(p_last->r, dividers.divider1);
   1416 	p->g = dc_fixpt_div(p_last->g, dividers.divider1);
   1417 	p->b = dc_fixpt_div(p_last->b, dividers.divider1);
   1418 
   1419 	++p;
   1420 
   1421 	p->r = dc_fixpt_div(p_last->r, dividers.divider2);
   1422 	p->g = dc_fixpt_div(p_last->g, dividers.divider2);
   1423 	p->b = dc_fixpt_div(p_last->b, dividers.divider2);
   1424 
   1425 	++p;
   1426 
   1427 	p->r = dc_fixpt_div(p_last->r, dividers.divider3);
   1428 	p->g = dc_fixpt_div(p_last->g, dividers.divider3);
   1429 	p->b = dc_fixpt_div(p_last->b, dividers.divider3);
   1430 }
   1431 
   1432 static inline void copy_rgb_regamma_to_coordinates_x(
   1433 		struct hw_x_point *coordinates_x,
   1434 		uint32_t hw_points_num,
   1435 		const struct pwl_float_data_ex *rgb_ex)
   1436 {
   1437 	struct hw_x_point *coords = coordinates_x;
   1438 	uint32_t i = 0;
   1439 	const struct pwl_float_data_ex *rgb_regamma = rgb_ex;
   1440 
   1441 	while (i <= hw_points_num + 1) {
   1442 		coords->regamma_y_red = rgb_regamma->r;
   1443 		coords->regamma_y_green = rgb_regamma->g;
   1444 		coords->regamma_y_blue = rgb_regamma->b;
   1445 
   1446 		++coords;
   1447 		++rgb_regamma;
   1448 		++i;
   1449 	}
   1450 }
   1451 
   1452 static bool calculate_interpolated_hardware_curve(
   1453 	const struct dc_gamma *ramp,
   1454 	struct pixel_gamma_point *coeff128,
   1455 	struct pwl_float_data *rgb_user,
   1456 	const struct hw_x_point *coordinates_x,
   1457 	const struct gamma_pixel *axis_x,
   1458 	uint32_t number_of_points,
   1459 	struct dc_transfer_func_distributed_points *tf_pts)
   1460 {
   1461 
   1462 	const struct pixel_gamma_point *coeff = coeff128;
   1463 	uint32_t max_entries = 3 - 1;
   1464 
   1465 	uint32_t i = 0;
   1466 
   1467 	for (i = 0; i < 3; i++) {
   1468 		if (!build_custom_gamma_mapping_coefficients_worker(
   1469 				ramp, coeff128, coordinates_x, axis_x, i,
   1470 				number_of_points))
   1471 			return false;
   1472 	}
   1473 
   1474 	i = 0;
   1475 	max_entries += ramp->num_entries;
   1476 
   1477 	/* TODO: float point case */
   1478 
   1479 	while (i <= number_of_points) {
   1480 		tf_pts->red[i] = calculate_mapped_value(
   1481 			rgb_user, coeff, CHANNEL_NAME_RED, max_entries);
   1482 		tf_pts->green[i] = calculate_mapped_value(
   1483 			rgb_user, coeff, CHANNEL_NAME_GREEN, max_entries);
   1484 		tf_pts->blue[i] = calculate_mapped_value(
   1485 			rgb_user, coeff, CHANNEL_NAME_BLUE, max_entries);
   1486 
   1487 		++coeff;
   1488 		++i;
   1489 	}
   1490 
   1491 	return true;
   1492 }
   1493 
   1494 /* The "old" interpolation uses a complicated scheme to build an array of
   1495  * coefficients while also using an array of 0-255 normalized to 0-1
   1496  * Then there's another loop using both of the above + new scaled user ramp
   1497  * and we concatenate them. It also searches for points of interpolation and
   1498  * uses enums for positions.
   1499  *
   1500  * This function uses a different approach:
   1501  * user ramp is always applied on X with 0/255, 1/255, 2/255, ..., 255/255
   1502  * To find index for hwX , we notice the following:
   1503  * i/255 <= hwX < (i+1)/255  <=> i <= 255*hwX < i+1
   1504  * See apply_lut_1d which is the same principle, but on 4K entry 1D LUT
   1505  *
   1506  * Once the index is known, combined Y is simply:
   1507  * user_ramp(index) + (hwX-index/255)*(user_ramp(index+1) - user_ramp(index)
   1508  *
   1509  * We should switch to this method in all cases, it's simpler and faster
   1510  * ToDo one day - for now this only applies to ADL regamma to avoid regression
   1511  * for regular use cases (sRGB and PQ)
   1512  */
   1513 static void interpolate_user_regamma(uint32_t hw_points_num,
   1514 		struct pwl_float_data *rgb_user,
   1515 		bool apply_degamma,
   1516 		struct dc_transfer_func_distributed_points *tf_pts)
   1517 {
   1518 	uint32_t i;
   1519 	uint32_t color = 0;
   1520 	int32_t index;
   1521 	int32_t index_next;
   1522 	struct fixed31_32 *tf_point;
   1523 	struct fixed31_32 hw_x;
   1524 	struct fixed31_32 norm_factor =
   1525 			dc_fixpt_from_int(255);
   1526 	struct fixed31_32 norm_x;
   1527 	struct fixed31_32 index_f;
   1528 	struct fixed31_32 lut1;
   1529 	struct fixed31_32 lut2;
   1530 	struct fixed31_32 delta_lut;
   1531 	struct fixed31_32 delta_index;
   1532 
   1533 	i = 0;
   1534 	/* fixed_pt library has problems handling too small values */
   1535 	while (i != 32) {
   1536 		tf_pts->red[i] = dc_fixpt_zero;
   1537 		tf_pts->green[i] = dc_fixpt_zero;
   1538 		tf_pts->blue[i] = dc_fixpt_zero;
   1539 		++i;
   1540 	}
   1541 	while (i <= hw_points_num + 1) {
   1542 		for (color = 0; color < 3; color++) {
   1543 			if (color == 0)
   1544 				tf_point = &tf_pts->red[i];
   1545 			else if (color == 1)
   1546 				tf_point = &tf_pts->green[i];
   1547 			else
   1548 				tf_point = &tf_pts->blue[i];
   1549 
   1550 			if (apply_degamma) {
   1551 				if (color == 0)
   1552 					hw_x = coordinates_x[i].regamma_y_red;
   1553 				else if (color == 1)
   1554 					hw_x = coordinates_x[i].regamma_y_green;
   1555 				else
   1556 					hw_x = coordinates_x[i].regamma_y_blue;
   1557 			} else
   1558 				hw_x = coordinates_x[i].x;
   1559 
   1560 			norm_x = dc_fixpt_mul(norm_factor, hw_x);
   1561 			index = dc_fixpt_floor(norm_x);
   1562 			if (index < 0 || index > 255)
   1563 				continue;
   1564 
   1565 			index_f = dc_fixpt_from_int(index);
   1566 			index_next = (index == 255) ? index : index + 1;
   1567 
   1568 			if (color == 0) {
   1569 				lut1 = rgb_user[index].r;
   1570 				lut2 = rgb_user[index_next].r;
   1571 			} else if (color == 1) {
   1572 				lut1 = rgb_user[index].g;
   1573 				lut2 = rgb_user[index_next].g;
   1574 			} else {
   1575 				lut1 = rgb_user[index].b;
   1576 				lut2 = rgb_user[index_next].b;
   1577 			}
   1578 
   1579 			// we have everything now, so interpolate
   1580 			delta_lut = dc_fixpt_sub(lut2, lut1);
   1581 			delta_index = dc_fixpt_sub(norm_x, index_f);
   1582 
   1583 			*tf_point = dc_fixpt_add(lut1,
   1584 				dc_fixpt_mul(delta_index, delta_lut));
   1585 		}
   1586 		++i;
   1587 	}
   1588 }
   1589 
   1590 static void build_new_custom_resulted_curve(
   1591 	uint32_t hw_points_num,
   1592 	struct dc_transfer_func_distributed_points *tf_pts)
   1593 {
   1594 	uint32_t i;
   1595 
   1596 	i = 0;
   1597 
   1598 	while (i != hw_points_num + 1) {
   1599 		tf_pts->red[i] = dc_fixpt_clamp(
   1600 			tf_pts->red[i], dc_fixpt_zero,
   1601 			dc_fixpt_one);
   1602 		tf_pts->green[i] = dc_fixpt_clamp(
   1603 			tf_pts->green[i], dc_fixpt_zero,
   1604 			dc_fixpt_one);
   1605 		tf_pts->blue[i] = dc_fixpt_clamp(
   1606 			tf_pts->blue[i], dc_fixpt_zero,
   1607 			dc_fixpt_one);
   1608 
   1609 		++i;
   1610 	}
   1611 }
   1612 
   1613 static void apply_degamma_for_user_regamma(struct pwl_float_data_ex *rgb_regamma,
   1614 		uint32_t hw_points_num)
   1615 {
   1616 	uint32_t i;
   1617 
   1618 	struct gamma_coefficients coeff;
   1619 	struct pwl_float_data_ex *rgb = rgb_regamma;
   1620 	const struct hw_x_point *coord_x = coordinates_x;
   1621 
   1622 	build_coefficients(&coeff, true);
   1623 
   1624 	i = 0;
   1625 	while (i != hw_points_num + 1) {
   1626 		rgb->r = translate_from_linear_space_ex(
   1627 				coord_x->x, &coeff, 0);
   1628 		rgb->g = rgb->r;
   1629 		rgb->b = rgb->r;
   1630 		++coord_x;
   1631 		++rgb;
   1632 		++i;
   1633 	}
   1634 }
   1635 
   1636 static bool map_regamma_hw_to_x_user(
   1637 	const struct dc_gamma *ramp,
   1638 	struct pixel_gamma_point *coeff128,
   1639 	struct pwl_float_data *rgb_user,
   1640 	struct hw_x_point *coords_x,
   1641 	const struct gamma_pixel *axis_x,
   1642 	const struct pwl_float_data_ex *rgb_regamma,
   1643 	uint32_t hw_points_num,
   1644 	struct dc_transfer_func_distributed_points *tf_pts,
   1645 	bool mapUserRamp)
   1646 {
   1647 	/* setup to spare calculated ideal regamma values */
   1648 
   1649 	int i = 0;
   1650 	struct hw_x_point *coords = coords_x;
   1651 	const struct pwl_float_data_ex *regamma = rgb_regamma;
   1652 
   1653 	if (ramp && mapUserRamp) {
   1654 		copy_rgb_regamma_to_coordinates_x(coords,
   1655 				hw_points_num,
   1656 				rgb_regamma);
   1657 
   1658 		calculate_interpolated_hardware_curve(
   1659 			ramp, coeff128, rgb_user, coords, axis_x,
   1660 			hw_points_num, tf_pts);
   1661 	} else {
   1662 		/* just copy current rgb_regamma into  tf_pts */
   1663 		while (i <= hw_points_num) {
   1664 			tf_pts->red[i] = regamma->r;
   1665 			tf_pts->green[i] = regamma->g;
   1666 			tf_pts->blue[i] = regamma->b;
   1667 
   1668 			++regamma;
   1669 			++i;
   1670 		}
   1671 	}
   1672 
   1673 	/* this should be named differently, all it does is clamp to 0-1 */
   1674 	build_new_custom_resulted_curve(hw_points_num, tf_pts);
   1675 
   1676 	return true;
   1677 }
   1678 
   1679 #define _EXTRA_POINTS 3
   1680 
   1681 bool calculate_user_regamma_coeff(struct dc_transfer_func *output_tf,
   1682 		const struct regamma_lut *regamma)
   1683 {
   1684 	struct gamma_coefficients coeff;
   1685 	const struct hw_x_point *coord_x = coordinates_x;
   1686 	uint32_t i = 0;
   1687 
   1688 	do {
   1689 		coeff.a0[i] = dc_fixpt_from_fraction(
   1690 				regamma->coeff.A0[i], 10000000);
   1691 		coeff.a1[i] = dc_fixpt_from_fraction(
   1692 				regamma->coeff.A1[i], 1000);
   1693 		coeff.a2[i] = dc_fixpt_from_fraction(
   1694 				regamma->coeff.A2[i], 1000);
   1695 		coeff.a3[i] = dc_fixpt_from_fraction(
   1696 				regamma->coeff.A3[i], 1000);
   1697 		coeff.user_gamma[i] = dc_fixpt_from_fraction(
   1698 				regamma->coeff.gamma[i], 1000);
   1699 
   1700 		++i;
   1701 	} while (i != 3);
   1702 
   1703 	i = 0;
   1704 	/* fixed_pt library has problems handling too small values */
   1705 	while (i != 32) {
   1706 		output_tf->tf_pts.red[i] = dc_fixpt_zero;
   1707 		output_tf->tf_pts.green[i] = dc_fixpt_zero;
   1708 		output_tf->tf_pts.blue[i] = dc_fixpt_zero;
   1709 		++coord_x;
   1710 		++i;
   1711 	}
   1712 	while (i != MAX_HW_POINTS + 1) {
   1713 		output_tf->tf_pts.red[i] = translate_from_linear_space_ex(
   1714 				coord_x->x, &coeff, 0);
   1715 		output_tf->tf_pts.green[i] = translate_from_linear_space_ex(
   1716 				coord_x->x, &coeff, 1);
   1717 		output_tf->tf_pts.blue[i] = translate_from_linear_space_ex(
   1718 				coord_x->x, &coeff, 2);
   1719 		++coord_x;
   1720 		++i;
   1721 	}
   1722 
   1723 	// this function just clamps output to 0-1
   1724 	build_new_custom_resulted_curve(MAX_HW_POINTS, &output_tf->tf_pts);
   1725 	output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
   1726 
   1727 	return true;
   1728 }
   1729 
   1730 bool calculate_user_regamma_ramp(struct dc_transfer_func *output_tf,
   1731 		const struct regamma_lut *regamma)
   1732 {
   1733 	struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts;
   1734 	struct dividers dividers;
   1735 
   1736 	struct pwl_float_data *rgb_user = NULL;
   1737 	struct pwl_float_data_ex *rgb_regamma = NULL;
   1738 	bool ret = false;
   1739 
   1740 	if (regamma == NULL)
   1741 		return false;
   1742 
   1743 	output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
   1744 
   1745 	rgb_user = kcalloc(GAMMA_RGB_256_ENTRIES + _EXTRA_POINTS,
   1746 			   sizeof(*rgb_user),
   1747 			   GFP_KERNEL);
   1748 	if (!rgb_user)
   1749 		goto rgb_user_alloc_fail;
   1750 
   1751 	rgb_regamma = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
   1752 			      sizeof(*rgb_regamma),
   1753 			      GFP_KERNEL);
   1754 	if (!rgb_regamma)
   1755 		goto rgb_regamma_alloc_fail;
   1756 
   1757 	dividers.divider1 = dc_fixpt_from_fraction(3, 2);
   1758 	dividers.divider2 = dc_fixpt_from_int(2);
   1759 	dividers.divider3 = dc_fixpt_from_fraction(5, 2);
   1760 
   1761 	scale_user_regamma_ramp(rgb_user, &regamma->ramp, dividers);
   1762 
   1763 	if (regamma->flags.bits.applyDegamma == 1) {
   1764 		apply_degamma_for_user_regamma(rgb_regamma, MAX_HW_POINTS);
   1765 		copy_rgb_regamma_to_coordinates_x(coordinates_x,
   1766 				MAX_HW_POINTS, rgb_regamma);
   1767 	}
   1768 
   1769 	interpolate_user_regamma(MAX_HW_POINTS, rgb_user,
   1770 			regamma->flags.bits.applyDegamma, tf_pts);
   1771 
   1772 	// no custom HDR curves!
   1773 	tf_pts->end_exponent = 0;
   1774 	tf_pts->x_point_at_y1_red = 1;
   1775 	tf_pts->x_point_at_y1_green = 1;
   1776 	tf_pts->x_point_at_y1_blue = 1;
   1777 
   1778 	// this function just clamps output to 0-1
   1779 	build_new_custom_resulted_curve(MAX_HW_POINTS, tf_pts);
   1780 
   1781 	ret = true;
   1782 
   1783 	kfree(rgb_regamma);
   1784 rgb_regamma_alloc_fail:
   1785 	kvfree(rgb_user);
   1786 rgb_user_alloc_fail:
   1787 	return ret;
   1788 }
   1789 
   1790 bool mod_color_calculate_degamma_params(struct dc_transfer_func *input_tf,
   1791 		const struct dc_gamma *ramp, bool mapUserRamp)
   1792 {
   1793 	struct dc_transfer_func_distributed_points *tf_pts = &input_tf->tf_pts;
   1794 	struct dividers dividers;
   1795 	struct pwl_float_data *rgb_user = NULL;
   1796 	struct pwl_float_data_ex *curve = NULL;
   1797 	struct gamma_pixel *axis_x = NULL;
   1798 	struct pixel_gamma_point *coeff = NULL;
   1799 	enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB;
   1800 	uint32_t i;
   1801 	bool ret = false;
   1802 
   1803 	if (input_tf->type == TF_TYPE_BYPASS)
   1804 		return false;
   1805 
   1806 	/* we can use hardcoded curve for plain SRGB TF
   1807 	 * If linear, it's bypass if on user ramp
   1808 	 */
   1809 	if (input_tf->type == TF_TYPE_PREDEFINED &&
   1810 			(input_tf->tf == TRANSFER_FUNCTION_SRGB ||
   1811 					input_tf->tf == TRANSFER_FUNCTION_LINEAR) &&
   1812 					!mapUserRamp)
   1813 		return true;
   1814 
   1815 	input_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
   1816 
   1817 	if (mapUserRamp && ramp && ramp->type == GAMMA_RGB_256) {
   1818 		rgb_user = kvcalloc(ramp->num_entries + _EXTRA_POINTS,
   1819 				sizeof(*rgb_user),
   1820 				GFP_KERNEL);
   1821 		if (!rgb_user)
   1822 			goto rgb_user_alloc_fail;
   1823 
   1824 		axis_x = kvcalloc(ramp->num_entries + _EXTRA_POINTS, sizeof(*axis_x),
   1825 				GFP_KERNEL);
   1826 		if (!axis_x)
   1827 			goto axis_x_alloc_fail;
   1828 
   1829 		dividers.divider1 = dc_fixpt_from_fraction(3, 2);
   1830 		dividers.divider2 = dc_fixpt_from_int(2);
   1831 		dividers.divider3 = dc_fixpt_from_fraction(5, 2);
   1832 
   1833 		build_evenly_distributed_points(
   1834 				axis_x,
   1835 				ramp->num_entries,
   1836 				dividers);
   1837 
   1838 		scale_gamma(rgb_user, ramp, dividers);
   1839 	}
   1840 
   1841 	curve = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*curve),
   1842 			GFP_KERNEL);
   1843 	if (!curve)
   1844 		goto curve_alloc_fail;
   1845 
   1846 	coeff = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*coeff),
   1847 			GFP_KERNEL);
   1848 	if (!coeff)
   1849 		goto coeff_alloc_fail;
   1850 
   1851 	tf = input_tf->tf;
   1852 
   1853 	if (tf == TRANSFER_FUNCTION_PQ)
   1854 		build_de_pq(curve,
   1855 				MAX_HW_POINTS,
   1856 				coordinates_x);
   1857 	else if (tf == TRANSFER_FUNCTION_SRGB ||
   1858 		tf == TRANSFER_FUNCTION_BT709 ||
   1859 		tf == TRANSFER_FUNCTION_GAMMA22 ||
   1860 		tf == TRANSFER_FUNCTION_GAMMA24 ||
   1861 		tf == TRANSFER_FUNCTION_GAMMA26)
   1862 		build_degamma(curve,
   1863 				MAX_HW_POINTS,
   1864 				coordinates_x,
   1865 				tf);
   1866 	else if (tf == TRANSFER_FUNCTION_HLG)
   1867 		build_hlg_degamma(curve,
   1868 				MAX_HW_POINTS,
   1869 				coordinates_x,
   1870 				80, 1000);
   1871 	else if (tf == TRANSFER_FUNCTION_LINEAR) {
   1872 		// just copy coordinates_x into curve
   1873 		i = 0;
   1874 		while (i != MAX_HW_POINTS + 1) {
   1875 			curve[i].r = coordinates_x[i].x;
   1876 			curve[i].g = curve[i].r;
   1877 			curve[i].b = curve[i].r;
   1878 			i++;
   1879 		}
   1880 	} else
   1881 		goto invalid_tf_fail;
   1882 
   1883 	tf_pts->end_exponent = 0;
   1884 	tf_pts->x_point_at_y1_red = 1;
   1885 	tf_pts->x_point_at_y1_green = 1;
   1886 	tf_pts->x_point_at_y1_blue = 1;
   1887 
   1888 	if (input_tf->tf == TRANSFER_FUNCTION_PQ) {
   1889 		/* just copy current rgb_regamma into  tf_pts */
   1890 		struct pwl_float_data_ex *curvePt = curve;
   1891 		int i = 0;
   1892 
   1893 		while (i <= MAX_HW_POINTS) {
   1894 			tf_pts->red[i]   = curvePt->r;
   1895 			tf_pts->green[i] = curvePt->g;
   1896 			tf_pts->blue[i]  = curvePt->b;
   1897 			++curvePt;
   1898 			++i;
   1899 		}
   1900 	} else {
   1901 		//clamps to 0-1
   1902 		map_regamma_hw_to_x_user(ramp, coeff, rgb_user,
   1903 				coordinates_x, axis_x, curve,
   1904 				MAX_HW_POINTS, tf_pts,
   1905 				mapUserRamp && ramp && ramp->type == GAMMA_RGB_256);
   1906 	}
   1907 
   1908 
   1909 
   1910 	if (ramp->type == GAMMA_CUSTOM)
   1911 		apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts);
   1912 
   1913 	ret = true;
   1914 
   1915 invalid_tf_fail:
   1916 	kvfree(coeff);
   1917 coeff_alloc_fail:
   1918 	kvfree(curve);
   1919 curve_alloc_fail:
   1920 	kvfree(axis_x);
   1921 axis_x_alloc_fail:
   1922 	kvfree(rgb_user);
   1923 rgb_user_alloc_fail:
   1924 
   1925 	return ret;
   1926 }
   1927 
   1928 static bool calculate_curve(enum dc_transfer_func_predefined trans,
   1929 				struct dc_transfer_func_distributed_points *points,
   1930 				struct pwl_float_data_ex *rgb_regamma,
   1931 				const struct freesync_hdr_tf_params *fs_params,
   1932 				uint32_t sdr_ref_white_level)
   1933 {
   1934 	uint32_t i;
   1935 	bool ret = false;
   1936 
   1937 	if (trans == TRANSFER_FUNCTION_UNITY ||
   1938 		trans == TRANSFER_FUNCTION_LINEAR) {
   1939 		points->end_exponent = 0;
   1940 		points->x_point_at_y1_red = 1;
   1941 		points->x_point_at_y1_green = 1;
   1942 		points->x_point_at_y1_blue = 1;
   1943 
   1944 		for (i = 0; i <= MAX_HW_POINTS ; i++) {
   1945 			rgb_regamma[i].r = coordinates_x[i].x;
   1946 			rgb_regamma[i].g = coordinates_x[i].x;
   1947 			rgb_regamma[i].b = coordinates_x[i].x;
   1948 		}
   1949 
   1950 		ret = true;
   1951 	} else if (trans == TRANSFER_FUNCTION_PQ) {
   1952 		points->end_exponent = 7;
   1953 		points->x_point_at_y1_red = 125;
   1954 		points->x_point_at_y1_green = 125;
   1955 		points->x_point_at_y1_blue = 125;
   1956 
   1957 		build_pq(rgb_regamma,
   1958 				MAX_HW_POINTS,
   1959 				coordinates_x,
   1960 				sdr_ref_white_level);
   1961 
   1962 		ret = true;
   1963 	} else if (trans == TRANSFER_FUNCTION_GAMMA22 &&
   1964 			fs_params != NULL && fs_params->skip_tm == 0) {
   1965 		build_freesync_hdr(rgb_regamma,
   1966 				MAX_HW_POINTS,
   1967 				coordinates_x,
   1968 				fs_params);
   1969 
   1970 		ret = true;
   1971 	} else if (trans == TRANSFER_FUNCTION_HLG) {
   1972 		points->end_exponent = 4;
   1973 		points->x_point_at_y1_red = 12;
   1974 		points->x_point_at_y1_green = 12;
   1975 		points->x_point_at_y1_blue = 12;
   1976 
   1977 		build_hlg_regamma(rgb_regamma,
   1978 				MAX_HW_POINTS,
   1979 				coordinates_x,
   1980 				80, 1000);
   1981 
   1982 		ret = true;
   1983 	} else {
   1984 		// trans == TRANSFER_FUNCTION_SRGB
   1985 		// trans == TRANSFER_FUNCTION_BT709
   1986 		// trans == TRANSFER_FUNCTION_GAMMA22
   1987 		// trans == TRANSFER_FUNCTION_GAMMA24
   1988 		// trans == TRANSFER_FUNCTION_GAMMA26
   1989 		points->end_exponent = 0;
   1990 		points->x_point_at_y1_red = 1;
   1991 		points->x_point_at_y1_green = 1;
   1992 		points->x_point_at_y1_blue = 1;
   1993 
   1994 		build_regamma(rgb_regamma,
   1995 				MAX_HW_POINTS,
   1996 				coordinates_x,
   1997 				trans);
   1998 
   1999 		ret = true;
   2000 	}
   2001 
   2002 	return ret;
   2003 }
   2004 
   2005 bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf,
   2006 		const struct dc_gamma *ramp, bool mapUserRamp, bool canRomBeUsed,
   2007 		const struct freesync_hdr_tf_params *fs_params)
   2008 {
   2009 	struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts;
   2010 	struct dividers dividers;
   2011 
   2012 	struct pwl_float_data *rgb_user = NULL;
   2013 	struct pwl_float_data_ex *rgb_regamma = NULL;
   2014 	struct gamma_pixel *axis_x = NULL;
   2015 	struct pixel_gamma_point *coeff = NULL;
   2016 	enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB;
   2017 	bool ret = false;
   2018 
   2019 	if (output_tf->type == TF_TYPE_BYPASS)
   2020 		return false;
   2021 
   2022 	/* we can use hardcoded curve for plain SRGB TF */
   2023 	if (output_tf->type == TF_TYPE_PREDEFINED && canRomBeUsed == true &&
   2024 			output_tf->tf == TRANSFER_FUNCTION_SRGB) {
   2025 		if (ramp == NULL)
   2026 			return true;
   2027 		if ((ramp->is_identity && ramp->type != GAMMA_CS_TFM_1D) ||
   2028 				(!mapUserRamp && ramp->type == GAMMA_RGB_256))
   2029 			return true;
   2030 	}
   2031 
   2032 	output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
   2033 
   2034 	if (ramp && ramp->type != GAMMA_CS_TFM_1D &&
   2035 			(mapUserRamp || ramp->type != GAMMA_RGB_256)) {
   2036 		rgb_user = kvcalloc(ramp->num_entries + _EXTRA_POINTS,
   2037 			    sizeof(*rgb_user),
   2038 			    GFP_KERNEL);
   2039 		if (!rgb_user)
   2040 			goto rgb_user_alloc_fail;
   2041 
   2042 		axis_x = kvcalloc(ramp->num_entries + 3, sizeof(*axis_x),
   2043 				GFP_KERNEL);
   2044 		if (!axis_x)
   2045 			goto axis_x_alloc_fail;
   2046 
   2047 		dividers.divider1 = dc_fixpt_from_fraction(3, 2);
   2048 		dividers.divider2 = dc_fixpt_from_int(2);
   2049 		dividers.divider3 = dc_fixpt_from_fraction(5, 2);
   2050 
   2051 		build_evenly_distributed_points(
   2052 				axis_x,
   2053 				ramp->num_entries,
   2054 				dividers);
   2055 
   2056 		if (ramp->type == GAMMA_RGB_256 && mapUserRamp)
   2057 			scale_gamma(rgb_user, ramp, dividers);
   2058 		else if (ramp->type == GAMMA_RGB_FLOAT_1024)
   2059 			scale_gamma_dx(rgb_user, ramp, dividers);
   2060 	}
   2061 
   2062 	rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
   2063 			       sizeof(*rgb_regamma),
   2064 			       GFP_KERNEL);
   2065 	if (!rgb_regamma)
   2066 		goto rgb_regamma_alloc_fail;
   2067 
   2068 	coeff = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*coeff),
   2069 			 GFP_KERNEL);
   2070 	if (!coeff)
   2071 		goto coeff_alloc_fail;
   2072 
   2073 	tf = output_tf->tf;
   2074 
   2075 	ret = calculate_curve(tf,
   2076 			tf_pts,
   2077 			rgb_regamma,
   2078 			fs_params,
   2079 			output_tf->sdr_ref_white_level);
   2080 
   2081 	if (ret) {
   2082 		map_regamma_hw_to_x_user(ramp, coeff, rgb_user,
   2083 				coordinates_x, axis_x, rgb_regamma,
   2084 				MAX_HW_POINTS, tf_pts,
   2085 				(mapUserRamp || (ramp && ramp->type != GAMMA_RGB_256)) &&
   2086 				(ramp && ramp->type != GAMMA_CS_TFM_1D));
   2087 
   2088 		if (ramp && ramp->type == GAMMA_CS_TFM_1D)
   2089 			apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts);
   2090 	}
   2091 
   2092 	kvfree(coeff);
   2093 coeff_alloc_fail:
   2094 	kvfree(rgb_regamma);
   2095 rgb_regamma_alloc_fail:
   2096 	kvfree(axis_x);
   2097 axis_x_alloc_fail:
   2098 	kvfree(rgb_user);
   2099 rgb_user_alloc_fail:
   2100 	return ret;
   2101 }
   2102 
   2103 bool  mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans,
   2104 				struct dc_transfer_func_distributed_points *points)
   2105 {
   2106 	uint32_t i;
   2107 	bool ret = false;
   2108 	struct pwl_float_data_ex *rgb_degamma = NULL;
   2109 
   2110 	if (trans == TRANSFER_FUNCTION_UNITY ||
   2111 		trans == TRANSFER_FUNCTION_LINEAR) {
   2112 
   2113 		for (i = 0; i <= MAX_HW_POINTS ; i++) {
   2114 			points->red[i]    = coordinates_x[i].x;
   2115 			points->green[i]  = coordinates_x[i].x;
   2116 			points->blue[i]   = coordinates_x[i].x;
   2117 		}
   2118 		ret = true;
   2119 	} else if (trans == TRANSFER_FUNCTION_PQ) {
   2120 		rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
   2121 				       sizeof(*rgb_degamma),
   2122 				       GFP_KERNEL);
   2123 		if (!rgb_degamma)
   2124 			goto rgb_degamma_alloc_fail;
   2125 
   2126 
   2127 		build_de_pq(rgb_degamma,
   2128 				MAX_HW_POINTS,
   2129 				coordinates_x);
   2130 		for (i = 0; i <= MAX_HW_POINTS ; i++) {
   2131 			points->red[i]    = rgb_degamma[i].r;
   2132 			points->green[i]  = rgb_degamma[i].g;
   2133 			points->blue[i]   = rgb_degamma[i].b;
   2134 		}
   2135 		ret = true;
   2136 
   2137 		kvfree(rgb_degamma);
   2138 	} else if (trans == TRANSFER_FUNCTION_SRGB ||
   2139 		trans == TRANSFER_FUNCTION_BT709 ||
   2140 		trans == TRANSFER_FUNCTION_GAMMA22 ||
   2141 		trans == TRANSFER_FUNCTION_GAMMA24 ||
   2142 		trans == TRANSFER_FUNCTION_GAMMA26) {
   2143 		rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
   2144 				       sizeof(*rgb_degamma),
   2145 				       GFP_KERNEL);
   2146 		if (!rgb_degamma)
   2147 			goto rgb_degamma_alloc_fail;
   2148 
   2149 		build_degamma(rgb_degamma,
   2150 				MAX_HW_POINTS,
   2151 				coordinates_x,
   2152 				trans);
   2153 		for (i = 0; i <= MAX_HW_POINTS ; i++) {
   2154 			points->red[i]    = rgb_degamma[i].r;
   2155 			points->green[i]  = rgb_degamma[i].g;
   2156 			points->blue[i]   = rgb_degamma[i].b;
   2157 		}
   2158 		ret = true;
   2159 
   2160 		kvfree(rgb_degamma);
   2161 	} else if (trans == TRANSFER_FUNCTION_HLG) {
   2162 		rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
   2163 				       sizeof(*rgb_degamma),
   2164 				       GFP_KERNEL);
   2165 		if (!rgb_degamma)
   2166 			goto rgb_degamma_alloc_fail;
   2167 
   2168 		build_hlg_degamma(rgb_degamma,
   2169 				MAX_HW_POINTS,
   2170 				coordinates_x,
   2171 				80, 1000);
   2172 		for (i = 0; i <= MAX_HW_POINTS ; i++) {
   2173 			points->red[i]    = rgb_degamma[i].r;
   2174 			points->green[i]  = rgb_degamma[i].g;
   2175 			points->blue[i]   = rgb_degamma[i].b;
   2176 		}
   2177 		ret = true;
   2178 		kvfree(rgb_degamma);
   2179 	}
   2180 	points->end_exponent = 0;
   2181 	points->x_point_at_y1_red = 1;
   2182 	points->x_point_at_y1_green = 1;
   2183 	points->x_point_at_y1_blue = 1;
   2184 
   2185 rgb_degamma_alloc_fail:
   2186 	return ret;
   2187 }
   2188