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