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