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