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