xref: /linux/drivers/gpu/drm/amd/display/modules/color/color_gamma.c (revision 60e8ffaf96267e8c520ffa9411f4893ace7a0a11)
1 /*
2  * Copyright 2016 Advanced Micro Devices, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Authors: AMD
23  *
24  */
25 
26 #include "dc.h"
27 #include "opp.h"
28 #include "color_gamma.h"
29 
30 /* When calculating LUT values the first region and at least one subsequent
31  * region are calculated with full precision. These defines are a demarcation
32  * of where the second region starts and ends.
33  * These are hardcoded values to avoid recalculating them in loops.
34  */
35 #define PRECISE_LUT_REGION_START 224
36 #define PRECISE_LUT_REGION_END 239
37 
38 static struct hw_x_point coordinates_x[MAX_HW_POINTS + 2];
39 
40 // Hardcoded table that depends on setup_x_points_distribution and sdr_level=80
41 // If x points are changed, then PQ Y points will be misaligned and a new
42 // table would need to be generated. Or use old method that calls compute_pq.
43 // The last point is above PQ formula range (0-125 in normalized FP16)
44 // The value for the last point (128) is such that interpolation from
45 // 120 to 128 will give 1.0 for X = 125.0
46 // first couple points are 0 - HW LUT is mirrored around zero, so making first
47 // segment 0 to 0 will effectively clip it, and these are very low PQ codes
48 // min nonzero value below (216825) is a little under 12-bit PQ code 1.
49 static const unsigned long long pq_divider = 1000000000;
50 static const unsigned long long pq_numerator[MAX_HW_POINTS + 1] = {
51 		0, 0, 0, 0, 216825, 222815,
52 		228691, 234460, 240128, 245702, 251187, 256587,
53 		261908, 267152, 272324, 277427, 282465, 292353,
54 		302011, 311456, 320704, 329768, 338661, 347394,
55 		355975, 364415, 372721, 380900, 388959, 396903,
56 		404739, 412471, 420104, 435089, 449727, 464042,
57 		478060, 491800, 505281, 518520, 531529, 544324,
58 		556916, 569316, 581533, 593576, 605454, 617175,
59 		628745, 651459, 673643, 695337, 716578, 737395,
60 		757817, 777869, 797572, 816947, 836012, 854782,
61 		873274, 891500, 909474, 927207, 944709, 979061,
62 		1012601, 1045391, 1077485, 1108931, 1139770, 1170042,
63 		1199778, 1229011, 1257767, 1286071, 1313948, 1341416,
64 		1368497, 1395207, 1421563, 1473272, 1523733, 1573041,
65 		1621279, 1668520, 1714828, 1760262, 1804874, 1848710,
66 		1891814, 1934223, 1975973, 2017096, 2057622, 2097578,
67 		2136989, 2214269, 2289629, 2363216, 2435157, 2505564,
68 		2574539, 2642169, 2708536, 2773711, 2837760, 2900742,
69 		2962712, 3023719, 3083810, 3143025, 3201405, 3315797,
70 		3427246, 3535974, 3642181, 3746038, 3847700, 3947305,
71 		4044975, 4140823, 4234949, 4327445, 4418394, 4507872,
72 		4595951, 4682694, 4768161, 4935487, 5098326, 5257022,
73 		5411878, 5563161, 5711107, 5855928, 5997812, 6136929,
74 		6273436, 6407471, 6539163, 6668629, 6795976, 6921304,
75 		7044703, 7286050, 7520623, 7748950, 7971492, 8188655,
76 		8400800, 8608247, 8811286, 9010175, 9205149, 9396421,
77 		9584186, 9768620, 9949889, 10128140, 10303513, 10646126,
78 		10978648, 11301874, 11616501, 11923142, 12222340, 12514578,
79 		12800290, 13079866, 13353659, 13621988, 13885144, 14143394,
80 		14396982, 14646132, 14891052, 15368951, 15832050, 16281537,
81 		16718448, 17143696, 17558086, 17962337, 18357092, 18742927,
82 		19120364, 19489877, 19851894, 20206810, 20554983, 20896745,
83 		21232399, 21886492, 22519276, 23132491, 23727656, 24306104,
84 		24869013, 25417430, 25952292, 26474438, 26984626, 27483542,
85 		27971811, 28450000, 28918632, 29378184, 29829095, 30706591,
86 		31554022, 32373894, 33168387, 33939412, 34688657, 35417620,
87 		36127636, 36819903, 37495502, 38155408, 38800507, 39431607,
88 		40049446, 40654702, 41247996, 42400951, 43512407, 44585892,
89 		45624474, 46630834, 47607339, 48556082, 49478931, 50377558,
90 		51253467, 52108015, 52942436, 53757848, 54555277, 55335659,
91 		56099856, 57582802, 59009766, 60385607, 61714540, 63000246,
92 		64245964, 65454559, 66628579, 67770304, 68881781, 69964856,
93 		71021203, 72052340, 73059655, 74044414, 75007782, 76874537,
94 		78667536, 80393312, 82057522, 83665098, 85220372, 86727167,
95 		88188883, 89608552, 90988895, 92332363, 93641173, 94917336,
96 		96162685, 97378894, 98567496, 100867409, 103072439, 105191162,
97 		107230989, 109198368, 111098951, 112937723, 114719105, 116447036,
98 		118125045, 119756307, 121343688, 122889787, 124396968, 125867388,
99 		127303021, 130077030, 132731849, 135278464, 137726346, 140083726,
100 		142357803, 144554913, 146680670, 148740067, 150737572, 152677197,
101 		154562560, 156396938, 158183306, 159924378, 161622632, 164899602,
102 		168030318, 171028513, 173906008, 176673051, 179338593, 181910502,
103 		184395731, 186800463, 189130216, 191389941, 193584098, 195716719,
104 		197791463, 199811660, 201780351, 205574133, 209192504, 212652233,
105 		215967720, 219151432, 222214238, 225165676, 228014163, 230767172,
106 		233431363, 236012706, 238516569, 240947800, 243310793, 245609544,
107 		247847696, 252155270, 256257056, 260173059, 263920427, 267513978,
108 		270966613, 274289634, 277493001, 280585542, 283575118, 286468763,
109 		289272796, 291992916, 294634284, 297201585, 299699091, 304500003,
110 		309064541, 313416043, 317574484, 321557096, 325378855, 329052864,
111 		332590655, 336002433, 339297275, 342483294, 345567766, 348557252,
112 		351457680, 354274432, 357012407, 362269536, 367260561, 372012143,
113 		376547060, 380884936, 385042798, 389035522, 392876185, 396576344,
114 		400146265, 403595112, 406931099, 410161619, 413293351, 416332348,
115 		419284117, 424945627, 430313203, 435416697, 440281572, 444929733,
116 		449380160, 453649415, 457752035, 461700854, 465507260, 469181407,
117 		472732388, 476168376, 479496748, 482724188, 485856764, 491858986,
118 		497542280, 502939446, 508078420, 512983199, 517674549, 522170569,
119 		526487126, 530638214, 534636233, 538492233, 542216094, 545816693,
120 		549302035, 552679362, 555955249, 562226134, 568156709, 573782374,
121 		579133244, 584235153, 589110430, 593778512, 598256421, 602559154,
122 		606699989, 610690741, 614541971, 618263157, 621862836, 625348729,
123 		628727839, 635190643, 641295921, 647081261, 652578597, 657815287,
124 		662814957, 667598146, 672182825, 676584810, 680818092, 684895111,
125 		688826974, 692623643, 696294085, 699846401, 703287935, 709864782,
126 		716071394, 721947076, 727525176, 732834238, 737898880, 742740485,
127 		747377745, 751827095, 756103063, 760218552, 764185078, 768012958,
128 		771711474, 775289005, 778753144, 785368225, 791604988, 797503949,
129 		803099452, 808420859, 813493471, 818339244, 822977353, 827424644,
130 		831695997, 835804619, 839762285, 843579541, 847265867, 850829815,
131 		854279128, 860861356, 867061719, 872921445, 878475444, 883753534,
132 		888781386, 893581259, 898172578, 902572393, 906795754, 910856010,
133 		914765057, 918533538, 922171018, 925686119, 929086644, 935571664,
134 		941675560, 947439782, 952899395, 958084324, 963020312, 967729662,
135 		972231821, 976543852, 980680801, 984656009, 988481353, 992167459,
136 		995723865, 999159168, 1002565681};
137 
138 // these are helpers for calculations to reduce stack usage
139 // do not depend on these being preserved across calls
140 
141 /* Helper to optimize gamma calculation, only use in translate_from_linear, in
142  * particular the dc_fixpt_pow function which is very expensive
143  * The idea is that our regions for X points are exponential and currently they all use
144  * the same number of points (NUM_PTS_IN_REGION) and in each region every point
145  * is exactly 2x the one at the same index in the previous region. In other words
146  * X[i] = 2 * X[i-NUM_PTS_IN_REGION] for i>=16
147  * The other fact is that (2x)^gamma = 2^gamma * x^gamma
148  * So we compute and save x^gamma for the first 16 regions, and for every next region
149  * just multiply with 2^gamma which can be computed once, and save the result so we
150  * recursively compute all the values.
151  */
152 
153 /*
154  * Regamma coefficients are used for both regamma and degamma. Degamma
155  * coefficients are calculated in our formula using the regamma coefficients.
156  */
157 									 /*sRGB     709     2.2 2.4 P3*/
158 static const int32_t numerator01[] = { 31308,   180000, 0,  0,  0};
159 static const int32_t numerator02[] = { 12920,   4500,   0,  0,  0};
160 static const int32_t numerator03[] = { 55,      99,     0,  0,  0};
161 static const int32_t numerator04[] = { 55,      99,     0,  0,  0};
162 static const int32_t numerator05[] = { 2400,    2222,   2200, 2400, 2600};
163 
164 /* one-time setup of X points */
165 void setup_x_points_distribution(void)
166 {
167 	struct fixed31_32 region_size = dc_fixpt_from_int(128);
168 	int32_t segment;
169 	uint32_t seg_offset;
170 	uint32_t index;
171 	struct fixed31_32 increment;
172 
173 	coordinates_x[MAX_HW_POINTS].x = region_size;
174 	coordinates_x[MAX_HW_POINTS + 1].x = region_size;
175 
176 	for (segment = 6; segment > (6 - NUM_REGIONS); segment--) {
177 		region_size = dc_fixpt_div_int(region_size, 2);
178 		increment = dc_fixpt_div_int(region_size,
179 						NUM_PTS_IN_REGION);
180 		seg_offset = (segment + (NUM_REGIONS - 7)) * NUM_PTS_IN_REGION;
181 		coordinates_x[seg_offset].x = region_size;
182 
183 		for (index = seg_offset + 1;
184 				index < seg_offset + NUM_PTS_IN_REGION;
185 				index++) {
186 			coordinates_x[index].x = dc_fixpt_add
187 					(coordinates_x[index-1].x, increment);
188 		}
189 	}
190 }
191 
192 void log_x_points_distribution(struct dal_logger *logger)
193 {
194 	int i = 0;
195 
196 	if (logger != NULL) {
197 		LOG_GAMMA_WRITE("Log X Distribution\n");
198 
199 		for (i = 0; i < MAX_HW_POINTS; i++)
200 			LOG_GAMMA_WRITE("%llu\n", coordinates_x[i].x.value);
201 	}
202 }
203 
204 static void compute_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y)
205 {
206 	/* consts for PQ gamma formula. */
207 	const struct fixed31_32 m1 =
208 		dc_fixpt_from_fraction(159301758, 1000000000);
209 	const struct fixed31_32 m2 =
210 		dc_fixpt_from_fraction(7884375, 100000);
211 	const struct fixed31_32 c1 =
212 		dc_fixpt_from_fraction(8359375, 10000000);
213 	const struct fixed31_32 c2 =
214 		dc_fixpt_from_fraction(188515625, 10000000);
215 	const struct fixed31_32 c3 =
216 		dc_fixpt_from_fraction(186875, 10000);
217 
218 	struct fixed31_32 l_pow_m1;
219 	struct fixed31_32 base;
220 
221 	if (dc_fixpt_lt(in_x, dc_fixpt_zero))
222 		in_x = dc_fixpt_zero;
223 
224 	l_pow_m1 = dc_fixpt_pow(in_x, m1);
225 	base = dc_fixpt_div(
226 			dc_fixpt_add(c1,
227 					(dc_fixpt_mul(c2, l_pow_m1))),
228 			dc_fixpt_add(dc_fixpt_one,
229 					(dc_fixpt_mul(c3, l_pow_m1))));
230 	*out_y = dc_fixpt_pow(base, m2);
231 }
232 
233 static void compute_de_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y)
234 {
235 	/* consts for dePQ gamma formula. */
236 	const struct fixed31_32 m1 =
237 		dc_fixpt_from_fraction(159301758, 1000000000);
238 	const struct fixed31_32 m2 =
239 		dc_fixpt_from_fraction(7884375, 100000);
240 	const struct fixed31_32 c1 =
241 		dc_fixpt_from_fraction(8359375, 10000000);
242 	const struct fixed31_32 c2 =
243 		dc_fixpt_from_fraction(188515625, 10000000);
244 	const struct fixed31_32 c3 =
245 		dc_fixpt_from_fraction(186875, 10000);
246 
247 	struct fixed31_32 l_pow_m1;
248 	struct fixed31_32 base, div;
249 	struct fixed31_32 base2;
250 
251 
252 	if (dc_fixpt_lt(in_x, dc_fixpt_zero))
253 		in_x = dc_fixpt_zero;
254 
255 	l_pow_m1 = dc_fixpt_pow(in_x,
256 			dc_fixpt_div(dc_fixpt_one, m2));
257 	base = dc_fixpt_sub(l_pow_m1, c1);
258 
259 	div = dc_fixpt_sub(c2, dc_fixpt_mul(c3, l_pow_m1));
260 
261 	base2 = dc_fixpt_div(base, div);
262 	// avoid complex numbers
263 	if (dc_fixpt_lt(base2, dc_fixpt_zero))
264 		base2 = dc_fixpt_sub(dc_fixpt_zero, base2);
265 
266 
267 	*out_y = dc_fixpt_pow(base2, dc_fixpt_div(dc_fixpt_one, m1));
268 
269 }
270 
271 
272 /* de gamma, non-linear to linear */
273 static void compute_hlg_eotf(struct fixed31_32 in_x,
274 		struct fixed31_32 *out_y,
275 		uint32_t sdr_white_level, uint32_t max_luminance_nits)
276 {
277 	struct fixed31_32 a;
278 	struct fixed31_32 b;
279 	struct fixed31_32 c;
280 	struct fixed31_32 threshold;
281 	struct fixed31_32 x;
282 
283 	struct fixed31_32 scaling_factor =
284 			dc_fixpt_from_fraction(max_luminance_nits, sdr_white_level);
285 	a = dc_fixpt_from_fraction(17883277, 100000000);
286 	b = dc_fixpt_from_fraction(28466892, 100000000);
287 	c = dc_fixpt_from_fraction(55991073, 100000000);
288 	threshold = dc_fixpt_from_fraction(1, 2);
289 
290 	if (dc_fixpt_lt(in_x, threshold)) {
291 		x = dc_fixpt_mul(in_x, in_x);
292 		x = dc_fixpt_div_int(x, 3);
293 	} else {
294 		x = dc_fixpt_sub(in_x, c);
295 		x = dc_fixpt_div(x, a);
296 		x = dc_fixpt_exp(x);
297 		x = dc_fixpt_add(x, b);
298 		x = dc_fixpt_div_int(x, 12);
299 	}
300 	*out_y = dc_fixpt_mul(x, scaling_factor);
301 
302 }
303 
304 /* re gamma, linear to non-linear */
305 static void compute_hlg_oetf(struct fixed31_32 in_x, struct fixed31_32 *out_y,
306 		uint32_t sdr_white_level, uint32_t max_luminance_nits)
307 {
308 	struct fixed31_32 a;
309 	struct fixed31_32 b;
310 	struct fixed31_32 c;
311 	struct fixed31_32 threshold;
312 	struct fixed31_32 x;
313 
314 	struct fixed31_32 scaling_factor =
315 			dc_fixpt_from_fraction(sdr_white_level, max_luminance_nits);
316 	a = dc_fixpt_from_fraction(17883277, 100000000);
317 	b = dc_fixpt_from_fraction(28466892, 100000000);
318 	c = dc_fixpt_from_fraction(55991073, 100000000);
319 	threshold = dc_fixpt_from_fraction(1, 12);
320 	x = dc_fixpt_mul(in_x, scaling_factor);
321 
322 
323 	if (dc_fixpt_lt(x, threshold)) {
324 		x = dc_fixpt_mul(x, dc_fixpt_from_fraction(3, 1));
325 		*out_y = dc_fixpt_pow(x, dc_fixpt_half);
326 	} else {
327 		x = dc_fixpt_mul(x, dc_fixpt_from_fraction(12, 1));
328 		x = dc_fixpt_sub(x, b);
329 		x = dc_fixpt_log(x);
330 		x = dc_fixpt_mul(a, x);
331 		*out_y = dc_fixpt_add(x, c);
332 	}
333 }
334 
335 
336 /* one-time pre-compute PQ values - only for sdr_white_level 80 */
337 void precompute_pq(void)
338 {
339 	int i;
340 	struct fixed31_32 *pq_table = mod_color_get_table(type_pq_table);
341 
342 	for (i = 0; i <= MAX_HW_POINTS; i++)
343 		pq_table[i] = dc_fixpt_from_fraction(pq_numerator[i], pq_divider);
344 
345 	/* below is old method that uses run-time calculation in fixed pt space */
346 	/* pow function has problems with arguments too small */
347 	/*
348 	struct fixed31_32 x;
349 	const struct hw_x_point *coord_x = coordinates_x + 32;
350 	struct fixed31_32 scaling_factor =
351 			dc_fixpt_from_fraction(80, 10000);
352 
353 	for (i = 0; i < 32; i++)
354 		pq_table[i] = dc_fixpt_zero;
355 
356 	for (i = 32; i <= MAX_HW_POINTS; i++) {
357 		x = dc_fixpt_mul(coord_x->x, scaling_factor);
358 		compute_pq(x, &pq_table[i]);
359 		++coord_x;
360 	}
361 	*/
362 }
363 
364 /* one-time pre-compute dePQ values - only for max pixel value 125 FP16 */
365 void precompute_de_pq(void)
366 {
367 	int i;
368 	struct fixed31_32  y;
369 	uint32_t begin_index, end_index;
370 
371 	struct fixed31_32 scaling_factor = dc_fixpt_from_int(125);
372 	struct fixed31_32 *de_pq_table = mod_color_get_table(type_de_pq_table);
373 	/* X points is 2^-25 to 2^7
374 	 * De-gamma X is 2^-12 to 2^0 – we are skipping first -12-(-25) = 13 regions
375 	 */
376 	begin_index = 13 * NUM_PTS_IN_REGION;
377 	end_index = begin_index + 12 * NUM_PTS_IN_REGION;
378 
379 	for (i = 0; i <= begin_index; i++)
380 		de_pq_table[i] = dc_fixpt_zero;
381 
382 	for (; i <= end_index; i++) {
383 		compute_de_pq(coordinates_x[i].x, &y);
384 		de_pq_table[i] = dc_fixpt_mul(y, scaling_factor);
385 	}
386 
387 	for (; i <= MAX_HW_POINTS; i++)
388 		de_pq_table[i] = de_pq_table[i-1];
389 }
390 struct dividers {
391 	struct fixed31_32 divider1;
392 	struct fixed31_32 divider2;
393 	struct fixed31_32 divider3;
394 };
395 
396 
397 static bool build_coefficients(struct gamma_coefficients *coefficients,
398 		enum dc_transfer_func_predefined type)
399 {
400 
401 	uint32_t i = 0;
402 	uint32_t index = 0;
403 	bool ret = true;
404 
405 	if (type == TRANSFER_FUNCTION_SRGB)
406 		index = 0;
407 	else if (type == TRANSFER_FUNCTION_BT709)
408 		index = 1;
409 	else if (type == TRANSFER_FUNCTION_GAMMA22)
410 		index = 2;
411 	else if (type == TRANSFER_FUNCTION_GAMMA24)
412 		index = 3;
413 	else if (type == TRANSFER_FUNCTION_GAMMA26)
414 		index = 4;
415 	else {
416 		ret = false;
417 		goto release;
418 	}
419 
420 	do {
421 		coefficients->a0[i] = dc_fixpt_from_fraction(
422 			numerator01[index], 10000000);
423 		coefficients->a1[i] = dc_fixpt_from_fraction(
424 			numerator02[index], 1000);
425 		coefficients->a2[i] = dc_fixpt_from_fraction(
426 			numerator03[index], 1000);
427 		coefficients->a3[i] = dc_fixpt_from_fraction(
428 			numerator04[index], 1000);
429 		coefficients->user_gamma[i] = dc_fixpt_from_fraction(
430 			numerator05[index], 1000);
431 
432 		++i;
433 	} while (i != ARRAY_SIZE(coefficients->a0));
434 release:
435 	return ret;
436 }
437 
438 static struct fixed31_32 translate_from_linear_space(
439 		struct translate_from_linear_space_args *args)
440 {
441 	const struct fixed31_32 one = dc_fixpt_from_int(1);
442 
443 	struct fixed31_32 scratch_1, scratch_2;
444 	struct calculate_buffer *cal_buffer = args->cal_buffer;
445 
446 	if (dc_fixpt_le(one, args->arg))
447 		return one;
448 
449 	if (dc_fixpt_le(args->arg, dc_fixpt_neg(args->a0))) {
450 		scratch_1 = dc_fixpt_add(one, args->a3);
451 		scratch_2 = dc_fixpt_pow(
452 				dc_fixpt_neg(args->arg),
453 				dc_fixpt_recip(args->gamma));
454 		scratch_1 = dc_fixpt_mul(scratch_1, scratch_2);
455 		scratch_1 = dc_fixpt_sub(args->a2, scratch_1);
456 
457 		return scratch_1;
458 	} else if (dc_fixpt_le(args->a0, args->arg)) {
459 		if (cal_buffer->buffer_index == 0) {
460 			cal_buffer->gamma_of_2 = dc_fixpt_pow(dc_fixpt_from_int(2),
461 					dc_fixpt_recip(args->gamma));
462 		}
463 		scratch_1 = dc_fixpt_add(one, args->a3);
464 		/* In the first region (first 16 points) and in the
465 		 * region delimited by START/END we calculate with
466 		 * full precision to avoid error accumulation.
467 		 */
468 		if ((cal_buffer->buffer_index >= PRECISE_LUT_REGION_START &&
469 			cal_buffer->buffer_index <= PRECISE_LUT_REGION_END) ||
470 			(cal_buffer->buffer_index < 16))
471 			scratch_2 = dc_fixpt_pow(args->arg,
472 					dc_fixpt_recip(args->gamma));
473 		else
474 			scratch_2 = dc_fixpt_mul(cal_buffer->gamma_of_2,
475 					cal_buffer->buffer[cal_buffer->buffer_index%16]);
476 
477 		if (cal_buffer->buffer_index != -1) {
478 			cal_buffer->buffer[cal_buffer->buffer_index%16] = scratch_2;
479 			cal_buffer->buffer_index++;
480 		}
481 
482 		scratch_1 = dc_fixpt_mul(scratch_1, scratch_2);
483 		scratch_1 = dc_fixpt_sub(scratch_1, args->a2);
484 
485 		return scratch_1;
486 	} else
487 		return dc_fixpt_mul(args->arg, args->a1);
488 }
489 
490 
491 static struct fixed31_32 translate_from_linear_space_long(
492 		struct translate_from_linear_space_args *args)
493 {
494 	const struct fixed31_32 one = dc_fixpt_from_int(1);
495 
496 	if (dc_fixpt_lt(one, args->arg))
497 		return one;
498 
499 	if (dc_fixpt_le(args->arg, dc_fixpt_neg(args->a0)))
500 		return dc_fixpt_sub(
501 			args->a2,
502 			dc_fixpt_mul(
503 				dc_fixpt_add(
504 					one,
505 					args->a3),
506 				dc_fixpt_pow(
507 					dc_fixpt_neg(args->arg),
508 					dc_fixpt_recip(args->gamma))));
509 	else if (dc_fixpt_le(args->a0, args->arg))
510 		return dc_fixpt_sub(
511 			dc_fixpt_mul(
512 				dc_fixpt_add(
513 					one,
514 					args->a3),
515 				dc_fixpt_pow(
516 						args->arg,
517 					dc_fixpt_recip(args->gamma))),
518 					args->a2);
519 	else
520 		return dc_fixpt_mul(args->arg, args->a1);
521 }
522 
523 static struct fixed31_32 calculate_gamma22(struct fixed31_32 arg, bool use_eetf, struct calculate_buffer *cal_buffer)
524 {
525 	struct fixed31_32 gamma = dc_fixpt_from_fraction(22, 10);
526 	struct translate_from_linear_space_args scratch_gamma_args;
527 
528 	scratch_gamma_args.arg = arg;
529 	scratch_gamma_args.a0 = dc_fixpt_zero;
530 	scratch_gamma_args.a1 = dc_fixpt_zero;
531 	scratch_gamma_args.a2 = dc_fixpt_zero;
532 	scratch_gamma_args.a3 = dc_fixpt_zero;
533 	scratch_gamma_args.cal_buffer = cal_buffer;
534 	scratch_gamma_args.gamma = gamma;
535 
536 	if (use_eetf)
537 		return translate_from_linear_space_long(&scratch_gamma_args);
538 
539 	return translate_from_linear_space(&scratch_gamma_args);
540 }
541 
542 
543 static struct fixed31_32 translate_to_linear_space(
544 	struct fixed31_32 arg,
545 	struct fixed31_32 a0,
546 	struct fixed31_32 a1,
547 	struct fixed31_32 a2,
548 	struct fixed31_32 a3,
549 	struct fixed31_32 gamma)
550 {
551 	struct fixed31_32 linear;
552 
553 	a0 = dc_fixpt_mul(a0, a1);
554 	if (dc_fixpt_le(arg, dc_fixpt_neg(a0)))
555 
556 		linear = dc_fixpt_neg(
557 				 dc_fixpt_pow(
558 				 dc_fixpt_div(
559 				 dc_fixpt_sub(a2, arg),
560 				 dc_fixpt_add(
561 				 dc_fixpt_one, a3)), gamma));
562 
563 	else if (dc_fixpt_le(dc_fixpt_neg(a0), arg) &&
564 			 dc_fixpt_le(arg, a0))
565 		linear = dc_fixpt_div(arg, a1);
566 	else
567 		linear =  dc_fixpt_pow(
568 					dc_fixpt_div(
569 					dc_fixpt_add(a2, arg),
570 					dc_fixpt_add(
571 					dc_fixpt_one, a3)), gamma);
572 
573 	return linear;
574 }
575 
576 static struct fixed31_32 translate_from_linear_space_ex(
577 	struct fixed31_32 arg,
578 	struct gamma_coefficients *coeff,
579 	uint32_t color_index,
580 	struct calculate_buffer *cal_buffer)
581 {
582 	struct translate_from_linear_space_args scratch_gamma_args;
583 
584 	scratch_gamma_args.arg = arg;
585 	scratch_gamma_args.a0 = coeff->a0[color_index];
586 	scratch_gamma_args.a1 = coeff->a1[color_index];
587 	scratch_gamma_args.a2 = coeff->a2[color_index];
588 	scratch_gamma_args.a3 = coeff->a3[color_index];
589 	scratch_gamma_args.gamma = coeff->user_gamma[color_index];
590 	scratch_gamma_args.cal_buffer = cal_buffer;
591 
592 	return translate_from_linear_space(&scratch_gamma_args);
593 }
594 
595 
596 static inline struct fixed31_32 translate_to_linear_space_ex(
597 	struct fixed31_32 arg,
598 	struct gamma_coefficients *coeff,
599 	uint32_t color_index)
600 {
601 	return translate_to_linear_space(
602 		arg,
603 		coeff->a0[color_index],
604 		coeff->a1[color_index],
605 		coeff->a2[color_index],
606 		coeff->a3[color_index],
607 		coeff->user_gamma[color_index]);
608 }
609 
610 
611 static bool find_software_points(
612 	const struct dc_gamma *ramp,
613 	const struct gamma_pixel *axis_x,
614 	struct fixed31_32 hw_point,
615 	enum channel_name channel,
616 	uint32_t *index_to_start,
617 	uint32_t *index_left,
618 	uint32_t *index_right,
619 	enum hw_point_position *pos)
620 {
621 	const uint32_t max_number = ramp->num_entries + 3;
622 
623 	struct fixed31_32 left, right;
624 
625 	uint32_t i = *index_to_start;
626 
627 	while (i < max_number) {
628 		if (channel == CHANNEL_NAME_RED) {
629 			left = axis_x[i].r;
630 
631 			if (i < max_number - 1)
632 				right = axis_x[i + 1].r;
633 			else
634 				right = axis_x[max_number - 1].r;
635 		} else if (channel == CHANNEL_NAME_GREEN) {
636 			left = axis_x[i].g;
637 
638 			if (i < max_number - 1)
639 				right = axis_x[i + 1].g;
640 			else
641 				right = axis_x[max_number - 1].g;
642 		} else {
643 			left = axis_x[i].b;
644 
645 			if (i < max_number - 1)
646 				right = axis_x[i + 1].b;
647 			else
648 				right = axis_x[max_number - 1].b;
649 		}
650 
651 		if (dc_fixpt_le(left, hw_point) &&
652 			dc_fixpt_le(hw_point, right)) {
653 			*index_to_start = i;
654 			*index_left = i;
655 
656 			if (i < max_number - 1)
657 				*index_right = i + 1;
658 			else
659 				*index_right = max_number - 1;
660 
661 			*pos = HW_POINT_POSITION_MIDDLE;
662 
663 			return true;
664 		} else if ((i == *index_to_start) &&
665 			dc_fixpt_le(hw_point, left)) {
666 			*index_to_start = i;
667 			*index_left = i;
668 			*index_right = i;
669 
670 			*pos = HW_POINT_POSITION_LEFT;
671 
672 			return true;
673 		} else if ((i == max_number - 1) &&
674 			dc_fixpt_le(right, hw_point)) {
675 			*index_to_start = i;
676 			*index_left = i;
677 			*index_right = i;
678 
679 			*pos = HW_POINT_POSITION_RIGHT;
680 
681 			return true;
682 		}
683 
684 		++i;
685 	}
686 
687 	return false;
688 }
689 
690 static bool build_custom_gamma_mapping_coefficients_worker(
691 	const struct dc_gamma *ramp,
692 	struct pixel_gamma_point *coeff,
693 	const struct hw_x_point *coordinates_x,
694 	const struct gamma_pixel *axis_x,
695 	enum channel_name channel,
696 	uint32_t number_of_points)
697 {
698 	uint32_t i = 0;
699 
700 	while (i <= number_of_points) {
701 		struct fixed31_32 coord_x;
702 
703 		uint32_t index_to_start = 0;
704 		uint32_t index_left = 0;
705 		uint32_t index_right = 0;
706 
707 		enum hw_point_position hw_pos;
708 
709 		struct gamma_point *point;
710 
711 		struct fixed31_32 left_pos;
712 		struct fixed31_32 right_pos;
713 
714 		if (channel == CHANNEL_NAME_RED)
715 			coord_x = coordinates_x[i].regamma_y_red;
716 		else if (channel == CHANNEL_NAME_GREEN)
717 			coord_x = coordinates_x[i].regamma_y_green;
718 		else
719 			coord_x = coordinates_x[i].regamma_y_blue;
720 
721 		if (!find_software_points(
722 			ramp, axis_x, coord_x, channel,
723 			&index_to_start, &index_left, &index_right, &hw_pos)) {
724 			BREAK_TO_DEBUGGER();
725 			return false;
726 		}
727 
728 		if (index_left >= ramp->num_entries + 3) {
729 			BREAK_TO_DEBUGGER();
730 			return false;
731 		}
732 
733 		if (index_right >= ramp->num_entries + 3) {
734 			BREAK_TO_DEBUGGER();
735 			return false;
736 		}
737 
738 		if (channel == CHANNEL_NAME_RED) {
739 			point = &coeff[i].r;
740 
741 			left_pos = axis_x[index_left].r;
742 			right_pos = axis_x[index_right].r;
743 		} else if (channel == CHANNEL_NAME_GREEN) {
744 			point = &coeff[i].g;
745 
746 			left_pos = axis_x[index_left].g;
747 			right_pos = axis_x[index_right].g;
748 		} else {
749 			point = &coeff[i].b;
750 
751 			left_pos = axis_x[index_left].b;
752 			right_pos = axis_x[index_right].b;
753 		}
754 
755 		if (hw_pos == HW_POINT_POSITION_MIDDLE)
756 			point->coeff = dc_fixpt_div(
757 				dc_fixpt_sub(
758 					coord_x,
759 					left_pos),
760 				dc_fixpt_sub(
761 					right_pos,
762 					left_pos));
763 		else if (hw_pos == HW_POINT_POSITION_LEFT)
764 			point->coeff = dc_fixpt_zero;
765 		else if (hw_pos == HW_POINT_POSITION_RIGHT)
766 			point->coeff = dc_fixpt_from_int(2);
767 		else {
768 			BREAK_TO_DEBUGGER();
769 			return false;
770 		}
771 
772 		point->left_index = index_left;
773 		point->right_index = index_right;
774 		point->pos = hw_pos;
775 
776 		++i;
777 	}
778 
779 	return true;
780 }
781 
782 static struct fixed31_32 calculate_mapped_value(
783 	struct pwl_float_data *rgb,
784 	const struct pixel_gamma_point *coeff,
785 	enum channel_name channel,
786 	uint32_t max_index)
787 {
788 	const struct gamma_point *point;
789 
790 	struct fixed31_32 result;
791 
792 	if (channel == CHANNEL_NAME_RED)
793 		point = &coeff->r;
794 	else if (channel == CHANNEL_NAME_GREEN)
795 		point = &coeff->g;
796 	else
797 		point = &coeff->b;
798 
799 	if ((point->left_index < 0) || (point->left_index > max_index)) {
800 		BREAK_TO_DEBUGGER();
801 		return dc_fixpt_zero;
802 	}
803 
804 	if ((point->right_index < 0) || (point->right_index > max_index)) {
805 		BREAK_TO_DEBUGGER();
806 		return dc_fixpt_zero;
807 	}
808 
809 	if (point->pos == HW_POINT_POSITION_MIDDLE)
810 		if (channel == CHANNEL_NAME_RED)
811 			result = dc_fixpt_add(
812 				dc_fixpt_mul(
813 					point->coeff,
814 					dc_fixpt_sub(
815 						rgb[point->right_index].r,
816 						rgb[point->left_index].r)),
817 				rgb[point->left_index].r);
818 		else if (channel == CHANNEL_NAME_GREEN)
819 			result = dc_fixpt_add(
820 				dc_fixpt_mul(
821 					point->coeff,
822 					dc_fixpt_sub(
823 						rgb[point->right_index].g,
824 						rgb[point->left_index].g)),
825 				rgb[point->left_index].g);
826 		else
827 			result = dc_fixpt_add(
828 				dc_fixpt_mul(
829 					point->coeff,
830 					dc_fixpt_sub(
831 						rgb[point->right_index].b,
832 						rgb[point->left_index].b)),
833 				rgb[point->left_index].b);
834 	else if (point->pos == HW_POINT_POSITION_LEFT) {
835 		BREAK_TO_DEBUGGER();
836 		result = dc_fixpt_zero;
837 	} else {
838 		result = dc_fixpt_one;
839 	}
840 
841 	return result;
842 }
843 
844 static void build_pq(struct pwl_float_data_ex *rgb_regamma,
845 		uint32_t hw_points_num,
846 		const struct hw_x_point *coordinate_x,
847 		uint32_t sdr_white_level)
848 {
849 	uint32_t i, start_index;
850 
851 	struct pwl_float_data_ex *rgb = rgb_regamma;
852 	const struct hw_x_point *coord_x = coordinate_x;
853 	struct fixed31_32 x;
854 	struct fixed31_32 output;
855 	struct fixed31_32 scaling_factor =
856 			dc_fixpt_from_fraction(sdr_white_level, 10000);
857 	struct fixed31_32 *pq_table = mod_color_get_table(type_pq_table);
858 
859 	if (!mod_color_is_table_init(type_pq_table) && sdr_white_level == 80) {
860 		precompute_pq();
861 		mod_color_set_table_init_state(type_pq_table, true);
862 	}
863 
864 	/* TODO: start index is from segment 2^-24, skipping first segment
865 	 * due to x values too small for power calculations
866 	 */
867 	start_index = 32;
868 	rgb += start_index;
869 	coord_x += start_index;
870 
871 	for (i = start_index; i <= hw_points_num; i++) {
872 		/* Multiply 0.008 as regamma is 0-1 and FP16 input is 0-125.
873 		 * FP 1.0 = 80nits
874 		 */
875 		if (sdr_white_level == 80) {
876 			output = pq_table[i];
877 		} else {
878 			x = dc_fixpt_mul(coord_x->x, scaling_factor);
879 			compute_pq(x, &output);
880 		}
881 
882 		/* should really not happen? */
883 		if (dc_fixpt_lt(output, dc_fixpt_zero))
884 			output = dc_fixpt_zero;
885 
886 		rgb->r = output;
887 		rgb->g = output;
888 		rgb->b = output;
889 
890 		++coord_x;
891 		++rgb;
892 	}
893 }
894 
895 static void build_de_pq(struct pwl_float_data_ex *de_pq,
896 		uint32_t hw_points_num,
897 		const struct hw_x_point *coordinate_x)
898 {
899 	(void)coordinate_x;
900 	uint32_t i;
901 	struct fixed31_32 output;
902 	struct fixed31_32 *de_pq_table = mod_color_get_table(type_de_pq_table);
903 	struct fixed31_32 scaling_factor = dc_fixpt_from_int(125);
904 
905 	if (!mod_color_is_table_init(type_de_pq_table)) {
906 		precompute_de_pq();
907 		mod_color_set_table_init_state(type_de_pq_table, true);
908 	}
909 
910 
911 	for (i = 0; i <= hw_points_num; i++) {
912 		output = de_pq_table[i];
913 		/* should really not happen? */
914 		if (dc_fixpt_lt(output, dc_fixpt_zero))
915 			output = dc_fixpt_zero;
916 		else if (dc_fixpt_lt(scaling_factor, output))
917 			output = scaling_factor;
918 		de_pq[i].r = output;
919 		de_pq[i].g = output;
920 		de_pq[i].b = output;
921 	}
922 }
923 
924 static bool build_regamma(struct pwl_float_data_ex *rgb_regamma,
925 		uint32_t hw_points_num,
926 		const struct hw_x_point *coordinate_x,
927 		enum dc_transfer_func_predefined type,
928 		struct calculate_buffer *cal_buffer)
929 {
930 	uint32_t i;
931 	bool ret = false;
932 
933 	struct gamma_coefficients *coeff;
934 	struct pwl_float_data_ex *rgb = rgb_regamma;
935 	const struct hw_x_point *coord_x = coordinate_x;
936 
937 	coeff = kvzalloc_obj(*coeff);
938 	if (!coeff)
939 		goto release;
940 
941 	if (!build_coefficients(coeff, type))
942 		goto release;
943 
944 	memset(cal_buffer->buffer, 0, NUM_PTS_IN_REGION * sizeof(struct fixed31_32));
945 	cal_buffer->buffer_index = 0; // see variable definition for more info
946 
947 	i = 0;
948 	while (i <= hw_points_num) {
949 		/* TODO use y vs r,g,b */
950 		rgb->r = translate_from_linear_space_ex(
951 			coord_x->x, coeff, 0, cal_buffer);
952 		rgb->g = rgb->r;
953 		rgb->b = rgb->r;
954 		++coord_x;
955 		++rgb;
956 		++i;
957 	}
958 	cal_buffer->buffer_index = -1;
959 	ret = true;
960 release:
961 	kvfree(coeff);
962 	return ret;
963 }
964 
965 static void hermite_spline_eetf(struct fixed31_32 input_x,
966 				struct fixed31_32 max_display,
967 				struct fixed31_32 min_display,
968 				struct fixed31_32 max_content,
969 				struct fixed31_32 *out_x)
970 {
971 	struct fixed31_32 min_lum_pq;
972 	struct fixed31_32 max_lum_pq;
973 	struct fixed31_32 max_content_pq;
974 	struct fixed31_32 ks;
975 	struct fixed31_32 E1;
976 	struct fixed31_32 E2;
977 	struct fixed31_32 E3;
978 	struct fixed31_32 t;
979 	struct fixed31_32 t2;
980 	struct fixed31_32 t3;
981 	struct fixed31_32 two;
982 	struct fixed31_32 three;
983 	struct fixed31_32 temp1;
984 	struct fixed31_32 temp2;
985 	struct fixed31_32 a = dc_fixpt_from_fraction(15, 10);
986 	struct fixed31_32 b = dc_fixpt_from_fraction(5, 10);
987 	struct fixed31_32 epsilon = dc_fixpt_from_fraction(1, 1000000); // dc_fixpt_epsilon is a bit too small
988 
989 	if (dc_fixpt_eq(max_content, dc_fixpt_zero)) {
990 		*out_x = dc_fixpt_zero;
991 		return;
992 	}
993 
994 	compute_pq(input_x, &E1);
995 	compute_pq(dc_fixpt_div(min_display, max_content), &min_lum_pq);
996 	compute_pq(dc_fixpt_div(max_display, max_content), &max_lum_pq);
997 	compute_pq(dc_fixpt_one, &max_content_pq); // always 1? DAL2 code is weird
998 	a = dc_fixpt_div(dc_fixpt_add(dc_fixpt_one, b), max_content_pq); // (1+b)/maxContent
999 	ks = dc_fixpt_sub(dc_fixpt_mul(a, max_lum_pq), b); // a * max_lum_pq - b
1000 
1001 	if (dc_fixpt_lt(E1, ks))
1002 		E2 = E1;
1003 	else if (dc_fixpt_le(ks, E1) && dc_fixpt_le(E1, dc_fixpt_one)) {
1004 		if (dc_fixpt_lt(epsilon, dc_fixpt_sub(dc_fixpt_one, ks)))
1005 			// t = (E1 - ks) / (1 - ks)
1006 			t = dc_fixpt_div(dc_fixpt_sub(E1, ks),
1007 					dc_fixpt_sub(dc_fixpt_one, ks));
1008 		else
1009 			t = dc_fixpt_zero;
1010 
1011 		two = dc_fixpt_from_int(2);
1012 		three = dc_fixpt_from_int(3);
1013 
1014 		t2 = dc_fixpt_mul(t, t);
1015 		t3 = dc_fixpt_mul(t2, t);
1016 		temp1 = dc_fixpt_mul(two, t3);
1017 		temp2 = dc_fixpt_mul(three, t2);
1018 
1019 		// (2t^3 - 3t^2 + 1) * ks
1020 		E2 = dc_fixpt_mul(ks, dc_fixpt_add(dc_fixpt_one,
1021 				dc_fixpt_sub(temp1, temp2)));
1022 
1023 		// (-2t^3 + 3t^2) * max_lum_pq
1024 		E2 = dc_fixpt_add(E2, dc_fixpt_mul(max_lum_pq,
1025 				dc_fixpt_sub(temp2, temp1)));
1026 
1027 		temp1 = dc_fixpt_mul(two, t2);
1028 		temp2 = dc_fixpt_sub(dc_fixpt_one, ks);
1029 
1030 		// (t^3 - 2t^2 + t) * (1-ks)
1031 		E2 = dc_fixpt_add(E2, dc_fixpt_mul(temp2,
1032 				dc_fixpt_add(t, dc_fixpt_sub(t3, temp1))));
1033 	} else
1034 		E2 = dc_fixpt_one;
1035 
1036 	temp1 = dc_fixpt_sub(dc_fixpt_one, E2);
1037 	temp2 = dc_fixpt_mul(temp1, temp1);
1038 	temp2 = dc_fixpt_mul(temp2, temp2);
1039 	// temp2 = (1-E2)^4
1040 
1041 	E3 =  dc_fixpt_add(E2, dc_fixpt_mul(min_lum_pq, temp2));
1042 	compute_de_pq(E3, out_x);
1043 
1044 	*out_x = dc_fixpt_div(*out_x, dc_fixpt_div(max_display, max_content));
1045 }
1046 
1047 static bool build_freesync_hdr(struct pwl_float_data_ex *rgb_regamma,
1048 		uint32_t hw_points_num,
1049 		const struct hw_x_point *coordinate_x,
1050 		const struct hdr_tm_params *fs_params,
1051 		struct calculate_buffer *cal_buffer)
1052 {
1053 	uint32_t i;
1054 	struct pwl_float_data_ex *rgb = rgb_regamma;
1055 	const struct hw_x_point *coord_x = coordinate_x;
1056 	const struct hw_x_point *prv_coord_x = coord_x;
1057 	struct fixed31_32 scaledX = dc_fixpt_zero;
1058 	struct fixed31_32 scaledX1 = dc_fixpt_zero;
1059 	struct fixed31_32 max_display;
1060 	struct fixed31_32 min_display;
1061 	struct fixed31_32 max_content;
1062 	struct fixed31_32 clip = dc_fixpt_one;
1063 	struct fixed31_32 output = dc_fixpt_zero;
1064 	bool use_eetf = false;
1065 	bool is_clipped = false;
1066 	struct fixed31_32 sdr_white_level;
1067 	struct fixed31_32 coordX_diff;
1068 	struct fixed31_32 out_dist_max;
1069 	struct fixed31_32 bright_norm;
1070 
1071 	if (fs_params->max_content == 0 ||
1072 			fs_params->max_display == 0)
1073 		return false;
1074 
1075 	max_display = dc_fixpt_from_int(fs_params->max_display);
1076 	min_display = dc_fixpt_from_fraction(fs_params->min_display, 10000);
1077 	max_content = dc_fixpt_from_int(fs_params->max_content);
1078 	sdr_white_level = dc_fixpt_from_int(fs_params->sdr_white_level);
1079 
1080 	if (fs_params->min_display > 1000) // cap at 0.1 at the bottom
1081 		min_display = dc_fixpt_from_fraction(1, 10);
1082 	if (fs_params->max_display < 100) // cap at 100 at the top
1083 		max_display = dc_fixpt_from_int(100);
1084 
1085 	// only max used, we don't adjust min luminance
1086 	if (fs_params->max_content > fs_params->max_display)
1087 		use_eetf = true;
1088 	else
1089 		max_content = max_display;
1090 
1091 	if (!use_eetf)
1092 		cal_buffer->buffer_index = 0; // see var definition for more info
1093 	rgb += 32; // first 32 points have problems with fixed point, too small
1094 	coord_x += 32;
1095 
1096 	for (i = 32; i <= hw_points_num; i++) {
1097 		if (!is_clipped) {
1098 			if (use_eetf) {
1099 				/* max content is equal 1 */
1100 				scaledX1 = dc_fixpt_div(coord_x->x,
1101 						dc_fixpt_div(max_content, sdr_white_level));
1102 				hermite_spline_eetf(scaledX1, max_display, min_display,
1103 						max_content, &scaledX);
1104 			} else
1105 				scaledX = dc_fixpt_div(coord_x->x,
1106 						dc_fixpt_div(max_display, sdr_white_level));
1107 
1108 			if (dc_fixpt_lt(scaledX, clip)) {
1109 				if (dc_fixpt_lt(scaledX, dc_fixpt_zero))
1110 					output = dc_fixpt_zero;
1111 				else
1112 					output = calculate_gamma22(scaledX, use_eetf, cal_buffer);
1113 
1114 				// Ensure output respects reasonable boundaries
1115 				output = dc_fixpt_clamp(output, dc_fixpt_zero, dc_fixpt_one);
1116 
1117 				rgb->r = output;
1118 				rgb->g = output;
1119 				rgb->b = output;
1120 			} else {
1121 				/* Here clipping happens for the first time */
1122 				is_clipped = true;
1123 
1124 				/* The next few lines implement the equation
1125 				 * output = prev_out +
1126 				 * (coord_x->x - prev_coord_x->x) *
1127 				 * (1.0 - prev_out) /
1128 				 * (maxDisp/sdr_white_level - prevCoordX)
1129 				 *
1130 				 * This equation interpolates the first point
1131 				 * after max_display/80 so that the slope from
1132 				 * hw_x_before_max and hw_x_after_max is such
1133 				 * that we hit Y=1.0 at max_display/80.
1134 				 */
1135 
1136 				coordX_diff = dc_fixpt_sub(coord_x->x, prv_coord_x->x);
1137 				out_dist_max = dc_fixpt_sub(dc_fixpt_one, output);
1138 				bright_norm = dc_fixpt_div(max_display, sdr_white_level);
1139 
1140 				output = dc_fixpt_add(
1141 					output, dc_fixpt_mul(
1142 						coordX_diff, dc_fixpt_div(
1143 							out_dist_max,
1144 							dc_fixpt_sub(bright_norm, prv_coord_x->x)
1145 						)
1146 					)
1147 				);
1148 
1149 				/* Relaxing the maximum boundary to 1.07 (instead of 1.0)
1150 				 * because the last point in the curve must be such that
1151 				 * the maximum display pixel brightness interpolates to
1152 				 * exactly 1.0. The worst case scenario was calculated
1153 				 * around 1.057, so the limit of 1.07 leaves some safety
1154 				 * margin.
1155 				 */
1156 				output = dc_fixpt_clamp(output, dc_fixpt_zero,
1157 					dc_fixpt_from_fraction(107, 100));
1158 
1159 				rgb->r = output;
1160 				rgb->g = output;
1161 				rgb->b = output;
1162 			}
1163 		} else {
1164 			/* Every other clipping after the first
1165 			 * one is dealt with here
1166 			 */
1167 			rgb->r = clip;
1168 			rgb->g = clip;
1169 			rgb->b = clip;
1170 		}
1171 
1172 		prv_coord_x = coord_x;
1173 		++coord_x;
1174 		++rgb;
1175 	}
1176 	cal_buffer->buffer_index = -1;
1177 
1178 	return true;
1179 }
1180 
1181 static bool build_degamma(struct pwl_float_data_ex *curve,
1182 		uint32_t hw_points_num,
1183 		const struct hw_x_point *coordinate_x, enum dc_transfer_func_predefined type)
1184 {
1185 	uint32_t i;
1186 	struct gamma_coefficients coeff;
1187 	uint32_t begin_index, end_index;
1188 	bool ret = false;
1189 
1190 	if (!build_coefficients(&coeff, type))
1191 		goto release;
1192 
1193 	i = 0;
1194 
1195 	/* X points is 2^-25 to 2^7
1196 	 * De-gamma X is 2^-12 to 2^0 – we are skipping first -12-(-25) = 13 regions
1197 	 */
1198 	begin_index = 13 * NUM_PTS_IN_REGION;
1199 	end_index = begin_index + 12 * NUM_PTS_IN_REGION;
1200 
1201 	while (i != begin_index) {
1202 		curve[i].r = dc_fixpt_zero;
1203 		curve[i].g = dc_fixpt_zero;
1204 		curve[i].b = dc_fixpt_zero;
1205 		i++;
1206 	}
1207 
1208 	while (i != end_index) {
1209 		curve[i].r = translate_to_linear_space_ex(
1210 				coordinate_x[i].x, &coeff, 0);
1211 		curve[i].g = curve[i].r;
1212 		curve[i].b = curve[i].r;
1213 		i++;
1214 	}
1215 	while (i != hw_points_num + 1) {
1216 		curve[i].r = dc_fixpt_one;
1217 		curve[i].g = dc_fixpt_one;
1218 		curve[i].b = dc_fixpt_one;
1219 		i++;
1220 	}
1221 	ret = true;
1222 release:
1223 	return ret;
1224 }
1225 
1226 
1227 
1228 
1229 
1230 static void build_hlg_degamma(struct pwl_float_data_ex *degamma,
1231 		uint32_t hw_points_num,
1232 		const struct hw_x_point *coordinate_x,
1233 		uint32_t sdr_white_level, uint32_t max_luminance_nits)
1234 {
1235 	uint32_t i;
1236 
1237 	struct pwl_float_data_ex *rgb = degamma;
1238 	const struct hw_x_point *coord_x = coordinate_x;
1239 
1240 	i = 0;
1241 	// check when i == 434
1242 	while (i != hw_points_num + 1) {
1243 		compute_hlg_eotf(coord_x->x, &rgb->r, sdr_white_level, max_luminance_nits);
1244 		rgb->g = rgb->r;
1245 		rgb->b = rgb->r;
1246 		++coord_x;
1247 		++rgb;
1248 		++i;
1249 	}
1250 }
1251 
1252 
1253 static void build_hlg_regamma(struct pwl_float_data_ex *regamma,
1254 		uint32_t hw_points_num,
1255 		const struct hw_x_point *coordinate_x,
1256 		uint32_t sdr_white_level, uint32_t max_luminance_nits)
1257 {
1258 	uint32_t i;
1259 
1260 	struct pwl_float_data_ex *rgb = regamma;
1261 	const struct hw_x_point *coord_x = coordinate_x;
1262 
1263 	i = 0;
1264 
1265 	// when i == 471
1266 	while (i != hw_points_num + 1) {
1267 		compute_hlg_oetf(coord_x->x, &rgb->r, sdr_white_level, max_luminance_nits);
1268 		rgb->g = rgb->r;
1269 		rgb->b = rgb->r;
1270 		++coord_x;
1271 		++rgb;
1272 		++i;
1273 	}
1274 }
1275 
1276 static void scale_gamma(struct pwl_float_data *pwl_rgb,
1277 		const struct dc_gamma *ramp,
1278 		struct dividers dividers)
1279 {
1280 	const struct fixed31_32 max_driver = dc_fixpt_from_int(0xFFFF);
1281 	const struct fixed31_32 max_os = dc_fixpt_from_int(0xFF00);
1282 	struct fixed31_32 scaler = max_os;
1283 	uint32_t i;
1284 	struct pwl_float_data *rgb = pwl_rgb;
1285 	struct pwl_float_data *rgb_last = rgb + ramp->num_entries - 1;
1286 
1287 	i = 0;
1288 
1289 	do {
1290 		if (dc_fixpt_lt(max_os, ramp->entries.red[i]) ||
1291 			dc_fixpt_lt(max_os, ramp->entries.green[i]) ||
1292 			dc_fixpt_lt(max_os, ramp->entries.blue[i])) {
1293 			scaler = max_driver;
1294 			break;
1295 		}
1296 		++i;
1297 	} while (i != ramp->num_entries);
1298 
1299 	i = 0;
1300 
1301 	do {
1302 		rgb->r = dc_fixpt_div(
1303 			ramp->entries.red[i], scaler);
1304 		rgb->g = dc_fixpt_div(
1305 			ramp->entries.green[i], scaler);
1306 		rgb->b = dc_fixpt_div(
1307 			ramp->entries.blue[i], scaler);
1308 
1309 		++rgb;
1310 		++i;
1311 	} while (i != ramp->num_entries);
1312 
1313 	rgb->r = dc_fixpt_mul(rgb_last->r,
1314 			dividers.divider1);
1315 	rgb->g = dc_fixpt_mul(rgb_last->g,
1316 			dividers.divider1);
1317 	rgb->b = dc_fixpt_mul(rgb_last->b,
1318 			dividers.divider1);
1319 
1320 	++rgb;
1321 
1322 	rgb->r = dc_fixpt_mul(rgb_last->r,
1323 			dividers.divider2);
1324 	rgb->g = dc_fixpt_mul(rgb_last->g,
1325 			dividers.divider2);
1326 	rgb->b = dc_fixpt_mul(rgb_last->b,
1327 			dividers.divider2);
1328 
1329 	++rgb;
1330 
1331 	rgb->r = dc_fixpt_mul(rgb_last->r,
1332 			dividers.divider3);
1333 	rgb->g = dc_fixpt_mul(rgb_last->g,
1334 			dividers.divider3);
1335 	rgb->b = dc_fixpt_mul(rgb_last->b,
1336 			dividers.divider3);
1337 }
1338 
1339 static void scale_gamma_dx(struct pwl_float_data *pwl_rgb,
1340 		const struct dc_gamma *ramp,
1341 		struct dividers dividers)
1342 {
1343 	(void)dividers;
1344 	uint32_t i;
1345 	struct fixed31_32 min = dc_fixpt_zero;
1346 	struct fixed31_32 max = dc_fixpt_one;
1347 
1348 	struct fixed31_32 delta = dc_fixpt_zero;
1349 	struct fixed31_32 offset = dc_fixpt_zero;
1350 
1351 	for (i = 0 ; i < ramp->num_entries; i++) {
1352 		if (dc_fixpt_lt(ramp->entries.red[i], min))
1353 			min = ramp->entries.red[i];
1354 
1355 		if (dc_fixpt_lt(ramp->entries.green[i], min))
1356 			min = ramp->entries.green[i];
1357 
1358 		if (dc_fixpt_lt(ramp->entries.blue[i], min))
1359 			min = ramp->entries.blue[i];
1360 
1361 		if (dc_fixpt_lt(max, ramp->entries.red[i]))
1362 			max = ramp->entries.red[i];
1363 
1364 		if (dc_fixpt_lt(max, ramp->entries.green[i]))
1365 			max = ramp->entries.green[i];
1366 
1367 		if (dc_fixpt_lt(max, ramp->entries.blue[i]))
1368 			max = ramp->entries.blue[i];
1369 	}
1370 
1371 	if (dc_fixpt_lt(min, dc_fixpt_zero))
1372 		delta = dc_fixpt_neg(min);
1373 
1374 	offset = dc_fixpt_add(min, max);
1375 
1376 	for (i = 0 ; i < ramp->num_entries; i++) {
1377 		pwl_rgb[i].r = dc_fixpt_div(
1378 			dc_fixpt_add(
1379 				ramp->entries.red[i], delta), offset);
1380 		pwl_rgb[i].g = dc_fixpt_div(
1381 			dc_fixpt_add(
1382 				ramp->entries.green[i], delta), offset);
1383 		pwl_rgb[i].b = dc_fixpt_div(
1384 			dc_fixpt_add(
1385 				ramp->entries.blue[i], delta), offset);
1386 
1387 	}
1388 
1389 	pwl_rgb[i].r =  dc_fixpt_sub(dc_fixpt_mul_int(
1390 				pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r);
1391 	pwl_rgb[i].g =  dc_fixpt_sub(dc_fixpt_mul_int(
1392 				pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g);
1393 	pwl_rgb[i].b =  dc_fixpt_sub(dc_fixpt_mul_int(
1394 				pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b);
1395 	++i;
1396 	pwl_rgb[i].r =  dc_fixpt_sub(dc_fixpt_mul_int(
1397 				pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r);
1398 	pwl_rgb[i].g =  dc_fixpt_sub(dc_fixpt_mul_int(
1399 				pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g);
1400 	pwl_rgb[i].b =  dc_fixpt_sub(dc_fixpt_mul_int(
1401 				pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b);
1402 }
1403 
1404 /*
1405  * RS3+ color transform DDI - 1D LUT adjustment is composed with regamma here
1406  * Input is evenly distributed in the output color space as specified in
1407  * SetTimings
1408  *
1409  * Interpolation details:
1410  * 1D LUT has 4096 values which give curve correction in 0-1 float range
1411  * for evenly spaced points in 0-1 range. lut1D[index] gives correction
1412  * for index/4095.
1413  * First we find index for which:
1414  *	index/4095 < regamma_y < (index+1)/4095 =>
1415  *	index < 4095*regamma_y < index + 1
1416  * norm_y = 4095*regamma_y, and index is just truncating to nearest integer
1417  * lut1 = lut1D[index], lut2 = lut1D[index+1]
1418  *
1419  * adjustedY is then linearly interpolating regamma Y between lut1 and lut2
1420  *
1421  * Custom degamma on Linux uses the same interpolation math, so is handled here
1422  */
1423 static void apply_lut_1d(
1424 		const struct dc_gamma *ramp,
1425 		uint32_t num_hw_points,
1426 		struct dc_transfer_func_distributed_points *tf_pts)
1427 {
1428 	int i = 0;
1429 	int color = 0;
1430 	struct fixed31_32 *regamma_y;
1431 	struct fixed31_32 norm_y;
1432 	struct fixed31_32 lut1;
1433 	struct fixed31_32 lut2;
1434 	const int max_lut_index = 4095;
1435 	const struct fixed31_32 penult_lut_index_f =
1436 			dc_fixpt_from_int(max_lut_index-1);
1437 	const struct fixed31_32 max_lut_index_f =
1438 			dc_fixpt_from_int(max_lut_index);
1439 	int32_t index = 0, index_next = 0;
1440 	struct fixed31_32 index_f;
1441 	struct fixed31_32 delta_lut;
1442 	struct fixed31_32 delta_index;
1443 
1444 	if (ramp->type != GAMMA_CS_TFM_1D && ramp->type != GAMMA_CUSTOM)
1445 		return; // this is not expected
1446 
1447 	for (i = 0; i < num_hw_points; i++) {
1448 		for (color = 0; color < 3; color++) {
1449 			if (color == 0)
1450 				regamma_y = &tf_pts->red[i];
1451 			else if (color == 1)
1452 				regamma_y = &tf_pts->green[i];
1453 			else
1454 				regamma_y = &tf_pts->blue[i];
1455 
1456 			norm_y = dc_fixpt_mul(max_lut_index_f,
1457 						   *regamma_y);
1458 			index = dc_fixpt_floor(norm_y);
1459 			index_f = dc_fixpt_from_int(index);
1460 
1461 			if (index < 0)
1462 				continue;
1463 
1464 			if (index <= max_lut_index)
1465 				index_next = (index == max_lut_index) ? index : index+1;
1466 			else {
1467 				/* Here we are dealing with the last point in the curve,
1468 				 * which in some cases might exceed the range given by
1469 				 * max_lut_index. So we interpolate the value using
1470 				 * max_lut_index and max_lut_index - 1.
1471 				 */
1472 				index = max_lut_index - 1;
1473 				index_next = max_lut_index;
1474 				index_f = penult_lut_index_f;
1475 			}
1476 
1477 			if (color == 0) {
1478 				lut1 = ramp->entries.red[index];
1479 				lut2 = ramp->entries.red[index_next];
1480 			} else if (color == 1) {
1481 				lut1 = ramp->entries.green[index];
1482 				lut2 = ramp->entries.green[index_next];
1483 			} else {
1484 				lut1 = ramp->entries.blue[index];
1485 				lut2 = ramp->entries.blue[index_next];
1486 			}
1487 
1488 			// we have everything now, so interpolate
1489 			delta_lut = dc_fixpt_sub(lut2, lut1);
1490 			delta_index = dc_fixpt_sub(norm_y, index_f);
1491 
1492 			*regamma_y = dc_fixpt_add(lut1,
1493 				dc_fixpt_mul(delta_index, delta_lut));
1494 		}
1495 	}
1496 }
1497 
1498 static void build_evenly_distributed_points(
1499 	struct gamma_pixel *points,
1500 	uint32_t numberof_points,
1501 	struct dividers dividers)
1502 {
1503 	struct gamma_pixel *p = points;
1504 	struct gamma_pixel *p_last;
1505 
1506 	uint32_t i = 0;
1507 
1508 	// This function should not gets called with 0 as a parameter
1509 	ASSERT(numberof_points > 0);
1510 	p_last = p + numberof_points - 1;
1511 
1512 	do {
1513 		struct fixed31_32 value = dc_fixpt_from_fraction(i,
1514 			numberof_points - 1);
1515 
1516 		p->r = value;
1517 		p->g = value;
1518 		p->b = value;
1519 
1520 		++p;
1521 		++i;
1522 	} while (i < numberof_points);
1523 
1524 	p->r = dc_fixpt_div(p_last->r, dividers.divider1);
1525 	p->g = dc_fixpt_div(p_last->g, dividers.divider1);
1526 	p->b = dc_fixpt_div(p_last->b, dividers.divider1);
1527 
1528 	++p;
1529 
1530 	p->r = dc_fixpt_div(p_last->r, dividers.divider2);
1531 	p->g = dc_fixpt_div(p_last->g, dividers.divider2);
1532 	p->b = dc_fixpt_div(p_last->b, dividers.divider2);
1533 
1534 	++p;
1535 
1536 	p->r = dc_fixpt_div(p_last->r, dividers.divider3);
1537 	p->g = dc_fixpt_div(p_last->g, dividers.divider3);
1538 	p->b = dc_fixpt_div(p_last->b, dividers.divider3);
1539 }
1540 
1541 static inline void copy_rgb_regamma_to_coordinates_x(
1542 		struct hw_x_point *coordinates_x,
1543 		uint32_t hw_points_num,
1544 		const struct pwl_float_data_ex *rgb_ex)
1545 {
1546 	struct hw_x_point *coords = coordinates_x;
1547 	uint32_t i = 0;
1548 	const struct pwl_float_data_ex *rgb_regamma = rgb_ex;
1549 
1550 	while (i <= hw_points_num + 1) {
1551 		coords->regamma_y_red = rgb_regamma->r;
1552 		coords->regamma_y_green = rgb_regamma->g;
1553 		coords->regamma_y_blue = rgb_regamma->b;
1554 
1555 		++coords;
1556 		++rgb_regamma;
1557 		++i;
1558 	}
1559 }
1560 
1561 static bool calculate_interpolated_hardware_curve(
1562 	const struct dc_gamma *ramp,
1563 	struct pixel_gamma_point *coeff128,
1564 	struct pwl_float_data *rgb_user,
1565 	const struct hw_x_point *coordinates_x,
1566 	const struct gamma_pixel *axis_x,
1567 	uint32_t number_of_points,
1568 	struct dc_transfer_func_distributed_points *tf_pts)
1569 {
1570 
1571 	const struct pixel_gamma_point *coeff = coeff128;
1572 	uint32_t max_entries = 3 - 1;
1573 
1574 	uint32_t i = 0;
1575 
1576 	for (i = 0; i < 3; i++) {
1577 		if (!build_custom_gamma_mapping_coefficients_worker(
1578 				ramp, coeff128, coordinates_x, axis_x, i,
1579 				number_of_points))
1580 			return false;
1581 	}
1582 
1583 	i = 0;
1584 	max_entries += ramp->num_entries;
1585 
1586 	/* TODO: float point case */
1587 
1588 	while (i <= number_of_points) {
1589 		tf_pts->red[i] = calculate_mapped_value(
1590 			rgb_user, coeff, CHANNEL_NAME_RED, max_entries);
1591 		tf_pts->green[i] = calculate_mapped_value(
1592 			rgb_user, coeff, CHANNEL_NAME_GREEN, max_entries);
1593 		tf_pts->blue[i] = calculate_mapped_value(
1594 			rgb_user, coeff, CHANNEL_NAME_BLUE, max_entries);
1595 
1596 		++coeff;
1597 		++i;
1598 	}
1599 
1600 	return true;
1601 }
1602 
1603 static void build_new_custom_resulted_curve(
1604 	uint32_t hw_points_num,
1605 	struct dc_transfer_func_distributed_points *tf_pts)
1606 {
1607 	uint32_t i = 0;
1608 
1609 	while (i != hw_points_num + 1) {
1610 		tf_pts->red[i] = dc_fixpt_clamp(
1611 			tf_pts->red[i], dc_fixpt_zero,
1612 			dc_fixpt_one);
1613 		tf_pts->green[i] = dc_fixpt_clamp(
1614 			tf_pts->green[i], dc_fixpt_zero,
1615 			dc_fixpt_one);
1616 		tf_pts->blue[i] = dc_fixpt_clamp(
1617 			tf_pts->blue[i], dc_fixpt_zero,
1618 			dc_fixpt_one);
1619 
1620 		++i;
1621 	}
1622 }
1623 
1624 static bool map_regamma_hw_to_x_user(
1625 	const struct dc_gamma *ramp,
1626 	struct pixel_gamma_point *coeff128,
1627 	struct pwl_float_data *rgb_user,
1628 	struct hw_x_point *coords_x,
1629 	const struct gamma_pixel *axis_x,
1630 	const struct pwl_float_data_ex *rgb_regamma,
1631 	uint32_t hw_points_num,
1632 	struct dc_transfer_func_distributed_points *tf_pts,
1633 	bool map_user_ramp,
1634 	bool do_clamping)
1635 {
1636 	/* setup to spare calculated ideal regamma values */
1637 
1638 	int i = 0;
1639 	struct hw_x_point *coords = coords_x;
1640 	const struct pwl_float_data_ex *regamma = rgb_regamma;
1641 
1642 	if (ramp && map_user_ramp) {
1643 		copy_rgb_regamma_to_coordinates_x(coords,
1644 				hw_points_num,
1645 				rgb_regamma);
1646 
1647 		calculate_interpolated_hardware_curve(
1648 			ramp, coeff128, rgb_user, coords, axis_x,
1649 			hw_points_num, tf_pts);
1650 	} else {
1651 		/* just copy current rgb_regamma into  tf_pts */
1652 		while (i <= hw_points_num) {
1653 			tf_pts->red[i] = regamma->r;
1654 			tf_pts->green[i] = regamma->g;
1655 			tf_pts->blue[i] = regamma->b;
1656 
1657 			++regamma;
1658 			++i;
1659 		}
1660 	}
1661 
1662 	if (do_clamping) {
1663 		/* this should be named differently, all it does is clamp to 0-1 */
1664 		build_new_custom_resulted_curve(hw_points_num, tf_pts);
1665 	}
1666 
1667 	return true;
1668 }
1669 
1670 #define _EXTRA_POINTS 3
1671 
1672 bool mod_color_calculate_degamma_params(struct dc_color_caps *dc_caps,
1673 		struct dc_transfer_func *input_tf,
1674 		const struct dc_gamma *ramp, bool map_user_ramp)
1675 {
1676 	struct dc_transfer_func_distributed_points *tf_pts = &input_tf->tf_pts;
1677 	struct dividers dividers;
1678 	struct pwl_float_data *rgb_user = NULL;
1679 	struct pwl_float_data_ex *curve = NULL;
1680 	struct gamma_pixel *axis_x = NULL;
1681 	struct pixel_gamma_point *coeff = NULL;
1682 	enum dc_transfer_func_predefined tf;
1683 	uint32_t i;
1684 	bool ret = false;
1685 
1686 	if (input_tf->type == TF_TYPE_BYPASS)
1687 		return false;
1688 
1689 	/* we can use hardcoded curve for plain SRGB TF
1690 	 * If linear, it's bypass if no user ramp
1691 	 */
1692 	if (input_tf->type == TF_TYPE_PREDEFINED) {
1693 		if ((input_tf->tf == TRANSFER_FUNCTION_SRGB ||
1694 				input_tf->tf == TRANSFER_FUNCTION_LINEAR) &&
1695 				!map_user_ramp)
1696 			return true;
1697 
1698 		if (dc_caps != NULL &&
1699 			dc_caps->dpp.dcn_arch == 1) {
1700 
1701 			if (input_tf->tf == TRANSFER_FUNCTION_PQ &&
1702 					dc_caps->dpp.dgam_rom_caps.pq == 1)
1703 				return true;
1704 
1705 			if (input_tf->tf == TRANSFER_FUNCTION_GAMMA22 &&
1706 					dc_caps->dpp.dgam_rom_caps.gamma2_2 == 1)
1707 				return true;
1708 
1709 			// HLG OOTF not accounted for
1710 			if (input_tf->tf == TRANSFER_FUNCTION_HLG &&
1711 					dc_caps->dpp.dgam_rom_caps.hlg == 1)
1712 				return true;
1713 		}
1714 	}
1715 
1716 	input_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1717 
1718 	if (map_user_ramp && ramp && ramp->type == GAMMA_RGB_256) {
1719 		rgb_user = kvzalloc_objs(*rgb_user,
1720 					 ramp->num_entries + _EXTRA_POINTS);
1721 		if (!rgb_user)
1722 			goto rgb_user_alloc_fail;
1723 
1724 		axis_x = kvzalloc_objs(*axis_x,
1725 				       ramp->num_entries + _EXTRA_POINTS);
1726 		if (!axis_x)
1727 			goto axis_x_alloc_fail;
1728 
1729 		dividers.divider1 = dc_fixpt_from_fraction(3, 2);
1730 		dividers.divider2 = dc_fixpt_from_int(2);
1731 		dividers.divider3 = dc_fixpt_from_fraction(5, 2);
1732 
1733 		build_evenly_distributed_points(
1734 				axis_x,
1735 				ramp->num_entries,
1736 				dividers);
1737 
1738 		scale_gamma(rgb_user, ramp, dividers);
1739 	}
1740 
1741 	curve = kvzalloc_objs(*curve, MAX_HW_POINTS + _EXTRA_POINTS);
1742 	if (!curve)
1743 		goto curve_alloc_fail;
1744 
1745 	coeff = kvzalloc_objs(*coeff, MAX_HW_POINTS + _EXTRA_POINTS);
1746 	if (!coeff)
1747 		goto coeff_alloc_fail;
1748 
1749 	tf = input_tf->tf;
1750 
1751 	if (tf == TRANSFER_FUNCTION_PQ)
1752 		build_de_pq(curve,
1753 				MAX_HW_POINTS,
1754 				coordinates_x);
1755 	else if (tf == TRANSFER_FUNCTION_SRGB ||
1756 		tf == TRANSFER_FUNCTION_BT709 ||
1757 		tf == TRANSFER_FUNCTION_GAMMA22 ||
1758 		tf == TRANSFER_FUNCTION_GAMMA24 ||
1759 		tf == TRANSFER_FUNCTION_GAMMA26)
1760 		build_degamma(curve,
1761 				MAX_HW_POINTS,
1762 				coordinates_x,
1763 				tf);
1764 	else if (tf == TRANSFER_FUNCTION_HLG)
1765 		build_hlg_degamma(curve,
1766 				MAX_HW_POINTS,
1767 				coordinates_x,
1768 				80, 1000);
1769 	else if (tf == TRANSFER_FUNCTION_LINEAR) {
1770 		// just copy coordinates_x into curve
1771 		i = 0;
1772 		while (i != MAX_HW_POINTS + 1) {
1773 			curve[i].r = coordinates_x[i].x;
1774 			curve[i].g = curve[i].r;
1775 			curve[i].b = curve[i].r;
1776 			i++;
1777 		}
1778 	} else
1779 		goto invalid_tf_fail;
1780 
1781 	tf_pts->end_exponent = 0;
1782 	tf_pts->x_point_at_y1_red = 1;
1783 	tf_pts->x_point_at_y1_green = 1;
1784 	tf_pts->x_point_at_y1_blue = 1;
1785 
1786 	if (input_tf->tf == TRANSFER_FUNCTION_PQ) {
1787 		/* just copy current rgb_regamma into  tf_pts */
1788 		struct pwl_float_data_ex *curvePt = curve;
1789 		int i = 0;
1790 
1791 		while (i <= MAX_HW_POINTS) {
1792 			tf_pts->red[i]   = curvePt->r;
1793 			tf_pts->green[i] = curvePt->g;
1794 			tf_pts->blue[i]  = curvePt->b;
1795 			++curvePt;
1796 			++i;
1797 		}
1798 	} else {
1799 		// clamps to 0-1
1800 		map_regamma_hw_to_x_user(ramp, coeff, rgb_user,
1801 				coordinates_x, axis_x, curve,
1802 				MAX_HW_POINTS, tf_pts,
1803 				map_user_ramp && ramp && ramp->type == GAMMA_RGB_256,
1804 				true);
1805 	}
1806 
1807 
1808 
1809 	if (ramp && ramp->type == GAMMA_CUSTOM)
1810 		apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts);
1811 
1812 	ret = true;
1813 
1814 invalid_tf_fail:
1815 	kvfree(coeff);
1816 coeff_alloc_fail:
1817 	kvfree(curve);
1818 curve_alloc_fail:
1819 	kvfree(axis_x);
1820 axis_x_alloc_fail:
1821 	kvfree(rgb_user);
1822 rgb_user_alloc_fail:
1823 
1824 	return ret;
1825 }
1826 
1827 static bool calculate_curve(enum dc_transfer_func_predefined trans,
1828 				struct dc_transfer_func_distributed_points *points,
1829 				struct pwl_float_data_ex *rgb_regamma,
1830 				const struct hdr_tm_params *fs_params,
1831 				uint32_t sdr_ref_white_level,
1832 				struct calculate_buffer *cal_buffer)
1833 {
1834 	uint32_t i;
1835 	bool ret = false;
1836 
1837 	if (trans == TRANSFER_FUNCTION_UNITY ||
1838 		trans == TRANSFER_FUNCTION_LINEAR) {
1839 		points->end_exponent = 0;
1840 		points->x_point_at_y1_red = 1;
1841 		points->x_point_at_y1_green = 1;
1842 		points->x_point_at_y1_blue = 1;
1843 
1844 		for (i = 0; i <= MAX_HW_POINTS ; i++) {
1845 			rgb_regamma[i].r = coordinates_x[i].x;
1846 			rgb_regamma[i].g = coordinates_x[i].x;
1847 			rgb_regamma[i].b = coordinates_x[i].x;
1848 		}
1849 
1850 		ret = true;
1851 	} else if (trans == TRANSFER_FUNCTION_PQ) {
1852 		points->end_exponent = 7;
1853 		points->x_point_at_y1_red = 125;
1854 		points->x_point_at_y1_green = 125;
1855 		points->x_point_at_y1_blue = 125;
1856 
1857 		build_pq(rgb_regamma,
1858 				MAX_HW_POINTS,
1859 				coordinates_x,
1860 				sdr_ref_white_level);
1861 
1862 		ret = true;
1863 	} else if (trans == TRANSFER_FUNCTION_GAMMA22 &&
1864 			fs_params != NULL && fs_params->skip_tm == 0) {
1865 		build_freesync_hdr(rgb_regamma,
1866 				MAX_HW_POINTS,
1867 				coordinates_x,
1868 				fs_params,
1869 				cal_buffer);
1870 
1871 		ret = true;
1872 	} else if (trans == TRANSFER_FUNCTION_HLG) {
1873 		points->end_exponent = 4;
1874 		points->x_point_at_y1_red = 12;
1875 		points->x_point_at_y1_green = 12;
1876 		points->x_point_at_y1_blue = 12;
1877 
1878 		build_hlg_regamma(rgb_regamma,
1879 				MAX_HW_POINTS,
1880 				coordinates_x,
1881 				80, 1000);
1882 
1883 		ret = true;
1884 	} else {
1885 		// trans == TRANSFER_FUNCTION_SRGB
1886 		// trans == TRANSFER_FUNCTION_BT709
1887 		// trans == TRANSFER_FUNCTION_GAMMA22
1888 		// trans == TRANSFER_FUNCTION_GAMMA24
1889 		// trans == TRANSFER_FUNCTION_GAMMA26
1890 		points->end_exponent = 0;
1891 		points->x_point_at_y1_red = 1;
1892 		points->x_point_at_y1_green = 1;
1893 		points->x_point_at_y1_blue = 1;
1894 
1895 		build_regamma(rgb_regamma,
1896 				MAX_HW_POINTS,
1897 				coordinates_x,
1898 				trans,
1899 				cal_buffer);
1900 
1901 		ret = true;
1902 	}
1903 
1904 	return ret;
1905 }
1906 
1907 bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf,
1908 					const struct dc_gamma *ramp,
1909 					bool map_user_ramp,
1910 					bool can_rom_be_used,
1911 					const struct hdr_tm_params *fs_params,
1912 					struct calculate_buffer *cal_buffer)
1913 {
1914 	struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts;
1915 	struct dividers dividers;
1916 
1917 	struct pwl_float_data *rgb_user = NULL;
1918 	struct pwl_float_data_ex *rgb_regamma = NULL;
1919 	struct gamma_pixel *axis_x = NULL;
1920 	struct pixel_gamma_point *coeff = NULL;
1921 	enum dc_transfer_func_predefined tf;
1922 	bool do_clamping = true;
1923 	bool ret = false;
1924 
1925 	if (output_tf->type == TF_TYPE_BYPASS)
1926 		return false;
1927 
1928 	/* we can use hardcoded curve for plain SRGB TF */
1929 	if (output_tf->type == TF_TYPE_PREDEFINED && can_rom_be_used == true &&
1930 			output_tf->tf == TRANSFER_FUNCTION_SRGB) {
1931 		if (ramp == NULL)
1932 			return true;
1933 		if ((ramp->is_identity && ramp->type != GAMMA_CS_TFM_1D) ||
1934 		    (!map_user_ramp && ramp->type == GAMMA_RGB_256))
1935 			return true;
1936 	}
1937 
1938 	output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1939 
1940 	if (ramp && ramp->type != GAMMA_CS_TFM_1D &&
1941 	    (map_user_ramp || ramp->type != GAMMA_RGB_256)) {
1942 		rgb_user = kvzalloc_objs(*rgb_user,
1943 					 ramp->num_entries + _EXTRA_POINTS);
1944 		if (!rgb_user)
1945 			goto rgb_user_alloc_fail;
1946 
1947 		axis_x = kvzalloc_objs(*axis_x, ramp->num_entries + 3);
1948 		if (!axis_x)
1949 			goto axis_x_alloc_fail;
1950 
1951 		dividers.divider1 = dc_fixpt_from_fraction(3, 2);
1952 		dividers.divider2 = dc_fixpt_from_int(2);
1953 		dividers.divider3 = dc_fixpt_from_fraction(5, 2);
1954 
1955 		build_evenly_distributed_points(
1956 				axis_x,
1957 				ramp->num_entries,
1958 				dividers);
1959 
1960 		if (ramp->type == GAMMA_RGB_256 && map_user_ramp)
1961 			scale_gamma(rgb_user, ramp, dividers);
1962 		else if (ramp->type == GAMMA_RGB_FLOAT_1024)
1963 			scale_gamma_dx(rgb_user, ramp, dividers);
1964 	}
1965 
1966 	rgb_regamma = kvzalloc_objs(*rgb_regamma, MAX_HW_POINTS + _EXTRA_POINTS);
1967 	if (!rgb_regamma)
1968 		goto rgb_regamma_alloc_fail;
1969 
1970 	coeff = kvzalloc_objs(*coeff, MAX_HW_POINTS + _EXTRA_POINTS);
1971 	if (!coeff)
1972 		goto coeff_alloc_fail;
1973 
1974 	tf = output_tf->tf;
1975 
1976 	ret = calculate_curve(tf,
1977 			tf_pts,
1978 			rgb_regamma,
1979 			fs_params,
1980 			output_tf->sdr_ref_white_level,
1981 			cal_buffer);
1982 
1983 	if (ret) {
1984 		do_clamping = !(output_tf->tf == TRANSFER_FUNCTION_PQ) &&
1985 				!(output_tf->tf == TRANSFER_FUNCTION_GAMMA22 &&
1986 				fs_params != NULL && fs_params->skip_tm == 0);
1987 
1988 		map_regamma_hw_to_x_user(ramp, coeff, rgb_user,
1989 					 coordinates_x, axis_x, rgb_regamma,
1990 					 MAX_HW_POINTS, tf_pts,
1991 					 (map_user_ramp || (ramp && ramp->type != GAMMA_RGB_256)) &&
1992 					 (ramp && ramp->type != GAMMA_CS_TFM_1D),
1993 					 do_clamping);
1994 
1995 		if (ramp && ramp->type == GAMMA_CS_TFM_1D)
1996 			apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts);
1997 	}
1998 
1999 	kvfree(coeff);
2000 coeff_alloc_fail:
2001 	kvfree(rgb_regamma);
2002 rgb_regamma_alloc_fail:
2003 	kvfree(axis_x);
2004 axis_x_alloc_fail:
2005 	kvfree(rgb_user);
2006 rgb_user_alloc_fail:
2007 	return ret;
2008 }
2009