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