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