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