2 * Copyright 2016 Advanced Micro Devices, Inc.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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.
27 #include <linux/slab.h>
31 #include "color_gamma.h"
34 #define NUM_PTS_IN_REGION 16
35 #define NUM_REGIONS 32
36 #define MAX_HW_POINTS (NUM_PTS_IN_REGION*NUM_REGIONS)
38 static struct hw_x_point coordinates_x[MAX_HW_POINTS + 2];
40 static struct fixed31_32 pq_table[MAX_HW_POINTS + 2];
41 static struct fixed31_32 de_pq_table[MAX_HW_POINTS + 2];
43 static bool pq_initialized; /* = false; */
44 static bool de_pq_initialized; /* = false; */
46 /* one-time setup of X points */
47 void setup_x_points_distribution(void)
49 struct fixed31_32 region_size = dc_fixpt_from_int(128);
53 struct fixed31_32 increment;
55 coordinates_x[MAX_HW_POINTS].x = region_size;
56 coordinates_x[MAX_HW_POINTS + 1].x = region_size;
58 for (segment = 6; segment > (6 - NUM_REGIONS); segment--) {
59 region_size = dc_fixpt_div_int(region_size, 2);
60 increment = dc_fixpt_div_int(region_size,
62 seg_offset = (segment + (NUM_REGIONS - 7)) * NUM_PTS_IN_REGION;
63 coordinates_x[seg_offset].x = region_size;
65 for (index = seg_offset + 1;
66 index < seg_offset + NUM_PTS_IN_REGION;
68 coordinates_x[index].x = dc_fixpt_add
69 (coordinates_x[index-1].x, increment);
74 static void compute_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y)
76 /* consts for PQ gamma formula. */
77 const struct fixed31_32 m1 =
78 dc_fixpt_from_fraction(159301758, 1000000000);
79 const struct fixed31_32 m2 =
80 dc_fixpt_from_fraction(7884375, 100000);
81 const struct fixed31_32 c1 =
82 dc_fixpt_from_fraction(8359375, 10000000);
83 const struct fixed31_32 c2 =
84 dc_fixpt_from_fraction(188515625, 10000000);
85 const struct fixed31_32 c3 =
86 dc_fixpt_from_fraction(186875, 10000);
88 struct fixed31_32 l_pow_m1;
89 struct fixed31_32 base;
91 if (dc_fixpt_lt(in_x, dc_fixpt_zero))
94 l_pow_m1 = dc_fixpt_pow(in_x, m1);
97 (dc_fixpt_mul(c2, l_pow_m1))),
98 dc_fixpt_add(dc_fixpt_one,
99 (dc_fixpt_mul(c3, l_pow_m1))));
100 *out_y = dc_fixpt_pow(base, m2);
103 static void compute_de_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y)
105 /* consts for dePQ gamma formula. */
106 const struct fixed31_32 m1 =
107 dc_fixpt_from_fraction(159301758, 1000000000);
108 const struct fixed31_32 m2 =
109 dc_fixpt_from_fraction(7884375, 100000);
110 const struct fixed31_32 c1 =
111 dc_fixpt_from_fraction(8359375, 10000000);
112 const struct fixed31_32 c2 =
113 dc_fixpt_from_fraction(188515625, 10000000);
114 const struct fixed31_32 c3 =
115 dc_fixpt_from_fraction(186875, 10000);
117 struct fixed31_32 l_pow_m1;
118 struct fixed31_32 base, div;
121 if (dc_fixpt_lt(in_x, dc_fixpt_zero))
122 in_x = dc_fixpt_zero;
124 l_pow_m1 = dc_fixpt_pow(in_x,
125 dc_fixpt_div(dc_fixpt_one, m2));
126 base = dc_fixpt_sub(l_pow_m1, c1);
128 if (dc_fixpt_lt(base, dc_fixpt_zero))
129 base = dc_fixpt_zero;
131 div = dc_fixpt_sub(c2, dc_fixpt_mul(c3, l_pow_m1));
133 *out_y = dc_fixpt_pow(dc_fixpt_div(base, div),
134 dc_fixpt_div(dc_fixpt_one, m1));
138 /*de gamma, none linear to linear*/
139 static void compute_hlg_oetf(struct fixed31_32 in_x, bool is_light0_12, struct fixed31_32 *out_y)
144 struct fixed31_32 threshold;
145 struct fixed31_32 reference_white_level;
147 a = dc_fixpt_from_fraction(17883277, 100000000);
150 b = dc_fixpt_from_fraction(28466892, 100000000);
151 c = dc_fixpt_from_fraction(55991073, 100000000);
152 threshold = dc_fixpt_one;
153 reference_white_level = dc_fixpt_half;
156 b = dc_fixpt_from_fraction(2372241, 100000000);
157 c = dc_fixpt_add(dc_fixpt_one, dc_fixpt_from_fraction(429347, 100000000));
158 threshold = dc_fixpt_from_fraction(1, 12);
159 reference_white_level = dc_fixpt_pow(dc_fixpt_from_fraction(3, 1), dc_fixpt_half);
161 if (dc_fixpt_lt(threshold, in_x))
162 *out_y = dc_fixpt_add(c, dc_fixpt_mul(a, dc_fixpt_log(dc_fixpt_sub(in_x, b))));
164 *out_y = dc_fixpt_mul(dc_fixpt_pow(in_x, dc_fixpt_half), reference_white_level);
167 /*re gamma, linear to none linear*/
168 static void compute_hlg_eotf(struct fixed31_32 in_x, bool is_light0_12, struct fixed31_32 *out_y)
173 struct fixed31_32 reference_white_level;
175 a = dc_fixpt_from_fraction(17883277, 100000000);
178 b = dc_fixpt_from_fraction(28466892, 100000000);
179 c = dc_fixpt_from_fraction(55991073, 100000000);
180 reference_white_level = dc_fixpt_from_fraction(4, 1);
183 b = dc_fixpt_from_fraction(2372241, 100000000);
184 c = dc_fixpt_add(dc_fixpt_one, dc_fixpt_from_fraction(429347, 100000000));
185 reference_white_level = dc_fixpt_from_fraction(1, 3);
187 if (dc_fixpt_lt(dc_fixpt_half, in_x))
188 *out_y = dc_fixpt_add(dc_fixpt_exp(dc_fixpt_div(dc_fixpt_sub(in_x, c), a)), b);
190 *out_y = dc_fixpt_mul(dc_fixpt_pow(in_x, dc_fixpt_from_fraction(2, 1)), reference_white_level);
194 /* one-time pre-compute PQ values - only for sdr_white_level 80 */
195 void precompute_pq(void)
199 const struct hw_x_point *coord_x = coordinates_x + 32;
200 struct fixed31_32 scaling_factor =
201 dc_fixpt_from_fraction(80, 10000);
203 /* pow function has problems with arguments too small */
204 for (i = 0; i < 32; i++)
205 pq_table[i] = dc_fixpt_zero;
207 for (i = 32; i <= MAX_HW_POINTS; i++) {
208 x = dc_fixpt_mul(coord_x->x, scaling_factor);
209 compute_pq(x, &pq_table[i]);
214 /* one-time pre-compute dePQ values - only for max pixel value 125 FP16 */
215 void precompute_de_pq(void)
219 uint32_t begin_index, end_index;
221 struct fixed31_32 scaling_factor = dc_fixpt_from_int(125);
223 /* X points is 2^-25 to 2^7
224 * De-gamma X is 2^-12 to 2^0 – we are skipping first -12-(-25) = 13 regions
226 begin_index = 13 * NUM_PTS_IN_REGION;
227 end_index = begin_index + 12 * NUM_PTS_IN_REGION;
229 for (i = 0; i <= begin_index; i++)
230 de_pq_table[i] = dc_fixpt_zero;
232 for (; i <= end_index; i++) {
233 compute_de_pq(coordinates_x[i].x, &y);
234 de_pq_table[i] = dc_fixpt_mul(y, scaling_factor);
237 for (; i <= MAX_HW_POINTS; i++)
238 de_pq_table[i] = de_pq_table[i-1];
241 struct fixed31_32 divider1;
242 struct fixed31_32 divider2;
243 struct fixed31_32 divider3;
246 enum gamma_type_index {
247 gamma_type_index_2_4,
248 gamma_type_index_2_2,
249 gamma_type_index_2_2_flat
252 static void build_coefficients(struct gamma_coefficients *coefficients, enum gamma_type_index type)
254 static const int32_t numerator01[] = { 31308, 180000, 0};
255 static const int32_t numerator02[] = { 12920, 4500, 0};
256 static const int32_t numerator03[] = { 55, 99, 0};
257 static const int32_t numerator04[] = { 55, 99, 0};
258 static const int32_t numerator05[] = { 2400, 2200, 2200};
263 if (type == gamma_type_index_2_2)
265 else if (type == gamma_type_index_2_2_flat)
269 coefficients->a0[i] = dc_fixpt_from_fraction(
270 numerator01[index], 10000000);
271 coefficients->a1[i] = dc_fixpt_from_fraction(
272 numerator02[index], 1000);
273 coefficients->a2[i] = dc_fixpt_from_fraction(
274 numerator03[index], 1000);
275 coefficients->a3[i] = dc_fixpt_from_fraction(
276 numerator04[index], 1000);
277 coefficients->user_gamma[i] = dc_fixpt_from_fraction(
278 numerator05[index], 1000);
281 } while (i != ARRAY_SIZE(coefficients->a0));
284 static struct fixed31_32 translate_from_linear_space(
285 struct fixed31_32 arg,
286 struct fixed31_32 a0,
287 struct fixed31_32 a1,
288 struct fixed31_32 a2,
289 struct fixed31_32 a3,
290 struct fixed31_32 gamma)
292 const struct fixed31_32 one = dc_fixpt_from_int(1);
294 if (dc_fixpt_lt(one, arg))
297 if (dc_fixpt_le(arg, dc_fixpt_neg(a0)))
306 dc_fixpt_recip(gamma))));
307 else if (dc_fixpt_le(a0, arg))
315 dc_fixpt_recip(gamma))),
323 static struct fixed31_32 calculate_gamma22(struct fixed31_32 arg)
325 struct fixed31_32 gamma = dc_fixpt_from_fraction(22, 10);
327 return translate_from_linear_space(arg,
335 static struct fixed31_32 translate_to_linear_space(
336 struct fixed31_32 arg,
337 struct fixed31_32 a0,
338 struct fixed31_32 a1,
339 struct fixed31_32 a2,
340 struct fixed31_32 a3,
341 struct fixed31_32 gamma)
343 struct fixed31_32 linear;
345 a0 = dc_fixpt_mul(a0, a1);
346 if (dc_fixpt_le(arg, dc_fixpt_neg(a0)))
348 linear = dc_fixpt_neg(
351 dc_fixpt_sub(a2, arg),
353 dc_fixpt_one, a3)), gamma));
355 else if (dc_fixpt_le(dc_fixpt_neg(a0), arg) &&
356 dc_fixpt_le(arg, a0))
357 linear = dc_fixpt_div(arg, a1);
359 linear = dc_fixpt_pow(
361 dc_fixpt_add(a2, arg),
363 dc_fixpt_one, a3)), gamma);
368 static inline struct fixed31_32 translate_from_linear_space_ex(
369 struct fixed31_32 arg,
370 struct gamma_coefficients *coeff,
371 uint32_t color_index)
373 return translate_from_linear_space(
375 coeff->a0[color_index],
376 coeff->a1[color_index],
377 coeff->a2[color_index],
378 coeff->a3[color_index],
379 coeff->user_gamma[color_index]);
383 static inline struct fixed31_32 translate_to_linear_space_ex(
384 struct fixed31_32 arg,
385 struct gamma_coefficients *coeff,
386 uint32_t color_index)
388 return translate_to_linear_space(
390 coeff->a0[color_index],
391 coeff->a1[color_index],
392 coeff->a2[color_index],
393 coeff->a3[color_index],
394 coeff->user_gamma[color_index]);
398 static bool find_software_points(
399 const struct dc_gamma *ramp,
400 const struct gamma_pixel *axis_x,
401 struct fixed31_32 hw_point,
402 enum channel_name channel,
403 uint32_t *index_to_start,
404 uint32_t *index_left,
405 uint32_t *index_right,
406 enum hw_point_position *pos)
408 const uint32_t max_number = ramp->num_entries + 3;
410 struct fixed31_32 left, right;
412 uint32_t i = *index_to_start;
414 while (i < max_number) {
415 if (channel == CHANNEL_NAME_RED) {
418 if (i < max_number - 1)
419 right = axis_x[i + 1].r;
421 right = axis_x[max_number - 1].r;
422 } else if (channel == CHANNEL_NAME_GREEN) {
425 if (i < max_number - 1)
426 right = axis_x[i + 1].g;
428 right = axis_x[max_number - 1].g;
432 if (i < max_number - 1)
433 right = axis_x[i + 1].b;
435 right = axis_x[max_number - 1].b;
438 if (dc_fixpt_le(left, hw_point) &&
439 dc_fixpt_le(hw_point, right)) {
443 if (i < max_number - 1)
444 *index_right = i + 1;
446 *index_right = max_number - 1;
448 *pos = HW_POINT_POSITION_MIDDLE;
451 } else if ((i == *index_to_start) &&
452 dc_fixpt_le(hw_point, left)) {
457 *pos = HW_POINT_POSITION_LEFT;
460 } else if ((i == max_number - 1) &&
461 dc_fixpt_le(right, hw_point)) {
466 *pos = HW_POINT_POSITION_RIGHT;
477 static bool build_custom_gamma_mapping_coefficients_worker(
478 const struct dc_gamma *ramp,
479 struct pixel_gamma_point *coeff,
480 const struct hw_x_point *coordinates_x,
481 const struct gamma_pixel *axis_x,
482 enum channel_name channel,
483 uint32_t number_of_points)
487 while (i <= number_of_points) {
488 struct fixed31_32 coord_x;
490 uint32_t index_to_start = 0;
491 uint32_t index_left = 0;
492 uint32_t index_right = 0;
494 enum hw_point_position hw_pos;
496 struct gamma_point *point;
498 struct fixed31_32 left_pos;
499 struct fixed31_32 right_pos;
501 if (channel == CHANNEL_NAME_RED)
502 coord_x = coordinates_x[i].regamma_y_red;
503 else if (channel == CHANNEL_NAME_GREEN)
504 coord_x = coordinates_x[i].regamma_y_green;
506 coord_x = coordinates_x[i].regamma_y_blue;
508 if (!find_software_points(
509 ramp, axis_x, coord_x, channel,
510 &index_to_start, &index_left, &index_right, &hw_pos)) {
515 if (index_left >= ramp->num_entries + 3) {
520 if (index_right >= ramp->num_entries + 3) {
525 if (channel == CHANNEL_NAME_RED) {
528 left_pos = axis_x[index_left].r;
529 right_pos = axis_x[index_right].r;
530 } else if (channel == CHANNEL_NAME_GREEN) {
533 left_pos = axis_x[index_left].g;
534 right_pos = axis_x[index_right].g;
538 left_pos = axis_x[index_left].b;
539 right_pos = axis_x[index_right].b;
542 if (hw_pos == HW_POINT_POSITION_MIDDLE)
543 point->coeff = dc_fixpt_div(
550 else if (hw_pos == HW_POINT_POSITION_LEFT)
551 point->coeff = dc_fixpt_zero;
552 else if (hw_pos == HW_POINT_POSITION_RIGHT)
553 point->coeff = dc_fixpt_from_int(2);
559 point->left_index = index_left;
560 point->right_index = index_right;
569 static struct fixed31_32 calculate_mapped_value(
570 struct pwl_float_data *rgb,
571 const struct pixel_gamma_point *coeff,
572 enum channel_name channel,
575 const struct gamma_point *point;
577 struct fixed31_32 result;
579 if (channel == CHANNEL_NAME_RED)
581 else if (channel == CHANNEL_NAME_GREEN)
586 if ((point->left_index < 0) || (point->left_index > max_index)) {
588 return dc_fixpt_zero;
591 if ((point->right_index < 0) || (point->right_index > max_index)) {
593 return dc_fixpt_zero;
596 if (point->pos == HW_POINT_POSITION_MIDDLE)
597 if (channel == CHANNEL_NAME_RED)
598 result = dc_fixpt_add(
602 rgb[point->right_index].r,
603 rgb[point->left_index].r)),
604 rgb[point->left_index].r);
605 else if (channel == CHANNEL_NAME_GREEN)
606 result = dc_fixpt_add(
610 rgb[point->right_index].g,
611 rgb[point->left_index].g)),
612 rgb[point->left_index].g);
614 result = dc_fixpt_add(
618 rgb[point->right_index].b,
619 rgb[point->left_index].b)),
620 rgb[point->left_index].b);
621 else if (point->pos == HW_POINT_POSITION_LEFT) {
623 result = dc_fixpt_zero;
626 result = dc_fixpt_one;
632 static void build_pq(struct pwl_float_data_ex *rgb_regamma,
633 uint32_t hw_points_num,
634 const struct hw_x_point *coordinate_x,
635 uint32_t sdr_white_level)
637 uint32_t i, start_index;
639 struct pwl_float_data_ex *rgb = rgb_regamma;
640 const struct hw_x_point *coord_x = coordinate_x;
642 struct fixed31_32 output;
643 struct fixed31_32 scaling_factor =
644 dc_fixpt_from_fraction(sdr_white_level, 10000);
646 if (!pq_initialized && sdr_white_level == 80) {
648 pq_initialized = true;
651 /* TODO: start index is from segment 2^-24, skipping first segment
652 * due to x values too small for power calculations
656 coord_x += start_index;
658 for (i = start_index; i <= hw_points_num; i++) {
659 /* Multiply 0.008 as regamma is 0-1 and FP16 input is 0-125.
662 if (sdr_white_level == 80) {
663 output = pq_table[i];
665 x = dc_fixpt_mul(coord_x->x, scaling_factor);
666 compute_pq(x, &output);
669 /* should really not happen? */
670 if (dc_fixpt_lt(output, dc_fixpt_zero))
671 output = dc_fixpt_zero;
672 else if (dc_fixpt_lt(dc_fixpt_one, output))
673 output = dc_fixpt_one;
684 static void build_de_pq(struct pwl_float_data_ex *de_pq,
685 uint32_t hw_points_num,
686 const struct hw_x_point *coordinate_x)
689 struct fixed31_32 output;
691 struct fixed31_32 scaling_factor = dc_fixpt_from_int(125);
693 if (!de_pq_initialized) {
695 de_pq_initialized = true;
699 for (i = 0; i <= hw_points_num; i++) {
700 output = de_pq_table[i];
701 /* should really not happen? */
702 if (dc_fixpt_lt(output, dc_fixpt_zero))
703 output = dc_fixpt_zero;
704 else if (dc_fixpt_lt(scaling_factor, output))
705 output = scaling_factor;
712 static void build_regamma(struct pwl_float_data_ex *rgb_regamma,
713 uint32_t hw_points_num,
714 const struct hw_x_point *coordinate_x, enum gamma_type_index type)
718 struct gamma_coefficients coeff;
719 struct pwl_float_data_ex *rgb = rgb_regamma;
720 const struct hw_x_point *coord_x = coordinate_x;
722 build_coefficients(&coeff, type);
726 while (i != hw_points_num + 1) {
727 /*TODO use y vs r,g,b*/
728 rgb->r = translate_from_linear_space_ex(
729 coord_x->x, &coeff, 0);
738 static void hermite_spline_eetf(struct fixed31_32 input_x,
739 struct fixed31_32 max_display,
740 struct fixed31_32 min_display,
741 struct fixed31_32 max_content,
742 struct fixed31_32 *out_x)
744 struct fixed31_32 min_lum_pq;
745 struct fixed31_32 max_lum_pq;
746 struct fixed31_32 max_content_pq;
747 struct fixed31_32 ks;
748 struct fixed31_32 E1;
749 struct fixed31_32 E2;
750 struct fixed31_32 E3;
752 struct fixed31_32 t2;
753 struct fixed31_32 t3;
754 struct fixed31_32 two;
755 struct fixed31_32 three;
756 struct fixed31_32 temp1;
757 struct fixed31_32 temp2;
758 struct fixed31_32 a = dc_fixpt_from_fraction(15, 10);
759 struct fixed31_32 b = dc_fixpt_from_fraction(5, 10);
760 struct fixed31_32 epsilon = dc_fixpt_from_fraction(1, 1000000); // dc_fixpt_epsilon is a bit too small
762 if (dc_fixpt_eq(max_content, dc_fixpt_zero)) {
763 *out_x = dc_fixpt_zero;
767 compute_pq(input_x, &E1);
768 compute_pq(dc_fixpt_div(min_display, max_content), &min_lum_pq);
769 compute_pq(dc_fixpt_div(max_display, max_content), &max_lum_pq);
770 compute_pq(dc_fixpt_one, &max_content_pq); // always 1? DAL2 code is weird
771 a = dc_fixpt_div(dc_fixpt_add(dc_fixpt_one, b), max_content_pq); // (1+b)/maxContent
772 ks = dc_fixpt_sub(dc_fixpt_mul(a, max_lum_pq), b); // a * max_lum_pq - b
774 if (dc_fixpt_lt(E1, ks))
776 else if (dc_fixpt_le(ks, E1) && dc_fixpt_le(E1, dc_fixpt_one)) {
777 if (dc_fixpt_lt(epsilon, dc_fixpt_sub(dc_fixpt_one, ks)))
778 // t = (E1 - ks) / (1 - ks)
779 t = dc_fixpt_div(dc_fixpt_sub(E1, ks),
780 dc_fixpt_sub(dc_fixpt_one, ks));
784 two = dc_fixpt_from_int(2);
785 three = dc_fixpt_from_int(3);
787 t2 = dc_fixpt_mul(t, t);
788 t3 = dc_fixpt_mul(t2, t);
789 temp1 = dc_fixpt_mul(two, t3);
790 temp2 = dc_fixpt_mul(three, t2);
792 // (2t^3 - 3t^2 + 1) * ks
793 E2 = dc_fixpt_mul(ks, dc_fixpt_add(dc_fixpt_one,
794 dc_fixpt_sub(temp1, temp2)));
796 // (-2t^3 + 3t^2) * max_lum_pq
797 E2 = dc_fixpt_add(E2, dc_fixpt_mul(max_lum_pq,
798 dc_fixpt_sub(temp2, temp1)));
800 temp1 = dc_fixpt_mul(two, t2);
801 temp2 = dc_fixpt_sub(dc_fixpt_one, ks);
803 // (t^3 - 2t^2 + t) * (1-ks)
804 E2 = dc_fixpt_add(E2, dc_fixpt_mul(temp2,
805 dc_fixpt_add(t, dc_fixpt_sub(t3, temp1))));
809 temp1 = dc_fixpt_sub(dc_fixpt_one, E2);
810 temp2 = dc_fixpt_mul(temp1, temp1);
811 temp2 = dc_fixpt_mul(temp2, temp2);
814 E3 = dc_fixpt_add(E2, dc_fixpt_mul(min_lum_pq, temp2));
815 compute_de_pq(E3, out_x);
817 *out_x = dc_fixpt_div(*out_x, dc_fixpt_div(max_display, max_content));
820 static bool build_freesync_hdr(struct pwl_float_data_ex *rgb_regamma,
821 uint32_t hw_points_num,
822 const struct hw_x_point *coordinate_x,
823 const struct freesync_hdr_tf_params *fs_params)
826 struct pwl_float_data_ex *rgb = rgb_regamma;
827 const struct hw_x_point *coord_x = coordinate_x;
828 struct fixed31_32 scaledX = dc_fixpt_zero;
829 struct fixed31_32 scaledX1 = dc_fixpt_zero;
830 struct fixed31_32 max_display;
831 struct fixed31_32 min_display;
832 struct fixed31_32 max_content;
833 struct fixed31_32 min_content;
834 struct fixed31_32 clip = dc_fixpt_one;
835 struct fixed31_32 output;
836 bool use_eetf = false;
837 bool is_clipped = false;
838 struct fixed31_32 sdr_white_level;
840 if (fs_params->max_content == 0 ||
841 fs_params->max_display == 0)
844 max_display = dc_fixpt_from_int(fs_params->max_display);
845 min_display = dc_fixpt_from_fraction(fs_params->min_display, 10000);
846 max_content = dc_fixpt_from_int(fs_params->max_content);
847 min_content = dc_fixpt_from_fraction(fs_params->min_content, 10000);
848 sdr_white_level = dc_fixpt_from_int(fs_params->sdr_white_level);
850 if (fs_params->min_display > 1000) // cap at 0.1 at the bottom
851 min_display = dc_fixpt_from_fraction(1, 10);
852 if (fs_params->max_display < 100) // cap at 100 at the top
853 max_display = dc_fixpt_from_int(100);
855 if (fs_params->min_content < fs_params->min_display)
858 min_content = min_display;
860 if (fs_params->max_content > fs_params->max_display)
863 max_content = max_display;
865 rgb += 32; // first 32 points have problems with fixed point, too small
867 for (i = 32; i <= hw_points_num; i++) {
870 /*max content is equal 1 */
871 scaledX1 = dc_fixpt_div(coord_x->x,
872 dc_fixpt_div(max_content, sdr_white_level));
873 hermite_spline_eetf(scaledX1, max_display, min_display,
874 max_content, &scaledX);
876 scaledX = dc_fixpt_div(coord_x->x,
877 dc_fixpt_div(max_display, sdr_white_level));
879 if (dc_fixpt_lt(scaledX, clip)) {
880 if (dc_fixpt_lt(scaledX, dc_fixpt_zero))
881 output = dc_fixpt_zero;
883 output = calculate_gamma22(scaledX);
907 static void build_degamma(struct pwl_float_data_ex *curve,
908 uint32_t hw_points_num,
909 const struct hw_x_point *coordinate_x, enum gamma_type_index type)
912 struct gamma_coefficients coeff;
913 uint32_t begin_index, end_index;
915 build_coefficients(&coeff, type);
918 /* X points is 2^-25 to 2^7
919 * De-gamma X is 2^-12 to 2^0 – we are skipping first -12-(-25) = 13 regions
921 begin_index = 13 * NUM_PTS_IN_REGION;
922 end_index = begin_index + 12 * NUM_PTS_IN_REGION;
924 while (i != begin_index) {
925 curve[i].r = dc_fixpt_zero;
926 curve[i].g = dc_fixpt_zero;
927 curve[i].b = dc_fixpt_zero;
931 while (i != end_index) {
932 curve[i].r = translate_to_linear_space_ex(
933 coordinate_x[i].x, &coeff, 0);
934 curve[i].g = curve[i].r;
935 curve[i].b = curve[i].r;
938 while (i != hw_points_num + 1) {
939 curve[i].r = dc_fixpt_one;
940 curve[i].g = dc_fixpt_one;
941 curve[i].b = dc_fixpt_one;
946 static void build_hlg_degamma(struct pwl_float_data_ex *degamma,
947 uint32_t hw_points_num,
948 const struct hw_x_point *coordinate_x, bool is_light0_12)
952 struct pwl_float_data_ex *rgb = degamma;
953 const struct hw_x_point *coord_x = coordinate_x;
957 while (i != hw_points_num + 1) {
958 compute_hlg_oetf(coord_x->x, is_light0_12, &rgb->r);
967 static void build_hlg_regamma(struct pwl_float_data_ex *regamma,
968 uint32_t hw_points_num,
969 const struct hw_x_point *coordinate_x, bool is_light0_12)
973 struct pwl_float_data_ex *rgb = regamma;
974 const struct hw_x_point *coord_x = coordinate_x;
978 while (i != hw_points_num + 1) {
979 compute_hlg_eotf(coord_x->x, is_light0_12, &rgb->r);
988 static void scale_gamma(struct pwl_float_data *pwl_rgb,
989 const struct dc_gamma *ramp,
990 struct dividers dividers)
992 const struct fixed31_32 max_driver = dc_fixpt_from_int(0xFFFF);
993 const struct fixed31_32 max_os = dc_fixpt_from_int(0xFF00);
994 struct fixed31_32 scaler = max_os;
996 struct pwl_float_data *rgb = pwl_rgb;
997 struct pwl_float_data *rgb_last = rgb + ramp->num_entries - 1;
1002 if (dc_fixpt_lt(max_os, ramp->entries.red[i]) ||
1003 dc_fixpt_lt(max_os, ramp->entries.green[i]) ||
1004 dc_fixpt_lt(max_os, ramp->entries.blue[i])) {
1005 scaler = max_driver;
1009 } while (i != ramp->num_entries);
1014 rgb->r = dc_fixpt_div(
1015 ramp->entries.red[i], scaler);
1016 rgb->g = dc_fixpt_div(
1017 ramp->entries.green[i], scaler);
1018 rgb->b = dc_fixpt_div(
1019 ramp->entries.blue[i], scaler);
1023 } while (i != ramp->num_entries);
1025 rgb->r = dc_fixpt_mul(rgb_last->r,
1027 rgb->g = dc_fixpt_mul(rgb_last->g,
1029 rgb->b = dc_fixpt_mul(rgb_last->b,
1034 rgb->r = dc_fixpt_mul(rgb_last->r,
1036 rgb->g = dc_fixpt_mul(rgb_last->g,
1038 rgb->b = dc_fixpt_mul(rgb_last->b,
1043 rgb->r = dc_fixpt_mul(rgb_last->r,
1045 rgb->g = dc_fixpt_mul(rgb_last->g,
1047 rgb->b = dc_fixpt_mul(rgb_last->b,
1051 static void scale_gamma_dx(struct pwl_float_data *pwl_rgb,
1052 const struct dc_gamma *ramp,
1053 struct dividers dividers)
1056 struct fixed31_32 min = dc_fixpt_zero;
1057 struct fixed31_32 max = dc_fixpt_one;
1059 struct fixed31_32 delta = dc_fixpt_zero;
1060 struct fixed31_32 offset = dc_fixpt_zero;
1062 for (i = 0 ; i < ramp->num_entries; i++) {
1063 if (dc_fixpt_lt(ramp->entries.red[i], min))
1064 min = ramp->entries.red[i];
1066 if (dc_fixpt_lt(ramp->entries.green[i], min))
1067 min = ramp->entries.green[i];
1069 if (dc_fixpt_lt(ramp->entries.blue[i], min))
1070 min = ramp->entries.blue[i];
1072 if (dc_fixpt_lt(max, ramp->entries.red[i]))
1073 max = ramp->entries.red[i];
1075 if (dc_fixpt_lt(max, ramp->entries.green[i]))
1076 max = ramp->entries.green[i];
1078 if (dc_fixpt_lt(max, ramp->entries.blue[i]))
1079 max = ramp->entries.blue[i];
1082 if (dc_fixpt_lt(min, dc_fixpt_zero))
1083 delta = dc_fixpt_neg(min);
1085 offset = dc_fixpt_add(min, max);
1087 for (i = 0 ; i < ramp->num_entries; i++) {
1088 pwl_rgb[i].r = dc_fixpt_div(
1090 ramp->entries.red[i], delta), offset);
1091 pwl_rgb[i].g = dc_fixpt_div(
1093 ramp->entries.green[i], delta), offset);
1094 pwl_rgb[i].b = dc_fixpt_div(
1096 ramp->entries.blue[i], delta), offset);
1100 pwl_rgb[i].r = dc_fixpt_sub(dc_fixpt_mul_int(
1101 pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r);
1102 pwl_rgb[i].g = dc_fixpt_sub(dc_fixpt_mul_int(
1103 pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g);
1104 pwl_rgb[i].b = dc_fixpt_sub(dc_fixpt_mul_int(
1105 pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b);
1107 pwl_rgb[i].r = dc_fixpt_sub(dc_fixpt_mul_int(
1108 pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r);
1109 pwl_rgb[i].g = dc_fixpt_sub(dc_fixpt_mul_int(
1110 pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g);
1111 pwl_rgb[i].b = dc_fixpt_sub(dc_fixpt_mul_int(
1112 pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b);
1115 /* todo: all these scale_gamma functions are inherently the same but
1116 * take different structures as params or different format for ramp
1117 * values. We could probably implement it in a more generic fashion
1119 static void scale_user_regamma_ramp(struct pwl_float_data *pwl_rgb,
1120 const struct regamma_ramp *ramp,
1121 struct dividers dividers)
1123 unsigned short max_driver = 0xFFFF;
1124 unsigned short max_os = 0xFF00;
1125 unsigned short scaler = max_os;
1127 struct pwl_float_data *rgb = pwl_rgb;
1128 struct pwl_float_data *rgb_last = rgb + GAMMA_RGB_256_ENTRIES - 1;
1132 if (ramp->gamma[i] > max_os ||
1133 ramp->gamma[i + 256] > max_os ||
1134 ramp->gamma[i + 512] > max_os) {
1135 scaler = max_driver;
1139 } while (i != GAMMA_RGB_256_ENTRIES);
1143 rgb->r = dc_fixpt_from_fraction(
1144 ramp->gamma[i], scaler);
1145 rgb->g = dc_fixpt_from_fraction(
1146 ramp->gamma[i + 256], scaler);
1147 rgb->b = dc_fixpt_from_fraction(
1148 ramp->gamma[i + 512], scaler);
1152 } while (i != GAMMA_RGB_256_ENTRIES);
1154 rgb->r = dc_fixpt_mul(rgb_last->r,
1156 rgb->g = dc_fixpt_mul(rgb_last->g,
1158 rgb->b = dc_fixpt_mul(rgb_last->b,
1163 rgb->r = dc_fixpt_mul(rgb_last->r,
1165 rgb->g = dc_fixpt_mul(rgb_last->g,
1167 rgb->b = dc_fixpt_mul(rgb_last->b,
1172 rgb->r = dc_fixpt_mul(rgb_last->r,
1174 rgb->g = dc_fixpt_mul(rgb_last->g,
1176 rgb->b = dc_fixpt_mul(rgb_last->b,
1181 * RS3+ color transform DDI - 1D LUT adjustment is composed with regamma here
1182 * Input is evenly distributed in the output color space as specified in
1185 * Interpolation details:
1186 * 1D LUT has 4096 values which give curve correction in 0-1 float range
1187 * for evenly spaced points in 0-1 range. lut1D[index] gives correction
1189 * First we find index for which:
1190 * index/4095 < regamma_y < (index+1)/4095 =>
1191 * index < 4095*regamma_y < index + 1
1192 * norm_y = 4095*regamma_y, and index is just truncating to nearest integer
1193 * lut1 = lut1D[index], lut2 = lut1D[index+1]
1195 * adjustedY is then linearly interpolating regamma Y between lut1 and lut2
1197 * Custom degamma on Linux uses the same interpolation math, so is handled here
1199 static void apply_lut_1d(
1200 const struct dc_gamma *ramp,
1201 uint32_t num_hw_points,
1202 struct dc_transfer_func_distributed_points *tf_pts)
1206 struct fixed31_32 *regamma_y;
1207 struct fixed31_32 norm_y;
1208 struct fixed31_32 lut1;
1209 struct fixed31_32 lut2;
1210 const int max_lut_index = 4095;
1211 const struct fixed31_32 max_lut_index_f =
1212 dc_fixpt_from_int(max_lut_index);
1213 int32_t index = 0, index_next = 0;
1214 struct fixed31_32 index_f;
1215 struct fixed31_32 delta_lut;
1216 struct fixed31_32 delta_index;
1218 if (ramp->type != GAMMA_CS_TFM_1D && ramp->type != GAMMA_CUSTOM)
1219 return; // this is not expected
1221 for (i = 0; i < num_hw_points; i++) {
1222 for (color = 0; color < 3; color++) {
1224 regamma_y = &tf_pts->red[i];
1225 else if (color == 1)
1226 regamma_y = &tf_pts->green[i];
1228 regamma_y = &tf_pts->blue[i];
1230 norm_y = dc_fixpt_mul(max_lut_index_f,
1232 index = dc_fixpt_floor(norm_y);
1233 index_f = dc_fixpt_from_int(index);
1235 if (index < 0 || index > max_lut_index)
1238 index_next = (index == max_lut_index) ? index : index+1;
1241 lut1 = ramp->entries.red[index];
1242 lut2 = ramp->entries.red[index_next];
1243 } else if (color == 1) {
1244 lut1 = ramp->entries.green[index];
1245 lut2 = ramp->entries.green[index_next];
1247 lut1 = ramp->entries.blue[index];
1248 lut2 = ramp->entries.blue[index_next];
1251 // we have everything now, so interpolate
1252 delta_lut = dc_fixpt_sub(lut2, lut1);
1253 delta_index = dc_fixpt_sub(norm_y, index_f);
1255 *regamma_y = dc_fixpt_add(lut1,
1256 dc_fixpt_mul(delta_index, delta_lut));
1261 static void build_evenly_distributed_points(
1262 struct gamma_pixel *points,
1263 uint32_t numberof_points,
1264 struct dividers dividers)
1266 struct gamma_pixel *p = points;
1267 struct gamma_pixel *p_last;
1271 // This function should not gets called with 0 as a parameter
1272 ASSERT(numberof_points > 0);
1273 p_last = p + numberof_points - 1;
1276 struct fixed31_32 value = dc_fixpt_from_fraction(i,
1277 numberof_points - 1);
1285 } while (i < numberof_points);
1287 p->r = dc_fixpt_div(p_last->r, dividers.divider1);
1288 p->g = dc_fixpt_div(p_last->g, dividers.divider1);
1289 p->b = dc_fixpt_div(p_last->b, dividers.divider1);
1293 p->r = dc_fixpt_div(p_last->r, dividers.divider2);
1294 p->g = dc_fixpt_div(p_last->g, dividers.divider2);
1295 p->b = dc_fixpt_div(p_last->b, dividers.divider2);
1299 p->r = dc_fixpt_div(p_last->r, dividers.divider3);
1300 p->g = dc_fixpt_div(p_last->g, dividers.divider3);
1301 p->b = dc_fixpt_div(p_last->b, dividers.divider3);
1304 static inline void copy_rgb_regamma_to_coordinates_x(
1305 struct hw_x_point *coordinates_x,
1306 uint32_t hw_points_num,
1307 const struct pwl_float_data_ex *rgb_ex)
1309 struct hw_x_point *coords = coordinates_x;
1311 const struct pwl_float_data_ex *rgb_regamma = rgb_ex;
1313 while (i <= hw_points_num + 1) {
1314 coords->regamma_y_red = rgb_regamma->r;
1315 coords->regamma_y_green = rgb_regamma->g;
1316 coords->regamma_y_blue = rgb_regamma->b;
1324 static bool calculate_interpolated_hardware_curve(
1325 const struct dc_gamma *ramp,
1326 struct pixel_gamma_point *coeff128,
1327 struct pwl_float_data *rgb_user,
1328 const struct hw_x_point *coordinates_x,
1329 const struct gamma_pixel *axis_x,
1330 uint32_t number_of_points,
1331 struct dc_transfer_func_distributed_points *tf_pts)
1334 const struct pixel_gamma_point *coeff = coeff128;
1335 uint32_t max_entries = 3 - 1;
1339 for (i = 0; i < 3; i++) {
1340 if (!build_custom_gamma_mapping_coefficients_worker(
1341 ramp, coeff128, coordinates_x, axis_x, i,
1347 max_entries += ramp->num_entries;
1349 /* TODO: float point case */
1351 while (i <= number_of_points) {
1352 tf_pts->red[i] = calculate_mapped_value(
1353 rgb_user, coeff, CHANNEL_NAME_RED, max_entries);
1354 tf_pts->green[i] = calculate_mapped_value(
1355 rgb_user, coeff, CHANNEL_NAME_GREEN, max_entries);
1356 tf_pts->blue[i] = calculate_mapped_value(
1357 rgb_user, coeff, CHANNEL_NAME_BLUE, max_entries);
1366 /* The "old" interpolation uses a complicated scheme to build an array of
1367 * coefficients while also using an array of 0-255 normalized to 0-1
1368 * Then there's another loop using both of the above + new scaled user ramp
1369 * and we concatenate them. It also searches for points of interpolation and
1370 * uses enums for positions.
1372 * This function uses a different approach:
1373 * user ramp is always applied on X with 0/255, 1/255, 2/255, ..., 255/255
1374 * To find index for hwX , we notice the following:
1375 * i/255 <= hwX < (i+1)/255 <=> i <= 255*hwX < i+1
1376 * See apply_lut_1d which is the same principle, but on 4K entry 1D LUT
1378 * Once the index is known, combined Y is simply:
1379 * user_ramp(index) + (hwX-index/255)*(user_ramp(index+1) - user_ramp(index)
1381 * We should switch to this method in all cases, it's simpler and faster
1382 * ToDo one day - for now this only applies to ADL regamma to avoid regression
1383 * for regular use cases (sRGB and PQ)
1385 static void interpolate_user_regamma(uint32_t hw_points_num,
1386 struct pwl_float_data *rgb_user,
1388 struct dc_transfer_func_distributed_points *tf_pts)
1394 struct fixed31_32 *tf_point;
1395 struct fixed31_32 hw_x;
1396 struct fixed31_32 norm_factor =
1397 dc_fixpt_from_int(255);
1398 struct fixed31_32 norm_x;
1399 struct fixed31_32 index_f;
1400 struct fixed31_32 lut1;
1401 struct fixed31_32 lut2;
1402 struct fixed31_32 delta_lut;
1403 struct fixed31_32 delta_index;
1406 /* fixed_pt library has problems handling too small values */
1408 tf_pts->red[i] = dc_fixpt_zero;
1409 tf_pts->green[i] = dc_fixpt_zero;
1410 tf_pts->blue[i] = dc_fixpt_zero;
1413 while (i <= hw_points_num + 1) {
1414 for (color = 0; color < 3; color++) {
1416 tf_point = &tf_pts->red[i];
1417 else if (color == 1)
1418 tf_point = &tf_pts->green[i];
1420 tf_point = &tf_pts->blue[i];
1422 if (apply_degamma) {
1424 hw_x = coordinates_x[i].regamma_y_red;
1425 else if (color == 1)
1426 hw_x = coordinates_x[i].regamma_y_green;
1428 hw_x = coordinates_x[i].regamma_y_blue;
1430 hw_x = coordinates_x[i].x;
1432 norm_x = dc_fixpt_mul(norm_factor, hw_x);
1433 index = dc_fixpt_floor(norm_x);
1434 if (index < 0 || index > 255)
1437 index_f = dc_fixpt_from_int(index);
1438 index_next = (index == 255) ? index : index + 1;
1441 lut1 = rgb_user[index].r;
1442 lut2 = rgb_user[index_next].r;
1443 } else if (color == 1) {
1444 lut1 = rgb_user[index].g;
1445 lut2 = rgb_user[index_next].g;
1447 lut1 = rgb_user[index].b;
1448 lut2 = rgb_user[index_next].b;
1451 // we have everything now, so interpolate
1452 delta_lut = dc_fixpt_sub(lut2, lut1);
1453 delta_index = dc_fixpt_sub(norm_x, index_f);
1455 *tf_point = dc_fixpt_add(lut1,
1456 dc_fixpt_mul(delta_index, delta_lut));
1462 static void build_new_custom_resulted_curve(
1463 uint32_t hw_points_num,
1464 struct dc_transfer_func_distributed_points *tf_pts)
1470 while (i != hw_points_num + 1) {
1471 tf_pts->red[i] = dc_fixpt_clamp(
1472 tf_pts->red[i], dc_fixpt_zero,
1474 tf_pts->green[i] = dc_fixpt_clamp(
1475 tf_pts->green[i], dc_fixpt_zero,
1477 tf_pts->blue[i] = dc_fixpt_clamp(
1478 tf_pts->blue[i], dc_fixpt_zero,
1485 static void apply_degamma_for_user_regamma(struct pwl_float_data_ex *rgb_regamma,
1486 uint32_t hw_points_num)
1490 struct gamma_coefficients coeff;
1491 struct pwl_float_data_ex *rgb = rgb_regamma;
1492 const struct hw_x_point *coord_x = coordinates_x;
1494 build_coefficients(&coeff, true);
1497 while (i != hw_points_num + 1) {
1498 rgb->r = translate_from_linear_space_ex(
1499 coord_x->x, &coeff, 0);
1508 static bool map_regamma_hw_to_x_user(
1509 const struct dc_gamma *ramp,
1510 struct pixel_gamma_point *coeff128,
1511 struct pwl_float_data *rgb_user,
1512 struct hw_x_point *coords_x,
1513 const struct gamma_pixel *axis_x,
1514 const struct pwl_float_data_ex *rgb_regamma,
1515 uint32_t hw_points_num,
1516 struct dc_transfer_func_distributed_points *tf_pts,
1519 /* setup to spare calculated ideal regamma values */
1522 struct hw_x_point *coords = coords_x;
1523 const struct pwl_float_data_ex *regamma = rgb_regamma;
1525 if (ramp && mapUserRamp) {
1526 copy_rgb_regamma_to_coordinates_x(coords,
1530 calculate_interpolated_hardware_curve(
1531 ramp, coeff128, rgb_user, coords, axis_x,
1532 hw_points_num, tf_pts);
1534 /* just copy current rgb_regamma into tf_pts */
1535 while (i <= hw_points_num) {
1536 tf_pts->red[i] = regamma->r;
1537 tf_pts->green[i] = regamma->g;
1538 tf_pts->blue[i] = regamma->b;
1545 /* this should be named differently, all it does is clamp to 0-1 */
1546 build_new_custom_resulted_curve(hw_points_num, tf_pts);
1551 #define _EXTRA_POINTS 3
1553 bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf,
1554 const struct dc_gamma *ramp, bool mapUserRamp, bool canRomBeUsed,
1555 const struct freesync_hdr_tf_params *fs_params)
1557 struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts;
1558 struct dividers dividers;
1560 struct pwl_float_data *rgb_user = NULL;
1561 struct pwl_float_data_ex *rgb_regamma = NULL;
1562 struct gamma_pixel *axis_x = NULL;
1563 struct pixel_gamma_point *coeff = NULL;
1564 enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB;
1567 if (output_tf->type == TF_TYPE_BYPASS)
1570 /* we can use hardcoded curve for plain SRGB TF */
1571 if (output_tf->type == TF_TYPE_PREDEFINED && canRomBeUsed == true &&
1572 output_tf->tf == TRANSFER_FUNCTION_SRGB) {
1575 if ((ramp->is_logical_identity) ||
1576 (!mapUserRamp && ramp->type == GAMMA_RGB_256))
1580 output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1582 if (ramp && (mapUserRamp || ramp->type != GAMMA_RGB_256)) {
1583 rgb_user = kvcalloc(ramp->num_entries + _EXTRA_POINTS,
1587 goto rgb_user_alloc_fail;
1589 axis_x = kvcalloc(ramp->num_entries + 3, sizeof(*axis_x),
1592 goto axis_x_alloc_fail;
1594 dividers.divider1 = dc_fixpt_from_fraction(3, 2);
1595 dividers.divider2 = dc_fixpt_from_int(2);
1596 dividers.divider3 = dc_fixpt_from_fraction(5, 2);
1598 build_evenly_distributed_points(
1603 if (ramp->type == GAMMA_RGB_256 && mapUserRamp)
1604 scale_gamma(rgb_user, ramp, dividers);
1605 else if (ramp->type == GAMMA_RGB_FLOAT_1024)
1606 scale_gamma_dx(rgb_user, ramp, dividers);
1609 rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1610 sizeof(*rgb_regamma),
1613 goto rgb_regamma_alloc_fail;
1615 coeff = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*coeff),
1618 goto coeff_alloc_fail;
1621 if (tf == TRANSFER_FUNCTION_PQ) {
1622 tf_pts->end_exponent = 7;
1623 tf_pts->x_point_at_y1_red = 125;
1624 tf_pts->x_point_at_y1_green = 125;
1625 tf_pts->x_point_at_y1_blue = 125;
1627 build_pq(rgb_regamma,
1630 output_tf->sdr_ref_white_level);
1631 } else if (tf == TRANSFER_FUNCTION_GAMMA22 &&
1632 fs_params != NULL && fs_params->skip_tm == 0) {
1633 build_freesync_hdr(rgb_regamma,
1638 tf_pts->end_exponent = 0;
1639 tf_pts->x_point_at_y1_red = 1;
1640 tf_pts->x_point_at_y1_green = 1;
1641 tf_pts->x_point_at_y1_blue = 1;
1643 build_regamma(rgb_regamma,
1645 coordinates_x, tf == TRANSFER_FUNCTION_SRGB ? gamma_type_index_2_4 :
1646 tf == TRANSFER_FUNCTION_GAMMA22 ?
1647 gamma_type_index_2_2_flat : gamma_type_index_2_2);
1649 map_regamma_hw_to_x_user(ramp, coeff, rgb_user,
1650 coordinates_x, axis_x, rgb_regamma,
1651 MAX_HW_POINTS, tf_pts,
1652 (mapUserRamp || (ramp && ramp->type != GAMMA_RGB_256)) &&
1653 (ramp && ramp->type != GAMMA_CS_TFM_1D));
1655 if (ramp && ramp->type == GAMMA_CS_TFM_1D)
1656 apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts);
1662 kvfree(rgb_regamma);
1663 rgb_regamma_alloc_fail:
1667 rgb_user_alloc_fail:
1671 bool calculate_user_regamma_coeff(struct dc_transfer_func *output_tf,
1672 const struct regamma_lut *regamma)
1674 struct gamma_coefficients coeff;
1675 const struct hw_x_point *coord_x = coordinates_x;
1679 coeff.a0[i] = dc_fixpt_from_fraction(
1680 regamma->coeff.A0[i], 10000000);
1681 coeff.a1[i] = dc_fixpt_from_fraction(
1682 regamma->coeff.A1[i], 1000);
1683 coeff.a2[i] = dc_fixpt_from_fraction(
1684 regamma->coeff.A2[i], 1000);
1685 coeff.a3[i] = dc_fixpt_from_fraction(
1686 regamma->coeff.A3[i], 1000);
1687 coeff.user_gamma[i] = dc_fixpt_from_fraction(
1688 regamma->coeff.gamma[i], 1000);
1694 /* fixed_pt library has problems handling too small values */
1696 output_tf->tf_pts.red[i] = dc_fixpt_zero;
1697 output_tf->tf_pts.green[i] = dc_fixpt_zero;
1698 output_tf->tf_pts.blue[i] = dc_fixpt_zero;
1702 while (i != MAX_HW_POINTS + 1) {
1703 output_tf->tf_pts.red[i] = translate_from_linear_space_ex(
1704 coord_x->x, &coeff, 0);
1705 output_tf->tf_pts.green[i] = translate_from_linear_space_ex(
1706 coord_x->x, &coeff, 1);
1707 output_tf->tf_pts.blue[i] = translate_from_linear_space_ex(
1708 coord_x->x, &coeff, 2);
1713 // this function just clamps output to 0-1
1714 build_new_custom_resulted_curve(MAX_HW_POINTS, &output_tf->tf_pts);
1715 output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1720 bool calculate_user_regamma_ramp(struct dc_transfer_func *output_tf,
1721 const struct regamma_lut *regamma)
1723 struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts;
1724 struct dividers dividers;
1726 struct pwl_float_data *rgb_user = NULL;
1727 struct pwl_float_data_ex *rgb_regamma = NULL;
1730 if (regamma == NULL)
1733 output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1735 rgb_user = kcalloc(GAMMA_RGB_256_ENTRIES + _EXTRA_POINTS,
1739 goto rgb_user_alloc_fail;
1741 rgb_regamma = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1742 sizeof(*rgb_regamma),
1745 goto rgb_regamma_alloc_fail;
1747 dividers.divider1 = dc_fixpt_from_fraction(3, 2);
1748 dividers.divider2 = dc_fixpt_from_int(2);
1749 dividers.divider3 = dc_fixpt_from_fraction(5, 2);
1751 scale_user_regamma_ramp(rgb_user, ®amma->ramp, dividers);
1753 if (regamma->flags.bits.applyDegamma == 1) {
1754 apply_degamma_for_user_regamma(rgb_regamma, MAX_HW_POINTS);
1755 copy_rgb_regamma_to_coordinates_x(coordinates_x,
1756 MAX_HW_POINTS, rgb_regamma);
1759 interpolate_user_regamma(MAX_HW_POINTS, rgb_user,
1760 regamma->flags.bits.applyDegamma, tf_pts);
1762 // no custom HDR curves!
1763 tf_pts->end_exponent = 0;
1764 tf_pts->x_point_at_y1_red = 1;
1765 tf_pts->x_point_at_y1_green = 1;
1766 tf_pts->x_point_at_y1_blue = 1;
1768 // this function just clamps output to 0-1
1769 build_new_custom_resulted_curve(MAX_HW_POINTS, tf_pts);
1774 rgb_regamma_alloc_fail:
1776 rgb_user_alloc_fail:
1780 bool mod_color_calculate_degamma_params(struct dc_transfer_func *input_tf,
1781 const struct dc_gamma *ramp, bool mapUserRamp)
1783 struct dc_transfer_func_distributed_points *tf_pts = &input_tf->tf_pts;
1784 struct dividers dividers;
1785 struct pwl_float_data *rgb_user = NULL;
1786 struct pwl_float_data_ex *curve = NULL;
1787 struct gamma_pixel *axis_x = NULL;
1788 struct pixel_gamma_point *coeff = NULL;
1789 enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB;
1793 if (input_tf->type == TF_TYPE_BYPASS)
1796 /* we can use hardcoded curve for plain SRGB TF
1797 * If linear, it's bypass if on user ramp
1799 if (input_tf->type == TF_TYPE_PREDEFINED &&
1800 (input_tf->tf == TRANSFER_FUNCTION_SRGB ||
1801 input_tf->tf == TRANSFER_FUNCTION_LINEAR) &&
1805 input_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1807 if (mapUserRamp && ramp && ramp->type == GAMMA_RGB_256) {
1808 rgb_user = kvcalloc(ramp->num_entries + _EXTRA_POINTS,
1812 goto rgb_user_alloc_fail;
1814 axis_x = kvcalloc(ramp->num_entries + _EXTRA_POINTS, sizeof(*axis_x),
1817 goto axis_x_alloc_fail;
1819 dividers.divider1 = dc_fixpt_from_fraction(3, 2);
1820 dividers.divider2 = dc_fixpt_from_int(2);
1821 dividers.divider3 = dc_fixpt_from_fraction(5, 2);
1823 build_evenly_distributed_points(
1828 scale_gamma(rgb_user, ramp, dividers);
1831 curve = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*curve),
1834 goto curve_alloc_fail;
1836 coeff = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*coeff),
1839 goto coeff_alloc_fail;
1843 if (tf == TRANSFER_FUNCTION_PQ)
1847 else if (tf == TRANSFER_FUNCTION_SRGB ||
1848 tf == TRANSFER_FUNCTION_BT709)
1849 build_degamma(curve,
1852 tf == TRANSFER_FUNCTION_SRGB ?
1853 gamma_type_index_2_4 : tf == TRANSFER_FUNCTION_GAMMA22 ?
1854 gamma_type_index_2_2_flat : gamma_type_index_2_2);
1855 else if (tf == TRANSFER_FUNCTION_LINEAR) {
1856 // just copy coordinates_x into curve
1858 while (i != MAX_HW_POINTS + 1) {
1859 curve[i].r = coordinates_x[i].x;
1860 curve[i].g = curve[i].r;
1861 curve[i].b = curve[i].r;
1865 goto invalid_tf_fail;
1867 tf_pts->end_exponent = 0;
1868 tf_pts->x_point_at_y1_red = 1;
1869 tf_pts->x_point_at_y1_green = 1;
1870 tf_pts->x_point_at_y1_blue = 1;
1872 map_regamma_hw_to_x_user(ramp, coeff, rgb_user,
1873 coordinates_x, axis_x, curve,
1874 MAX_HW_POINTS, tf_pts,
1875 mapUserRamp && ramp && ramp->type == GAMMA_RGB_256);
1876 if (ramp->type == GAMMA_CUSTOM)
1877 apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts);
1889 rgb_user_alloc_fail:
1895 bool mod_color_calculate_curve(enum dc_transfer_func_predefined trans,
1896 struct dc_transfer_func_distributed_points *points,
1897 uint32_t sdr_ref_white_level)
1901 struct pwl_float_data_ex *rgb_regamma = NULL;
1903 if (trans == TRANSFER_FUNCTION_UNITY ||
1904 trans == TRANSFER_FUNCTION_LINEAR) {
1905 points->end_exponent = 0;
1906 points->x_point_at_y1_red = 1;
1907 points->x_point_at_y1_green = 1;
1908 points->x_point_at_y1_blue = 1;
1910 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1911 points->red[i] = coordinates_x[i].x;
1912 points->green[i] = coordinates_x[i].x;
1913 points->blue[i] = coordinates_x[i].x;
1916 } else if (trans == TRANSFER_FUNCTION_PQ) {
1917 rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1918 sizeof(*rgb_regamma),
1921 goto rgb_regamma_alloc_fail;
1922 points->end_exponent = 7;
1923 points->x_point_at_y1_red = 125;
1924 points->x_point_at_y1_green = 125;
1925 points->x_point_at_y1_blue = 125;
1928 build_pq(rgb_regamma,
1931 sdr_ref_white_level);
1932 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1933 points->red[i] = rgb_regamma[i].r;
1934 points->green[i] = rgb_regamma[i].g;
1935 points->blue[i] = rgb_regamma[i].b;
1939 kvfree(rgb_regamma);
1940 } else if (trans == TRANSFER_FUNCTION_SRGB ||
1941 trans == TRANSFER_FUNCTION_BT709) {
1942 rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1943 sizeof(*rgb_regamma),
1946 goto rgb_regamma_alloc_fail;
1947 points->end_exponent = 0;
1948 points->x_point_at_y1_red = 1;
1949 points->x_point_at_y1_green = 1;
1950 points->x_point_at_y1_blue = 1;
1952 build_regamma(rgb_regamma,
1955 trans == TRANSFER_FUNCTION_SRGB ?
1956 gamma_type_index_2_4 : trans == TRANSFER_FUNCTION_GAMMA22 ?
1957 gamma_type_index_2_2_flat : gamma_type_index_2_2);
1958 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1959 points->red[i] = rgb_regamma[i].r;
1960 points->green[i] = rgb_regamma[i].g;
1961 points->blue[i] = rgb_regamma[i].b;
1965 kvfree(rgb_regamma);
1966 } else if (trans == TRANSFER_FUNCTION_HLG ||
1967 trans == TRANSFER_FUNCTION_HLG12) {
1968 rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1969 sizeof(*rgb_regamma),
1972 goto rgb_regamma_alloc_fail;
1974 build_hlg_regamma(rgb_regamma,
1977 trans == TRANSFER_FUNCTION_HLG12 ? true:false);
1978 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1979 points->red[i] = rgb_regamma[i].r;
1980 points->green[i] = rgb_regamma[i].g;
1981 points->blue[i] = rgb_regamma[i].b;
1984 kvfree(rgb_regamma);
1986 rgb_regamma_alloc_fail:
1991 bool mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans,
1992 struct dc_transfer_func_distributed_points *points)
1996 struct pwl_float_data_ex *rgb_degamma = NULL;
1998 if (trans == TRANSFER_FUNCTION_UNITY ||
1999 trans == TRANSFER_FUNCTION_LINEAR) {
2001 for (i = 0; i <= MAX_HW_POINTS ; i++) {
2002 points->red[i] = coordinates_x[i].x;
2003 points->green[i] = coordinates_x[i].x;
2004 points->blue[i] = coordinates_x[i].x;
2007 } else if (trans == TRANSFER_FUNCTION_PQ) {
2008 rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
2009 sizeof(*rgb_degamma),
2012 goto rgb_degamma_alloc_fail;
2015 build_de_pq(rgb_degamma,
2018 for (i = 0; i <= MAX_HW_POINTS ; i++) {
2019 points->red[i] = rgb_degamma[i].r;
2020 points->green[i] = rgb_degamma[i].g;
2021 points->blue[i] = rgb_degamma[i].b;
2025 kvfree(rgb_degamma);
2026 } else if (trans == TRANSFER_FUNCTION_SRGB ||
2027 trans == TRANSFER_FUNCTION_BT709 ||
2028 trans == TRANSFER_FUNCTION_GAMMA22) {
2029 rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
2030 sizeof(*rgb_degamma),
2033 goto rgb_degamma_alloc_fail;
2035 build_degamma(rgb_degamma,
2038 trans == TRANSFER_FUNCTION_SRGB ?
2039 gamma_type_index_2_4 : trans == TRANSFER_FUNCTION_GAMMA22 ?
2040 gamma_type_index_2_2_flat : gamma_type_index_2_2);
2041 for (i = 0; i <= MAX_HW_POINTS ; i++) {
2042 points->red[i] = rgb_degamma[i].r;
2043 points->green[i] = rgb_degamma[i].g;
2044 points->blue[i] = rgb_degamma[i].b;
2048 kvfree(rgb_degamma);
2049 } else if (trans == TRANSFER_FUNCTION_HLG ||
2050 trans == TRANSFER_FUNCTION_HLG12) {
2051 rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
2052 sizeof(*rgb_degamma),
2055 goto rgb_degamma_alloc_fail;
2057 build_hlg_degamma(rgb_degamma,
2060 trans == TRANSFER_FUNCTION_HLG12 ? true:false);
2061 for (i = 0; i <= MAX_HW_POINTS ; i++) {
2062 points->red[i] = rgb_degamma[i].r;
2063 points->green[i] = rgb_degamma[i].g;
2064 points->blue[i] = rgb_degamma[i].b;
2067 kvfree(rgb_degamma);
2069 points->end_exponent = 0;
2070 points->x_point_at_y1_red = 1;
2071 points->x_point_at_y1_green = 1;
2072 points->x_point_at_y1_blue = 1;
2074 rgb_degamma_alloc_fail: