GNU Linux-libre 4.19.304-gnu1
[releases.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 "dc.h"
27 #include "opp.h"
28 #include "color_gamma.h"
29
30
31 #define NUM_PTS_IN_REGION 16
32 #define NUM_REGIONS 32
33 #define MAX_HW_POINTS (NUM_PTS_IN_REGION*NUM_REGIONS)
34
35 static struct hw_x_point coordinates_x[MAX_HW_POINTS + 2];
36
37 static struct fixed31_32 pq_table[MAX_HW_POINTS + 2];
38 static struct fixed31_32 de_pq_table[MAX_HW_POINTS + 2];
39
40 static bool pq_initialized; /* = false; */
41 static bool de_pq_initialized; /* = false; */
42
43 /* one-time setup of X points */
44 void setup_x_points_distribution(void)
45 {
46         struct fixed31_32 region_size = dc_fixpt_from_int(128);
47         int32_t segment;
48         uint32_t seg_offset;
49         uint32_t index;
50         struct fixed31_32 increment;
51
52         coordinates_x[MAX_HW_POINTS].x = region_size;
53         coordinates_x[MAX_HW_POINTS + 1].x = region_size;
54
55         for (segment = 6; segment > (6 - NUM_REGIONS); segment--) {
56                 region_size = dc_fixpt_div_int(region_size, 2);
57                 increment = dc_fixpt_div_int(region_size,
58                                                 NUM_PTS_IN_REGION);
59                 seg_offset = (segment + (NUM_REGIONS - 7)) * NUM_PTS_IN_REGION;
60                 coordinates_x[seg_offset].x = region_size;
61
62                 for (index = seg_offset + 1;
63                                 index < seg_offset + NUM_PTS_IN_REGION;
64                                 index++) {
65                         coordinates_x[index].x = dc_fixpt_add
66                                         (coordinates_x[index-1].x, increment);
67                 }
68         }
69 }
70
71 static void compute_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y)
72 {
73         /* consts for PQ gamma formula. */
74         const struct fixed31_32 m1 =
75                 dc_fixpt_from_fraction(159301758, 1000000000);
76         const struct fixed31_32 m2 =
77                 dc_fixpt_from_fraction(7884375, 100000);
78         const struct fixed31_32 c1 =
79                 dc_fixpt_from_fraction(8359375, 10000000);
80         const struct fixed31_32 c2 =
81                 dc_fixpt_from_fraction(188515625, 10000000);
82         const struct fixed31_32 c3 =
83                 dc_fixpt_from_fraction(186875, 10000);
84
85         struct fixed31_32 l_pow_m1;
86         struct fixed31_32 base;
87
88         if (dc_fixpt_lt(in_x, dc_fixpt_zero))
89                 in_x = dc_fixpt_zero;
90
91         l_pow_m1 = dc_fixpt_pow(in_x, m1);
92         base = dc_fixpt_div(
93                         dc_fixpt_add(c1,
94                                         (dc_fixpt_mul(c2, l_pow_m1))),
95                         dc_fixpt_add(dc_fixpt_one,
96                                         (dc_fixpt_mul(c3, l_pow_m1))));
97         *out_y = dc_fixpt_pow(base, m2);
98 }
99
100 static void compute_de_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y)
101 {
102         /* consts for dePQ gamma formula. */
103         const struct fixed31_32 m1 =
104                 dc_fixpt_from_fraction(159301758, 1000000000);
105         const struct fixed31_32 m2 =
106                 dc_fixpt_from_fraction(7884375, 100000);
107         const struct fixed31_32 c1 =
108                 dc_fixpt_from_fraction(8359375, 10000000);
109         const struct fixed31_32 c2 =
110                 dc_fixpt_from_fraction(188515625, 10000000);
111         const struct fixed31_32 c3 =
112                 dc_fixpt_from_fraction(186875, 10000);
113
114         struct fixed31_32 l_pow_m1;
115         struct fixed31_32 base, div;
116
117
118         if (dc_fixpt_lt(in_x, dc_fixpt_zero))
119                 in_x = dc_fixpt_zero;
120
121         l_pow_m1 = dc_fixpt_pow(in_x,
122                         dc_fixpt_div(dc_fixpt_one, m2));
123         base = dc_fixpt_sub(l_pow_m1, c1);
124
125         if (dc_fixpt_lt(base, dc_fixpt_zero))
126                 base = dc_fixpt_zero;
127
128         div = dc_fixpt_sub(c2, dc_fixpt_mul(c3, l_pow_m1));
129
130         *out_y = dc_fixpt_pow(dc_fixpt_div(base, div),
131                         dc_fixpt_div(dc_fixpt_one, m1));
132
133 }
134
135 /*de gamma, none linear to linear*/
136 static void compute_hlg_oetf(struct fixed31_32 in_x, bool is_light0_12, struct fixed31_32 *out_y)
137 {
138         struct fixed31_32 a;
139         struct fixed31_32 b;
140         struct fixed31_32 c;
141         struct fixed31_32 threshold;
142         struct fixed31_32 reference_white_level;
143
144         a = dc_fixpt_from_fraction(17883277, 100000000);
145         if (is_light0_12) {
146                 /*light 0-12*/
147                 b = dc_fixpt_from_fraction(28466892, 100000000);
148                 c = dc_fixpt_from_fraction(55991073, 100000000);
149                 threshold = dc_fixpt_one;
150                 reference_white_level = dc_fixpt_half;
151         } else {
152                 /*light 0-1*/
153                 b = dc_fixpt_from_fraction(2372241, 100000000);
154                 c = dc_fixpt_add(dc_fixpt_one, dc_fixpt_from_fraction(429347, 100000000));
155                 threshold = dc_fixpt_from_fraction(1, 12);
156                 reference_white_level = dc_fixpt_pow(dc_fixpt_from_fraction(3, 1), dc_fixpt_half);
157         }
158         if (dc_fixpt_lt(threshold, in_x))
159                 *out_y = dc_fixpt_add(c, dc_fixpt_mul(a, dc_fixpt_log(dc_fixpt_sub(in_x, b))));
160         else
161                 *out_y = dc_fixpt_mul(dc_fixpt_pow(in_x, dc_fixpt_half), reference_white_level);
162 }
163
164 /*re gamma, linear to none linear*/
165 static void compute_hlg_eotf(struct fixed31_32 in_x, bool is_light0_12, struct fixed31_32 *out_y)
166 {
167         struct fixed31_32 a;
168         struct fixed31_32 b;
169         struct fixed31_32 c;
170         struct fixed31_32 reference_white_level;
171
172         a = dc_fixpt_from_fraction(17883277, 100000000);
173         if (is_light0_12) {
174                 /*light 0-12*/
175                 b = dc_fixpt_from_fraction(28466892, 100000000);
176                 c = dc_fixpt_from_fraction(55991073, 100000000);
177                 reference_white_level = dc_fixpt_from_fraction(4, 1);
178         } else {
179                 /*light 0-1*/
180                 b = dc_fixpt_from_fraction(2372241, 100000000);
181                 c = dc_fixpt_add(dc_fixpt_one, dc_fixpt_from_fraction(429347, 100000000));
182                 reference_white_level = dc_fixpt_from_fraction(1, 3);
183         }
184         if (dc_fixpt_lt(dc_fixpt_half, in_x))
185                 *out_y = dc_fixpt_add(dc_fixpt_exp(dc_fixpt_div(dc_fixpt_sub(in_x, c), a)), b);
186         else
187                 *out_y = dc_fixpt_mul(dc_fixpt_pow(in_x, dc_fixpt_from_fraction(2, 1)), reference_white_level);
188 }
189
190
191 /* one-time pre-compute PQ values - only for sdr_white_level 80 */
192 void precompute_pq(void)
193 {
194         int i;
195         struct fixed31_32 x;
196         const struct hw_x_point *coord_x = coordinates_x + 32;
197         struct fixed31_32 scaling_factor =
198                         dc_fixpt_from_fraction(80, 10000);
199
200         /* pow function has problems with arguments too small */
201         for (i = 0; i < 32; i++)
202                 pq_table[i] = dc_fixpt_zero;
203
204         for (i = 32; i <= MAX_HW_POINTS; i++) {
205                 x = dc_fixpt_mul(coord_x->x, scaling_factor);
206                 compute_pq(x, &pq_table[i]);
207                 ++coord_x;
208         }
209 }
210
211 /* one-time pre-compute dePQ values - only for max pixel value 125 FP16 */
212 void precompute_de_pq(void)
213 {
214         int i;
215         struct fixed31_32  y;
216         uint32_t begin_index, end_index;
217
218         struct fixed31_32 scaling_factor = dc_fixpt_from_int(125);
219
220         /* X points is 2^-25 to 2^7
221          * De-gamma X is 2^-12 to 2^0 â€“ we are skipping first -12-(-25) = 13 regions
222          */
223         begin_index = 13 * NUM_PTS_IN_REGION;
224         end_index = begin_index + 12 * NUM_PTS_IN_REGION;
225
226         for (i = 0; i <= begin_index; i++)
227                 de_pq_table[i] = dc_fixpt_zero;
228
229         for (; i <= end_index; i++) {
230                 compute_de_pq(coordinates_x[i].x, &y);
231                 de_pq_table[i] = dc_fixpt_mul(y, scaling_factor);
232         }
233
234         for (; i <= MAX_HW_POINTS; i++)
235                 de_pq_table[i] = de_pq_table[i-1];
236 }
237 struct dividers {
238         struct fixed31_32 divider1;
239         struct fixed31_32 divider2;
240         struct fixed31_32 divider3;
241 };
242
243 static void build_coefficients(struct gamma_coefficients *coefficients, bool is_2_4)
244 {
245         static const int32_t numerator01[] = { 31308, 180000};
246         static const int32_t numerator02[] = { 12920, 4500};
247         static const int32_t numerator03[] = { 55, 99};
248         static const int32_t numerator04[] = { 55, 99};
249         static const int32_t numerator05[] = { 2400, 2200};
250
251         uint32_t i = 0;
252         uint32_t index = is_2_4 == true ? 0:1;
253
254         do {
255                 coefficients->a0[i] = dc_fixpt_from_fraction(
256                         numerator01[index], 10000000);
257                 coefficients->a1[i] = dc_fixpt_from_fraction(
258                         numerator02[index], 1000);
259                 coefficients->a2[i] = dc_fixpt_from_fraction(
260                         numerator03[index], 1000);
261                 coefficients->a3[i] = dc_fixpt_from_fraction(
262                         numerator04[index], 1000);
263                 coefficients->user_gamma[i] = dc_fixpt_from_fraction(
264                         numerator05[index], 1000);
265
266                 ++i;
267         } while (i != ARRAY_SIZE(coefficients->a0));
268 }
269
270 static struct fixed31_32 translate_from_linear_space(
271         struct fixed31_32 arg,
272         struct fixed31_32 a0,
273         struct fixed31_32 a1,
274         struct fixed31_32 a2,
275         struct fixed31_32 a3,
276         struct fixed31_32 gamma)
277 {
278         const struct fixed31_32 one = dc_fixpt_from_int(1);
279
280         if (dc_fixpt_lt(one, arg))
281                 return one;
282
283         if (dc_fixpt_le(arg, dc_fixpt_neg(a0)))
284                 return dc_fixpt_sub(
285                         a2,
286                         dc_fixpt_mul(
287                                 dc_fixpt_add(
288                                         one,
289                                         a3),
290                                 dc_fixpt_pow(
291                                         dc_fixpt_neg(arg),
292                                         dc_fixpt_recip(gamma))));
293         else if (dc_fixpt_le(a0, arg))
294                 return dc_fixpt_sub(
295                         dc_fixpt_mul(
296                                 dc_fixpt_add(
297                                         one,
298                                         a3),
299                                 dc_fixpt_pow(
300                                         arg,
301                                         dc_fixpt_recip(gamma))),
302                         a2);
303         else
304                 return dc_fixpt_mul(
305                         arg,
306                         a1);
307 }
308
309 static struct fixed31_32 translate_to_linear_space(
310         struct fixed31_32 arg,
311         struct fixed31_32 a0,
312         struct fixed31_32 a1,
313         struct fixed31_32 a2,
314         struct fixed31_32 a3,
315         struct fixed31_32 gamma)
316 {
317         struct fixed31_32 linear;
318
319         a0 = dc_fixpt_mul(a0, a1);
320         if (dc_fixpt_le(arg, dc_fixpt_neg(a0)))
321
322                 linear = dc_fixpt_neg(
323                                  dc_fixpt_pow(
324                                  dc_fixpt_div(
325                                  dc_fixpt_sub(a2, arg),
326                                  dc_fixpt_add(
327                                  dc_fixpt_one, a3)), gamma));
328
329         else if (dc_fixpt_le(dc_fixpt_neg(a0), arg) &&
330                          dc_fixpt_le(arg, a0))
331                 linear = dc_fixpt_div(arg, a1);
332         else
333                 linear =  dc_fixpt_pow(
334                                         dc_fixpt_div(
335                                         dc_fixpt_add(a2, arg),
336                                         dc_fixpt_add(
337                                         dc_fixpt_one, a3)), gamma);
338
339         return linear;
340 }
341
342 static inline struct fixed31_32 translate_from_linear_space_ex(
343         struct fixed31_32 arg,
344         struct gamma_coefficients *coeff,
345         uint32_t color_index)
346 {
347         return translate_from_linear_space(
348                 arg,
349                 coeff->a0[color_index],
350                 coeff->a1[color_index],
351                 coeff->a2[color_index],
352                 coeff->a3[color_index],
353                 coeff->user_gamma[color_index]);
354 }
355
356
357 static inline struct fixed31_32 translate_to_linear_space_ex(
358         struct fixed31_32 arg,
359         struct gamma_coefficients *coeff,
360         uint32_t color_index)
361 {
362         return translate_to_linear_space(
363                 arg,
364                 coeff->a0[color_index],
365                 coeff->a1[color_index],
366                 coeff->a2[color_index],
367                 coeff->a3[color_index],
368                 coeff->user_gamma[color_index]);
369 }
370
371
372 static bool find_software_points(
373         const struct dc_gamma *ramp,
374         const struct gamma_pixel *axis_x,
375         struct fixed31_32 hw_point,
376         enum channel_name channel,
377         uint32_t *index_to_start,
378         uint32_t *index_left,
379         uint32_t *index_right,
380         enum hw_point_position *pos)
381 {
382         const uint32_t max_number = ramp->num_entries + 3;
383
384         struct fixed31_32 left, right;
385
386         uint32_t i = *index_to_start;
387
388         while (i < max_number) {
389                 if (channel == CHANNEL_NAME_RED) {
390                         left = axis_x[i].r;
391
392                         if (i < max_number - 1)
393                                 right = axis_x[i + 1].r;
394                         else
395                                 right = axis_x[max_number - 1].r;
396                 } else if (channel == CHANNEL_NAME_GREEN) {
397                         left = axis_x[i].g;
398
399                         if (i < max_number - 1)
400                                 right = axis_x[i + 1].g;
401                         else
402                                 right = axis_x[max_number - 1].g;
403                 } else {
404                         left = axis_x[i].b;
405
406                         if (i < max_number - 1)
407                                 right = axis_x[i + 1].b;
408                         else
409                                 right = axis_x[max_number - 1].b;
410                 }
411
412                 if (dc_fixpt_le(left, hw_point) &&
413                         dc_fixpt_le(hw_point, right)) {
414                         *index_to_start = i;
415                         *index_left = i;
416
417                         if (i < max_number - 1)
418                                 *index_right = i + 1;
419                         else
420                                 *index_right = max_number - 1;
421
422                         *pos = HW_POINT_POSITION_MIDDLE;
423
424                         return true;
425                 } else if ((i == *index_to_start) &&
426                         dc_fixpt_le(hw_point, left)) {
427                         *index_to_start = i;
428                         *index_left = i;
429                         *index_right = i;
430
431                         *pos = HW_POINT_POSITION_LEFT;
432
433                         return true;
434                 } else if ((i == max_number - 1) &&
435                         dc_fixpt_le(right, hw_point)) {
436                         *index_to_start = i;
437                         *index_left = i;
438                         *index_right = i;
439
440                         *pos = HW_POINT_POSITION_RIGHT;
441
442                         return true;
443                 }
444
445                 ++i;
446         }
447
448         return false;
449 }
450
451 static bool build_custom_gamma_mapping_coefficients_worker(
452         const struct dc_gamma *ramp,
453         struct pixel_gamma_point *coeff,
454         const struct hw_x_point *coordinates_x,
455         const struct gamma_pixel *axis_x,
456         enum channel_name channel,
457         uint32_t number_of_points)
458 {
459         uint32_t i = 0;
460
461         while (i <= number_of_points) {
462                 struct fixed31_32 coord_x;
463
464                 uint32_t index_to_start = 0;
465                 uint32_t index_left = 0;
466                 uint32_t index_right = 0;
467
468                 enum hw_point_position hw_pos;
469
470                 struct gamma_point *point;
471
472                 struct fixed31_32 left_pos;
473                 struct fixed31_32 right_pos;
474
475                 if (channel == CHANNEL_NAME_RED)
476                         coord_x = coordinates_x[i].regamma_y_red;
477                 else if (channel == CHANNEL_NAME_GREEN)
478                         coord_x = coordinates_x[i].regamma_y_green;
479                 else
480                         coord_x = coordinates_x[i].regamma_y_blue;
481
482                 if (!find_software_points(
483                         ramp, axis_x, coord_x, channel,
484                         &index_to_start, &index_left, &index_right, &hw_pos)) {
485                         BREAK_TO_DEBUGGER();
486                         return false;
487                 }
488
489                 if (index_left >= ramp->num_entries + 3) {
490                         BREAK_TO_DEBUGGER();
491                         return false;
492                 }
493
494                 if (index_right >= ramp->num_entries + 3) {
495                         BREAK_TO_DEBUGGER();
496                         return false;
497                 }
498
499                 if (channel == CHANNEL_NAME_RED) {
500                         point = &coeff[i].r;
501
502                         left_pos = axis_x[index_left].r;
503                         right_pos = axis_x[index_right].r;
504                 } else if (channel == CHANNEL_NAME_GREEN) {
505                         point = &coeff[i].g;
506
507                         left_pos = axis_x[index_left].g;
508                         right_pos = axis_x[index_right].g;
509                 } else {
510                         point = &coeff[i].b;
511
512                         left_pos = axis_x[index_left].b;
513                         right_pos = axis_x[index_right].b;
514                 }
515
516                 if (hw_pos == HW_POINT_POSITION_MIDDLE)
517                         point->coeff = dc_fixpt_div(
518                                 dc_fixpt_sub(
519                                         coord_x,
520                                         left_pos),
521                                 dc_fixpt_sub(
522                                         right_pos,
523                                         left_pos));
524                 else if (hw_pos == HW_POINT_POSITION_LEFT)
525                         point->coeff = dc_fixpt_zero;
526                 else if (hw_pos == HW_POINT_POSITION_RIGHT)
527                         point->coeff = dc_fixpt_from_int(2);
528                 else {
529                         BREAK_TO_DEBUGGER();
530                         return false;
531                 }
532
533                 point->left_index = index_left;
534                 point->right_index = index_right;
535                 point->pos = hw_pos;
536
537                 ++i;
538         }
539
540         return true;
541 }
542
543 static struct fixed31_32 calculate_mapped_value(
544         struct pwl_float_data *rgb,
545         const struct pixel_gamma_point *coeff,
546         enum channel_name channel,
547         uint32_t max_index)
548 {
549         const struct gamma_point *point;
550
551         struct fixed31_32 result;
552
553         if (channel == CHANNEL_NAME_RED)
554                 point = &coeff->r;
555         else if (channel == CHANNEL_NAME_GREEN)
556                 point = &coeff->g;
557         else
558                 point = &coeff->b;
559
560         if ((point->left_index < 0) || (point->left_index > max_index)) {
561                 BREAK_TO_DEBUGGER();
562                 return dc_fixpt_zero;
563         }
564
565         if ((point->right_index < 0) || (point->right_index > max_index)) {
566                 BREAK_TO_DEBUGGER();
567                 return dc_fixpt_zero;
568         }
569
570         if (point->pos == HW_POINT_POSITION_MIDDLE)
571                 if (channel == CHANNEL_NAME_RED)
572                         result = dc_fixpt_add(
573                                 dc_fixpt_mul(
574                                         point->coeff,
575                                         dc_fixpt_sub(
576                                                 rgb[point->right_index].r,
577                                                 rgb[point->left_index].r)),
578                                 rgb[point->left_index].r);
579                 else if (channel == CHANNEL_NAME_GREEN)
580                         result = dc_fixpt_add(
581                                 dc_fixpt_mul(
582                                         point->coeff,
583                                         dc_fixpt_sub(
584                                                 rgb[point->right_index].g,
585                                                 rgb[point->left_index].g)),
586                                 rgb[point->left_index].g);
587                 else
588                         result = dc_fixpt_add(
589                                 dc_fixpt_mul(
590                                         point->coeff,
591                                         dc_fixpt_sub(
592                                                 rgb[point->right_index].b,
593                                                 rgb[point->left_index].b)),
594                                 rgb[point->left_index].b);
595         else if (point->pos == HW_POINT_POSITION_LEFT) {
596                 BREAK_TO_DEBUGGER();
597                 result = dc_fixpt_zero;
598         } else {
599                 BREAK_TO_DEBUGGER();
600                 result = dc_fixpt_one;
601         }
602
603         return result;
604 }
605
606 static void build_pq(struct pwl_float_data_ex *rgb_regamma,
607                 uint32_t hw_points_num,
608                 const struct hw_x_point *coordinate_x,
609                 uint32_t sdr_white_level)
610 {
611         uint32_t i, start_index;
612
613         struct pwl_float_data_ex *rgb = rgb_regamma;
614         const struct hw_x_point *coord_x = coordinate_x;
615         struct fixed31_32 x;
616         struct fixed31_32 output;
617         struct fixed31_32 scaling_factor =
618                         dc_fixpt_from_fraction(sdr_white_level, 10000);
619
620         if (!pq_initialized && sdr_white_level == 80) {
621                 precompute_pq();
622                 pq_initialized = true;
623         }
624
625         /* TODO: start index is from segment 2^-24, skipping first segment
626          * due to x values too small for power calculations
627          */
628         start_index = 32;
629         rgb += start_index;
630         coord_x += start_index;
631
632         for (i = start_index; i <= hw_points_num; i++) {
633                 /* Multiply 0.008 as regamma is 0-1 and FP16 input is 0-125.
634                  * FP 1.0 = 80nits
635                  */
636                 if (sdr_white_level == 80) {
637                         output = pq_table[i];
638                 } else {
639                         x = dc_fixpt_mul(coord_x->x, scaling_factor);
640                         compute_pq(x, &output);
641                 }
642
643                 /* should really not happen? */
644                 if (dc_fixpt_lt(output, dc_fixpt_zero))
645                         output = dc_fixpt_zero;
646                 else if (dc_fixpt_lt(dc_fixpt_one, output))
647                         output = dc_fixpt_one;
648
649                 rgb->r = output;
650                 rgb->g = output;
651                 rgb->b = output;
652
653                 ++coord_x;
654                 ++rgb;
655         }
656 }
657
658 static void build_de_pq(struct pwl_float_data_ex *de_pq,
659                 uint32_t hw_points_num,
660                 const struct hw_x_point *coordinate_x)
661 {
662         uint32_t i;
663         struct fixed31_32 output;
664
665         struct fixed31_32 scaling_factor = dc_fixpt_from_int(125);
666
667         if (!de_pq_initialized) {
668                 precompute_de_pq();
669                 de_pq_initialized = true;
670         }
671
672
673         for (i = 0; i <= hw_points_num; i++) {
674                 output = de_pq_table[i];
675                 /* should really not happen? */
676                 if (dc_fixpt_lt(output, dc_fixpt_zero))
677                         output = dc_fixpt_zero;
678                 else if (dc_fixpt_lt(scaling_factor, output))
679                         output = scaling_factor;
680                 de_pq[i].r = output;
681                 de_pq[i].g = output;
682                 de_pq[i].b = output;
683         }
684 }
685
686 static void build_regamma(struct pwl_float_data_ex *rgb_regamma,
687                 uint32_t hw_points_num,
688                 const struct hw_x_point *coordinate_x, bool is_2_4)
689 {
690         uint32_t i;
691
692         struct gamma_coefficients coeff;
693         struct pwl_float_data_ex *rgb = rgb_regamma;
694         const struct hw_x_point *coord_x = coordinate_x;
695
696         build_coefficients(&coeff, is_2_4);
697
698         i = 0;
699
700         while (i != hw_points_num + 1) {
701                 /*TODO use y vs r,g,b*/
702                 rgb->r = translate_from_linear_space_ex(
703                         coord_x->x, &coeff, 0);
704                 rgb->g = rgb->r;
705                 rgb->b = rgb->r;
706                 ++coord_x;
707                 ++rgb;
708                 ++i;
709         }
710 }
711
712 static void build_degamma(struct pwl_float_data_ex *curve,
713                 uint32_t hw_points_num,
714                 const struct hw_x_point *coordinate_x, bool is_2_4)
715 {
716         uint32_t i;
717         struct gamma_coefficients coeff;
718         uint32_t begin_index, end_index;
719
720         build_coefficients(&coeff, is_2_4);
721         i = 0;
722
723         /* X points is 2^-25 to 2^7
724          * De-gamma X is 2^-12 to 2^0 â€“ we are skipping first -12-(-25) = 13 regions
725          */
726         begin_index = 13 * NUM_PTS_IN_REGION;
727         end_index = begin_index + 12 * NUM_PTS_IN_REGION;
728
729         while (i != begin_index) {
730                 curve[i].r = dc_fixpt_zero;
731                 curve[i].g = dc_fixpt_zero;
732                 curve[i].b = dc_fixpt_zero;
733                 i++;
734         }
735
736         while (i != end_index) {
737                 curve[i].r = translate_to_linear_space_ex(
738                                 coordinate_x[i].x, &coeff, 0);
739                 curve[i].g = curve[i].r;
740                 curve[i].b = curve[i].r;
741                 i++;
742         }
743         while (i != hw_points_num + 1) {
744                 curve[i].r = dc_fixpt_one;
745                 curve[i].g = dc_fixpt_one;
746                 curve[i].b = dc_fixpt_one;
747                 i++;
748         }
749 }
750
751 static void build_hlg_degamma(struct pwl_float_data_ex *degamma,
752                 uint32_t hw_points_num,
753                 const struct hw_x_point *coordinate_x, bool is_light0_12)
754 {
755         uint32_t i;
756
757         struct pwl_float_data_ex *rgb = degamma;
758         const struct hw_x_point *coord_x = coordinate_x;
759
760         i = 0;
761
762         while (i != hw_points_num + 1) {
763                 compute_hlg_oetf(coord_x->x, is_light0_12, &rgb->r);
764                 rgb->g = rgb->r;
765                 rgb->b = rgb->r;
766                 ++coord_x;
767                 ++rgb;
768                 ++i;
769         }
770 }
771
772 static void build_hlg_regamma(struct pwl_float_data_ex *regamma,
773                 uint32_t hw_points_num,
774                 const struct hw_x_point *coordinate_x, bool is_light0_12)
775 {
776         uint32_t i;
777
778         struct pwl_float_data_ex *rgb = regamma;
779         const struct hw_x_point *coord_x = coordinate_x;
780
781         i = 0;
782
783         while (i != hw_points_num + 1) {
784                 compute_hlg_eotf(coord_x->x, is_light0_12, &rgb->r);
785                 rgb->g = rgb->r;
786                 rgb->b = rgb->r;
787                 ++coord_x;
788                 ++rgb;
789                 ++i;
790         }
791 }
792
793 static void scale_gamma(struct pwl_float_data *pwl_rgb,
794                 const struct dc_gamma *ramp,
795                 struct dividers dividers)
796 {
797         const struct fixed31_32 max_driver = dc_fixpt_from_int(0xFFFF);
798         const struct fixed31_32 max_os = dc_fixpt_from_int(0xFF00);
799         struct fixed31_32 scaler = max_os;
800         uint32_t i;
801         struct pwl_float_data *rgb = pwl_rgb;
802         struct pwl_float_data *rgb_last = rgb + ramp->num_entries - 1;
803
804         i = 0;
805
806         do {
807                 if (dc_fixpt_lt(max_os, ramp->entries.red[i]) ||
808                         dc_fixpt_lt(max_os, ramp->entries.green[i]) ||
809                         dc_fixpt_lt(max_os, ramp->entries.blue[i])) {
810                         scaler = max_driver;
811                         break;
812                 }
813                 ++i;
814         } while (i != ramp->num_entries);
815
816         i = 0;
817
818         do {
819                 rgb->r = dc_fixpt_div(
820                         ramp->entries.red[i], scaler);
821                 rgb->g = dc_fixpt_div(
822                         ramp->entries.green[i], scaler);
823                 rgb->b = dc_fixpt_div(
824                         ramp->entries.blue[i], scaler);
825
826                 ++rgb;
827                 ++i;
828         } while (i != ramp->num_entries);
829
830         rgb->r = dc_fixpt_mul(rgb_last->r,
831                         dividers.divider1);
832         rgb->g = dc_fixpt_mul(rgb_last->g,
833                         dividers.divider1);
834         rgb->b = dc_fixpt_mul(rgb_last->b,
835                         dividers.divider1);
836
837         ++rgb;
838
839         rgb->r = dc_fixpt_mul(rgb_last->r,
840                         dividers.divider2);
841         rgb->g = dc_fixpt_mul(rgb_last->g,
842                         dividers.divider2);
843         rgb->b = dc_fixpt_mul(rgb_last->b,
844                         dividers.divider2);
845
846         ++rgb;
847
848         rgb->r = dc_fixpt_mul(rgb_last->r,
849                         dividers.divider3);
850         rgb->g = dc_fixpt_mul(rgb_last->g,
851                         dividers.divider3);
852         rgb->b = dc_fixpt_mul(rgb_last->b,
853                         dividers.divider3);
854 }
855
856 static void scale_gamma_dx(struct pwl_float_data *pwl_rgb,
857                 const struct dc_gamma *ramp,
858                 struct dividers dividers)
859 {
860         uint32_t i;
861         struct fixed31_32 min = dc_fixpt_zero;
862         struct fixed31_32 max = dc_fixpt_one;
863
864         struct fixed31_32 delta = dc_fixpt_zero;
865         struct fixed31_32 offset = dc_fixpt_zero;
866
867         for (i = 0 ; i < ramp->num_entries; i++) {
868                 if (dc_fixpt_lt(ramp->entries.red[i], min))
869                         min = ramp->entries.red[i];
870
871                 if (dc_fixpt_lt(ramp->entries.green[i], min))
872                         min = ramp->entries.green[i];
873
874                 if (dc_fixpt_lt(ramp->entries.blue[i], min))
875                         min = ramp->entries.blue[i];
876
877                 if (dc_fixpt_lt(max, ramp->entries.red[i]))
878                         max = ramp->entries.red[i];
879
880                 if (dc_fixpt_lt(max, ramp->entries.green[i]))
881                         max = ramp->entries.green[i];
882
883                 if (dc_fixpt_lt(max, ramp->entries.blue[i]))
884                         max = ramp->entries.blue[i];
885         }
886
887         if (dc_fixpt_lt(min, dc_fixpt_zero))
888                 delta = dc_fixpt_neg(min);
889
890         offset = dc_fixpt_add(min, max);
891
892         for (i = 0 ; i < ramp->num_entries; i++) {
893                 pwl_rgb[i].r = dc_fixpt_div(
894                         dc_fixpt_add(
895                                 ramp->entries.red[i], delta), offset);
896                 pwl_rgb[i].g = dc_fixpt_div(
897                         dc_fixpt_add(
898                                 ramp->entries.green[i], delta), offset);
899                 pwl_rgb[i].b = dc_fixpt_div(
900                         dc_fixpt_add(
901                                 ramp->entries.blue[i], delta), offset);
902
903         }
904
905         pwl_rgb[i].r =  dc_fixpt_sub(dc_fixpt_mul_int(
906                                 pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r);
907         pwl_rgb[i].g =  dc_fixpt_sub(dc_fixpt_mul_int(
908                                 pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g);
909         pwl_rgb[i].b =  dc_fixpt_sub(dc_fixpt_mul_int(
910                                 pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b);
911         ++i;
912         pwl_rgb[i].r =  dc_fixpt_sub(dc_fixpt_mul_int(
913                                 pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r);
914         pwl_rgb[i].g =  dc_fixpt_sub(dc_fixpt_mul_int(
915                                 pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g);
916         pwl_rgb[i].b =  dc_fixpt_sub(dc_fixpt_mul_int(
917                                 pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b);
918 }
919
920 /* todo: all these scale_gamma functions are inherently the same but
921  *  take different structures as params or different format for ramp
922  *  values. We could probably implement it in a more generic fashion
923  */
924 static void scale_user_regamma_ramp(struct pwl_float_data *pwl_rgb,
925                 const struct regamma_ramp *ramp,
926                 struct dividers dividers)
927 {
928         unsigned short max_driver = 0xFFFF;
929         unsigned short max_os = 0xFF00;
930         unsigned short scaler = max_os;
931         uint32_t i;
932         struct pwl_float_data *rgb = pwl_rgb;
933         struct pwl_float_data *rgb_last = rgb + GAMMA_RGB_256_ENTRIES - 1;
934
935         i = 0;
936         do {
937                 if (ramp->gamma[i] > max_os ||
938                                 ramp->gamma[i + 256] > max_os ||
939                                 ramp->gamma[i + 512] > max_os) {
940                         scaler = max_driver;
941                         break;
942                 }
943                 i++;
944         } while (i != GAMMA_RGB_256_ENTRIES);
945
946         i = 0;
947         do {
948                 rgb->r = dc_fixpt_from_fraction(
949                                 ramp->gamma[i], scaler);
950                 rgb->g = dc_fixpt_from_fraction(
951                                 ramp->gamma[i + 256], scaler);
952                 rgb->b = dc_fixpt_from_fraction(
953                                 ramp->gamma[i + 512], scaler);
954
955                 ++rgb;
956                 ++i;
957         } while (i != GAMMA_RGB_256_ENTRIES);
958
959         rgb->r = dc_fixpt_mul(rgb_last->r,
960                         dividers.divider1);
961         rgb->g = dc_fixpt_mul(rgb_last->g,
962                         dividers.divider1);
963         rgb->b = dc_fixpt_mul(rgb_last->b,
964                         dividers.divider1);
965
966         ++rgb;
967
968         rgb->r = dc_fixpt_mul(rgb_last->r,
969                         dividers.divider2);
970         rgb->g = dc_fixpt_mul(rgb_last->g,
971                         dividers.divider2);
972         rgb->b = dc_fixpt_mul(rgb_last->b,
973                         dividers.divider2);
974
975         ++rgb;
976
977         rgb->r = dc_fixpt_mul(rgb_last->r,
978                         dividers.divider3);
979         rgb->g = dc_fixpt_mul(rgb_last->g,
980                         dividers.divider3);
981         rgb->b = dc_fixpt_mul(rgb_last->b,
982                         dividers.divider3);
983 }
984
985 /*
986  * RS3+ color transform DDI - 1D LUT adjustment is composed with regamma here
987  * Input is evenly distributed in the output color space as specified in
988  * SetTimings
989  *
990  * Interpolation details:
991  * 1D LUT has 4096 values which give curve correction in 0-1 float range
992  * for evenly spaced points in 0-1 range. lut1D[index] gives correction
993  * for index/4095.
994  * First we find index for which:
995  *      index/4095 < regamma_y < (index+1)/4095 =>
996  *      index < 4095*regamma_y < index + 1
997  * norm_y = 4095*regamma_y, and index is just truncating to nearest integer
998  * lut1 = lut1D[index], lut2 = lut1D[index+1]
999  *
1000  * adjustedY is then linearly interpolating regamma Y between lut1 and lut2
1001  *
1002  * Custom degamma on Linux uses the same interpolation math, so is handled here
1003  */
1004 static void apply_lut_1d(
1005                 const struct dc_gamma *ramp,
1006                 uint32_t num_hw_points,
1007                 struct dc_transfer_func_distributed_points *tf_pts)
1008 {
1009         int i = 0;
1010         int color = 0;
1011         struct fixed31_32 *regamma_y;
1012         struct fixed31_32 norm_y;
1013         struct fixed31_32 lut1;
1014         struct fixed31_32 lut2;
1015         const int max_lut_index = 4095;
1016         const struct fixed31_32 max_lut_index_f =
1017                         dc_fixpt_from_int(max_lut_index);
1018         int32_t index = 0, index_next = 0;
1019         struct fixed31_32 index_f;
1020         struct fixed31_32 delta_lut;
1021         struct fixed31_32 delta_index;
1022
1023         if (ramp->type != GAMMA_CS_TFM_1D && ramp->type != GAMMA_CUSTOM)
1024                 return; // this is not expected
1025
1026         for (i = 0; i < num_hw_points; i++) {
1027                 for (color = 0; color < 3; color++) {
1028                         if (color == 0)
1029                                 regamma_y = &tf_pts->red[i];
1030                         else if (color == 1)
1031                                 regamma_y = &tf_pts->green[i];
1032                         else
1033                                 regamma_y = &tf_pts->blue[i];
1034
1035                         norm_y = dc_fixpt_mul(max_lut_index_f,
1036                                                    *regamma_y);
1037                         index = dc_fixpt_floor(norm_y);
1038                         index_f = dc_fixpt_from_int(index);
1039
1040                         if (index < 0 || index > max_lut_index)
1041                                 continue;
1042
1043                         index_next = (index == max_lut_index) ? index : index+1;
1044
1045                         if (color == 0) {
1046                                 lut1 = ramp->entries.red[index];
1047                                 lut2 = ramp->entries.red[index_next];
1048                         } else if (color == 1) {
1049                                 lut1 = ramp->entries.green[index];
1050                                 lut2 = ramp->entries.green[index_next];
1051                         } else {
1052                                 lut1 = ramp->entries.blue[index];
1053                                 lut2 = ramp->entries.blue[index_next];
1054                         }
1055
1056                         // we have everything now, so interpolate
1057                         delta_lut = dc_fixpt_sub(lut2, lut1);
1058                         delta_index = dc_fixpt_sub(norm_y, index_f);
1059
1060                         *regamma_y = dc_fixpt_add(lut1,
1061                                 dc_fixpt_mul(delta_index, delta_lut));
1062                 }
1063         }
1064 }
1065
1066 static void build_evenly_distributed_points(
1067         struct gamma_pixel *points,
1068         uint32_t numberof_points,
1069         struct dividers dividers)
1070 {
1071         struct gamma_pixel *p = points;
1072         struct gamma_pixel *p_last;
1073
1074         uint32_t i = 0;
1075
1076         // This function should not gets called with 0 as a parameter
1077         ASSERT(numberof_points > 0);
1078         p_last = p + numberof_points - 1;
1079
1080         do {
1081                 struct fixed31_32 value = dc_fixpt_from_fraction(i,
1082                         numberof_points - 1);
1083
1084                 p->r = value;
1085                 p->g = value;
1086                 p->b = value;
1087
1088                 ++p;
1089                 ++i;
1090         } while (i < numberof_points);
1091
1092         p->r = dc_fixpt_div(p_last->r, dividers.divider1);
1093         p->g = dc_fixpt_div(p_last->g, dividers.divider1);
1094         p->b = dc_fixpt_div(p_last->b, dividers.divider1);
1095
1096         ++p;
1097
1098         p->r = dc_fixpt_div(p_last->r, dividers.divider2);
1099         p->g = dc_fixpt_div(p_last->g, dividers.divider2);
1100         p->b = dc_fixpt_div(p_last->b, dividers.divider2);
1101
1102         ++p;
1103
1104         p->r = dc_fixpt_div(p_last->r, dividers.divider3);
1105         p->g = dc_fixpt_div(p_last->g, dividers.divider3);
1106         p->b = dc_fixpt_div(p_last->b, dividers.divider3);
1107 }
1108
1109 static inline void copy_rgb_regamma_to_coordinates_x(
1110                 struct hw_x_point *coordinates_x,
1111                 uint32_t hw_points_num,
1112                 const struct pwl_float_data_ex *rgb_ex)
1113 {
1114         struct hw_x_point *coords = coordinates_x;
1115         uint32_t i = 0;
1116         const struct pwl_float_data_ex *rgb_regamma = rgb_ex;
1117
1118         while (i <= hw_points_num + 1) {
1119                 coords->regamma_y_red = rgb_regamma->r;
1120                 coords->regamma_y_green = rgb_regamma->g;
1121                 coords->regamma_y_blue = rgb_regamma->b;
1122
1123                 ++coords;
1124                 ++rgb_regamma;
1125                 ++i;
1126         }
1127 }
1128
1129 static bool calculate_interpolated_hardware_curve(
1130         const struct dc_gamma *ramp,
1131         struct pixel_gamma_point *coeff128,
1132         struct pwl_float_data *rgb_user,
1133         const struct hw_x_point *coordinates_x,
1134         const struct gamma_pixel *axis_x,
1135         uint32_t number_of_points,
1136         struct dc_transfer_func_distributed_points *tf_pts)
1137 {
1138
1139         const struct pixel_gamma_point *coeff = coeff128;
1140         uint32_t max_entries = 3 - 1;
1141
1142         uint32_t i = 0;
1143
1144         for (i = 0; i < 3; i++) {
1145                 if (!build_custom_gamma_mapping_coefficients_worker(
1146                                 ramp, coeff128, coordinates_x, axis_x, i,
1147                                 number_of_points))
1148                         return false;
1149         }
1150
1151         i = 0;
1152         max_entries += ramp->num_entries;
1153
1154         /* TODO: float point case */
1155
1156         while (i <= number_of_points) {
1157                 tf_pts->red[i] = calculate_mapped_value(
1158                         rgb_user, coeff, CHANNEL_NAME_RED, max_entries);
1159                 tf_pts->green[i] = calculate_mapped_value(
1160                         rgb_user, coeff, CHANNEL_NAME_GREEN, max_entries);
1161                 tf_pts->blue[i] = calculate_mapped_value(
1162                         rgb_user, coeff, CHANNEL_NAME_BLUE, max_entries);
1163
1164                 ++coeff;
1165                 ++i;
1166         }
1167
1168         return true;
1169 }
1170
1171 /* The "old" interpolation uses a complicated scheme to build an array of
1172  * coefficients while also using an array of 0-255 normalized to 0-1
1173  * Then there's another loop using both of the above + new scaled user ramp
1174  * and we concatenate them. It also searches for points of interpolation and
1175  * uses enums for positions.
1176  *
1177  * This function uses a different approach:
1178  * user ramp is always applied on X with 0/255, 1/255, 2/255, ..., 255/255
1179  * To find index for hwX , we notice the following:
1180  * i/255 <= hwX < (i+1)/255  <=> i <= 255*hwX < i+1
1181  * See apply_lut_1d which is the same principle, but on 4K entry 1D LUT
1182  *
1183  * Once the index is known, combined Y is simply:
1184  * user_ramp(index) + (hwX-index/255)*(user_ramp(index+1) - user_ramp(index)
1185  *
1186  * We should switch to this method in all cases, it's simpler and faster
1187  * ToDo one day - for now this only applies to ADL regamma to avoid regression
1188  * for regular use cases (sRGB and PQ)
1189  */
1190 static void interpolate_user_regamma(uint32_t hw_points_num,
1191                 struct pwl_float_data *rgb_user,
1192                 bool apply_degamma,
1193                 struct dc_transfer_func_distributed_points *tf_pts)
1194 {
1195         uint32_t i;
1196         uint32_t color = 0;
1197         int32_t index;
1198         int32_t index_next;
1199         struct fixed31_32 *tf_point;
1200         struct fixed31_32 hw_x;
1201         struct fixed31_32 norm_factor =
1202                         dc_fixpt_from_int(255);
1203         struct fixed31_32 norm_x;
1204         struct fixed31_32 index_f;
1205         struct fixed31_32 lut1;
1206         struct fixed31_32 lut2;
1207         struct fixed31_32 delta_lut;
1208         struct fixed31_32 delta_index;
1209         const struct fixed31_32 one = dc_fixpt_from_int(1);
1210
1211         i = 0;
1212         /* fixed_pt library has problems handling too small values */
1213         while (i != 32) {
1214                 tf_pts->red[i] = dc_fixpt_zero;
1215                 tf_pts->green[i] = dc_fixpt_zero;
1216                 tf_pts->blue[i] = dc_fixpt_zero;
1217                 ++i;
1218         }
1219         while (i <= hw_points_num + 1) {
1220                 for (color = 0; color < 3; color++) {
1221                         if (color == 0)
1222                                 tf_point = &tf_pts->red[i];
1223                         else if (color == 1)
1224                                 tf_point = &tf_pts->green[i];
1225                         else
1226                                 tf_point = &tf_pts->blue[i];
1227
1228                         if (apply_degamma) {
1229                                 if (color == 0)
1230                                         hw_x = coordinates_x[i].regamma_y_red;
1231                                 else if (color == 1)
1232                                         hw_x = coordinates_x[i].regamma_y_green;
1233                                 else
1234                                         hw_x = coordinates_x[i].regamma_y_blue;
1235                         } else
1236                                 hw_x = coordinates_x[i].x;
1237
1238                         if (dc_fixpt_le(one, hw_x))
1239                                 hw_x = one;
1240
1241                         norm_x = dc_fixpt_mul(norm_factor, hw_x);
1242                         index = dc_fixpt_floor(norm_x);
1243                         if (index < 0 || index > 255)
1244                                 continue;
1245
1246                         index_f = dc_fixpt_from_int(index);
1247                         index_next = (index == 255) ? index : index + 1;
1248
1249                         if (color == 0) {
1250                                 lut1 = rgb_user[index].r;
1251                                 lut2 = rgb_user[index_next].r;
1252                         } else if (color == 1) {
1253                                 lut1 = rgb_user[index].g;
1254                                 lut2 = rgb_user[index_next].g;
1255                         } else {
1256                                 lut1 = rgb_user[index].b;
1257                                 lut2 = rgb_user[index_next].b;
1258                         }
1259
1260                         // we have everything now, so interpolate
1261                         delta_lut = dc_fixpt_sub(lut2, lut1);
1262                         delta_index = dc_fixpt_sub(norm_x, index_f);
1263
1264                         *tf_point = dc_fixpt_add(lut1,
1265                                 dc_fixpt_mul(delta_index, delta_lut));
1266                 }
1267                 ++i;
1268         }
1269 }
1270
1271 static void build_new_custom_resulted_curve(
1272         uint32_t hw_points_num,
1273         struct dc_transfer_func_distributed_points *tf_pts)
1274 {
1275         uint32_t i;
1276
1277         i = 0;
1278
1279         while (i != hw_points_num + 1) {
1280                 tf_pts->red[i] = dc_fixpt_clamp(
1281                         tf_pts->red[i], dc_fixpt_zero,
1282                         dc_fixpt_one);
1283                 tf_pts->green[i] = dc_fixpt_clamp(
1284                         tf_pts->green[i], dc_fixpt_zero,
1285                         dc_fixpt_one);
1286                 tf_pts->blue[i] = dc_fixpt_clamp(
1287                         tf_pts->blue[i], dc_fixpt_zero,
1288                         dc_fixpt_one);
1289
1290                 ++i;
1291         }
1292 }
1293
1294 static void apply_degamma_for_user_regamma(struct pwl_float_data_ex *rgb_regamma,
1295                 uint32_t hw_points_num)
1296 {
1297         uint32_t i;
1298
1299         struct gamma_coefficients coeff;
1300         struct pwl_float_data_ex *rgb = rgb_regamma;
1301         const struct hw_x_point *coord_x = coordinates_x;
1302
1303         build_coefficients(&coeff, true);
1304
1305         i = 0;
1306         while (i != hw_points_num + 1) {
1307                 rgb->r = translate_from_linear_space_ex(
1308                                 coord_x->x, &coeff, 0);
1309                 rgb->g = rgb->r;
1310                 rgb->b = rgb->r;
1311                 ++coord_x;
1312                 ++rgb;
1313                 ++i;
1314         }
1315 }
1316
1317 static bool map_regamma_hw_to_x_user(
1318         const struct dc_gamma *ramp,
1319         struct pixel_gamma_point *coeff128,
1320         struct pwl_float_data *rgb_user,
1321         struct hw_x_point *coords_x,
1322         const struct gamma_pixel *axis_x,
1323         const struct pwl_float_data_ex *rgb_regamma,
1324         uint32_t hw_points_num,
1325         struct dc_transfer_func_distributed_points *tf_pts,
1326         bool mapUserRamp)
1327 {
1328         /* setup to spare calculated ideal regamma values */
1329
1330         int i = 0;
1331         struct hw_x_point *coords = coords_x;
1332         const struct pwl_float_data_ex *regamma = rgb_regamma;
1333
1334         if (mapUserRamp) {
1335                 copy_rgb_regamma_to_coordinates_x(coords,
1336                                 hw_points_num,
1337                                 rgb_regamma);
1338
1339                 calculate_interpolated_hardware_curve(
1340                         ramp, coeff128, rgb_user, coords, axis_x,
1341                         hw_points_num, tf_pts);
1342         } else {
1343                 /* just copy current rgb_regamma into  tf_pts */
1344                 while (i <= hw_points_num) {
1345                         tf_pts->red[i] = regamma->r;
1346                         tf_pts->green[i] = regamma->g;
1347                         tf_pts->blue[i] = regamma->b;
1348
1349                         ++regamma;
1350                         ++i;
1351                 }
1352         }
1353
1354         /* this should be named differently, all it does is clamp to 0-1 */
1355         build_new_custom_resulted_curve(hw_points_num, tf_pts);
1356
1357         return true;
1358 }
1359
1360 #define _EXTRA_POINTS 3
1361
1362 bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf,
1363                 const struct dc_gamma *ramp, bool mapUserRamp)
1364 {
1365         struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts;
1366         struct dividers dividers;
1367
1368         struct pwl_float_data *rgb_user = NULL;
1369         struct pwl_float_data_ex *rgb_regamma = NULL;
1370         struct gamma_pixel *axix_x = NULL;
1371         struct pixel_gamma_point *coeff = NULL;
1372         enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB;
1373         bool ret = false;
1374
1375         if (output_tf->type == TF_TYPE_BYPASS)
1376                 return false;
1377
1378         /* we can use hardcoded curve for plain SRGB TF */
1379         if (output_tf->type == TF_TYPE_PREDEFINED &&
1380                         output_tf->tf == TRANSFER_FUNCTION_SRGB &&
1381                         (!mapUserRamp && ramp->type == GAMMA_RGB_256))
1382                 return true;
1383
1384         output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1385
1386         rgb_user = kvcalloc(ramp->num_entries + _EXTRA_POINTS,
1387                             sizeof(*rgb_user),
1388                             GFP_KERNEL);
1389         if (!rgb_user)
1390                 goto rgb_user_alloc_fail;
1391         rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1392                                sizeof(*rgb_regamma),
1393                                GFP_KERNEL);
1394         if (!rgb_regamma)
1395                 goto rgb_regamma_alloc_fail;
1396         axix_x = kvcalloc(ramp->num_entries + 3, sizeof(*axix_x),
1397                           GFP_KERNEL);
1398         if (!axix_x)
1399                 goto axix_x_alloc_fail;
1400         coeff = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*coeff),
1401                          GFP_KERNEL);
1402         if (!coeff)
1403                 goto coeff_alloc_fail;
1404
1405         dividers.divider1 = dc_fixpt_from_fraction(3, 2);
1406         dividers.divider2 = dc_fixpt_from_int(2);
1407         dividers.divider3 = dc_fixpt_from_fraction(5, 2);
1408
1409         tf = output_tf->tf;
1410
1411         build_evenly_distributed_points(
1412                         axix_x,
1413                         ramp->num_entries,
1414                         dividers);
1415
1416         if (ramp->type == GAMMA_RGB_256 && mapUserRamp)
1417                 scale_gamma(rgb_user, ramp, dividers);
1418         else if (ramp->type == GAMMA_RGB_FLOAT_1024)
1419                 scale_gamma_dx(rgb_user, ramp, dividers);
1420
1421         if (tf == TRANSFER_FUNCTION_PQ) {
1422                 tf_pts->end_exponent = 7;
1423                 tf_pts->x_point_at_y1_red = 125;
1424                 tf_pts->x_point_at_y1_green = 125;
1425                 tf_pts->x_point_at_y1_blue = 125;
1426
1427                 build_pq(rgb_regamma,
1428                                 MAX_HW_POINTS,
1429                                 coordinates_x,
1430                                 output_tf->sdr_ref_white_level);
1431         } else {
1432                 tf_pts->end_exponent = 0;
1433                 tf_pts->x_point_at_y1_red = 1;
1434                 tf_pts->x_point_at_y1_green = 1;
1435                 tf_pts->x_point_at_y1_blue = 1;
1436
1437                 build_regamma(rgb_regamma,
1438                                 MAX_HW_POINTS,
1439                                 coordinates_x, tf == TRANSFER_FUNCTION_SRGB ? true:false);
1440         }
1441
1442         map_regamma_hw_to_x_user(ramp, coeff, rgb_user,
1443                         coordinates_x, axix_x, rgb_regamma,
1444                         MAX_HW_POINTS, tf_pts,
1445                         (mapUserRamp || ramp->type != GAMMA_RGB_256) &&
1446                         ramp->type != GAMMA_CS_TFM_1D);
1447
1448         if (ramp->type == GAMMA_CS_TFM_1D)
1449                 apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts);
1450
1451         ret = true;
1452
1453         kvfree(coeff);
1454 coeff_alloc_fail:
1455         kvfree(axix_x);
1456 axix_x_alloc_fail:
1457         kvfree(rgb_regamma);
1458 rgb_regamma_alloc_fail:
1459         kvfree(rgb_user);
1460 rgb_user_alloc_fail:
1461         return ret;
1462 }
1463
1464 bool calculate_user_regamma_coeff(struct dc_transfer_func *output_tf,
1465                 const struct regamma_lut *regamma)
1466 {
1467         struct gamma_coefficients coeff;
1468         const struct hw_x_point *coord_x = coordinates_x;
1469         uint32_t i = 0;
1470
1471         do {
1472                 coeff.a0[i] = dc_fixpt_from_fraction(
1473                                 regamma->coeff.A0[i], 10000000);
1474                 coeff.a1[i] = dc_fixpt_from_fraction(
1475                                 regamma->coeff.A1[i], 1000);
1476                 coeff.a2[i] = dc_fixpt_from_fraction(
1477                                 regamma->coeff.A2[i], 1000);
1478                 coeff.a3[i] = dc_fixpt_from_fraction(
1479                                 regamma->coeff.A3[i], 1000);
1480                 coeff.user_gamma[i] = dc_fixpt_from_fraction(
1481                                 regamma->coeff.gamma[i], 1000);
1482
1483                 ++i;
1484         } while (i != 3);
1485
1486         i = 0;
1487         /* fixed_pt library has problems handling too small values */
1488         while (i != 32) {
1489                 output_tf->tf_pts.red[i] = dc_fixpt_zero;
1490                 output_tf->tf_pts.green[i] = dc_fixpt_zero;
1491                 output_tf->tf_pts.blue[i] = dc_fixpt_zero;
1492                 ++coord_x;
1493                 ++i;
1494         }
1495         while (i != MAX_HW_POINTS + 1) {
1496                 output_tf->tf_pts.red[i] = translate_from_linear_space_ex(
1497                                 coord_x->x, &coeff, 0);
1498                 output_tf->tf_pts.green[i] = translate_from_linear_space_ex(
1499                                 coord_x->x, &coeff, 1);
1500                 output_tf->tf_pts.blue[i] = translate_from_linear_space_ex(
1501                                 coord_x->x, &coeff, 2);
1502                 ++coord_x;
1503                 ++i;
1504         }
1505
1506         // this function just clamps output to 0-1
1507         build_new_custom_resulted_curve(MAX_HW_POINTS, &output_tf->tf_pts);
1508         output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1509
1510         return true;
1511 }
1512
1513 bool calculate_user_regamma_ramp(struct dc_transfer_func *output_tf,
1514                 const struct regamma_lut *regamma)
1515 {
1516         struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts;
1517         struct dividers dividers;
1518
1519         struct pwl_float_data *rgb_user = NULL;
1520         struct pwl_float_data_ex *rgb_regamma = NULL;
1521         bool ret = false;
1522
1523         if (regamma == NULL)
1524                 return false;
1525
1526         output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1527
1528         rgb_user = kcalloc(GAMMA_RGB_256_ENTRIES + _EXTRA_POINTS,
1529                            sizeof(*rgb_user),
1530                            GFP_KERNEL);
1531         if (!rgb_user)
1532                 goto rgb_user_alloc_fail;
1533
1534         rgb_regamma = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1535                               sizeof(*rgb_regamma),
1536                               GFP_KERNEL);
1537         if (!rgb_regamma)
1538                 goto rgb_regamma_alloc_fail;
1539
1540         dividers.divider1 = dc_fixpt_from_fraction(3, 2);
1541         dividers.divider2 = dc_fixpt_from_int(2);
1542         dividers.divider3 = dc_fixpt_from_fraction(5, 2);
1543
1544         scale_user_regamma_ramp(rgb_user, &regamma->ramp, dividers);
1545
1546         if (regamma->flags.bits.applyDegamma == 1) {
1547                 apply_degamma_for_user_regamma(rgb_regamma, MAX_HW_POINTS);
1548                 copy_rgb_regamma_to_coordinates_x(coordinates_x,
1549                                 MAX_HW_POINTS, rgb_regamma);
1550         }
1551
1552         interpolate_user_regamma(MAX_HW_POINTS, rgb_user,
1553                         regamma->flags.bits.applyDegamma, tf_pts);
1554
1555         // no custom HDR curves!
1556         tf_pts->end_exponent = 0;
1557         tf_pts->x_point_at_y1_red = 1;
1558         tf_pts->x_point_at_y1_green = 1;
1559         tf_pts->x_point_at_y1_blue = 1;
1560
1561         // this function just clamps output to 0-1
1562         build_new_custom_resulted_curve(MAX_HW_POINTS, tf_pts);
1563
1564         ret = true;
1565
1566         kfree(rgb_regamma);
1567 rgb_regamma_alloc_fail:
1568         kfree(rgb_user);
1569 rgb_user_alloc_fail:
1570         return ret;
1571 }
1572
1573 bool mod_color_calculate_degamma_params(struct dc_transfer_func *input_tf,
1574                 const struct dc_gamma *ramp, bool mapUserRamp)
1575 {
1576         struct dc_transfer_func_distributed_points *tf_pts = &input_tf->tf_pts;
1577         struct dividers dividers;
1578
1579         struct pwl_float_data *rgb_user = NULL;
1580         struct pwl_float_data_ex *curve = NULL;
1581         struct gamma_pixel *axix_x = NULL;
1582         struct pixel_gamma_point *coeff = NULL;
1583         enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB;
1584         bool ret = false;
1585
1586         if (input_tf->type == TF_TYPE_BYPASS)
1587                 return false;
1588
1589         /* we can use hardcoded curve for plain SRGB TF */
1590         if (input_tf->type == TF_TYPE_PREDEFINED &&
1591                         input_tf->tf == TRANSFER_FUNCTION_SRGB &&
1592                         (!mapUserRamp && ramp->type == GAMMA_RGB_256))
1593                 return true;
1594
1595         input_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1596
1597         rgb_user = kvcalloc(ramp->num_entries + _EXTRA_POINTS,
1598                             sizeof(*rgb_user),
1599                             GFP_KERNEL);
1600         if (!rgb_user)
1601                 goto rgb_user_alloc_fail;
1602         curve = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*curve),
1603                          GFP_KERNEL);
1604         if (!curve)
1605                 goto curve_alloc_fail;
1606         axix_x = kvcalloc(ramp->num_entries + _EXTRA_POINTS, sizeof(*axix_x),
1607                           GFP_KERNEL);
1608         if (!axix_x)
1609                 goto axix_x_alloc_fail;
1610         coeff = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*coeff),
1611                          GFP_KERNEL);
1612         if (!coeff)
1613                 goto coeff_alloc_fail;
1614
1615         dividers.divider1 = dc_fixpt_from_fraction(3, 2);
1616         dividers.divider2 = dc_fixpt_from_int(2);
1617         dividers.divider3 = dc_fixpt_from_fraction(5, 2);
1618
1619         tf = input_tf->tf;
1620
1621         build_evenly_distributed_points(
1622                         axix_x,
1623                         ramp->num_entries,
1624                         dividers);
1625
1626         if (ramp->type == GAMMA_RGB_256 && mapUserRamp)
1627                 scale_gamma(rgb_user, ramp, dividers);
1628         else if (ramp->type == GAMMA_RGB_FLOAT_1024)
1629                 scale_gamma_dx(rgb_user, ramp, dividers);
1630
1631         if (tf == TRANSFER_FUNCTION_PQ)
1632                 build_de_pq(curve,
1633                                 MAX_HW_POINTS,
1634                                 coordinates_x);
1635         else
1636                 build_degamma(curve,
1637                                 MAX_HW_POINTS,
1638                                 coordinates_x,
1639                                 tf == TRANSFER_FUNCTION_SRGB ? true:false);
1640
1641         tf_pts->end_exponent = 0;
1642         tf_pts->x_point_at_y1_red = 1;
1643         tf_pts->x_point_at_y1_green = 1;
1644         tf_pts->x_point_at_y1_blue = 1;
1645
1646         map_regamma_hw_to_x_user(ramp, coeff, rgb_user,
1647                         coordinates_x, axix_x, curve,
1648                         MAX_HW_POINTS, tf_pts,
1649                         mapUserRamp && ramp->type != GAMMA_CUSTOM);
1650         if (ramp->type == GAMMA_CUSTOM)
1651                 apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts);
1652
1653         ret = true;
1654
1655         kvfree(coeff);
1656 coeff_alloc_fail:
1657         kvfree(axix_x);
1658 axix_x_alloc_fail:
1659         kvfree(curve);
1660 curve_alloc_fail:
1661         kvfree(rgb_user);
1662 rgb_user_alloc_fail:
1663
1664         return ret;
1665
1666 }
1667
1668
1669 bool  mod_color_calculate_curve(enum dc_transfer_func_predefined trans,
1670                                 struct dc_transfer_func_distributed_points *points)
1671 {
1672         uint32_t i;
1673         bool ret = false;
1674         struct pwl_float_data_ex *rgb_regamma = NULL;
1675
1676         if (trans == TRANSFER_FUNCTION_UNITY ||
1677                 trans == TRANSFER_FUNCTION_LINEAR) {
1678                 points->end_exponent = 0;
1679                 points->x_point_at_y1_red = 1;
1680                 points->x_point_at_y1_green = 1;
1681                 points->x_point_at_y1_blue = 1;
1682
1683                 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1684                         points->red[i]    = coordinates_x[i].x;
1685                         points->green[i]  = coordinates_x[i].x;
1686                         points->blue[i]   = coordinates_x[i].x;
1687                 }
1688                 ret = true;
1689         } else if (trans == TRANSFER_FUNCTION_PQ) {
1690                 rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1691                                        sizeof(*rgb_regamma),
1692                                        GFP_KERNEL);
1693                 if (!rgb_regamma)
1694                         goto rgb_regamma_alloc_fail;
1695                 points->end_exponent = 7;
1696                 points->x_point_at_y1_red = 125;
1697                 points->x_point_at_y1_green = 125;
1698                 points->x_point_at_y1_blue = 125;
1699
1700
1701                 build_pq(rgb_regamma,
1702                                 MAX_HW_POINTS,
1703                                 coordinates_x,
1704                                 80);
1705                 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1706                         points->red[i]    = rgb_regamma[i].r;
1707                         points->green[i]  = rgb_regamma[i].g;
1708                         points->blue[i]   = rgb_regamma[i].b;
1709                 }
1710                 ret = true;
1711
1712                 kvfree(rgb_regamma);
1713         } else if (trans == TRANSFER_FUNCTION_SRGB ||
1714                           trans == TRANSFER_FUNCTION_BT709) {
1715                 rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1716                                        sizeof(*rgb_regamma),
1717                                        GFP_KERNEL);
1718                 if (!rgb_regamma)
1719                         goto rgb_regamma_alloc_fail;
1720                 points->end_exponent = 0;
1721                 points->x_point_at_y1_red = 1;
1722                 points->x_point_at_y1_green = 1;
1723                 points->x_point_at_y1_blue = 1;
1724
1725                 build_regamma(rgb_regamma,
1726                                 MAX_HW_POINTS,
1727                                 coordinates_x, trans == TRANSFER_FUNCTION_SRGB ? true:false);
1728                 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1729                         points->red[i]    = rgb_regamma[i].r;
1730                         points->green[i]  = rgb_regamma[i].g;
1731                         points->blue[i]   = rgb_regamma[i].b;
1732                 }
1733                 ret = true;
1734
1735                 kvfree(rgb_regamma);
1736         } else if (trans == TRANSFER_FUNCTION_HLG ||
1737                 trans == TRANSFER_FUNCTION_HLG12) {
1738                 rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1739                                        sizeof(*rgb_regamma),
1740                                        GFP_KERNEL);
1741                 if (!rgb_regamma)
1742                         goto rgb_regamma_alloc_fail;
1743
1744                 build_hlg_regamma(rgb_regamma,
1745                                 MAX_HW_POINTS,
1746                                 coordinates_x,
1747                                 trans == TRANSFER_FUNCTION_HLG12 ? true:false);
1748                 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1749                         points->red[i]    = rgb_regamma[i].r;
1750                         points->green[i]  = rgb_regamma[i].g;
1751                         points->blue[i]   = rgb_regamma[i].b;
1752                 }
1753                 ret = true;
1754                 kvfree(rgb_regamma);
1755         }
1756 rgb_regamma_alloc_fail:
1757         return ret;
1758 }
1759
1760
1761 bool  mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans,
1762                                 struct dc_transfer_func_distributed_points *points)
1763 {
1764         uint32_t i;
1765         bool ret = false;
1766         struct pwl_float_data_ex *rgb_degamma = NULL;
1767
1768         if (trans == TRANSFER_FUNCTION_UNITY ||
1769                 trans == TRANSFER_FUNCTION_LINEAR) {
1770
1771                 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1772                         points->red[i]    = coordinates_x[i].x;
1773                         points->green[i]  = coordinates_x[i].x;
1774                         points->blue[i]   = coordinates_x[i].x;
1775                 }
1776                 ret = true;
1777         } else if (trans == TRANSFER_FUNCTION_PQ) {
1778                 rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1779                                        sizeof(*rgb_degamma),
1780                                        GFP_KERNEL);
1781                 if (!rgb_degamma)
1782                         goto rgb_degamma_alloc_fail;
1783
1784
1785                 build_de_pq(rgb_degamma,
1786                                 MAX_HW_POINTS,
1787                                 coordinates_x);
1788                 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1789                         points->red[i]    = rgb_degamma[i].r;
1790                         points->green[i]  = rgb_degamma[i].g;
1791                         points->blue[i]   = rgb_degamma[i].b;
1792                 }
1793                 ret = true;
1794
1795                 kvfree(rgb_degamma);
1796         } else if (trans == TRANSFER_FUNCTION_SRGB ||
1797                           trans == TRANSFER_FUNCTION_BT709) {
1798                 rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1799                                        sizeof(*rgb_degamma),
1800                                        GFP_KERNEL);
1801                 if (!rgb_degamma)
1802                         goto rgb_degamma_alloc_fail;
1803
1804                 build_degamma(rgb_degamma,
1805                                 MAX_HW_POINTS,
1806                                 coordinates_x, trans == TRANSFER_FUNCTION_SRGB ? true:false);
1807                 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1808                         points->red[i]    = rgb_degamma[i].r;
1809                         points->green[i]  = rgb_degamma[i].g;
1810                         points->blue[i]   = rgb_degamma[i].b;
1811                 }
1812                 ret = true;
1813
1814                 kvfree(rgb_degamma);
1815         } else if (trans == TRANSFER_FUNCTION_HLG ||
1816                 trans == TRANSFER_FUNCTION_HLG12) {
1817                 rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1818                                        sizeof(*rgb_degamma),
1819                                        GFP_KERNEL);
1820                 if (!rgb_degamma)
1821                         goto rgb_degamma_alloc_fail;
1822
1823                 build_hlg_degamma(rgb_degamma,
1824                                 MAX_HW_POINTS,
1825                                 coordinates_x,
1826                                 trans == TRANSFER_FUNCTION_HLG12 ? true:false);
1827                 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1828                         points->red[i]    = rgb_degamma[i].r;
1829                         points->green[i]  = rgb_degamma[i].g;
1830                         points->blue[i]   = rgb_degamma[i].b;
1831                 }
1832                 ret = true;
1833                 kvfree(rgb_degamma);
1834         }
1835         points->end_exponent = 0;
1836         points->x_point_at_y1_red = 1;
1837         points->x_point_at_y1_green = 1;
1838         points->x_point_at_y1_blue = 1;
1839
1840 rgb_degamma_alloc_fail:
1841         return ret;
1842 }
1843
1844