GNU Linux-libre 4.19.245-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
1210         i = 0;
1211         /* fixed_pt library has problems handling too small values */
1212         while (i != 32) {
1213                 tf_pts->red[i] = dc_fixpt_zero;
1214                 tf_pts->green[i] = dc_fixpt_zero;
1215                 tf_pts->blue[i] = dc_fixpt_zero;
1216                 ++i;
1217         }
1218         while (i <= hw_points_num + 1) {
1219                 for (color = 0; color < 3; color++) {
1220                         if (color == 0)
1221                                 tf_point = &tf_pts->red[i];
1222                         else if (color == 1)
1223                                 tf_point = &tf_pts->green[i];
1224                         else
1225                                 tf_point = &tf_pts->blue[i];
1226
1227                         if (apply_degamma) {
1228                                 if (color == 0)
1229                                         hw_x = coordinates_x[i].regamma_y_red;
1230                                 else if (color == 1)
1231                                         hw_x = coordinates_x[i].regamma_y_green;
1232                                 else
1233                                         hw_x = coordinates_x[i].regamma_y_blue;
1234                         } else
1235                                 hw_x = coordinates_x[i].x;
1236
1237                         norm_x = dc_fixpt_mul(norm_factor, hw_x);
1238                         index = dc_fixpt_floor(norm_x);
1239                         if (index < 0 || index > 255)
1240                                 continue;
1241
1242                         index_f = dc_fixpt_from_int(index);
1243                         index_next = (index == 255) ? index : index + 1;
1244
1245                         if (color == 0) {
1246                                 lut1 = rgb_user[index].r;
1247                                 lut2 = rgb_user[index_next].r;
1248                         } else if (color == 1) {
1249                                 lut1 = rgb_user[index].g;
1250                                 lut2 = rgb_user[index_next].g;
1251                         } else {
1252                                 lut1 = rgb_user[index].b;
1253                                 lut2 = rgb_user[index_next].b;
1254                         }
1255
1256                         // we have everything now, so interpolate
1257                         delta_lut = dc_fixpt_sub(lut2, lut1);
1258                         delta_index = dc_fixpt_sub(norm_x, index_f);
1259
1260                         *tf_point = dc_fixpt_add(lut1,
1261                                 dc_fixpt_mul(delta_index, delta_lut));
1262                 }
1263                 ++i;
1264         }
1265 }
1266
1267 static void build_new_custom_resulted_curve(
1268         uint32_t hw_points_num,
1269         struct dc_transfer_func_distributed_points *tf_pts)
1270 {
1271         uint32_t i;
1272
1273         i = 0;
1274
1275         while (i != hw_points_num + 1) {
1276                 tf_pts->red[i] = dc_fixpt_clamp(
1277                         tf_pts->red[i], dc_fixpt_zero,
1278                         dc_fixpt_one);
1279                 tf_pts->green[i] = dc_fixpt_clamp(
1280                         tf_pts->green[i], dc_fixpt_zero,
1281                         dc_fixpt_one);
1282                 tf_pts->blue[i] = dc_fixpt_clamp(
1283                         tf_pts->blue[i], dc_fixpt_zero,
1284                         dc_fixpt_one);
1285
1286                 ++i;
1287         }
1288 }
1289
1290 static void apply_degamma_for_user_regamma(struct pwl_float_data_ex *rgb_regamma,
1291                 uint32_t hw_points_num)
1292 {
1293         uint32_t i;
1294
1295         struct gamma_coefficients coeff;
1296         struct pwl_float_data_ex *rgb = rgb_regamma;
1297         const struct hw_x_point *coord_x = coordinates_x;
1298
1299         build_coefficients(&coeff, true);
1300
1301         i = 0;
1302         while (i != hw_points_num + 1) {
1303                 rgb->r = translate_from_linear_space_ex(
1304                                 coord_x->x, &coeff, 0);
1305                 rgb->g = rgb->r;
1306                 rgb->b = rgb->r;
1307                 ++coord_x;
1308                 ++rgb;
1309                 ++i;
1310         }
1311 }
1312
1313 static bool map_regamma_hw_to_x_user(
1314         const struct dc_gamma *ramp,
1315         struct pixel_gamma_point *coeff128,
1316         struct pwl_float_data *rgb_user,
1317         struct hw_x_point *coords_x,
1318         const struct gamma_pixel *axis_x,
1319         const struct pwl_float_data_ex *rgb_regamma,
1320         uint32_t hw_points_num,
1321         struct dc_transfer_func_distributed_points *tf_pts,
1322         bool mapUserRamp)
1323 {
1324         /* setup to spare calculated ideal regamma values */
1325
1326         int i = 0;
1327         struct hw_x_point *coords = coords_x;
1328         const struct pwl_float_data_ex *regamma = rgb_regamma;
1329
1330         if (mapUserRamp) {
1331                 copy_rgb_regamma_to_coordinates_x(coords,
1332                                 hw_points_num,
1333                                 rgb_regamma);
1334
1335                 calculate_interpolated_hardware_curve(
1336                         ramp, coeff128, rgb_user, coords, axis_x,
1337                         hw_points_num, tf_pts);
1338         } else {
1339                 /* just copy current rgb_regamma into  tf_pts */
1340                 while (i <= hw_points_num) {
1341                         tf_pts->red[i] = regamma->r;
1342                         tf_pts->green[i] = regamma->g;
1343                         tf_pts->blue[i] = regamma->b;
1344
1345                         ++regamma;
1346                         ++i;
1347                 }
1348         }
1349
1350         /* this should be named differently, all it does is clamp to 0-1 */
1351         build_new_custom_resulted_curve(hw_points_num, tf_pts);
1352
1353         return true;
1354 }
1355
1356 #define _EXTRA_POINTS 3
1357
1358 bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf,
1359                 const struct dc_gamma *ramp, bool mapUserRamp)
1360 {
1361         struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts;
1362         struct dividers dividers;
1363
1364         struct pwl_float_data *rgb_user = NULL;
1365         struct pwl_float_data_ex *rgb_regamma = NULL;
1366         struct gamma_pixel *axix_x = NULL;
1367         struct pixel_gamma_point *coeff = NULL;
1368         enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB;
1369         bool ret = false;
1370
1371         if (output_tf->type == TF_TYPE_BYPASS)
1372                 return false;
1373
1374         /* we can use hardcoded curve for plain SRGB TF */
1375         if (output_tf->type == TF_TYPE_PREDEFINED &&
1376                         output_tf->tf == TRANSFER_FUNCTION_SRGB &&
1377                         (!mapUserRamp && ramp->type == GAMMA_RGB_256))
1378                 return true;
1379
1380         output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1381
1382         rgb_user = kvcalloc(ramp->num_entries + _EXTRA_POINTS,
1383                             sizeof(*rgb_user),
1384                             GFP_KERNEL);
1385         if (!rgb_user)
1386                 goto rgb_user_alloc_fail;
1387         rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1388                                sizeof(*rgb_regamma),
1389                                GFP_KERNEL);
1390         if (!rgb_regamma)
1391                 goto rgb_regamma_alloc_fail;
1392         axix_x = kvcalloc(ramp->num_entries + 3, sizeof(*axix_x),
1393                           GFP_KERNEL);
1394         if (!axix_x)
1395                 goto axix_x_alloc_fail;
1396         coeff = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*coeff),
1397                          GFP_KERNEL);
1398         if (!coeff)
1399                 goto coeff_alloc_fail;
1400
1401         dividers.divider1 = dc_fixpt_from_fraction(3, 2);
1402         dividers.divider2 = dc_fixpt_from_int(2);
1403         dividers.divider3 = dc_fixpt_from_fraction(5, 2);
1404
1405         tf = output_tf->tf;
1406
1407         build_evenly_distributed_points(
1408                         axix_x,
1409                         ramp->num_entries,
1410                         dividers);
1411
1412         if (ramp->type == GAMMA_RGB_256 && mapUserRamp)
1413                 scale_gamma(rgb_user, ramp, dividers);
1414         else if (ramp->type == GAMMA_RGB_FLOAT_1024)
1415                 scale_gamma_dx(rgb_user, ramp, dividers);
1416
1417         if (tf == TRANSFER_FUNCTION_PQ) {
1418                 tf_pts->end_exponent = 7;
1419                 tf_pts->x_point_at_y1_red = 125;
1420                 tf_pts->x_point_at_y1_green = 125;
1421                 tf_pts->x_point_at_y1_blue = 125;
1422
1423                 build_pq(rgb_regamma,
1424                                 MAX_HW_POINTS,
1425                                 coordinates_x,
1426                                 output_tf->sdr_ref_white_level);
1427         } else {
1428                 tf_pts->end_exponent = 0;
1429                 tf_pts->x_point_at_y1_red = 1;
1430                 tf_pts->x_point_at_y1_green = 1;
1431                 tf_pts->x_point_at_y1_blue = 1;
1432
1433                 build_regamma(rgb_regamma,
1434                                 MAX_HW_POINTS,
1435                                 coordinates_x, tf == TRANSFER_FUNCTION_SRGB ? true:false);
1436         }
1437
1438         map_regamma_hw_to_x_user(ramp, coeff, rgb_user,
1439                         coordinates_x, axix_x, rgb_regamma,
1440                         MAX_HW_POINTS, tf_pts,
1441                         (mapUserRamp || ramp->type != GAMMA_RGB_256) &&
1442                         ramp->type != GAMMA_CS_TFM_1D);
1443
1444         if (ramp->type == GAMMA_CS_TFM_1D)
1445                 apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts);
1446
1447         ret = true;
1448
1449         kvfree(coeff);
1450 coeff_alloc_fail:
1451         kvfree(axix_x);
1452 axix_x_alloc_fail:
1453         kvfree(rgb_regamma);
1454 rgb_regamma_alloc_fail:
1455         kvfree(rgb_user);
1456 rgb_user_alloc_fail:
1457         return ret;
1458 }
1459
1460 bool calculate_user_regamma_coeff(struct dc_transfer_func *output_tf,
1461                 const struct regamma_lut *regamma)
1462 {
1463         struct gamma_coefficients coeff;
1464         const struct hw_x_point *coord_x = coordinates_x;
1465         uint32_t i = 0;
1466
1467         do {
1468                 coeff.a0[i] = dc_fixpt_from_fraction(
1469                                 regamma->coeff.A0[i], 10000000);
1470                 coeff.a1[i] = dc_fixpt_from_fraction(
1471                                 regamma->coeff.A1[i], 1000);
1472                 coeff.a2[i] = dc_fixpt_from_fraction(
1473                                 regamma->coeff.A2[i], 1000);
1474                 coeff.a3[i] = dc_fixpt_from_fraction(
1475                                 regamma->coeff.A3[i], 1000);
1476                 coeff.user_gamma[i] = dc_fixpt_from_fraction(
1477                                 regamma->coeff.gamma[i], 1000);
1478
1479                 ++i;
1480         } while (i != 3);
1481
1482         i = 0;
1483         /* fixed_pt library has problems handling too small values */
1484         while (i != 32) {
1485                 output_tf->tf_pts.red[i] = dc_fixpt_zero;
1486                 output_tf->tf_pts.green[i] = dc_fixpt_zero;
1487                 output_tf->tf_pts.blue[i] = dc_fixpt_zero;
1488                 ++coord_x;
1489                 ++i;
1490         }
1491         while (i != MAX_HW_POINTS + 1) {
1492                 output_tf->tf_pts.red[i] = translate_from_linear_space_ex(
1493                                 coord_x->x, &coeff, 0);
1494                 output_tf->tf_pts.green[i] = translate_from_linear_space_ex(
1495                                 coord_x->x, &coeff, 1);
1496                 output_tf->tf_pts.blue[i] = translate_from_linear_space_ex(
1497                                 coord_x->x, &coeff, 2);
1498                 ++coord_x;
1499                 ++i;
1500         }
1501
1502         // this function just clamps output to 0-1
1503         build_new_custom_resulted_curve(MAX_HW_POINTS, &output_tf->tf_pts);
1504         output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1505
1506         return true;
1507 }
1508
1509 bool calculate_user_regamma_ramp(struct dc_transfer_func *output_tf,
1510                 const struct regamma_lut *regamma)
1511 {
1512         struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts;
1513         struct dividers dividers;
1514
1515         struct pwl_float_data *rgb_user = NULL;
1516         struct pwl_float_data_ex *rgb_regamma = NULL;
1517         bool ret = false;
1518
1519         if (regamma == NULL)
1520                 return false;
1521
1522         output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1523
1524         rgb_user = kcalloc(GAMMA_RGB_256_ENTRIES + _EXTRA_POINTS,
1525                            sizeof(*rgb_user),
1526                            GFP_KERNEL);
1527         if (!rgb_user)
1528                 goto rgb_user_alloc_fail;
1529
1530         rgb_regamma = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1531                               sizeof(*rgb_regamma),
1532                               GFP_KERNEL);
1533         if (!rgb_regamma)
1534                 goto rgb_regamma_alloc_fail;
1535
1536         dividers.divider1 = dc_fixpt_from_fraction(3, 2);
1537         dividers.divider2 = dc_fixpt_from_int(2);
1538         dividers.divider3 = dc_fixpt_from_fraction(5, 2);
1539
1540         scale_user_regamma_ramp(rgb_user, &regamma->ramp, dividers);
1541
1542         if (regamma->flags.bits.applyDegamma == 1) {
1543                 apply_degamma_for_user_regamma(rgb_regamma, MAX_HW_POINTS);
1544                 copy_rgb_regamma_to_coordinates_x(coordinates_x,
1545                                 MAX_HW_POINTS, rgb_regamma);
1546         }
1547
1548         interpolate_user_regamma(MAX_HW_POINTS, rgb_user,
1549                         regamma->flags.bits.applyDegamma, tf_pts);
1550
1551         // no custom HDR curves!
1552         tf_pts->end_exponent = 0;
1553         tf_pts->x_point_at_y1_red = 1;
1554         tf_pts->x_point_at_y1_green = 1;
1555         tf_pts->x_point_at_y1_blue = 1;
1556
1557         // this function just clamps output to 0-1
1558         build_new_custom_resulted_curve(MAX_HW_POINTS, tf_pts);
1559
1560         ret = true;
1561
1562         kfree(rgb_regamma);
1563 rgb_regamma_alloc_fail:
1564         kfree(rgb_user);
1565 rgb_user_alloc_fail:
1566         return ret;
1567 }
1568
1569 bool mod_color_calculate_degamma_params(struct dc_transfer_func *input_tf,
1570                 const struct dc_gamma *ramp, bool mapUserRamp)
1571 {
1572         struct dc_transfer_func_distributed_points *tf_pts = &input_tf->tf_pts;
1573         struct dividers dividers;
1574
1575         struct pwl_float_data *rgb_user = NULL;
1576         struct pwl_float_data_ex *curve = NULL;
1577         struct gamma_pixel *axix_x = NULL;
1578         struct pixel_gamma_point *coeff = NULL;
1579         enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB;
1580         bool ret = false;
1581
1582         if (input_tf->type == TF_TYPE_BYPASS)
1583                 return false;
1584
1585         /* we can use hardcoded curve for plain SRGB TF */
1586         if (input_tf->type == TF_TYPE_PREDEFINED &&
1587                         input_tf->tf == TRANSFER_FUNCTION_SRGB &&
1588                         (!mapUserRamp && ramp->type == GAMMA_RGB_256))
1589                 return true;
1590
1591         input_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1592
1593         rgb_user = kvcalloc(ramp->num_entries + _EXTRA_POINTS,
1594                             sizeof(*rgb_user),
1595                             GFP_KERNEL);
1596         if (!rgb_user)
1597                 goto rgb_user_alloc_fail;
1598         curve = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*curve),
1599                          GFP_KERNEL);
1600         if (!curve)
1601                 goto curve_alloc_fail;
1602         axix_x = kvcalloc(ramp->num_entries + _EXTRA_POINTS, sizeof(*axix_x),
1603                           GFP_KERNEL);
1604         if (!axix_x)
1605                 goto axix_x_alloc_fail;
1606         coeff = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*coeff),
1607                          GFP_KERNEL);
1608         if (!coeff)
1609                 goto coeff_alloc_fail;
1610
1611         dividers.divider1 = dc_fixpt_from_fraction(3, 2);
1612         dividers.divider2 = dc_fixpt_from_int(2);
1613         dividers.divider3 = dc_fixpt_from_fraction(5, 2);
1614
1615         tf = input_tf->tf;
1616
1617         build_evenly_distributed_points(
1618                         axix_x,
1619                         ramp->num_entries,
1620                         dividers);
1621
1622         if (ramp->type == GAMMA_RGB_256 && mapUserRamp)
1623                 scale_gamma(rgb_user, ramp, dividers);
1624         else if (ramp->type == GAMMA_RGB_FLOAT_1024)
1625                 scale_gamma_dx(rgb_user, ramp, dividers);
1626
1627         if (tf == TRANSFER_FUNCTION_PQ)
1628                 build_de_pq(curve,
1629                                 MAX_HW_POINTS,
1630                                 coordinates_x);
1631         else
1632                 build_degamma(curve,
1633                                 MAX_HW_POINTS,
1634                                 coordinates_x,
1635                                 tf == TRANSFER_FUNCTION_SRGB ? true:false);
1636
1637         tf_pts->end_exponent = 0;
1638         tf_pts->x_point_at_y1_red = 1;
1639         tf_pts->x_point_at_y1_green = 1;
1640         tf_pts->x_point_at_y1_blue = 1;
1641
1642         map_regamma_hw_to_x_user(ramp, coeff, rgb_user,
1643                         coordinates_x, axix_x, curve,
1644                         MAX_HW_POINTS, tf_pts,
1645                         mapUserRamp && ramp->type != GAMMA_CUSTOM);
1646         if (ramp->type == GAMMA_CUSTOM)
1647                 apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts);
1648
1649         ret = true;
1650
1651         kvfree(coeff);
1652 coeff_alloc_fail:
1653         kvfree(axix_x);
1654 axix_x_alloc_fail:
1655         kvfree(curve);
1656 curve_alloc_fail:
1657         kvfree(rgb_user);
1658 rgb_user_alloc_fail:
1659
1660         return ret;
1661
1662 }
1663
1664
1665 bool  mod_color_calculate_curve(enum dc_transfer_func_predefined trans,
1666                                 struct dc_transfer_func_distributed_points *points)
1667 {
1668         uint32_t i;
1669         bool ret = false;
1670         struct pwl_float_data_ex *rgb_regamma = NULL;
1671
1672         if (trans == TRANSFER_FUNCTION_UNITY ||
1673                 trans == TRANSFER_FUNCTION_LINEAR) {
1674                 points->end_exponent = 0;
1675                 points->x_point_at_y1_red = 1;
1676                 points->x_point_at_y1_green = 1;
1677                 points->x_point_at_y1_blue = 1;
1678
1679                 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1680                         points->red[i]    = coordinates_x[i].x;
1681                         points->green[i]  = coordinates_x[i].x;
1682                         points->blue[i]   = coordinates_x[i].x;
1683                 }
1684                 ret = true;
1685         } else if (trans == TRANSFER_FUNCTION_PQ) {
1686                 rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1687                                        sizeof(*rgb_regamma),
1688                                        GFP_KERNEL);
1689                 if (!rgb_regamma)
1690                         goto rgb_regamma_alloc_fail;
1691                 points->end_exponent = 7;
1692                 points->x_point_at_y1_red = 125;
1693                 points->x_point_at_y1_green = 125;
1694                 points->x_point_at_y1_blue = 125;
1695
1696
1697                 build_pq(rgb_regamma,
1698                                 MAX_HW_POINTS,
1699                                 coordinates_x,
1700                                 80);
1701                 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1702                         points->red[i]    = rgb_regamma[i].r;
1703                         points->green[i]  = rgb_regamma[i].g;
1704                         points->blue[i]   = rgb_regamma[i].b;
1705                 }
1706                 ret = true;
1707
1708                 kvfree(rgb_regamma);
1709         } else if (trans == TRANSFER_FUNCTION_SRGB ||
1710                           trans == TRANSFER_FUNCTION_BT709) {
1711                 rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1712                                        sizeof(*rgb_regamma),
1713                                        GFP_KERNEL);
1714                 if (!rgb_regamma)
1715                         goto rgb_regamma_alloc_fail;
1716                 points->end_exponent = 0;
1717                 points->x_point_at_y1_red = 1;
1718                 points->x_point_at_y1_green = 1;
1719                 points->x_point_at_y1_blue = 1;
1720
1721                 build_regamma(rgb_regamma,
1722                                 MAX_HW_POINTS,
1723                                 coordinates_x, trans == TRANSFER_FUNCTION_SRGB ? true:false);
1724                 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1725                         points->red[i]    = rgb_regamma[i].r;
1726                         points->green[i]  = rgb_regamma[i].g;
1727                         points->blue[i]   = rgb_regamma[i].b;
1728                 }
1729                 ret = true;
1730
1731                 kvfree(rgb_regamma);
1732         } else if (trans == TRANSFER_FUNCTION_HLG ||
1733                 trans == TRANSFER_FUNCTION_HLG12) {
1734                 rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1735                                        sizeof(*rgb_regamma),
1736                                        GFP_KERNEL);
1737                 if (!rgb_regamma)
1738                         goto rgb_regamma_alloc_fail;
1739
1740                 build_hlg_regamma(rgb_regamma,
1741                                 MAX_HW_POINTS,
1742                                 coordinates_x,
1743                                 trans == TRANSFER_FUNCTION_HLG12 ? true:false);
1744                 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1745                         points->red[i]    = rgb_regamma[i].r;
1746                         points->green[i]  = rgb_regamma[i].g;
1747                         points->blue[i]   = rgb_regamma[i].b;
1748                 }
1749                 ret = true;
1750                 kvfree(rgb_regamma);
1751         }
1752 rgb_regamma_alloc_fail:
1753         return ret;
1754 }
1755
1756
1757 bool  mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans,
1758                                 struct dc_transfer_func_distributed_points *points)
1759 {
1760         uint32_t i;
1761         bool ret = false;
1762         struct pwl_float_data_ex *rgb_degamma = NULL;
1763
1764         if (trans == TRANSFER_FUNCTION_UNITY ||
1765                 trans == TRANSFER_FUNCTION_LINEAR) {
1766
1767                 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1768                         points->red[i]    = coordinates_x[i].x;
1769                         points->green[i]  = coordinates_x[i].x;
1770                         points->blue[i]   = coordinates_x[i].x;
1771                 }
1772                 ret = true;
1773         } else if (trans == TRANSFER_FUNCTION_PQ) {
1774                 rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1775                                        sizeof(*rgb_degamma),
1776                                        GFP_KERNEL);
1777                 if (!rgb_degamma)
1778                         goto rgb_degamma_alloc_fail;
1779
1780
1781                 build_de_pq(rgb_degamma,
1782                                 MAX_HW_POINTS,
1783                                 coordinates_x);
1784                 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1785                         points->red[i]    = rgb_degamma[i].r;
1786                         points->green[i]  = rgb_degamma[i].g;
1787                         points->blue[i]   = rgb_degamma[i].b;
1788                 }
1789                 ret = true;
1790
1791                 kvfree(rgb_degamma);
1792         } else if (trans == TRANSFER_FUNCTION_SRGB ||
1793                           trans == TRANSFER_FUNCTION_BT709) {
1794                 rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1795                                        sizeof(*rgb_degamma),
1796                                        GFP_KERNEL);
1797                 if (!rgb_degamma)
1798                         goto rgb_degamma_alloc_fail;
1799
1800                 build_degamma(rgb_degamma,
1801                                 MAX_HW_POINTS,
1802                                 coordinates_x, trans == TRANSFER_FUNCTION_SRGB ? true:false);
1803                 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1804                         points->red[i]    = rgb_degamma[i].r;
1805                         points->green[i]  = rgb_degamma[i].g;
1806                         points->blue[i]   = rgb_degamma[i].b;
1807                 }
1808                 ret = true;
1809
1810                 kvfree(rgb_degamma);
1811         } else if (trans == TRANSFER_FUNCTION_HLG ||
1812                 trans == TRANSFER_FUNCTION_HLG12) {
1813                 rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1814                                        sizeof(*rgb_degamma),
1815                                        GFP_KERNEL);
1816                 if (!rgb_degamma)
1817                         goto rgb_degamma_alloc_fail;
1818
1819                 build_hlg_degamma(rgb_degamma,
1820                                 MAX_HW_POINTS,
1821                                 coordinates_x,
1822                                 trans == TRANSFER_FUNCTION_HLG12 ? true:false);
1823                 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1824                         points->red[i]    = rgb_degamma[i].r;
1825                         points->green[i]  = rgb_degamma[i].g;
1826                         points->blue[i]   = rgb_degamma[i].b;
1827                 }
1828                 ret = true;
1829                 kvfree(rgb_degamma);
1830         }
1831         points->end_exponent = 0;
1832         points->x_point_at_y1_red = 1;
1833         points->x_point_at_y1_green = 1;
1834         points->x_point_at_y1_blue = 1;
1835
1836 rgb_degamma_alloc_fail:
1837         return ret;
1838 }
1839
1840