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