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 */
setup_x_points_distribution(void)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 
log_x_points_distribution(struct dal_logger * logger)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 
compute_pq(struct fixed31_32 in_x,struct fixed31_32 * out_y)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 
compute_de_pq(struct fixed31_32 in_x,struct fixed31_32 * out_y)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 */
compute_hlg_eotf(struct fixed31_32 in_x,struct fixed31_32 * out_y,uint32_t sdr_white_level,uint32_t max_luminance_nits)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 */
compute_hlg_oetf(struct fixed31_32 in_x,struct fixed31_32 * out_y,uint32_t sdr_white_level,uint32_t max_luminance_nits)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 */
precompute_pq(void)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 */
precompute_de_pq(void)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 
build_coefficients(struct gamma_coefficients * coefficients,enum dc_transfer_func_predefined type)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 
translate_from_linear_space(struct translate_from_linear_space_args * args)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 
translate_from_linear_space_long(struct translate_from_linear_space_args * args)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 
calculate_gamma22(struct fixed31_32 arg,bool use_eetf,struct calculate_buffer * cal_buffer)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 
translate_to_linear_space(struct fixed31_32 arg,struct fixed31_32 a0,struct fixed31_32 a1,struct fixed31_32 a2,struct fixed31_32 a3,struct fixed31_32 gamma)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 
translate_from_linear_space_ex(struct fixed31_32 arg,struct gamma_coefficients * coeff,uint32_t color_index,struct calculate_buffer * cal_buffer)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 
translate_to_linear_space_ex(struct fixed31_32 arg,struct gamma_coefficients * coeff,uint32_t color_index)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 
find_software_points(const struct dc_gamma * ramp,const struct gamma_pixel * axis_x,struct fixed31_32 hw_point,enum channel_name channel,uint32_t * index_to_start,uint32_t * index_left,uint32_t * index_right,enum hw_point_position * pos)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 
build_custom_gamma_mapping_coefficients_worker(const struct dc_gamma * ramp,struct pixel_gamma_point * coeff,const struct hw_x_point * coordinates_x,const struct gamma_pixel * axis_x,enum channel_name channel,uint32_t number_of_points)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 
calculate_mapped_value(struct pwl_float_data * rgb,const struct pixel_gamma_point * coeff,enum channel_name channel,uint32_t max_index)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 
build_pq(struct pwl_float_data_ex * rgb_regamma,uint32_t hw_points_num,const struct hw_x_point * coordinate_x,uint32_t sdr_white_level)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 
build_de_pq(struct pwl_float_data_ex * de_pq,uint32_t hw_points_num,const struct hw_x_point * coordinate_x)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 	uint32_t i;
900 	struct fixed31_32 output;
901 	struct fixed31_32 *de_pq_table = mod_color_get_table(type_de_pq_table);
902 	struct fixed31_32 scaling_factor = dc_fixpt_from_int(125);
903 
904 	if (!mod_color_is_table_init(type_de_pq_table)) {
905 		precompute_de_pq();
906 		mod_color_set_table_init_state(type_de_pq_table, true);
907 	}
908 
909 
910 	for (i = 0; i <= hw_points_num; i++) {
911 		output = de_pq_table[i];
912 		/* should really not happen? */
913 		if (dc_fixpt_lt(output, dc_fixpt_zero))
914 			output = dc_fixpt_zero;
915 		else if (dc_fixpt_lt(scaling_factor, output))
916 			output = scaling_factor;
917 		de_pq[i].r = output;
918 		de_pq[i].g = output;
919 		de_pq[i].b = output;
920 	}
921 }
922 
build_regamma(struct pwl_float_data_ex * rgb_regamma,uint32_t hw_points_num,const struct hw_x_point * coordinate_x,enum dc_transfer_func_predefined type,struct calculate_buffer * cal_buffer)923 static bool build_regamma(struct pwl_float_data_ex *rgb_regamma,
924 		uint32_t hw_points_num,
925 		const struct hw_x_point *coordinate_x,
926 		enum dc_transfer_func_predefined type,
927 		struct calculate_buffer *cal_buffer)
928 {
929 	uint32_t i;
930 	bool ret = false;
931 
932 	struct gamma_coefficients *coeff;
933 	struct pwl_float_data_ex *rgb = rgb_regamma;
934 	const struct hw_x_point *coord_x = coordinate_x;
935 
936 	coeff = kvzalloc(sizeof(*coeff), GFP_KERNEL);
937 	if (!coeff)
938 		goto release;
939 
940 	if (!build_coefficients(coeff, type))
941 		goto release;
942 
943 	memset(cal_buffer->buffer, 0, NUM_PTS_IN_REGION * sizeof(struct fixed31_32));
944 	cal_buffer->buffer_index = 0; // see variable definition for more info
945 
946 	i = 0;
947 	while (i <= hw_points_num) {
948 		/* TODO use y vs r,g,b */
949 		rgb->r = translate_from_linear_space_ex(
950 			coord_x->x, coeff, 0, cal_buffer);
951 		rgb->g = rgb->r;
952 		rgb->b = rgb->r;
953 		++coord_x;
954 		++rgb;
955 		++i;
956 	}
957 	cal_buffer->buffer_index = -1;
958 	ret = true;
959 release:
960 	kvfree(coeff);
961 	return ret;
962 }
963 
hermite_spline_eetf(struct fixed31_32 input_x,struct fixed31_32 max_display,struct fixed31_32 min_display,struct fixed31_32 max_content,struct fixed31_32 * out_x)964 static void hermite_spline_eetf(struct fixed31_32 input_x,
965 				struct fixed31_32 max_display,
966 				struct fixed31_32 min_display,
967 				struct fixed31_32 max_content,
968 				struct fixed31_32 *out_x)
969 {
970 	struct fixed31_32 min_lum_pq;
971 	struct fixed31_32 max_lum_pq;
972 	struct fixed31_32 max_content_pq;
973 	struct fixed31_32 ks;
974 	struct fixed31_32 E1;
975 	struct fixed31_32 E2;
976 	struct fixed31_32 E3;
977 	struct fixed31_32 t;
978 	struct fixed31_32 t2;
979 	struct fixed31_32 t3;
980 	struct fixed31_32 two;
981 	struct fixed31_32 three;
982 	struct fixed31_32 temp1;
983 	struct fixed31_32 temp2;
984 	struct fixed31_32 a = dc_fixpt_from_fraction(15, 10);
985 	struct fixed31_32 b = dc_fixpt_from_fraction(5, 10);
986 	struct fixed31_32 epsilon = dc_fixpt_from_fraction(1, 1000000); // dc_fixpt_epsilon is a bit too small
987 
988 	if (dc_fixpt_eq(max_content, dc_fixpt_zero)) {
989 		*out_x = dc_fixpt_zero;
990 		return;
991 	}
992 
993 	compute_pq(input_x, &E1);
994 	compute_pq(dc_fixpt_div(min_display, max_content), &min_lum_pq);
995 	compute_pq(dc_fixpt_div(max_display, max_content), &max_lum_pq);
996 	compute_pq(dc_fixpt_one, &max_content_pq); // always 1? DAL2 code is weird
997 	a = dc_fixpt_div(dc_fixpt_add(dc_fixpt_one, b), max_content_pq); // (1+b)/maxContent
998 	ks = dc_fixpt_sub(dc_fixpt_mul(a, max_lum_pq), b); // a * max_lum_pq - b
999 
1000 	if (dc_fixpt_lt(E1, ks))
1001 		E2 = E1;
1002 	else if (dc_fixpt_le(ks, E1) && dc_fixpt_le(E1, dc_fixpt_one)) {
1003 		if (dc_fixpt_lt(epsilon, dc_fixpt_sub(dc_fixpt_one, ks)))
1004 			// t = (E1 - ks) / (1 - ks)
1005 			t = dc_fixpt_div(dc_fixpt_sub(E1, ks),
1006 					dc_fixpt_sub(dc_fixpt_one, ks));
1007 		else
1008 			t = dc_fixpt_zero;
1009 
1010 		two = dc_fixpt_from_int(2);
1011 		three = dc_fixpt_from_int(3);
1012 
1013 		t2 = dc_fixpt_mul(t, t);
1014 		t3 = dc_fixpt_mul(t2, t);
1015 		temp1 = dc_fixpt_mul(two, t3);
1016 		temp2 = dc_fixpt_mul(three, t2);
1017 
1018 		// (2t^3 - 3t^2 + 1) * ks
1019 		E2 = dc_fixpt_mul(ks, dc_fixpt_add(dc_fixpt_one,
1020 				dc_fixpt_sub(temp1, temp2)));
1021 
1022 		// (-2t^3 + 3t^2) * max_lum_pq
1023 		E2 = dc_fixpt_add(E2, dc_fixpt_mul(max_lum_pq,
1024 				dc_fixpt_sub(temp2, temp1)));
1025 
1026 		temp1 = dc_fixpt_mul(two, t2);
1027 		temp2 = dc_fixpt_sub(dc_fixpt_one, ks);
1028 
1029 		// (t^3 - 2t^2 + t) * (1-ks)
1030 		E2 = dc_fixpt_add(E2, dc_fixpt_mul(temp2,
1031 				dc_fixpt_add(t, dc_fixpt_sub(t3, temp1))));
1032 	} else
1033 		E2 = dc_fixpt_one;
1034 
1035 	temp1 = dc_fixpt_sub(dc_fixpt_one, E2);
1036 	temp2 = dc_fixpt_mul(temp1, temp1);
1037 	temp2 = dc_fixpt_mul(temp2, temp2);
1038 	// temp2 = (1-E2)^4
1039 
1040 	E3 =  dc_fixpt_add(E2, dc_fixpt_mul(min_lum_pq, temp2));
1041 	compute_de_pq(E3, out_x);
1042 
1043 	*out_x = dc_fixpt_div(*out_x, dc_fixpt_div(max_display, max_content));
1044 }
1045 
build_freesync_hdr(struct pwl_float_data_ex * rgb_regamma,uint32_t hw_points_num,const struct hw_x_point * coordinate_x,const struct hdr_tm_params * fs_params,struct calculate_buffer * cal_buffer)1046 static bool build_freesync_hdr(struct pwl_float_data_ex *rgb_regamma,
1047 		uint32_t hw_points_num,
1048 		const struct hw_x_point *coordinate_x,
1049 		const struct hdr_tm_params *fs_params,
1050 		struct calculate_buffer *cal_buffer)
1051 {
1052 	uint32_t i;
1053 	struct pwl_float_data_ex *rgb = rgb_regamma;
1054 	const struct hw_x_point *coord_x = coordinate_x;
1055 	const struct hw_x_point *prv_coord_x = coord_x;
1056 	struct fixed31_32 scaledX = dc_fixpt_zero;
1057 	struct fixed31_32 scaledX1 = dc_fixpt_zero;
1058 	struct fixed31_32 max_display;
1059 	struct fixed31_32 min_display;
1060 	struct fixed31_32 max_content;
1061 	struct fixed31_32 clip = dc_fixpt_one;
1062 	struct fixed31_32 output = dc_fixpt_zero;
1063 	bool use_eetf = false;
1064 	bool is_clipped = false;
1065 	struct fixed31_32 sdr_white_level;
1066 	struct fixed31_32 coordX_diff;
1067 	struct fixed31_32 out_dist_max;
1068 	struct fixed31_32 bright_norm;
1069 
1070 	if (fs_params->max_content == 0 ||
1071 			fs_params->max_display == 0)
1072 		return false;
1073 
1074 	max_display = dc_fixpt_from_int(fs_params->max_display);
1075 	min_display = dc_fixpt_from_fraction(fs_params->min_display, 10000);
1076 	max_content = dc_fixpt_from_int(fs_params->max_content);
1077 	sdr_white_level = dc_fixpt_from_int(fs_params->sdr_white_level);
1078 
1079 	if (fs_params->min_display > 1000) // cap at 0.1 at the bottom
1080 		min_display = dc_fixpt_from_fraction(1, 10);
1081 	if (fs_params->max_display < 100) // cap at 100 at the top
1082 		max_display = dc_fixpt_from_int(100);
1083 
1084 	// only max used, we don't adjust min luminance
1085 	if (fs_params->max_content > fs_params->max_display)
1086 		use_eetf = true;
1087 	else
1088 		max_content = max_display;
1089 
1090 	if (!use_eetf)
1091 		cal_buffer->buffer_index = 0; // see var definition for more info
1092 	rgb += 32; // first 32 points have problems with fixed point, too small
1093 	coord_x += 32;
1094 
1095 	for (i = 32; i <= hw_points_num; i++) {
1096 		if (!is_clipped) {
1097 			if (use_eetf) {
1098 				/* max content is equal 1 */
1099 				scaledX1 = dc_fixpt_div(coord_x->x,
1100 						dc_fixpt_div(max_content, sdr_white_level));
1101 				hermite_spline_eetf(scaledX1, max_display, min_display,
1102 						max_content, &scaledX);
1103 			} else
1104 				scaledX = dc_fixpt_div(coord_x->x,
1105 						dc_fixpt_div(max_display, sdr_white_level));
1106 
1107 			if (dc_fixpt_lt(scaledX, clip)) {
1108 				if (dc_fixpt_lt(scaledX, dc_fixpt_zero))
1109 					output = dc_fixpt_zero;
1110 				else
1111 					output = calculate_gamma22(scaledX, use_eetf, cal_buffer);
1112 
1113 				// Ensure output respects reasonable boundaries
1114 				output = dc_fixpt_clamp(output, dc_fixpt_zero, dc_fixpt_one);
1115 
1116 				rgb->r = output;
1117 				rgb->g = output;
1118 				rgb->b = output;
1119 			} else {
1120 				/* Here clipping happens for the first time */
1121 				is_clipped = true;
1122 
1123 				/* The next few lines implement the equation
1124 				 * output = prev_out +
1125 				 * (coord_x->x - prev_coord_x->x) *
1126 				 * (1.0 - prev_out) /
1127 				 * (maxDisp/sdr_white_level - prevCoordX)
1128 				 *
1129 				 * This equation interpolates the first point
1130 				 * after max_display/80 so that the slope from
1131 				 * hw_x_before_max and hw_x_after_max is such
1132 				 * that we hit Y=1.0 at max_display/80.
1133 				 */
1134 
1135 				coordX_diff = dc_fixpt_sub(coord_x->x, prv_coord_x->x);
1136 				out_dist_max = dc_fixpt_sub(dc_fixpt_one, output);
1137 				bright_norm = dc_fixpt_div(max_display, sdr_white_level);
1138 
1139 				output = dc_fixpt_add(
1140 					output, dc_fixpt_mul(
1141 						coordX_diff, dc_fixpt_div(
1142 							out_dist_max,
1143 							dc_fixpt_sub(bright_norm, prv_coord_x->x)
1144 						)
1145 					)
1146 				);
1147 
1148 				/* Relaxing the maximum boundary to 1.07 (instead of 1.0)
1149 				 * because the last point in the curve must be such that
1150 				 * the maximum display pixel brightness interpolates to
1151 				 * exactly 1.0. The worst case scenario was calculated
1152 				 * around 1.057, so the limit of 1.07 leaves some safety
1153 				 * margin.
1154 				 */
1155 				output = dc_fixpt_clamp(output, dc_fixpt_zero,
1156 					dc_fixpt_from_fraction(107, 100));
1157 
1158 				rgb->r = output;
1159 				rgb->g = output;
1160 				rgb->b = output;
1161 			}
1162 		} else {
1163 			/* Every other clipping after the first
1164 			 * one is dealt with here
1165 			 */
1166 			rgb->r = clip;
1167 			rgb->g = clip;
1168 			rgb->b = clip;
1169 		}
1170 
1171 		prv_coord_x = coord_x;
1172 		++coord_x;
1173 		++rgb;
1174 	}
1175 	cal_buffer->buffer_index = -1;
1176 
1177 	return true;
1178 }
1179 
build_degamma(struct pwl_float_data_ex * curve,uint32_t hw_points_num,const struct hw_x_point * coordinate_x,enum dc_transfer_func_predefined type)1180 static bool build_degamma(struct pwl_float_data_ex *curve,
1181 		uint32_t hw_points_num,
1182 		const struct hw_x_point *coordinate_x, enum dc_transfer_func_predefined type)
1183 {
1184 	uint32_t i;
1185 	struct gamma_coefficients coeff;
1186 	uint32_t begin_index, end_index;
1187 	bool ret = false;
1188 
1189 	if (!build_coefficients(&coeff, type))
1190 		goto release;
1191 
1192 	i = 0;
1193 
1194 	/* X points is 2^-25 to 2^7
1195 	 * De-gamma X is 2^-12 to 2^0 – we are skipping first -12-(-25) = 13 regions
1196 	 */
1197 	begin_index = 13 * NUM_PTS_IN_REGION;
1198 	end_index = begin_index + 12 * NUM_PTS_IN_REGION;
1199 
1200 	while (i != begin_index) {
1201 		curve[i].r = dc_fixpt_zero;
1202 		curve[i].g = dc_fixpt_zero;
1203 		curve[i].b = dc_fixpt_zero;
1204 		i++;
1205 	}
1206 
1207 	while (i != end_index) {
1208 		curve[i].r = translate_to_linear_space_ex(
1209 				coordinate_x[i].x, &coeff, 0);
1210 		curve[i].g = curve[i].r;
1211 		curve[i].b = curve[i].r;
1212 		i++;
1213 	}
1214 	while (i != hw_points_num + 1) {
1215 		curve[i].r = dc_fixpt_one;
1216 		curve[i].g = dc_fixpt_one;
1217 		curve[i].b = dc_fixpt_one;
1218 		i++;
1219 	}
1220 	ret = true;
1221 release:
1222 	return ret;
1223 }
1224 
1225 
1226 
1227 
1228 
build_hlg_degamma(struct pwl_float_data_ex * degamma,uint32_t hw_points_num,const struct hw_x_point * coordinate_x,uint32_t sdr_white_level,uint32_t max_luminance_nits)1229 static void build_hlg_degamma(struct pwl_float_data_ex *degamma,
1230 		uint32_t hw_points_num,
1231 		const struct hw_x_point *coordinate_x,
1232 		uint32_t sdr_white_level, uint32_t max_luminance_nits)
1233 {
1234 	uint32_t i;
1235 
1236 	struct pwl_float_data_ex *rgb = degamma;
1237 	const struct hw_x_point *coord_x = coordinate_x;
1238 
1239 	i = 0;
1240 	// check when i == 434
1241 	while (i != hw_points_num + 1) {
1242 		compute_hlg_eotf(coord_x->x, &rgb->r, sdr_white_level, max_luminance_nits);
1243 		rgb->g = rgb->r;
1244 		rgb->b = rgb->r;
1245 		++coord_x;
1246 		++rgb;
1247 		++i;
1248 	}
1249 }
1250 
1251 
build_hlg_regamma(struct pwl_float_data_ex * regamma,uint32_t hw_points_num,const struct hw_x_point * coordinate_x,uint32_t sdr_white_level,uint32_t max_luminance_nits)1252 static void build_hlg_regamma(struct pwl_float_data_ex *regamma,
1253 		uint32_t hw_points_num,
1254 		const struct hw_x_point *coordinate_x,
1255 		uint32_t sdr_white_level, uint32_t max_luminance_nits)
1256 {
1257 	uint32_t i;
1258 
1259 	struct pwl_float_data_ex *rgb = regamma;
1260 	const struct hw_x_point *coord_x = coordinate_x;
1261 
1262 	i = 0;
1263 
1264 	// when i == 471
1265 	while (i != hw_points_num + 1) {
1266 		compute_hlg_oetf(coord_x->x, &rgb->r, sdr_white_level, max_luminance_nits);
1267 		rgb->g = rgb->r;
1268 		rgb->b = rgb->r;
1269 		++coord_x;
1270 		++rgb;
1271 		++i;
1272 	}
1273 }
1274 
scale_gamma(struct pwl_float_data * pwl_rgb,const struct dc_gamma * ramp,struct dividers dividers)1275 static void scale_gamma(struct pwl_float_data *pwl_rgb,
1276 		const struct dc_gamma *ramp,
1277 		struct dividers dividers)
1278 {
1279 	const struct fixed31_32 max_driver = dc_fixpt_from_int(0xFFFF);
1280 	const struct fixed31_32 max_os = dc_fixpt_from_int(0xFF00);
1281 	struct fixed31_32 scaler = max_os;
1282 	uint32_t i;
1283 	struct pwl_float_data *rgb = pwl_rgb;
1284 	struct pwl_float_data *rgb_last = rgb + ramp->num_entries - 1;
1285 
1286 	i = 0;
1287 
1288 	do {
1289 		if (dc_fixpt_lt(max_os, ramp->entries.red[i]) ||
1290 			dc_fixpt_lt(max_os, ramp->entries.green[i]) ||
1291 			dc_fixpt_lt(max_os, ramp->entries.blue[i])) {
1292 			scaler = max_driver;
1293 			break;
1294 		}
1295 		++i;
1296 	} while (i != ramp->num_entries);
1297 
1298 	i = 0;
1299 
1300 	do {
1301 		rgb->r = dc_fixpt_div(
1302 			ramp->entries.red[i], scaler);
1303 		rgb->g = dc_fixpt_div(
1304 			ramp->entries.green[i], scaler);
1305 		rgb->b = dc_fixpt_div(
1306 			ramp->entries.blue[i], scaler);
1307 
1308 		++rgb;
1309 		++i;
1310 	} while (i != ramp->num_entries);
1311 
1312 	rgb->r = dc_fixpt_mul(rgb_last->r,
1313 			dividers.divider1);
1314 	rgb->g = dc_fixpt_mul(rgb_last->g,
1315 			dividers.divider1);
1316 	rgb->b = dc_fixpt_mul(rgb_last->b,
1317 			dividers.divider1);
1318 
1319 	++rgb;
1320 
1321 	rgb->r = dc_fixpt_mul(rgb_last->r,
1322 			dividers.divider2);
1323 	rgb->g = dc_fixpt_mul(rgb_last->g,
1324 			dividers.divider2);
1325 	rgb->b = dc_fixpt_mul(rgb_last->b,
1326 			dividers.divider2);
1327 
1328 	++rgb;
1329 
1330 	rgb->r = dc_fixpt_mul(rgb_last->r,
1331 			dividers.divider3);
1332 	rgb->g = dc_fixpt_mul(rgb_last->g,
1333 			dividers.divider3);
1334 	rgb->b = dc_fixpt_mul(rgb_last->b,
1335 			dividers.divider3);
1336 }
1337 
scale_gamma_dx(struct pwl_float_data * pwl_rgb,const struct dc_gamma * ramp,struct dividers dividers)1338 static void scale_gamma_dx(struct pwl_float_data *pwl_rgb,
1339 		const struct dc_gamma *ramp,
1340 		struct dividers dividers)
1341 {
1342 	uint32_t i;
1343 	struct fixed31_32 min = dc_fixpt_zero;
1344 	struct fixed31_32 max = dc_fixpt_one;
1345 
1346 	struct fixed31_32 delta = dc_fixpt_zero;
1347 	struct fixed31_32 offset = dc_fixpt_zero;
1348 
1349 	for (i = 0 ; i < ramp->num_entries; i++) {
1350 		if (dc_fixpt_lt(ramp->entries.red[i], min))
1351 			min = ramp->entries.red[i];
1352 
1353 		if (dc_fixpt_lt(ramp->entries.green[i], min))
1354 			min = ramp->entries.green[i];
1355 
1356 		if (dc_fixpt_lt(ramp->entries.blue[i], min))
1357 			min = ramp->entries.blue[i];
1358 
1359 		if (dc_fixpt_lt(max, ramp->entries.red[i]))
1360 			max = ramp->entries.red[i];
1361 
1362 		if (dc_fixpt_lt(max, ramp->entries.green[i]))
1363 			max = ramp->entries.green[i];
1364 
1365 		if (dc_fixpt_lt(max, ramp->entries.blue[i]))
1366 			max = ramp->entries.blue[i];
1367 	}
1368 
1369 	if (dc_fixpt_lt(min, dc_fixpt_zero))
1370 		delta = dc_fixpt_neg(min);
1371 
1372 	offset = dc_fixpt_add(min, max);
1373 
1374 	for (i = 0 ; i < ramp->num_entries; i++) {
1375 		pwl_rgb[i].r = dc_fixpt_div(
1376 			dc_fixpt_add(
1377 				ramp->entries.red[i], delta), offset);
1378 		pwl_rgb[i].g = dc_fixpt_div(
1379 			dc_fixpt_add(
1380 				ramp->entries.green[i], delta), offset);
1381 		pwl_rgb[i].b = dc_fixpt_div(
1382 			dc_fixpt_add(
1383 				ramp->entries.blue[i], delta), offset);
1384 
1385 	}
1386 
1387 	pwl_rgb[i].r =  dc_fixpt_sub(dc_fixpt_mul_int(
1388 				pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r);
1389 	pwl_rgb[i].g =  dc_fixpt_sub(dc_fixpt_mul_int(
1390 				pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g);
1391 	pwl_rgb[i].b =  dc_fixpt_sub(dc_fixpt_mul_int(
1392 				pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b);
1393 	++i;
1394 	pwl_rgb[i].r =  dc_fixpt_sub(dc_fixpt_mul_int(
1395 				pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r);
1396 	pwl_rgb[i].g =  dc_fixpt_sub(dc_fixpt_mul_int(
1397 				pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g);
1398 	pwl_rgb[i].b =  dc_fixpt_sub(dc_fixpt_mul_int(
1399 				pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b);
1400 }
1401 
1402 /* todo: all these scale_gamma functions are inherently the same but
1403  *  take different structures as params or different format for ramp
1404  *  values. We could probably implement it in a more generic fashion
1405  */
scale_user_regamma_ramp(struct pwl_float_data * pwl_rgb,const struct regamma_ramp * ramp,struct dividers dividers)1406 static void scale_user_regamma_ramp(struct pwl_float_data *pwl_rgb,
1407 		const struct regamma_ramp *ramp,
1408 		struct dividers dividers)
1409 {
1410 	unsigned short max_driver = 0xFFFF;
1411 	unsigned short max_os = 0xFF00;
1412 	unsigned short scaler = max_os;
1413 	uint32_t i;
1414 	struct pwl_float_data *rgb = pwl_rgb;
1415 	struct pwl_float_data *rgb_last = rgb + GAMMA_RGB_256_ENTRIES - 1;
1416 
1417 	i = 0;
1418 	do {
1419 		if (ramp->gamma[i] > max_os ||
1420 				ramp->gamma[i + 256] > max_os ||
1421 				ramp->gamma[i + 512] > max_os) {
1422 			scaler = max_driver;
1423 			break;
1424 		}
1425 		i++;
1426 	} while (i != GAMMA_RGB_256_ENTRIES);
1427 
1428 	i = 0;
1429 	do {
1430 		rgb->r = dc_fixpt_from_fraction(
1431 				ramp->gamma[i], scaler);
1432 		rgb->g = dc_fixpt_from_fraction(
1433 				ramp->gamma[i + 256], scaler);
1434 		rgb->b = dc_fixpt_from_fraction(
1435 				ramp->gamma[i + 512], scaler);
1436 
1437 		++rgb;
1438 		++i;
1439 	} while (i != GAMMA_RGB_256_ENTRIES);
1440 
1441 	rgb->r = dc_fixpt_mul(rgb_last->r,
1442 			dividers.divider1);
1443 	rgb->g = dc_fixpt_mul(rgb_last->g,
1444 			dividers.divider1);
1445 	rgb->b = dc_fixpt_mul(rgb_last->b,
1446 			dividers.divider1);
1447 
1448 	++rgb;
1449 
1450 	rgb->r = dc_fixpt_mul(rgb_last->r,
1451 			dividers.divider2);
1452 	rgb->g = dc_fixpt_mul(rgb_last->g,
1453 			dividers.divider2);
1454 	rgb->b = dc_fixpt_mul(rgb_last->b,
1455 			dividers.divider2);
1456 
1457 	++rgb;
1458 
1459 	rgb->r = dc_fixpt_mul(rgb_last->r,
1460 			dividers.divider3);
1461 	rgb->g = dc_fixpt_mul(rgb_last->g,
1462 			dividers.divider3);
1463 	rgb->b = dc_fixpt_mul(rgb_last->b,
1464 			dividers.divider3);
1465 }
1466 
1467 /*
1468  * RS3+ color transform DDI - 1D LUT adjustment is composed with regamma here
1469  * Input is evenly distributed in the output color space as specified in
1470  * SetTimings
1471  *
1472  * Interpolation details:
1473  * 1D LUT has 4096 values which give curve correction in 0-1 float range
1474  * for evenly spaced points in 0-1 range. lut1D[index] gives correction
1475  * for index/4095.
1476  * First we find index for which:
1477  *	index/4095 < regamma_y < (index+1)/4095 =>
1478  *	index < 4095*regamma_y < index + 1
1479  * norm_y = 4095*regamma_y, and index is just truncating to nearest integer
1480  * lut1 = lut1D[index], lut2 = lut1D[index+1]
1481  *
1482  * adjustedY is then linearly interpolating regamma Y between lut1 and lut2
1483  *
1484  * Custom degamma on Linux uses the same interpolation math, so is handled here
1485  */
apply_lut_1d(const struct dc_gamma * ramp,uint32_t num_hw_points,struct dc_transfer_func_distributed_points * tf_pts)1486 static void apply_lut_1d(
1487 		const struct dc_gamma *ramp,
1488 		uint32_t num_hw_points,
1489 		struct dc_transfer_func_distributed_points *tf_pts)
1490 {
1491 	int i = 0;
1492 	int color = 0;
1493 	struct fixed31_32 *regamma_y;
1494 	struct fixed31_32 norm_y;
1495 	struct fixed31_32 lut1;
1496 	struct fixed31_32 lut2;
1497 	const int max_lut_index = 4095;
1498 	const struct fixed31_32 penult_lut_index_f =
1499 			dc_fixpt_from_int(max_lut_index-1);
1500 	const struct fixed31_32 max_lut_index_f =
1501 			dc_fixpt_from_int(max_lut_index);
1502 	int32_t index = 0, index_next = 0;
1503 	struct fixed31_32 index_f;
1504 	struct fixed31_32 delta_lut;
1505 	struct fixed31_32 delta_index;
1506 
1507 	if (ramp->type != GAMMA_CS_TFM_1D && ramp->type != GAMMA_CUSTOM)
1508 		return; // this is not expected
1509 
1510 	for (i = 0; i < num_hw_points; i++) {
1511 		for (color = 0; color < 3; color++) {
1512 			if (color == 0)
1513 				regamma_y = &tf_pts->red[i];
1514 			else if (color == 1)
1515 				regamma_y = &tf_pts->green[i];
1516 			else
1517 				regamma_y = &tf_pts->blue[i];
1518 
1519 			norm_y = dc_fixpt_mul(max_lut_index_f,
1520 						   *regamma_y);
1521 			index = dc_fixpt_floor(norm_y);
1522 			index_f = dc_fixpt_from_int(index);
1523 
1524 			if (index < 0)
1525 				continue;
1526 
1527 			if (index <= max_lut_index)
1528 				index_next = (index == max_lut_index) ? index : index+1;
1529 			else {
1530 				/* Here we are dealing with the last point in the curve,
1531 				 * which in some cases might exceed the range given by
1532 				 * max_lut_index. So we interpolate the value using
1533 				 * max_lut_index and max_lut_index - 1.
1534 				 */
1535 				index = max_lut_index - 1;
1536 				index_next = max_lut_index;
1537 				index_f = penult_lut_index_f;
1538 			}
1539 
1540 			if (color == 0) {
1541 				lut1 = ramp->entries.red[index];
1542 				lut2 = ramp->entries.red[index_next];
1543 			} else if (color == 1) {
1544 				lut1 = ramp->entries.green[index];
1545 				lut2 = ramp->entries.green[index_next];
1546 			} else {
1547 				lut1 = ramp->entries.blue[index];
1548 				lut2 = ramp->entries.blue[index_next];
1549 			}
1550 
1551 			// we have everything now, so interpolate
1552 			delta_lut = dc_fixpt_sub(lut2, lut1);
1553 			delta_index = dc_fixpt_sub(norm_y, index_f);
1554 
1555 			*regamma_y = dc_fixpt_add(lut1,
1556 				dc_fixpt_mul(delta_index, delta_lut));
1557 		}
1558 	}
1559 }
1560 
build_evenly_distributed_points(struct gamma_pixel * points,uint32_t numberof_points,struct dividers dividers)1561 static void build_evenly_distributed_points(
1562 	struct gamma_pixel *points,
1563 	uint32_t numberof_points,
1564 	struct dividers dividers)
1565 {
1566 	struct gamma_pixel *p = points;
1567 	struct gamma_pixel *p_last;
1568 
1569 	uint32_t i = 0;
1570 
1571 	// This function should not gets called with 0 as a parameter
1572 	ASSERT(numberof_points > 0);
1573 	p_last = p + numberof_points - 1;
1574 
1575 	do {
1576 		struct fixed31_32 value = dc_fixpt_from_fraction(i,
1577 			numberof_points - 1);
1578 
1579 		p->r = value;
1580 		p->g = value;
1581 		p->b = value;
1582 
1583 		++p;
1584 		++i;
1585 	} while (i < numberof_points);
1586 
1587 	p->r = dc_fixpt_div(p_last->r, dividers.divider1);
1588 	p->g = dc_fixpt_div(p_last->g, dividers.divider1);
1589 	p->b = dc_fixpt_div(p_last->b, dividers.divider1);
1590 
1591 	++p;
1592 
1593 	p->r = dc_fixpt_div(p_last->r, dividers.divider2);
1594 	p->g = dc_fixpt_div(p_last->g, dividers.divider2);
1595 	p->b = dc_fixpt_div(p_last->b, dividers.divider2);
1596 
1597 	++p;
1598 
1599 	p->r = dc_fixpt_div(p_last->r, dividers.divider3);
1600 	p->g = dc_fixpt_div(p_last->g, dividers.divider3);
1601 	p->b = dc_fixpt_div(p_last->b, dividers.divider3);
1602 }
1603 
copy_rgb_regamma_to_coordinates_x(struct hw_x_point * coordinates_x,uint32_t hw_points_num,const struct pwl_float_data_ex * rgb_ex)1604 static inline void copy_rgb_regamma_to_coordinates_x(
1605 		struct hw_x_point *coordinates_x,
1606 		uint32_t hw_points_num,
1607 		const struct pwl_float_data_ex *rgb_ex)
1608 {
1609 	struct hw_x_point *coords = coordinates_x;
1610 	uint32_t i = 0;
1611 	const struct pwl_float_data_ex *rgb_regamma = rgb_ex;
1612 
1613 	while (i <= hw_points_num + 1) {
1614 		coords->regamma_y_red = rgb_regamma->r;
1615 		coords->regamma_y_green = rgb_regamma->g;
1616 		coords->regamma_y_blue = rgb_regamma->b;
1617 
1618 		++coords;
1619 		++rgb_regamma;
1620 		++i;
1621 	}
1622 }
1623 
calculate_interpolated_hardware_curve(const struct dc_gamma * ramp,struct pixel_gamma_point * coeff128,struct pwl_float_data * rgb_user,const struct hw_x_point * coordinates_x,const struct gamma_pixel * axis_x,uint32_t number_of_points,struct dc_transfer_func_distributed_points * tf_pts)1624 static bool calculate_interpolated_hardware_curve(
1625 	const struct dc_gamma *ramp,
1626 	struct pixel_gamma_point *coeff128,
1627 	struct pwl_float_data *rgb_user,
1628 	const struct hw_x_point *coordinates_x,
1629 	const struct gamma_pixel *axis_x,
1630 	uint32_t number_of_points,
1631 	struct dc_transfer_func_distributed_points *tf_pts)
1632 {
1633 
1634 	const struct pixel_gamma_point *coeff = coeff128;
1635 	uint32_t max_entries = 3 - 1;
1636 
1637 	uint32_t i = 0;
1638 
1639 	for (i = 0; i < 3; i++) {
1640 		if (!build_custom_gamma_mapping_coefficients_worker(
1641 				ramp, coeff128, coordinates_x, axis_x, i,
1642 				number_of_points))
1643 			return false;
1644 	}
1645 
1646 	i = 0;
1647 	max_entries += ramp->num_entries;
1648 
1649 	/* TODO: float point case */
1650 
1651 	while (i <= number_of_points) {
1652 		tf_pts->red[i] = calculate_mapped_value(
1653 			rgb_user, coeff, CHANNEL_NAME_RED, max_entries);
1654 		tf_pts->green[i] = calculate_mapped_value(
1655 			rgb_user, coeff, CHANNEL_NAME_GREEN, max_entries);
1656 		tf_pts->blue[i] = calculate_mapped_value(
1657 			rgb_user, coeff, CHANNEL_NAME_BLUE, max_entries);
1658 
1659 		++coeff;
1660 		++i;
1661 	}
1662 
1663 	return true;
1664 }
1665 
1666 /* The "old" interpolation uses a complicated scheme to build an array of
1667  * coefficients while also using an array of 0-255 normalized to 0-1
1668  * Then there's another loop using both of the above + new scaled user ramp
1669  * and we concatenate them. It also searches for points of interpolation and
1670  * uses enums for positions.
1671  *
1672  * This function uses a different approach:
1673  * user ramp is always applied on X with 0/255, 1/255, 2/255, ..., 255/255
1674  * To find index for hwX , we notice the following:
1675  * i/255 <= hwX < (i+1)/255  <=> i <= 255*hwX < i+1
1676  * See apply_lut_1d which is the same principle, but on 4K entry 1D LUT
1677  *
1678  * Once the index is known, combined Y is simply:
1679  * user_ramp(index) + (hwX-index/255)*(user_ramp(index+1) - user_ramp(index)
1680  *
1681  * We should switch to this method in all cases, it's simpler and faster
1682  * ToDo one day - for now this only applies to ADL regamma to avoid regression
1683  * for regular use cases (sRGB and PQ)
1684  */
interpolate_user_regamma(uint32_t hw_points_num,struct pwl_float_data * rgb_user,bool apply_degamma,struct dc_transfer_func_distributed_points * tf_pts)1685 static void interpolate_user_regamma(uint32_t hw_points_num,
1686 		struct pwl_float_data *rgb_user,
1687 		bool apply_degamma,
1688 		struct dc_transfer_func_distributed_points *tf_pts)
1689 {
1690 	uint32_t i;
1691 	uint32_t color = 0;
1692 	int32_t index;
1693 	int32_t index_next;
1694 	struct fixed31_32 *tf_point;
1695 	struct fixed31_32 hw_x;
1696 	struct fixed31_32 norm_factor =
1697 			dc_fixpt_from_int(255);
1698 	struct fixed31_32 norm_x;
1699 	struct fixed31_32 index_f;
1700 	struct fixed31_32 lut1;
1701 	struct fixed31_32 lut2;
1702 	struct fixed31_32 delta_lut;
1703 	struct fixed31_32 delta_index;
1704 	const struct fixed31_32 one = dc_fixpt_from_int(1);
1705 
1706 	i = 0;
1707 	/* fixed_pt library has problems handling too small values */
1708 	while (i != 32) {
1709 		tf_pts->red[i] = dc_fixpt_zero;
1710 		tf_pts->green[i] = dc_fixpt_zero;
1711 		tf_pts->blue[i] = dc_fixpt_zero;
1712 		++i;
1713 	}
1714 	while (i <= hw_points_num + 1) {
1715 		for (color = 0; color < 3; color++) {
1716 			if (color == 0)
1717 				tf_point = &tf_pts->red[i];
1718 			else if (color == 1)
1719 				tf_point = &tf_pts->green[i];
1720 			else
1721 				tf_point = &tf_pts->blue[i];
1722 
1723 			if (apply_degamma) {
1724 				if (color == 0)
1725 					hw_x = coordinates_x[i].regamma_y_red;
1726 				else if (color == 1)
1727 					hw_x = coordinates_x[i].regamma_y_green;
1728 				else
1729 					hw_x = coordinates_x[i].regamma_y_blue;
1730 			} else
1731 				hw_x = coordinates_x[i].x;
1732 
1733 			if (dc_fixpt_le(one, hw_x))
1734 				hw_x = one;
1735 
1736 			norm_x = dc_fixpt_mul(norm_factor, hw_x);
1737 			index = dc_fixpt_floor(norm_x);
1738 			if (index < 0 || index > 255)
1739 				continue;
1740 
1741 			index_f = dc_fixpt_from_int(index);
1742 			index_next = (index == 255) ? index : index + 1;
1743 
1744 			if (color == 0) {
1745 				lut1 = rgb_user[index].r;
1746 				lut2 = rgb_user[index_next].r;
1747 			} else if (color == 1) {
1748 				lut1 = rgb_user[index].g;
1749 				lut2 = rgb_user[index_next].g;
1750 			} else {
1751 				lut1 = rgb_user[index].b;
1752 				lut2 = rgb_user[index_next].b;
1753 			}
1754 
1755 			// we have everything now, so interpolate
1756 			delta_lut = dc_fixpt_sub(lut2, lut1);
1757 			delta_index = dc_fixpt_sub(norm_x, index_f);
1758 
1759 			*tf_point = dc_fixpt_add(lut1,
1760 				dc_fixpt_mul(delta_index, delta_lut));
1761 		}
1762 		++i;
1763 	}
1764 }
1765 
build_new_custom_resulted_curve(uint32_t hw_points_num,struct dc_transfer_func_distributed_points * tf_pts)1766 static void build_new_custom_resulted_curve(
1767 	uint32_t hw_points_num,
1768 	struct dc_transfer_func_distributed_points *tf_pts)
1769 {
1770 	uint32_t i = 0;
1771 
1772 	while (i != hw_points_num + 1) {
1773 		tf_pts->red[i] = dc_fixpt_clamp(
1774 			tf_pts->red[i], dc_fixpt_zero,
1775 			dc_fixpt_one);
1776 		tf_pts->green[i] = dc_fixpt_clamp(
1777 			tf_pts->green[i], dc_fixpt_zero,
1778 			dc_fixpt_one);
1779 		tf_pts->blue[i] = dc_fixpt_clamp(
1780 			tf_pts->blue[i], dc_fixpt_zero,
1781 			dc_fixpt_one);
1782 
1783 		++i;
1784 	}
1785 }
1786 
apply_degamma_for_user_regamma(struct pwl_float_data_ex * rgb_regamma,uint32_t hw_points_num,struct calculate_buffer * cal_buffer)1787 static void apply_degamma_for_user_regamma(struct pwl_float_data_ex *rgb_regamma,
1788 		uint32_t hw_points_num, struct calculate_buffer *cal_buffer)
1789 {
1790 	uint32_t i;
1791 
1792 	struct gamma_coefficients coeff;
1793 	struct pwl_float_data_ex *rgb = rgb_regamma;
1794 	const struct hw_x_point *coord_x = coordinates_x;
1795 
1796 	build_coefficients(&coeff, TRANSFER_FUNCTION_SRGB);
1797 
1798 	i = 0;
1799 	while (i != hw_points_num + 1) {
1800 		rgb->r = translate_from_linear_space_ex(
1801 				coord_x->x, &coeff, 0, cal_buffer);
1802 		rgb->g = rgb->r;
1803 		rgb->b = rgb->r;
1804 		++coord_x;
1805 		++rgb;
1806 		++i;
1807 	}
1808 }
1809 
map_regamma_hw_to_x_user(const struct dc_gamma * ramp,struct pixel_gamma_point * coeff128,struct pwl_float_data * rgb_user,struct hw_x_point * coords_x,const struct gamma_pixel * axis_x,const struct pwl_float_data_ex * rgb_regamma,uint32_t hw_points_num,struct dc_transfer_func_distributed_points * tf_pts,bool map_user_ramp,bool do_clamping)1810 static bool map_regamma_hw_to_x_user(
1811 	const struct dc_gamma *ramp,
1812 	struct pixel_gamma_point *coeff128,
1813 	struct pwl_float_data *rgb_user,
1814 	struct hw_x_point *coords_x,
1815 	const struct gamma_pixel *axis_x,
1816 	const struct pwl_float_data_ex *rgb_regamma,
1817 	uint32_t hw_points_num,
1818 	struct dc_transfer_func_distributed_points *tf_pts,
1819 	bool map_user_ramp,
1820 	bool do_clamping)
1821 {
1822 	/* setup to spare calculated ideal regamma values */
1823 
1824 	int i = 0;
1825 	struct hw_x_point *coords = coords_x;
1826 	const struct pwl_float_data_ex *regamma = rgb_regamma;
1827 
1828 	if (ramp && map_user_ramp) {
1829 		copy_rgb_regamma_to_coordinates_x(coords,
1830 				hw_points_num,
1831 				rgb_regamma);
1832 
1833 		calculate_interpolated_hardware_curve(
1834 			ramp, coeff128, rgb_user, coords, axis_x,
1835 			hw_points_num, tf_pts);
1836 	} else {
1837 		/* just copy current rgb_regamma into  tf_pts */
1838 		while (i <= hw_points_num) {
1839 			tf_pts->red[i] = regamma->r;
1840 			tf_pts->green[i] = regamma->g;
1841 			tf_pts->blue[i] = regamma->b;
1842 
1843 			++regamma;
1844 			++i;
1845 		}
1846 	}
1847 
1848 	if (do_clamping) {
1849 		/* this should be named differently, all it does is clamp to 0-1 */
1850 		build_new_custom_resulted_curve(hw_points_num, tf_pts);
1851 	}
1852 
1853 	return true;
1854 }
1855 
1856 #define _EXTRA_POINTS 3
1857 
calculate_user_regamma_coeff(struct dc_transfer_func * output_tf,const struct regamma_lut * regamma,struct calculate_buffer * cal_buffer,const struct dc_gamma * ramp)1858 bool calculate_user_regamma_coeff(struct dc_transfer_func *output_tf,
1859 		const struct regamma_lut *regamma,
1860 		struct calculate_buffer *cal_buffer,
1861 		const struct dc_gamma *ramp)
1862 {
1863 	struct gamma_coefficients coeff;
1864 	const struct hw_x_point *coord_x = coordinates_x;
1865 	uint32_t i = 0;
1866 
1867 	do {
1868 		coeff.a0[i] = dc_fixpt_from_fraction(
1869 				regamma->coeff.A0[i], 10000000);
1870 		coeff.a1[i] = dc_fixpt_from_fraction(
1871 				regamma->coeff.A1[i], 1000);
1872 		coeff.a2[i] = dc_fixpt_from_fraction(
1873 				regamma->coeff.A2[i], 1000);
1874 		coeff.a3[i] = dc_fixpt_from_fraction(
1875 				regamma->coeff.A3[i], 1000);
1876 		coeff.user_gamma[i] = dc_fixpt_from_fraction(
1877 				regamma->coeff.gamma[i], 1000);
1878 
1879 		++i;
1880 	} while (i != 3);
1881 
1882 	i = 0;
1883 	/* fixed_pt library has problems handling too small values */
1884 	while (i != 32) {
1885 		output_tf->tf_pts.red[i] = dc_fixpt_zero;
1886 		output_tf->tf_pts.green[i] = dc_fixpt_zero;
1887 		output_tf->tf_pts.blue[i] = dc_fixpt_zero;
1888 		++coord_x;
1889 		++i;
1890 	}
1891 	while (i != MAX_HW_POINTS + 1) {
1892 		output_tf->tf_pts.red[i] = translate_from_linear_space_ex(
1893 				coord_x->x, &coeff, 0, cal_buffer);
1894 		output_tf->tf_pts.green[i] = translate_from_linear_space_ex(
1895 				coord_x->x, &coeff, 1, cal_buffer);
1896 		output_tf->tf_pts.blue[i] = translate_from_linear_space_ex(
1897 				coord_x->x, &coeff, 2, cal_buffer);
1898 		++coord_x;
1899 		++i;
1900 	}
1901 
1902 	if (ramp && ramp->type == GAMMA_CS_TFM_1D)
1903 		apply_lut_1d(ramp, MAX_HW_POINTS, &output_tf->tf_pts);
1904 
1905 	// this function just clamps output to 0-1
1906 	build_new_custom_resulted_curve(MAX_HW_POINTS, &output_tf->tf_pts);
1907 	output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1908 
1909 	return true;
1910 }
1911 
calculate_user_regamma_ramp(struct dc_transfer_func * output_tf,const struct regamma_lut * regamma,struct calculate_buffer * cal_buffer,const struct dc_gamma * ramp)1912 bool calculate_user_regamma_ramp(struct dc_transfer_func *output_tf,
1913 		const struct regamma_lut *regamma,
1914 		struct calculate_buffer *cal_buffer,
1915 		const struct dc_gamma *ramp)
1916 {
1917 	struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts;
1918 	struct dividers dividers;
1919 
1920 	struct pwl_float_data *rgb_user = NULL;
1921 	struct pwl_float_data_ex *rgb_regamma = NULL;
1922 	bool ret = false;
1923 
1924 	if (regamma == NULL)
1925 		return false;
1926 
1927 	output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1928 
1929 	rgb_user = kcalloc(GAMMA_RGB_256_ENTRIES + _EXTRA_POINTS,
1930 			   sizeof(*rgb_user),
1931 			   GFP_KERNEL);
1932 	if (!rgb_user)
1933 		goto rgb_user_alloc_fail;
1934 
1935 	rgb_regamma = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1936 			      sizeof(*rgb_regamma),
1937 			      GFP_KERNEL);
1938 	if (!rgb_regamma)
1939 		goto rgb_regamma_alloc_fail;
1940 
1941 	dividers.divider1 = dc_fixpt_from_fraction(3, 2);
1942 	dividers.divider2 = dc_fixpt_from_int(2);
1943 	dividers.divider3 = dc_fixpt_from_fraction(5, 2);
1944 
1945 	scale_user_regamma_ramp(rgb_user, &regamma->ramp, dividers);
1946 
1947 	if (regamma->flags.bits.applyDegamma == 1) {
1948 		apply_degamma_for_user_regamma(rgb_regamma, MAX_HW_POINTS, cal_buffer);
1949 		copy_rgb_regamma_to_coordinates_x(coordinates_x,
1950 				MAX_HW_POINTS, rgb_regamma);
1951 	}
1952 
1953 	interpolate_user_regamma(MAX_HW_POINTS, rgb_user,
1954 			regamma->flags.bits.applyDegamma, tf_pts);
1955 
1956 	// no custom HDR curves!
1957 	tf_pts->end_exponent = 0;
1958 	tf_pts->x_point_at_y1_red = 1;
1959 	tf_pts->x_point_at_y1_green = 1;
1960 	tf_pts->x_point_at_y1_blue = 1;
1961 
1962 	if (ramp && ramp->type == GAMMA_CS_TFM_1D)
1963 		apply_lut_1d(ramp, MAX_HW_POINTS, &output_tf->tf_pts);
1964 
1965 	// this function just clamps output to 0-1
1966 	build_new_custom_resulted_curve(MAX_HW_POINTS, tf_pts);
1967 
1968 	ret = true;
1969 
1970 	kfree(rgb_regamma);
1971 rgb_regamma_alloc_fail:
1972 	kfree(rgb_user);
1973 rgb_user_alloc_fail:
1974 	return ret;
1975 }
1976 
mod_color_calculate_degamma_params(struct dc_color_caps * dc_caps,struct dc_transfer_func * input_tf,const struct dc_gamma * ramp,bool map_user_ramp)1977 bool mod_color_calculate_degamma_params(struct dc_color_caps *dc_caps,
1978 		struct dc_transfer_func *input_tf,
1979 		const struct dc_gamma *ramp, bool map_user_ramp)
1980 {
1981 	struct dc_transfer_func_distributed_points *tf_pts = &input_tf->tf_pts;
1982 	struct dividers dividers;
1983 	struct pwl_float_data *rgb_user = NULL;
1984 	struct pwl_float_data_ex *curve = NULL;
1985 	struct gamma_pixel *axis_x = NULL;
1986 	struct pixel_gamma_point *coeff = NULL;
1987 	enum dc_transfer_func_predefined tf;
1988 	uint32_t i;
1989 	bool ret = false;
1990 
1991 	if (input_tf->type == TF_TYPE_BYPASS)
1992 		return false;
1993 
1994 	/* we can use hardcoded curve for plain SRGB TF
1995 	 * If linear, it's bypass if no user ramp
1996 	 */
1997 	if (input_tf->type == TF_TYPE_PREDEFINED) {
1998 		if ((input_tf->tf == TRANSFER_FUNCTION_SRGB ||
1999 				input_tf->tf == TRANSFER_FUNCTION_LINEAR) &&
2000 				!map_user_ramp)
2001 			return true;
2002 
2003 		if (dc_caps != NULL &&
2004 			dc_caps->dpp.dcn_arch == 1) {
2005 
2006 			if (input_tf->tf == TRANSFER_FUNCTION_PQ &&
2007 					dc_caps->dpp.dgam_rom_caps.pq == 1)
2008 				return true;
2009 
2010 			if (input_tf->tf == TRANSFER_FUNCTION_GAMMA22 &&
2011 					dc_caps->dpp.dgam_rom_caps.gamma2_2 == 1)
2012 				return true;
2013 
2014 			// HLG OOTF not accounted for
2015 			if (input_tf->tf == TRANSFER_FUNCTION_HLG &&
2016 					dc_caps->dpp.dgam_rom_caps.hlg == 1)
2017 				return true;
2018 		}
2019 	}
2020 
2021 	input_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
2022 
2023 	if (map_user_ramp && ramp && ramp->type == GAMMA_RGB_256) {
2024 		rgb_user = kvcalloc(ramp->num_entries + _EXTRA_POINTS,
2025 				sizeof(*rgb_user),
2026 				GFP_KERNEL);
2027 		if (!rgb_user)
2028 			goto rgb_user_alloc_fail;
2029 
2030 		axis_x = kvcalloc(ramp->num_entries + _EXTRA_POINTS, sizeof(*axis_x),
2031 				GFP_KERNEL);
2032 		if (!axis_x)
2033 			goto axis_x_alloc_fail;
2034 
2035 		dividers.divider1 = dc_fixpt_from_fraction(3, 2);
2036 		dividers.divider2 = dc_fixpt_from_int(2);
2037 		dividers.divider3 = dc_fixpt_from_fraction(5, 2);
2038 
2039 		build_evenly_distributed_points(
2040 				axis_x,
2041 				ramp->num_entries,
2042 				dividers);
2043 
2044 		scale_gamma(rgb_user, ramp, dividers);
2045 	}
2046 
2047 	curve = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*curve),
2048 			GFP_KERNEL);
2049 	if (!curve)
2050 		goto curve_alloc_fail;
2051 
2052 	coeff = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*coeff),
2053 			GFP_KERNEL);
2054 	if (!coeff)
2055 		goto coeff_alloc_fail;
2056 
2057 	tf = input_tf->tf;
2058 
2059 	if (tf == TRANSFER_FUNCTION_PQ)
2060 		build_de_pq(curve,
2061 				MAX_HW_POINTS,
2062 				coordinates_x);
2063 	else if (tf == TRANSFER_FUNCTION_SRGB ||
2064 		tf == TRANSFER_FUNCTION_BT709 ||
2065 		tf == TRANSFER_FUNCTION_GAMMA22 ||
2066 		tf == TRANSFER_FUNCTION_GAMMA24 ||
2067 		tf == TRANSFER_FUNCTION_GAMMA26)
2068 		build_degamma(curve,
2069 				MAX_HW_POINTS,
2070 				coordinates_x,
2071 				tf);
2072 	else if (tf == TRANSFER_FUNCTION_HLG)
2073 		build_hlg_degamma(curve,
2074 				MAX_HW_POINTS,
2075 				coordinates_x,
2076 				80, 1000);
2077 	else if (tf == TRANSFER_FUNCTION_LINEAR) {
2078 		// just copy coordinates_x into curve
2079 		i = 0;
2080 		while (i != MAX_HW_POINTS + 1) {
2081 			curve[i].r = coordinates_x[i].x;
2082 			curve[i].g = curve[i].r;
2083 			curve[i].b = curve[i].r;
2084 			i++;
2085 		}
2086 	} else
2087 		goto invalid_tf_fail;
2088 
2089 	tf_pts->end_exponent = 0;
2090 	tf_pts->x_point_at_y1_red = 1;
2091 	tf_pts->x_point_at_y1_green = 1;
2092 	tf_pts->x_point_at_y1_blue = 1;
2093 
2094 	if (input_tf->tf == TRANSFER_FUNCTION_PQ) {
2095 		/* just copy current rgb_regamma into  tf_pts */
2096 		struct pwl_float_data_ex *curvePt = curve;
2097 		int i = 0;
2098 
2099 		while (i <= MAX_HW_POINTS) {
2100 			tf_pts->red[i]   = curvePt->r;
2101 			tf_pts->green[i] = curvePt->g;
2102 			tf_pts->blue[i]  = curvePt->b;
2103 			++curvePt;
2104 			++i;
2105 		}
2106 	} else {
2107 		// clamps to 0-1
2108 		map_regamma_hw_to_x_user(ramp, coeff, rgb_user,
2109 				coordinates_x, axis_x, curve,
2110 				MAX_HW_POINTS, tf_pts,
2111 				map_user_ramp && ramp && ramp->type == GAMMA_RGB_256,
2112 				true);
2113 	}
2114 
2115 
2116 
2117 	if (ramp && ramp->type == GAMMA_CUSTOM)
2118 		apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts);
2119 
2120 	ret = true;
2121 
2122 invalid_tf_fail:
2123 	kvfree(coeff);
2124 coeff_alloc_fail:
2125 	kvfree(curve);
2126 curve_alloc_fail:
2127 	kvfree(axis_x);
2128 axis_x_alloc_fail:
2129 	kvfree(rgb_user);
2130 rgb_user_alloc_fail:
2131 
2132 	return ret;
2133 }
2134 
calculate_curve(enum dc_transfer_func_predefined trans,struct dc_transfer_func_distributed_points * points,struct pwl_float_data_ex * rgb_regamma,const struct hdr_tm_params * fs_params,uint32_t sdr_ref_white_level,struct calculate_buffer * cal_buffer)2135 static bool calculate_curve(enum dc_transfer_func_predefined trans,
2136 				struct dc_transfer_func_distributed_points *points,
2137 				struct pwl_float_data_ex *rgb_regamma,
2138 				const struct hdr_tm_params *fs_params,
2139 				uint32_t sdr_ref_white_level,
2140 				struct calculate_buffer *cal_buffer)
2141 {
2142 	uint32_t i;
2143 	bool ret = false;
2144 
2145 	if (trans == TRANSFER_FUNCTION_UNITY ||
2146 		trans == TRANSFER_FUNCTION_LINEAR) {
2147 		points->end_exponent = 0;
2148 		points->x_point_at_y1_red = 1;
2149 		points->x_point_at_y1_green = 1;
2150 		points->x_point_at_y1_blue = 1;
2151 
2152 		for (i = 0; i <= MAX_HW_POINTS ; i++) {
2153 			rgb_regamma[i].r = coordinates_x[i].x;
2154 			rgb_regamma[i].g = coordinates_x[i].x;
2155 			rgb_regamma[i].b = coordinates_x[i].x;
2156 		}
2157 
2158 		ret = true;
2159 	} else if (trans == TRANSFER_FUNCTION_PQ) {
2160 		points->end_exponent = 7;
2161 		points->x_point_at_y1_red = 125;
2162 		points->x_point_at_y1_green = 125;
2163 		points->x_point_at_y1_blue = 125;
2164 
2165 		build_pq(rgb_regamma,
2166 				MAX_HW_POINTS,
2167 				coordinates_x,
2168 				sdr_ref_white_level);
2169 
2170 		ret = true;
2171 	} else if (trans == TRANSFER_FUNCTION_GAMMA22 &&
2172 			fs_params != NULL && fs_params->skip_tm == 0) {
2173 		build_freesync_hdr(rgb_regamma,
2174 				MAX_HW_POINTS,
2175 				coordinates_x,
2176 				fs_params,
2177 				cal_buffer);
2178 
2179 		ret = true;
2180 	} else if (trans == TRANSFER_FUNCTION_HLG) {
2181 		points->end_exponent = 4;
2182 		points->x_point_at_y1_red = 12;
2183 		points->x_point_at_y1_green = 12;
2184 		points->x_point_at_y1_blue = 12;
2185 
2186 		build_hlg_regamma(rgb_regamma,
2187 				MAX_HW_POINTS,
2188 				coordinates_x,
2189 				80, 1000);
2190 
2191 		ret = true;
2192 	} else {
2193 		// trans == TRANSFER_FUNCTION_SRGB
2194 		// trans == TRANSFER_FUNCTION_BT709
2195 		// trans == TRANSFER_FUNCTION_GAMMA22
2196 		// trans == TRANSFER_FUNCTION_GAMMA24
2197 		// trans == TRANSFER_FUNCTION_GAMMA26
2198 		points->end_exponent = 0;
2199 		points->x_point_at_y1_red = 1;
2200 		points->x_point_at_y1_green = 1;
2201 		points->x_point_at_y1_blue = 1;
2202 
2203 		build_regamma(rgb_regamma,
2204 				MAX_HW_POINTS,
2205 				coordinates_x,
2206 				trans,
2207 				cal_buffer);
2208 
2209 		ret = true;
2210 	}
2211 
2212 	return ret;
2213 }
2214 
mod_color_calculate_regamma_params(struct dc_transfer_func * output_tf,const struct dc_gamma * ramp,bool map_user_ramp,bool can_rom_be_used,const struct hdr_tm_params * fs_params,struct calculate_buffer * cal_buffer)2215 bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf,
2216 					const struct dc_gamma *ramp,
2217 					bool map_user_ramp,
2218 					bool can_rom_be_used,
2219 					const struct hdr_tm_params *fs_params,
2220 					struct calculate_buffer *cal_buffer)
2221 {
2222 	struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts;
2223 	struct dividers dividers;
2224 
2225 	struct pwl_float_data *rgb_user = NULL;
2226 	struct pwl_float_data_ex *rgb_regamma = NULL;
2227 	struct gamma_pixel *axis_x = NULL;
2228 	struct pixel_gamma_point *coeff = NULL;
2229 	enum dc_transfer_func_predefined tf;
2230 	bool do_clamping = true;
2231 	bool ret = false;
2232 
2233 	if (output_tf->type == TF_TYPE_BYPASS)
2234 		return false;
2235 
2236 	/* we can use hardcoded curve for plain SRGB TF */
2237 	if (output_tf->type == TF_TYPE_PREDEFINED && can_rom_be_used == true &&
2238 			output_tf->tf == TRANSFER_FUNCTION_SRGB) {
2239 		if (ramp == NULL)
2240 			return true;
2241 		if ((ramp->is_identity && ramp->type != GAMMA_CS_TFM_1D) ||
2242 		    (!map_user_ramp && ramp->type == GAMMA_RGB_256))
2243 			return true;
2244 	}
2245 
2246 	output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
2247 
2248 	if (ramp && ramp->type != GAMMA_CS_TFM_1D &&
2249 	    (map_user_ramp || ramp->type != GAMMA_RGB_256)) {
2250 		rgb_user = kvcalloc(ramp->num_entries + _EXTRA_POINTS,
2251 			    sizeof(*rgb_user),
2252 			    GFP_KERNEL);
2253 		if (!rgb_user)
2254 			goto rgb_user_alloc_fail;
2255 
2256 		axis_x = kvcalloc(ramp->num_entries + 3, sizeof(*axis_x),
2257 				GFP_KERNEL);
2258 		if (!axis_x)
2259 			goto axis_x_alloc_fail;
2260 
2261 		dividers.divider1 = dc_fixpt_from_fraction(3, 2);
2262 		dividers.divider2 = dc_fixpt_from_int(2);
2263 		dividers.divider3 = dc_fixpt_from_fraction(5, 2);
2264 
2265 		build_evenly_distributed_points(
2266 				axis_x,
2267 				ramp->num_entries,
2268 				dividers);
2269 
2270 		if (ramp->type == GAMMA_RGB_256 && map_user_ramp)
2271 			scale_gamma(rgb_user, ramp, dividers);
2272 		else if (ramp->type == GAMMA_RGB_FLOAT_1024)
2273 			scale_gamma_dx(rgb_user, ramp, dividers);
2274 	}
2275 
2276 	rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
2277 			       sizeof(*rgb_regamma),
2278 			       GFP_KERNEL);
2279 	if (!rgb_regamma)
2280 		goto rgb_regamma_alloc_fail;
2281 
2282 	coeff = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*coeff),
2283 			 GFP_KERNEL);
2284 	if (!coeff)
2285 		goto coeff_alloc_fail;
2286 
2287 	tf = output_tf->tf;
2288 
2289 	ret = calculate_curve(tf,
2290 			tf_pts,
2291 			rgb_regamma,
2292 			fs_params,
2293 			output_tf->sdr_ref_white_level,
2294 			cal_buffer);
2295 
2296 	if (ret) {
2297 		do_clamping = !(output_tf->tf == TRANSFER_FUNCTION_PQ) &&
2298 				!(output_tf->tf == TRANSFER_FUNCTION_GAMMA22 &&
2299 				fs_params != NULL && fs_params->skip_tm == 0);
2300 
2301 		map_regamma_hw_to_x_user(ramp, coeff, rgb_user,
2302 					 coordinates_x, axis_x, rgb_regamma,
2303 					 MAX_HW_POINTS, tf_pts,
2304 					 (map_user_ramp || (ramp && ramp->type != GAMMA_RGB_256)) &&
2305 					 (ramp && ramp->type != GAMMA_CS_TFM_1D),
2306 					 do_clamping);
2307 
2308 		if (ramp && ramp->type == GAMMA_CS_TFM_1D)
2309 			apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts);
2310 	}
2311 
2312 	kvfree(coeff);
2313 coeff_alloc_fail:
2314 	kvfree(rgb_regamma);
2315 rgb_regamma_alloc_fail:
2316 	kvfree(axis_x);
2317 axis_x_alloc_fail:
2318 	kvfree(rgb_user);
2319 rgb_user_alloc_fail:
2320 	return ret;
2321 }
2322