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