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