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