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