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"
33 #define NUM_PTS_IN_REGION 16
34 #define NUM_REGIONS 32
35 #define MAX_HW_POINTS (NUM_PTS_IN_REGION*NUM_REGIONS)
37 static struct hw_x_point coordinates_x[MAX_HW_POINTS + 2];
39 static struct fixed31_32 pq_table[MAX_HW_POINTS + 2];
40 static struct fixed31_32 de_pq_table[MAX_HW_POINTS + 2];
42 // these are helpers for calculations to reduce stack usage
43 // do not depend on these being preserved across calls
44 static struct fixed31_32 scratch_1;
45 static struct fixed31_32 scratch_2;
46 static struct translate_from_linear_space_args scratch_gamma_args;
48 /* Helper to optimize gamma calculation, only use in translate_from_linear, in
49 * particular the dc_fixpt_pow function which is very expensive
50 * The idea is that our regions for X points are exponential and currently they all use
51 * the same number of points (NUM_PTS_IN_REGION) and in each region every point
52 * is exactly 2x the one at the same index in the previous region. In other words
53 * X[i] = 2 * X[i-NUM_PTS_IN_REGION] for i>=16
54 * The other fact is that (2x)^gamma = 2^gamma * x^gamma
55 * So we compute and save x^gamma for the first 16 regions, and for every next region
56 * just multiply with 2^gamma which can be computed once, and save the result so we
57 * recursively compute all the values.
59 static struct fixed31_32 pow_buffer[NUM_PTS_IN_REGION];
60 static struct fixed31_32 gamma_of_2; // 2^gamma
61 int pow_buffer_ptr = -1;
62 /*sRGB 709 2.2 2.4 P3*/
63 static const int32_t gamma_numerator01[] = { 31308, 180000, 0, 0, 0};
64 static const int32_t gamma_numerator02[] = { 12920, 4500, 0, 0, 0};
65 static const int32_t gamma_numerator03[] = { 55, 99, 0, 0, 0};
66 static const int32_t gamma_numerator04[] = { 55, 99, 0, 0, 0};
67 static const int32_t gamma_numerator05[] = { 2400, 2200, 2200, 2400, 2600};
69 static bool pq_initialized; /* = false; */
70 static bool de_pq_initialized; /* = false; */
72 /* one-time setup of X points */
73 void setup_x_points_distribution(void)
75 struct fixed31_32 region_size = dc_fixpt_from_int(128);
79 struct fixed31_32 increment;
81 coordinates_x[MAX_HW_POINTS].x = region_size;
82 coordinates_x[MAX_HW_POINTS + 1].x = region_size;
84 for (segment = 6; segment > (6 - NUM_REGIONS); segment--) {
85 region_size = dc_fixpt_div_int(region_size, 2);
86 increment = dc_fixpt_div_int(region_size,
88 seg_offset = (segment + (NUM_REGIONS - 7)) * NUM_PTS_IN_REGION;
89 coordinates_x[seg_offset].x = region_size;
91 for (index = seg_offset + 1;
92 index < seg_offset + NUM_PTS_IN_REGION;
94 coordinates_x[index].x = dc_fixpt_add
95 (coordinates_x[index-1].x, increment);
100 void log_x_points_distribution(struct dal_logger *logger)
104 if (logger != NULL) {
105 LOG_GAMMA_WRITE("Log X Distribution\n");
107 for (i = 0; i < MAX_HW_POINTS; i++)
108 LOG_GAMMA_WRITE("%llu\n", coordinates_x[i].x.value);
112 static void compute_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y)
114 /* consts for PQ gamma formula. */
115 const struct fixed31_32 m1 =
116 dc_fixpt_from_fraction(159301758, 1000000000);
117 const struct fixed31_32 m2 =
118 dc_fixpt_from_fraction(7884375, 100000);
119 const struct fixed31_32 c1 =
120 dc_fixpt_from_fraction(8359375, 10000000);
121 const struct fixed31_32 c2 =
122 dc_fixpt_from_fraction(188515625, 10000000);
123 const struct fixed31_32 c3 =
124 dc_fixpt_from_fraction(186875, 10000);
126 struct fixed31_32 l_pow_m1;
127 struct fixed31_32 base;
129 if (dc_fixpt_lt(in_x, dc_fixpt_zero))
130 in_x = dc_fixpt_zero;
132 l_pow_m1 = dc_fixpt_pow(in_x, m1);
135 (dc_fixpt_mul(c2, l_pow_m1))),
136 dc_fixpt_add(dc_fixpt_one,
137 (dc_fixpt_mul(c3, l_pow_m1))));
138 *out_y = dc_fixpt_pow(base, m2);
141 static void compute_de_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y)
143 /* consts for dePQ gamma formula. */
144 const struct fixed31_32 m1 =
145 dc_fixpt_from_fraction(159301758, 1000000000);
146 const struct fixed31_32 m2 =
147 dc_fixpt_from_fraction(7884375, 100000);
148 const struct fixed31_32 c1 =
149 dc_fixpt_from_fraction(8359375, 10000000);
150 const struct fixed31_32 c2 =
151 dc_fixpt_from_fraction(188515625, 10000000);
152 const struct fixed31_32 c3 =
153 dc_fixpt_from_fraction(186875, 10000);
155 struct fixed31_32 l_pow_m1;
156 struct fixed31_32 base, div;
157 struct fixed31_32 base2;
160 if (dc_fixpt_lt(in_x, dc_fixpt_zero))
161 in_x = dc_fixpt_zero;
163 l_pow_m1 = dc_fixpt_pow(in_x,
164 dc_fixpt_div(dc_fixpt_one, m2));
165 base = dc_fixpt_sub(l_pow_m1, c1);
167 div = dc_fixpt_sub(c2, dc_fixpt_mul(c3, l_pow_m1));
169 base2 = dc_fixpt_div(base, div);
170 //avoid complex numbers
171 if (dc_fixpt_lt(base2, dc_fixpt_zero))
172 base2 = dc_fixpt_sub(dc_fixpt_zero, base2);
175 *out_y = dc_fixpt_pow(base2, dc_fixpt_div(dc_fixpt_one, m1));
180 /*de gamma, none linear to linear*/
181 static void compute_hlg_eotf(struct fixed31_32 in_x,
182 struct fixed31_32 *out_y,
183 uint32_t sdr_white_level, uint32_t max_luminance_nits)
188 struct fixed31_32 threshold;
191 struct fixed31_32 scaling_factor =
192 dc_fixpt_from_fraction(max_luminance_nits, sdr_white_level);
193 a = dc_fixpt_from_fraction(17883277, 100000000);
194 b = dc_fixpt_from_fraction(28466892, 100000000);
195 c = dc_fixpt_from_fraction(55991073, 100000000);
196 threshold = dc_fixpt_from_fraction(1, 2);
198 if (dc_fixpt_lt(in_x, threshold)) {
199 x = dc_fixpt_mul(in_x, in_x);
200 x = dc_fixpt_div_int(x, 3);
202 x = dc_fixpt_sub(in_x, c);
203 x = dc_fixpt_div(x, a);
205 x = dc_fixpt_add(x, b);
206 x = dc_fixpt_div_int(x, 12);
208 *out_y = dc_fixpt_mul(x, scaling_factor);
212 /*re gamma, linear to none linear*/
213 static void compute_hlg_oetf(struct fixed31_32 in_x, struct fixed31_32 *out_y,
214 uint32_t sdr_white_level, uint32_t max_luminance_nits)
219 struct fixed31_32 threshold;
222 struct fixed31_32 scaling_factor =
223 dc_fixpt_from_fraction(sdr_white_level, max_luminance_nits);
224 a = dc_fixpt_from_fraction(17883277, 100000000);
225 b = dc_fixpt_from_fraction(28466892, 100000000);
226 c = dc_fixpt_from_fraction(55991073, 100000000);
227 threshold = dc_fixpt_from_fraction(1, 12);
228 x = dc_fixpt_mul(in_x, scaling_factor);
231 if (dc_fixpt_lt(x, threshold)) {
232 x = dc_fixpt_mul(x, dc_fixpt_from_fraction(3, 1));
233 *out_y = dc_fixpt_pow(x, dc_fixpt_half);
235 x = dc_fixpt_mul(x, dc_fixpt_from_fraction(12, 1));
236 x = dc_fixpt_sub(x, b);
238 x = dc_fixpt_mul(a, x);
239 *out_y = dc_fixpt_add(x, c);
244 /* one-time pre-compute PQ values - only for sdr_white_level 80 */
245 void precompute_pq(void)
249 const struct hw_x_point *coord_x = coordinates_x + 32;
250 struct fixed31_32 scaling_factor =
251 dc_fixpt_from_fraction(80, 10000);
253 /* pow function has problems with arguments too small */
254 for (i = 0; i < 32; i++)
255 pq_table[i] = dc_fixpt_zero;
257 for (i = 32; i <= MAX_HW_POINTS; i++) {
258 x = dc_fixpt_mul(coord_x->x, scaling_factor);
259 compute_pq(x, &pq_table[i]);
264 /* one-time pre-compute dePQ values - only for max pixel value 125 FP16 */
265 void precompute_de_pq(void)
269 uint32_t begin_index, end_index;
271 struct fixed31_32 scaling_factor = dc_fixpt_from_int(125);
273 /* X points is 2^-25 to 2^7
274 * De-gamma X is 2^-12 to 2^0 – we are skipping first -12-(-25) = 13 regions
276 begin_index = 13 * NUM_PTS_IN_REGION;
277 end_index = begin_index + 12 * NUM_PTS_IN_REGION;
279 for (i = 0; i <= begin_index; i++)
280 de_pq_table[i] = dc_fixpt_zero;
282 for (; i <= end_index; i++) {
283 compute_de_pq(coordinates_x[i].x, &y);
284 de_pq_table[i] = dc_fixpt_mul(y, scaling_factor);
287 for (; i <= MAX_HW_POINTS; i++)
288 de_pq_table[i] = de_pq_table[i-1];
291 struct fixed31_32 divider1;
292 struct fixed31_32 divider2;
293 struct fixed31_32 divider3;
297 static bool build_coefficients(struct gamma_coefficients *coefficients, enum dc_transfer_func_predefined type)
304 if (type == TRANSFER_FUNCTION_SRGB)
306 else if (type == TRANSFER_FUNCTION_BT709)
308 else if (type == TRANSFER_FUNCTION_GAMMA22)
310 else if (type == TRANSFER_FUNCTION_GAMMA24)
312 else if (type == TRANSFER_FUNCTION_GAMMA26)
320 coefficients->a0[i] = dc_fixpt_from_fraction(
321 gamma_numerator01[index], 10000000);
322 coefficients->a1[i] = dc_fixpt_from_fraction(
323 gamma_numerator02[index], 1000);
324 coefficients->a2[i] = dc_fixpt_from_fraction(
325 gamma_numerator03[index], 1000);
326 coefficients->a3[i] = dc_fixpt_from_fraction(
327 gamma_numerator04[index], 1000);
328 coefficients->user_gamma[i] = dc_fixpt_from_fraction(
329 gamma_numerator05[index], 1000);
332 } while (i != ARRAY_SIZE(coefficients->a0));
337 static struct fixed31_32 translate_from_linear_space(
338 struct translate_from_linear_space_args *args)
340 const struct fixed31_32 one = dc_fixpt_from_int(1);
342 if (dc_fixpt_le(one, args->arg))
345 if (dc_fixpt_le(args->arg, dc_fixpt_neg(args->a0))) {
346 scratch_1 = dc_fixpt_add(one, args->a3);
347 scratch_2 = dc_fixpt_pow(
348 dc_fixpt_neg(args->arg),
349 dc_fixpt_recip(args->gamma));
350 scratch_1 = dc_fixpt_mul(scratch_1, scratch_2);
351 scratch_1 = dc_fixpt_sub(args->a2, scratch_1);
354 } else if (dc_fixpt_le(args->a0, args->arg)) {
355 if (pow_buffer_ptr == 0) {
356 gamma_of_2 = dc_fixpt_pow(dc_fixpt_from_int(2),
357 dc_fixpt_recip(args->gamma));
359 scratch_1 = dc_fixpt_add(one, args->a3);
360 if (pow_buffer_ptr < 16)
361 scratch_2 = dc_fixpt_pow(args->arg,
362 dc_fixpt_recip(args->gamma));
364 scratch_2 = dc_fixpt_mul(gamma_of_2,
365 pow_buffer[pow_buffer_ptr%16]);
367 if (pow_buffer_ptr != -1) {
368 pow_buffer[pow_buffer_ptr%16] = scratch_2;
372 scratch_1 = dc_fixpt_mul(scratch_1, scratch_2);
373 scratch_1 = dc_fixpt_sub(scratch_1, args->a2);
378 return dc_fixpt_mul(args->arg, args->a1);
382 static struct fixed31_32 translate_from_linear_space_long(
383 struct translate_from_linear_space_args *args)
385 const struct fixed31_32 one = dc_fixpt_from_int(1);
387 if (dc_fixpt_lt(one, args->arg))
390 if (dc_fixpt_le(args->arg, dc_fixpt_neg(args->a0)))
398 dc_fixpt_neg(args->arg),
399 dc_fixpt_recip(args->gamma))));
400 else if (dc_fixpt_le(args->a0, args->arg))
408 dc_fixpt_recip(args->gamma))),
416 static struct fixed31_32 calculate_gamma22(struct fixed31_32 arg, bool use_eetf)
418 struct fixed31_32 gamma = dc_fixpt_from_fraction(22, 10);
420 scratch_gamma_args.arg = arg;
421 scratch_gamma_args.a0 = dc_fixpt_zero;
422 scratch_gamma_args.a1 = dc_fixpt_zero;
423 scratch_gamma_args.a2 = dc_fixpt_zero;
424 scratch_gamma_args.a3 = dc_fixpt_zero;
425 scratch_gamma_args.gamma = gamma;
428 return translate_from_linear_space_long(&scratch_gamma_args);
430 return translate_from_linear_space(&scratch_gamma_args);
434 static struct fixed31_32 translate_to_linear_space(
435 struct fixed31_32 arg,
436 struct fixed31_32 a0,
437 struct fixed31_32 a1,
438 struct fixed31_32 a2,
439 struct fixed31_32 a3,
440 struct fixed31_32 gamma)
442 struct fixed31_32 linear;
444 a0 = dc_fixpt_mul(a0, a1);
445 if (dc_fixpt_le(arg, dc_fixpt_neg(a0)))
447 linear = dc_fixpt_neg(
450 dc_fixpt_sub(a2, arg),
452 dc_fixpt_one, a3)), gamma));
454 else if (dc_fixpt_le(dc_fixpt_neg(a0), arg) &&
455 dc_fixpt_le(arg, a0))
456 linear = dc_fixpt_div(arg, a1);
458 linear = dc_fixpt_pow(
460 dc_fixpt_add(a2, arg),
462 dc_fixpt_one, a3)), gamma);
467 static struct fixed31_32 translate_from_linear_space_ex(
468 struct fixed31_32 arg,
469 struct gamma_coefficients *coeff,
470 uint32_t color_index)
472 scratch_gamma_args.arg = arg;
473 scratch_gamma_args.a0 = coeff->a0[color_index];
474 scratch_gamma_args.a1 = coeff->a1[color_index];
475 scratch_gamma_args.a2 = coeff->a2[color_index];
476 scratch_gamma_args.a3 = coeff->a3[color_index];
477 scratch_gamma_args.gamma = coeff->user_gamma[color_index];
479 return translate_from_linear_space(&scratch_gamma_args);
483 static inline struct fixed31_32 translate_to_linear_space_ex(
484 struct fixed31_32 arg,
485 struct gamma_coefficients *coeff,
486 uint32_t color_index)
488 return translate_to_linear_space(
490 coeff->a0[color_index],
491 coeff->a1[color_index],
492 coeff->a2[color_index],
493 coeff->a3[color_index],
494 coeff->user_gamma[color_index]);
498 static bool find_software_points(
499 const struct dc_gamma *ramp,
500 const struct gamma_pixel *axis_x,
501 struct fixed31_32 hw_point,
502 enum channel_name channel,
503 uint32_t *index_to_start,
504 uint32_t *index_left,
505 uint32_t *index_right,
506 enum hw_point_position *pos)
508 const uint32_t max_number = ramp->num_entries + 3;
510 struct fixed31_32 left, right;
512 uint32_t i = *index_to_start;
514 while (i < max_number) {
515 if (channel == CHANNEL_NAME_RED) {
518 if (i < max_number - 1)
519 right = axis_x[i + 1].r;
521 right = axis_x[max_number - 1].r;
522 } else if (channel == CHANNEL_NAME_GREEN) {
525 if (i < max_number - 1)
526 right = axis_x[i + 1].g;
528 right = axis_x[max_number - 1].g;
532 if (i < max_number - 1)
533 right = axis_x[i + 1].b;
535 right = axis_x[max_number - 1].b;
538 if (dc_fixpt_le(left, hw_point) &&
539 dc_fixpt_le(hw_point, right)) {
543 if (i < max_number - 1)
544 *index_right = i + 1;
546 *index_right = max_number - 1;
548 *pos = HW_POINT_POSITION_MIDDLE;
551 } else if ((i == *index_to_start) &&
552 dc_fixpt_le(hw_point, left)) {
557 *pos = HW_POINT_POSITION_LEFT;
560 } else if ((i == max_number - 1) &&
561 dc_fixpt_le(right, hw_point)) {
566 *pos = HW_POINT_POSITION_RIGHT;
577 static bool build_custom_gamma_mapping_coefficients_worker(
578 const struct dc_gamma *ramp,
579 struct pixel_gamma_point *coeff,
580 const struct hw_x_point *coordinates_x,
581 const struct gamma_pixel *axis_x,
582 enum channel_name channel,
583 uint32_t number_of_points)
587 while (i <= number_of_points) {
588 struct fixed31_32 coord_x;
590 uint32_t index_to_start = 0;
591 uint32_t index_left = 0;
592 uint32_t index_right = 0;
594 enum hw_point_position hw_pos;
596 struct gamma_point *point;
598 struct fixed31_32 left_pos;
599 struct fixed31_32 right_pos;
601 if (channel == CHANNEL_NAME_RED)
602 coord_x = coordinates_x[i].regamma_y_red;
603 else if (channel == CHANNEL_NAME_GREEN)
604 coord_x = coordinates_x[i].regamma_y_green;
606 coord_x = coordinates_x[i].regamma_y_blue;
608 if (!find_software_points(
609 ramp, axis_x, coord_x, channel,
610 &index_to_start, &index_left, &index_right, &hw_pos)) {
615 if (index_left >= ramp->num_entries + 3) {
620 if (index_right >= ramp->num_entries + 3) {
625 if (channel == CHANNEL_NAME_RED) {
628 left_pos = axis_x[index_left].r;
629 right_pos = axis_x[index_right].r;
630 } else if (channel == CHANNEL_NAME_GREEN) {
633 left_pos = axis_x[index_left].g;
634 right_pos = axis_x[index_right].g;
638 left_pos = axis_x[index_left].b;
639 right_pos = axis_x[index_right].b;
642 if (hw_pos == HW_POINT_POSITION_MIDDLE)
643 point->coeff = dc_fixpt_div(
650 else if (hw_pos == HW_POINT_POSITION_LEFT)
651 point->coeff = dc_fixpt_zero;
652 else if (hw_pos == HW_POINT_POSITION_RIGHT)
653 point->coeff = dc_fixpt_from_int(2);
659 point->left_index = index_left;
660 point->right_index = index_right;
669 static struct fixed31_32 calculate_mapped_value(
670 struct pwl_float_data *rgb,
671 const struct pixel_gamma_point *coeff,
672 enum channel_name channel,
675 const struct gamma_point *point;
677 struct fixed31_32 result;
679 if (channel == CHANNEL_NAME_RED)
681 else if (channel == CHANNEL_NAME_GREEN)
686 if ((point->left_index < 0) || (point->left_index > max_index)) {
688 return dc_fixpt_zero;
691 if ((point->right_index < 0) || (point->right_index > max_index)) {
693 return dc_fixpt_zero;
696 if (point->pos == HW_POINT_POSITION_MIDDLE)
697 if (channel == CHANNEL_NAME_RED)
698 result = dc_fixpt_add(
702 rgb[point->right_index].r,
703 rgb[point->left_index].r)),
704 rgb[point->left_index].r);
705 else if (channel == CHANNEL_NAME_GREEN)
706 result = dc_fixpt_add(
710 rgb[point->right_index].g,
711 rgb[point->left_index].g)),
712 rgb[point->left_index].g);
714 result = dc_fixpt_add(
718 rgb[point->right_index].b,
719 rgb[point->left_index].b)),
720 rgb[point->left_index].b);
721 else if (point->pos == HW_POINT_POSITION_LEFT) {
723 result = dc_fixpt_zero;
726 result = dc_fixpt_one;
732 static void build_pq(struct pwl_float_data_ex *rgb_regamma,
733 uint32_t hw_points_num,
734 const struct hw_x_point *coordinate_x,
735 uint32_t sdr_white_level)
737 uint32_t i, start_index;
739 struct pwl_float_data_ex *rgb = rgb_regamma;
740 const struct hw_x_point *coord_x = coordinate_x;
742 struct fixed31_32 output;
743 struct fixed31_32 scaling_factor =
744 dc_fixpt_from_fraction(sdr_white_level, 10000);
746 if (!pq_initialized && sdr_white_level == 80) {
748 pq_initialized = true;
751 /* TODO: start index is from segment 2^-24, skipping first segment
752 * due to x values too small for power calculations
756 coord_x += start_index;
758 for (i = start_index; i <= hw_points_num; i++) {
759 /* Multiply 0.008 as regamma is 0-1 and FP16 input is 0-125.
762 if (sdr_white_level == 80) {
763 output = pq_table[i];
765 x = dc_fixpt_mul(coord_x->x, scaling_factor);
766 compute_pq(x, &output);
769 /* should really not happen? */
770 if (dc_fixpt_lt(output, dc_fixpt_zero))
771 output = dc_fixpt_zero;
772 else if (dc_fixpt_lt(dc_fixpt_one, output))
773 output = dc_fixpt_one;
784 static void build_de_pq(struct pwl_float_data_ex *de_pq,
785 uint32_t hw_points_num,
786 const struct hw_x_point *coordinate_x)
789 struct fixed31_32 output;
791 struct fixed31_32 scaling_factor = dc_fixpt_from_int(125);
793 if (!de_pq_initialized) {
795 de_pq_initialized = true;
799 for (i = 0; i <= hw_points_num; i++) {
800 output = de_pq_table[i];
801 /* should really not happen? */
802 if (dc_fixpt_lt(output, dc_fixpt_zero))
803 output = dc_fixpt_zero;
804 else if (dc_fixpt_lt(scaling_factor, output))
805 output = scaling_factor;
812 static bool build_regamma(struct pwl_float_data_ex *rgb_regamma,
813 uint32_t hw_points_num,
814 const struct hw_x_point *coordinate_x, enum dc_transfer_func_predefined type)
819 struct gamma_coefficients *coeff;
820 struct pwl_float_data_ex *rgb = rgb_regamma;
821 const struct hw_x_point *coord_x = coordinate_x;
823 coeff = kvzalloc(sizeof(*coeff), GFP_KERNEL);
827 if (!build_coefficients(coeff, type))
830 memset(pow_buffer, 0, NUM_PTS_IN_REGION * sizeof(struct fixed31_32));
831 pow_buffer_ptr = 0; // see variable definition for more info
833 while (i <= hw_points_num) {
834 /*TODO use y vs r,g,b*/
835 rgb->r = translate_from_linear_space_ex(
836 coord_x->x, coeff, 0);
843 pow_buffer_ptr = -1; // reset back to no optimize
850 static void hermite_spline_eetf(struct fixed31_32 input_x,
851 struct fixed31_32 max_display,
852 struct fixed31_32 min_display,
853 struct fixed31_32 max_content,
854 struct fixed31_32 *out_x)
856 struct fixed31_32 min_lum_pq;
857 struct fixed31_32 max_lum_pq;
858 struct fixed31_32 max_content_pq;
859 struct fixed31_32 ks;
860 struct fixed31_32 E1;
861 struct fixed31_32 E2;
862 struct fixed31_32 E3;
864 struct fixed31_32 t2;
865 struct fixed31_32 t3;
866 struct fixed31_32 two;
867 struct fixed31_32 three;
868 struct fixed31_32 temp1;
869 struct fixed31_32 temp2;
870 struct fixed31_32 a = dc_fixpt_from_fraction(15, 10);
871 struct fixed31_32 b = dc_fixpt_from_fraction(5, 10);
872 struct fixed31_32 epsilon = dc_fixpt_from_fraction(1, 1000000); // dc_fixpt_epsilon is a bit too small
874 if (dc_fixpt_eq(max_content, dc_fixpt_zero)) {
875 *out_x = dc_fixpt_zero;
879 compute_pq(input_x, &E1);
880 compute_pq(dc_fixpt_div(min_display, max_content), &min_lum_pq);
881 compute_pq(dc_fixpt_div(max_display, max_content), &max_lum_pq);
882 compute_pq(dc_fixpt_one, &max_content_pq); // always 1? DAL2 code is weird
883 a = dc_fixpt_div(dc_fixpt_add(dc_fixpt_one, b), max_content_pq); // (1+b)/maxContent
884 ks = dc_fixpt_sub(dc_fixpt_mul(a, max_lum_pq), b); // a * max_lum_pq - b
886 if (dc_fixpt_lt(E1, ks))
888 else if (dc_fixpt_le(ks, E1) && dc_fixpt_le(E1, dc_fixpt_one)) {
889 if (dc_fixpt_lt(epsilon, dc_fixpt_sub(dc_fixpt_one, ks)))
890 // t = (E1 - ks) / (1 - ks)
891 t = dc_fixpt_div(dc_fixpt_sub(E1, ks),
892 dc_fixpt_sub(dc_fixpt_one, ks));
896 two = dc_fixpt_from_int(2);
897 three = dc_fixpt_from_int(3);
899 t2 = dc_fixpt_mul(t, t);
900 t3 = dc_fixpt_mul(t2, t);
901 temp1 = dc_fixpt_mul(two, t3);
902 temp2 = dc_fixpt_mul(three, t2);
904 // (2t^3 - 3t^2 + 1) * ks
905 E2 = dc_fixpt_mul(ks, dc_fixpt_add(dc_fixpt_one,
906 dc_fixpt_sub(temp1, temp2)));
908 // (-2t^3 + 3t^2) * max_lum_pq
909 E2 = dc_fixpt_add(E2, dc_fixpt_mul(max_lum_pq,
910 dc_fixpt_sub(temp2, temp1)));
912 temp1 = dc_fixpt_mul(two, t2);
913 temp2 = dc_fixpt_sub(dc_fixpt_one, ks);
915 // (t^3 - 2t^2 + t) * (1-ks)
916 E2 = dc_fixpt_add(E2, dc_fixpt_mul(temp2,
917 dc_fixpt_add(t, dc_fixpt_sub(t3, temp1))));
921 temp1 = dc_fixpt_sub(dc_fixpt_one, E2);
922 temp2 = dc_fixpt_mul(temp1, temp1);
923 temp2 = dc_fixpt_mul(temp2, temp2);
926 E3 = dc_fixpt_add(E2, dc_fixpt_mul(min_lum_pq, temp2));
927 compute_de_pq(E3, out_x);
929 *out_x = dc_fixpt_div(*out_x, dc_fixpt_div(max_display, max_content));
932 static bool build_freesync_hdr(struct pwl_float_data_ex *rgb_regamma,
933 uint32_t hw_points_num,
934 const struct hw_x_point *coordinate_x,
935 const struct freesync_hdr_tf_params *fs_params)
938 struct pwl_float_data_ex *rgb = rgb_regamma;
939 const struct hw_x_point *coord_x = coordinate_x;
940 struct fixed31_32 scaledX = dc_fixpt_zero;
941 struct fixed31_32 scaledX1 = dc_fixpt_zero;
942 struct fixed31_32 max_display;
943 struct fixed31_32 min_display;
944 struct fixed31_32 max_content;
945 struct fixed31_32 clip = dc_fixpt_one;
946 struct fixed31_32 output;
947 bool use_eetf = false;
948 bool is_clipped = false;
949 struct fixed31_32 sdr_white_level;
951 if (fs_params->max_content == 0 ||
952 fs_params->max_display == 0)
955 max_display = dc_fixpt_from_int(fs_params->max_display);
956 min_display = dc_fixpt_from_fraction(fs_params->min_display, 10000);
957 max_content = dc_fixpt_from_int(fs_params->max_content);
958 sdr_white_level = dc_fixpt_from_int(fs_params->sdr_white_level);
960 if (fs_params->min_display > 1000) // cap at 0.1 at the bottom
961 min_display = dc_fixpt_from_fraction(1, 10);
962 if (fs_params->max_display < 100) // cap at 100 at the top
963 max_display = dc_fixpt_from_int(100);
965 // only max used, we don't adjust min luminance
966 if (fs_params->max_content > fs_params->max_display)
969 max_content = max_display;
972 pow_buffer_ptr = 0; // see var definition for more info
973 rgb += 32; // first 32 points have problems with fixed point, too small
975 for (i = 32; i <= hw_points_num; i++) {
978 /*max content is equal 1 */
979 scaledX1 = dc_fixpt_div(coord_x->x,
980 dc_fixpt_div(max_content, sdr_white_level));
981 hermite_spline_eetf(scaledX1, max_display, min_display,
982 max_content, &scaledX);
984 scaledX = dc_fixpt_div(coord_x->x,
985 dc_fixpt_div(max_display, sdr_white_level));
987 if (dc_fixpt_lt(scaledX, clip)) {
988 if (dc_fixpt_lt(scaledX, dc_fixpt_zero))
989 output = dc_fixpt_zero;
991 output = calculate_gamma22(scaledX, use_eetf);
1011 pow_buffer_ptr = -1;
1016 static bool build_degamma(struct pwl_float_data_ex *curve,
1017 uint32_t hw_points_num,
1018 const struct hw_x_point *coordinate_x, enum dc_transfer_func_predefined type)
1021 struct gamma_coefficients coeff;
1022 uint32_t begin_index, end_index;
1025 if (!build_coefficients(&coeff, type))
1030 /* X points is 2^-25 to 2^7
1031 * De-gamma X is 2^-12 to 2^0 – we are skipping first -12-(-25) = 13 regions
1033 begin_index = 13 * NUM_PTS_IN_REGION;
1034 end_index = begin_index + 12 * NUM_PTS_IN_REGION;
1036 while (i != begin_index) {
1037 curve[i].r = dc_fixpt_zero;
1038 curve[i].g = dc_fixpt_zero;
1039 curve[i].b = dc_fixpt_zero;
1043 while (i != end_index) {
1044 curve[i].r = translate_to_linear_space_ex(
1045 coordinate_x[i].x, &coeff, 0);
1046 curve[i].g = curve[i].r;
1047 curve[i].b = curve[i].r;
1050 while (i != hw_points_num + 1) {
1051 curve[i].r = dc_fixpt_one;
1052 curve[i].g = dc_fixpt_one;
1053 curve[i].b = dc_fixpt_one;
1065 static void build_hlg_degamma(struct pwl_float_data_ex *degamma,
1066 uint32_t hw_points_num,
1067 const struct hw_x_point *coordinate_x,
1068 uint32_t sdr_white_level, uint32_t max_luminance_nits)
1072 struct pwl_float_data_ex *rgb = degamma;
1073 const struct hw_x_point *coord_x = coordinate_x;
1076 //check when i == 434
1077 while (i != hw_points_num + 1) {
1078 compute_hlg_eotf(coord_x->x, &rgb->r, sdr_white_level, max_luminance_nits);
1088 static void build_hlg_regamma(struct pwl_float_data_ex *regamma,
1089 uint32_t hw_points_num,
1090 const struct hw_x_point *coordinate_x,
1091 uint32_t sdr_white_level, uint32_t max_luminance_nits)
1095 struct pwl_float_data_ex *rgb = regamma;
1096 const struct hw_x_point *coord_x = coordinate_x;
1101 while (i != hw_points_num + 1) {
1102 compute_hlg_oetf(coord_x->x, &rgb->r, sdr_white_level, max_luminance_nits);
1111 static void scale_gamma(struct pwl_float_data *pwl_rgb,
1112 const struct dc_gamma *ramp,
1113 struct dividers dividers)
1115 const struct fixed31_32 max_driver = dc_fixpt_from_int(0xFFFF);
1116 const struct fixed31_32 max_os = dc_fixpt_from_int(0xFF00);
1117 struct fixed31_32 scaler = max_os;
1119 struct pwl_float_data *rgb = pwl_rgb;
1120 struct pwl_float_data *rgb_last = rgb + ramp->num_entries - 1;
1125 if (dc_fixpt_lt(max_os, ramp->entries.red[i]) ||
1126 dc_fixpt_lt(max_os, ramp->entries.green[i]) ||
1127 dc_fixpt_lt(max_os, ramp->entries.blue[i])) {
1128 scaler = max_driver;
1132 } while (i != ramp->num_entries);
1137 rgb->r = dc_fixpt_div(
1138 ramp->entries.red[i], scaler);
1139 rgb->g = dc_fixpt_div(
1140 ramp->entries.green[i], scaler);
1141 rgb->b = dc_fixpt_div(
1142 ramp->entries.blue[i], scaler);
1146 } while (i != ramp->num_entries);
1148 rgb->r = dc_fixpt_mul(rgb_last->r,
1150 rgb->g = dc_fixpt_mul(rgb_last->g,
1152 rgb->b = dc_fixpt_mul(rgb_last->b,
1157 rgb->r = dc_fixpt_mul(rgb_last->r,
1159 rgb->g = dc_fixpt_mul(rgb_last->g,
1161 rgb->b = dc_fixpt_mul(rgb_last->b,
1166 rgb->r = dc_fixpt_mul(rgb_last->r,
1168 rgb->g = dc_fixpt_mul(rgb_last->g,
1170 rgb->b = dc_fixpt_mul(rgb_last->b,
1174 static void scale_gamma_dx(struct pwl_float_data *pwl_rgb,
1175 const struct dc_gamma *ramp,
1176 struct dividers dividers)
1179 struct fixed31_32 min = dc_fixpt_zero;
1180 struct fixed31_32 max = dc_fixpt_one;
1182 struct fixed31_32 delta = dc_fixpt_zero;
1183 struct fixed31_32 offset = dc_fixpt_zero;
1185 for (i = 0 ; i < ramp->num_entries; i++) {
1186 if (dc_fixpt_lt(ramp->entries.red[i], min))
1187 min = ramp->entries.red[i];
1189 if (dc_fixpt_lt(ramp->entries.green[i], min))
1190 min = ramp->entries.green[i];
1192 if (dc_fixpt_lt(ramp->entries.blue[i], min))
1193 min = ramp->entries.blue[i];
1195 if (dc_fixpt_lt(max, ramp->entries.red[i]))
1196 max = ramp->entries.red[i];
1198 if (dc_fixpt_lt(max, ramp->entries.green[i]))
1199 max = ramp->entries.green[i];
1201 if (dc_fixpt_lt(max, ramp->entries.blue[i]))
1202 max = ramp->entries.blue[i];
1205 if (dc_fixpt_lt(min, dc_fixpt_zero))
1206 delta = dc_fixpt_neg(min);
1208 offset = dc_fixpt_add(min, max);
1210 for (i = 0 ; i < ramp->num_entries; i++) {
1211 pwl_rgb[i].r = dc_fixpt_div(
1213 ramp->entries.red[i], delta), offset);
1214 pwl_rgb[i].g = dc_fixpt_div(
1216 ramp->entries.green[i], delta), offset);
1217 pwl_rgb[i].b = dc_fixpt_div(
1219 ramp->entries.blue[i], delta), offset);
1223 pwl_rgb[i].r = dc_fixpt_sub(dc_fixpt_mul_int(
1224 pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r);
1225 pwl_rgb[i].g = dc_fixpt_sub(dc_fixpt_mul_int(
1226 pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g);
1227 pwl_rgb[i].b = dc_fixpt_sub(dc_fixpt_mul_int(
1228 pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b);
1230 pwl_rgb[i].r = dc_fixpt_sub(dc_fixpt_mul_int(
1231 pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r);
1232 pwl_rgb[i].g = dc_fixpt_sub(dc_fixpt_mul_int(
1233 pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g);
1234 pwl_rgb[i].b = dc_fixpt_sub(dc_fixpt_mul_int(
1235 pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b);
1238 /* todo: all these scale_gamma functions are inherently the same but
1239 * take different structures as params or different format for ramp
1240 * values. We could probably implement it in a more generic fashion
1242 static void scale_user_regamma_ramp(struct pwl_float_data *pwl_rgb,
1243 const struct regamma_ramp *ramp,
1244 struct dividers dividers)
1246 unsigned short max_driver = 0xFFFF;
1247 unsigned short max_os = 0xFF00;
1248 unsigned short scaler = max_os;
1250 struct pwl_float_data *rgb = pwl_rgb;
1251 struct pwl_float_data *rgb_last = rgb + GAMMA_RGB_256_ENTRIES - 1;
1255 if (ramp->gamma[i] > max_os ||
1256 ramp->gamma[i + 256] > max_os ||
1257 ramp->gamma[i + 512] > max_os) {
1258 scaler = max_driver;
1262 } while (i != GAMMA_RGB_256_ENTRIES);
1266 rgb->r = dc_fixpt_from_fraction(
1267 ramp->gamma[i], scaler);
1268 rgb->g = dc_fixpt_from_fraction(
1269 ramp->gamma[i + 256], scaler);
1270 rgb->b = dc_fixpt_from_fraction(
1271 ramp->gamma[i + 512], scaler);
1275 } while (i != GAMMA_RGB_256_ENTRIES);
1277 rgb->r = dc_fixpt_mul(rgb_last->r,
1279 rgb->g = dc_fixpt_mul(rgb_last->g,
1281 rgb->b = dc_fixpt_mul(rgb_last->b,
1286 rgb->r = dc_fixpt_mul(rgb_last->r,
1288 rgb->g = dc_fixpt_mul(rgb_last->g,
1290 rgb->b = dc_fixpt_mul(rgb_last->b,
1295 rgb->r = dc_fixpt_mul(rgb_last->r,
1297 rgb->g = dc_fixpt_mul(rgb_last->g,
1299 rgb->b = dc_fixpt_mul(rgb_last->b,
1304 * RS3+ color transform DDI - 1D LUT adjustment is composed with regamma here
1305 * Input is evenly distributed in the output color space as specified in
1308 * Interpolation details:
1309 * 1D LUT has 4096 values which give curve correction in 0-1 float range
1310 * for evenly spaced points in 0-1 range. lut1D[index] gives correction
1312 * First we find index for which:
1313 * index/4095 < regamma_y < (index+1)/4095 =>
1314 * index < 4095*regamma_y < index + 1
1315 * norm_y = 4095*regamma_y, and index is just truncating to nearest integer
1316 * lut1 = lut1D[index], lut2 = lut1D[index+1]
1318 * adjustedY is then linearly interpolating regamma Y between lut1 and lut2
1320 * Custom degamma on Linux uses the same interpolation math, so is handled here
1322 static void apply_lut_1d(
1323 const struct dc_gamma *ramp,
1324 uint32_t num_hw_points,
1325 struct dc_transfer_func_distributed_points *tf_pts)
1329 struct fixed31_32 *regamma_y;
1330 struct fixed31_32 norm_y;
1331 struct fixed31_32 lut1;
1332 struct fixed31_32 lut2;
1333 const int max_lut_index = 4095;
1334 const struct fixed31_32 max_lut_index_f =
1335 dc_fixpt_from_int(max_lut_index);
1336 int32_t index = 0, index_next = 0;
1337 struct fixed31_32 index_f;
1338 struct fixed31_32 delta_lut;
1339 struct fixed31_32 delta_index;
1341 if (ramp->type != GAMMA_CS_TFM_1D && ramp->type != GAMMA_CUSTOM)
1342 return; // this is not expected
1344 for (i = 0; i < num_hw_points; i++) {
1345 for (color = 0; color < 3; color++) {
1347 regamma_y = &tf_pts->red[i];
1348 else if (color == 1)
1349 regamma_y = &tf_pts->green[i];
1351 regamma_y = &tf_pts->blue[i];
1353 norm_y = dc_fixpt_mul(max_lut_index_f,
1355 index = dc_fixpt_floor(norm_y);
1356 index_f = dc_fixpt_from_int(index);
1358 if (index < 0 || index > max_lut_index)
1361 index_next = (index == max_lut_index) ? index : index+1;
1364 lut1 = ramp->entries.red[index];
1365 lut2 = ramp->entries.red[index_next];
1366 } else if (color == 1) {
1367 lut1 = ramp->entries.green[index];
1368 lut2 = ramp->entries.green[index_next];
1370 lut1 = ramp->entries.blue[index];
1371 lut2 = ramp->entries.blue[index_next];
1374 // we have everything now, so interpolate
1375 delta_lut = dc_fixpt_sub(lut2, lut1);
1376 delta_index = dc_fixpt_sub(norm_y, index_f);
1378 *regamma_y = dc_fixpt_add(lut1,
1379 dc_fixpt_mul(delta_index, delta_lut));
1384 static void build_evenly_distributed_points(
1385 struct gamma_pixel *points,
1386 uint32_t numberof_points,
1387 struct dividers dividers)
1389 struct gamma_pixel *p = points;
1390 struct gamma_pixel *p_last;
1394 // This function should not gets called with 0 as a parameter
1395 ASSERT(numberof_points > 0);
1396 p_last = p + numberof_points - 1;
1399 struct fixed31_32 value = dc_fixpt_from_fraction(i,
1400 numberof_points - 1);
1408 } while (i < numberof_points);
1410 p->r = dc_fixpt_div(p_last->r, dividers.divider1);
1411 p->g = dc_fixpt_div(p_last->g, dividers.divider1);
1412 p->b = dc_fixpt_div(p_last->b, dividers.divider1);
1416 p->r = dc_fixpt_div(p_last->r, dividers.divider2);
1417 p->g = dc_fixpt_div(p_last->g, dividers.divider2);
1418 p->b = dc_fixpt_div(p_last->b, dividers.divider2);
1422 p->r = dc_fixpt_div(p_last->r, dividers.divider3);
1423 p->g = dc_fixpt_div(p_last->g, dividers.divider3);
1424 p->b = dc_fixpt_div(p_last->b, dividers.divider3);
1427 static inline void copy_rgb_regamma_to_coordinates_x(
1428 struct hw_x_point *coordinates_x,
1429 uint32_t hw_points_num,
1430 const struct pwl_float_data_ex *rgb_ex)
1432 struct hw_x_point *coords = coordinates_x;
1434 const struct pwl_float_data_ex *rgb_regamma = rgb_ex;
1436 while (i <= hw_points_num + 1) {
1437 coords->regamma_y_red = rgb_regamma->r;
1438 coords->regamma_y_green = rgb_regamma->g;
1439 coords->regamma_y_blue = rgb_regamma->b;
1447 static bool calculate_interpolated_hardware_curve(
1448 const struct dc_gamma *ramp,
1449 struct pixel_gamma_point *coeff128,
1450 struct pwl_float_data *rgb_user,
1451 const struct hw_x_point *coordinates_x,
1452 const struct gamma_pixel *axis_x,
1453 uint32_t number_of_points,
1454 struct dc_transfer_func_distributed_points *tf_pts)
1457 const struct pixel_gamma_point *coeff = coeff128;
1458 uint32_t max_entries = 3 - 1;
1462 for (i = 0; i < 3; i++) {
1463 if (!build_custom_gamma_mapping_coefficients_worker(
1464 ramp, coeff128, coordinates_x, axis_x, i,
1470 max_entries += ramp->num_entries;
1472 /* TODO: float point case */
1474 while (i <= number_of_points) {
1475 tf_pts->red[i] = calculate_mapped_value(
1476 rgb_user, coeff, CHANNEL_NAME_RED, max_entries);
1477 tf_pts->green[i] = calculate_mapped_value(
1478 rgb_user, coeff, CHANNEL_NAME_GREEN, max_entries);
1479 tf_pts->blue[i] = calculate_mapped_value(
1480 rgb_user, coeff, CHANNEL_NAME_BLUE, max_entries);
1489 /* The "old" interpolation uses a complicated scheme to build an array of
1490 * coefficients while also using an array of 0-255 normalized to 0-1
1491 * Then there's another loop using both of the above + new scaled user ramp
1492 * and we concatenate them. It also searches for points of interpolation and
1493 * uses enums for positions.
1495 * This function uses a different approach:
1496 * user ramp is always applied on X with 0/255, 1/255, 2/255, ..., 255/255
1497 * To find index for hwX , we notice the following:
1498 * i/255 <= hwX < (i+1)/255 <=> i <= 255*hwX < i+1
1499 * See apply_lut_1d which is the same principle, but on 4K entry 1D LUT
1501 * Once the index is known, combined Y is simply:
1502 * user_ramp(index) + (hwX-index/255)*(user_ramp(index+1) - user_ramp(index)
1504 * We should switch to this method in all cases, it's simpler and faster
1505 * ToDo one day - for now this only applies to ADL regamma to avoid regression
1506 * for regular use cases (sRGB and PQ)
1508 static void interpolate_user_regamma(uint32_t hw_points_num,
1509 struct pwl_float_data *rgb_user,
1511 struct dc_transfer_func_distributed_points *tf_pts)
1517 struct fixed31_32 *tf_point;
1518 struct fixed31_32 hw_x;
1519 struct fixed31_32 norm_factor =
1520 dc_fixpt_from_int(255);
1521 struct fixed31_32 norm_x;
1522 struct fixed31_32 index_f;
1523 struct fixed31_32 lut1;
1524 struct fixed31_32 lut2;
1525 struct fixed31_32 delta_lut;
1526 struct fixed31_32 delta_index;
1529 /* fixed_pt library has problems handling too small values */
1531 tf_pts->red[i] = dc_fixpt_zero;
1532 tf_pts->green[i] = dc_fixpt_zero;
1533 tf_pts->blue[i] = dc_fixpt_zero;
1536 while (i <= hw_points_num + 1) {
1537 for (color = 0; color < 3; color++) {
1539 tf_point = &tf_pts->red[i];
1540 else if (color == 1)
1541 tf_point = &tf_pts->green[i];
1543 tf_point = &tf_pts->blue[i];
1545 if (apply_degamma) {
1547 hw_x = coordinates_x[i].regamma_y_red;
1548 else if (color == 1)
1549 hw_x = coordinates_x[i].regamma_y_green;
1551 hw_x = coordinates_x[i].regamma_y_blue;
1553 hw_x = coordinates_x[i].x;
1555 norm_x = dc_fixpt_mul(norm_factor, hw_x);
1556 index = dc_fixpt_floor(norm_x);
1557 if (index < 0 || index > 255)
1560 index_f = dc_fixpt_from_int(index);
1561 index_next = (index == 255) ? index : index + 1;
1564 lut1 = rgb_user[index].r;
1565 lut2 = rgb_user[index_next].r;
1566 } else if (color == 1) {
1567 lut1 = rgb_user[index].g;
1568 lut2 = rgb_user[index_next].g;
1570 lut1 = rgb_user[index].b;
1571 lut2 = rgb_user[index_next].b;
1574 // we have everything now, so interpolate
1575 delta_lut = dc_fixpt_sub(lut2, lut1);
1576 delta_index = dc_fixpt_sub(norm_x, index_f);
1578 *tf_point = dc_fixpt_add(lut1,
1579 dc_fixpt_mul(delta_index, delta_lut));
1585 static void build_new_custom_resulted_curve(
1586 uint32_t hw_points_num,
1587 struct dc_transfer_func_distributed_points *tf_pts)
1593 while (i != hw_points_num + 1) {
1594 tf_pts->red[i] = dc_fixpt_clamp(
1595 tf_pts->red[i], dc_fixpt_zero,
1597 tf_pts->green[i] = dc_fixpt_clamp(
1598 tf_pts->green[i], dc_fixpt_zero,
1600 tf_pts->blue[i] = dc_fixpt_clamp(
1601 tf_pts->blue[i], dc_fixpt_zero,
1608 static void apply_degamma_for_user_regamma(struct pwl_float_data_ex *rgb_regamma,
1609 uint32_t hw_points_num)
1613 struct gamma_coefficients coeff;
1614 struct pwl_float_data_ex *rgb = rgb_regamma;
1615 const struct hw_x_point *coord_x = coordinates_x;
1617 build_coefficients(&coeff, true);
1620 while (i != hw_points_num + 1) {
1621 rgb->r = translate_from_linear_space_ex(
1622 coord_x->x, &coeff, 0);
1631 static bool map_regamma_hw_to_x_user(
1632 const struct dc_gamma *ramp,
1633 struct pixel_gamma_point *coeff128,
1634 struct pwl_float_data *rgb_user,
1635 struct hw_x_point *coords_x,
1636 const struct gamma_pixel *axis_x,
1637 const struct pwl_float_data_ex *rgb_regamma,
1638 uint32_t hw_points_num,
1639 struct dc_transfer_func_distributed_points *tf_pts,
1642 /* setup to spare calculated ideal regamma values */
1645 struct hw_x_point *coords = coords_x;
1646 const struct pwl_float_data_ex *regamma = rgb_regamma;
1648 if (ramp && mapUserRamp) {
1649 copy_rgb_regamma_to_coordinates_x(coords,
1653 calculate_interpolated_hardware_curve(
1654 ramp, coeff128, rgb_user, coords, axis_x,
1655 hw_points_num, tf_pts);
1657 /* just copy current rgb_regamma into tf_pts */
1658 while (i <= hw_points_num) {
1659 tf_pts->red[i] = regamma->r;
1660 tf_pts->green[i] = regamma->g;
1661 tf_pts->blue[i] = regamma->b;
1668 /* this should be named differently, all it does is clamp to 0-1 */
1669 build_new_custom_resulted_curve(hw_points_num, tf_pts);
1674 #define _EXTRA_POINTS 3
1676 bool calculate_user_regamma_coeff(struct dc_transfer_func *output_tf,
1677 const struct regamma_lut *regamma)
1679 struct gamma_coefficients coeff;
1680 const struct hw_x_point *coord_x = coordinates_x;
1684 coeff.a0[i] = dc_fixpt_from_fraction(
1685 regamma->coeff.A0[i], 10000000);
1686 coeff.a1[i] = dc_fixpt_from_fraction(
1687 regamma->coeff.A1[i], 1000);
1688 coeff.a2[i] = dc_fixpt_from_fraction(
1689 regamma->coeff.A2[i], 1000);
1690 coeff.a3[i] = dc_fixpt_from_fraction(
1691 regamma->coeff.A3[i], 1000);
1692 coeff.user_gamma[i] = dc_fixpt_from_fraction(
1693 regamma->coeff.gamma[i], 1000);
1699 /* fixed_pt library has problems handling too small values */
1701 output_tf->tf_pts.red[i] = dc_fixpt_zero;
1702 output_tf->tf_pts.green[i] = dc_fixpt_zero;
1703 output_tf->tf_pts.blue[i] = dc_fixpt_zero;
1707 while (i != MAX_HW_POINTS + 1) {
1708 output_tf->tf_pts.red[i] = translate_from_linear_space_ex(
1709 coord_x->x, &coeff, 0);
1710 output_tf->tf_pts.green[i] = translate_from_linear_space_ex(
1711 coord_x->x, &coeff, 1);
1712 output_tf->tf_pts.blue[i] = translate_from_linear_space_ex(
1713 coord_x->x, &coeff, 2);
1718 // this function just clamps output to 0-1
1719 build_new_custom_resulted_curve(MAX_HW_POINTS, &output_tf->tf_pts);
1720 output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1725 bool calculate_user_regamma_ramp(struct dc_transfer_func *output_tf,
1726 const struct regamma_lut *regamma)
1728 struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts;
1729 struct dividers dividers;
1731 struct pwl_float_data *rgb_user = NULL;
1732 struct pwl_float_data_ex *rgb_regamma = NULL;
1735 if (regamma == NULL)
1738 output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1740 rgb_user = kcalloc(GAMMA_RGB_256_ENTRIES + _EXTRA_POINTS,
1744 goto rgb_user_alloc_fail;
1746 rgb_regamma = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1747 sizeof(*rgb_regamma),
1750 goto rgb_regamma_alloc_fail;
1752 dividers.divider1 = dc_fixpt_from_fraction(3, 2);
1753 dividers.divider2 = dc_fixpt_from_int(2);
1754 dividers.divider3 = dc_fixpt_from_fraction(5, 2);
1756 scale_user_regamma_ramp(rgb_user, ®amma->ramp, dividers);
1758 if (regamma->flags.bits.applyDegamma == 1) {
1759 apply_degamma_for_user_regamma(rgb_regamma, MAX_HW_POINTS);
1760 copy_rgb_regamma_to_coordinates_x(coordinates_x,
1761 MAX_HW_POINTS, rgb_regamma);
1764 interpolate_user_regamma(MAX_HW_POINTS, rgb_user,
1765 regamma->flags.bits.applyDegamma, tf_pts);
1767 // no custom HDR curves!
1768 tf_pts->end_exponent = 0;
1769 tf_pts->x_point_at_y1_red = 1;
1770 tf_pts->x_point_at_y1_green = 1;
1771 tf_pts->x_point_at_y1_blue = 1;
1773 // this function just clamps output to 0-1
1774 build_new_custom_resulted_curve(MAX_HW_POINTS, tf_pts);
1779 rgb_regamma_alloc_fail:
1781 rgb_user_alloc_fail:
1785 bool mod_color_calculate_degamma_params(struct dc_color_caps *dc_caps,
1786 struct dc_transfer_func *input_tf,
1787 const struct dc_gamma *ramp, bool mapUserRamp)
1789 struct dc_transfer_func_distributed_points *tf_pts = &input_tf->tf_pts;
1790 struct dividers dividers;
1791 struct pwl_float_data *rgb_user = NULL;
1792 struct pwl_float_data_ex *curve = NULL;
1793 struct gamma_pixel *axis_x = NULL;
1794 struct pixel_gamma_point *coeff = NULL;
1795 enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB;
1799 if (input_tf->type == TF_TYPE_BYPASS)
1802 /* we can use hardcoded curve for plain SRGB TF
1803 * If linear, it's bypass if on user ramp
1805 if (input_tf->type == TF_TYPE_PREDEFINED) {
1806 if ((input_tf->tf == TRANSFER_FUNCTION_SRGB ||
1807 input_tf->tf == TRANSFER_FUNCTION_LINEAR) &&
1811 if (dc_caps != NULL &&
1812 dc_caps->dpp.dcn_arch == 1) {
1814 if (input_tf->tf == TRANSFER_FUNCTION_PQ &&
1815 dc_caps->dpp.dgam_rom_caps.pq == 1)
1818 if (input_tf->tf == TRANSFER_FUNCTION_GAMMA22 &&
1819 dc_caps->dpp.dgam_rom_caps.gamma2_2 == 1)
1822 // HLG OOTF not accounted for
1823 if (input_tf->tf == TRANSFER_FUNCTION_HLG &&
1824 dc_caps->dpp.dgam_rom_caps.hlg == 1)
1829 input_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1831 if (mapUserRamp && ramp && ramp->type == GAMMA_RGB_256) {
1832 rgb_user = kvcalloc(ramp->num_entries + _EXTRA_POINTS,
1836 goto rgb_user_alloc_fail;
1838 axis_x = kvcalloc(ramp->num_entries + _EXTRA_POINTS, sizeof(*axis_x),
1841 goto axis_x_alloc_fail;
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);
1847 build_evenly_distributed_points(
1852 scale_gamma(rgb_user, ramp, dividers);
1855 curve = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*curve),
1858 goto curve_alloc_fail;
1860 coeff = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*coeff),
1863 goto coeff_alloc_fail;
1867 if (tf == TRANSFER_FUNCTION_PQ)
1871 else if (tf == TRANSFER_FUNCTION_SRGB ||
1872 tf == TRANSFER_FUNCTION_BT709 ||
1873 tf == TRANSFER_FUNCTION_GAMMA22 ||
1874 tf == TRANSFER_FUNCTION_GAMMA24 ||
1875 tf == TRANSFER_FUNCTION_GAMMA26)
1876 build_degamma(curve,
1880 else if (tf == TRANSFER_FUNCTION_HLG)
1881 build_hlg_degamma(curve,
1885 else if (tf == TRANSFER_FUNCTION_LINEAR) {
1886 // just copy coordinates_x into curve
1888 while (i != MAX_HW_POINTS + 1) {
1889 curve[i].r = coordinates_x[i].x;
1890 curve[i].g = curve[i].r;
1891 curve[i].b = curve[i].r;
1895 goto invalid_tf_fail;
1897 tf_pts->end_exponent = 0;
1898 tf_pts->x_point_at_y1_red = 1;
1899 tf_pts->x_point_at_y1_green = 1;
1900 tf_pts->x_point_at_y1_blue = 1;
1902 if (input_tf->tf == TRANSFER_FUNCTION_PQ) {
1903 /* just copy current rgb_regamma into tf_pts */
1904 struct pwl_float_data_ex *curvePt = curve;
1907 while (i <= MAX_HW_POINTS) {
1908 tf_pts->red[i] = curvePt->r;
1909 tf_pts->green[i] = curvePt->g;
1910 tf_pts->blue[i] = curvePt->b;
1916 map_regamma_hw_to_x_user(ramp, coeff, rgb_user,
1917 coordinates_x, axis_x, curve,
1918 MAX_HW_POINTS, tf_pts,
1919 mapUserRamp && ramp && ramp->type == GAMMA_RGB_256);
1924 if (ramp && ramp->type == GAMMA_CUSTOM)
1925 apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts);
1937 rgb_user_alloc_fail:
1942 static bool calculate_curve(enum dc_transfer_func_predefined trans,
1943 struct dc_transfer_func_distributed_points *points,
1944 struct pwl_float_data_ex *rgb_regamma,
1945 const struct freesync_hdr_tf_params *fs_params,
1946 uint32_t sdr_ref_white_level)
1951 if (trans == TRANSFER_FUNCTION_UNITY ||
1952 trans == TRANSFER_FUNCTION_LINEAR) {
1953 points->end_exponent = 0;
1954 points->x_point_at_y1_red = 1;
1955 points->x_point_at_y1_green = 1;
1956 points->x_point_at_y1_blue = 1;
1958 for (i = 0; i <= MAX_HW_POINTS ; i++) {
1959 rgb_regamma[i].r = coordinates_x[i].x;
1960 rgb_regamma[i].g = coordinates_x[i].x;
1961 rgb_regamma[i].b = coordinates_x[i].x;
1965 } else if (trans == TRANSFER_FUNCTION_PQ) {
1966 points->end_exponent = 7;
1967 points->x_point_at_y1_red = 125;
1968 points->x_point_at_y1_green = 125;
1969 points->x_point_at_y1_blue = 125;
1971 build_pq(rgb_regamma,
1974 sdr_ref_white_level);
1977 } else if (trans == TRANSFER_FUNCTION_GAMMA22 &&
1978 fs_params != NULL && fs_params->skip_tm == 0) {
1979 build_freesync_hdr(rgb_regamma,
1985 } else if (trans == TRANSFER_FUNCTION_HLG) {
1986 points->end_exponent = 4;
1987 points->x_point_at_y1_red = 12;
1988 points->x_point_at_y1_green = 12;
1989 points->x_point_at_y1_blue = 12;
1991 build_hlg_regamma(rgb_regamma,
1998 // trans == TRANSFER_FUNCTION_SRGB
1999 // trans == TRANSFER_FUNCTION_BT709
2000 // trans == TRANSFER_FUNCTION_GAMMA22
2001 // trans == TRANSFER_FUNCTION_GAMMA24
2002 // trans == TRANSFER_FUNCTION_GAMMA26
2003 points->end_exponent = 0;
2004 points->x_point_at_y1_red = 1;
2005 points->x_point_at_y1_green = 1;
2006 points->x_point_at_y1_blue = 1;
2008 build_regamma(rgb_regamma,
2019 bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf,
2020 const struct dc_gamma *ramp, bool mapUserRamp, bool canRomBeUsed,
2021 const struct freesync_hdr_tf_params *fs_params)
2023 struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts;
2024 struct dividers dividers;
2026 struct pwl_float_data *rgb_user = NULL;
2027 struct pwl_float_data_ex *rgb_regamma = NULL;
2028 struct gamma_pixel *axis_x = NULL;
2029 struct pixel_gamma_point *coeff = NULL;
2030 enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB;
2033 if (output_tf->type == TF_TYPE_BYPASS)
2036 /* we can use hardcoded curve for plain SRGB TF */
2037 if (output_tf->type == TF_TYPE_PREDEFINED && canRomBeUsed == true &&
2038 output_tf->tf == TRANSFER_FUNCTION_SRGB) {
2041 if ((ramp->is_identity && ramp->type != GAMMA_CS_TFM_1D) ||
2042 (!mapUserRamp && ramp->type == GAMMA_RGB_256))
2046 output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
2048 if (ramp && ramp->type != GAMMA_CS_TFM_1D &&
2049 (mapUserRamp || ramp->type != GAMMA_RGB_256)) {
2050 rgb_user = kvcalloc(ramp->num_entries + _EXTRA_POINTS,
2054 goto rgb_user_alloc_fail;
2056 axis_x = kvcalloc(ramp->num_entries + 3, sizeof(*axis_x),
2059 goto axis_x_alloc_fail;
2061 dividers.divider1 = dc_fixpt_from_fraction(3, 2);
2062 dividers.divider2 = dc_fixpt_from_int(2);
2063 dividers.divider3 = dc_fixpt_from_fraction(5, 2);
2065 build_evenly_distributed_points(
2070 if (ramp->type == GAMMA_RGB_256 && mapUserRamp)
2071 scale_gamma(rgb_user, ramp, dividers);
2072 else if (ramp->type == GAMMA_RGB_FLOAT_1024)
2073 scale_gamma_dx(rgb_user, ramp, dividers);
2076 rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
2077 sizeof(*rgb_regamma),
2080 goto rgb_regamma_alloc_fail;
2082 coeff = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*coeff),
2085 goto coeff_alloc_fail;
2089 ret = calculate_curve(tf,
2093 output_tf->sdr_ref_white_level);
2096 map_regamma_hw_to_x_user(ramp, coeff, rgb_user,
2097 coordinates_x, axis_x, rgb_regamma,
2098 MAX_HW_POINTS, tf_pts,
2099 (mapUserRamp || (ramp && ramp->type != GAMMA_RGB_256)) &&
2100 (ramp && ramp->type != GAMMA_CS_TFM_1D));
2102 if (ramp && ramp->type == GAMMA_CS_TFM_1D)
2103 apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts);
2108 kvfree(rgb_regamma);
2109 rgb_regamma_alloc_fail:
2113 rgb_user_alloc_fail:
2117 bool mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans,
2118 struct dc_transfer_func_distributed_points *points)
2122 struct pwl_float_data_ex *rgb_degamma = NULL;
2124 if (trans == TRANSFER_FUNCTION_UNITY ||
2125 trans == TRANSFER_FUNCTION_LINEAR) {
2127 for (i = 0; i <= MAX_HW_POINTS ; i++) {
2128 points->red[i] = coordinates_x[i].x;
2129 points->green[i] = coordinates_x[i].x;
2130 points->blue[i] = coordinates_x[i].x;
2133 } else if (trans == TRANSFER_FUNCTION_PQ) {
2134 rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
2135 sizeof(*rgb_degamma),
2138 goto rgb_degamma_alloc_fail;
2141 build_de_pq(rgb_degamma,
2144 for (i = 0; i <= MAX_HW_POINTS ; i++) {
2145 points->red[i] = rgb_degamma[i].r;
2146 points->green[i] = rgb_degamma[i].g;
2147 points->blue[i] = rgb_degamma[i].b;
2151 kvfree(rgb_degamma);
2152 } else if (trans == TRANSFER_FUNCTION_SRGB ||
2153 trans == TRANSFER_FUNCTION_BT709 ||
2154 trans == TRANSFER_FUNCTION_GAMMA22 ||
2155 trans == TRANSFER_FUNCTION_GAMMA24 ||
2156 trans == TRANSFER_FUNCTION_GAMMA26) {
2157 rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
2158 sizeof(*rgb_degamma),
2161 goto rgb_degamma_alloc_fail;
2163 build_degamma(rgb_degamma,
2167 for (i = 0; i <= MAX_HW_POINTS ; i++) {
2168 points->red[i] = rgb_degamma[i].r;
2169 points->green[i] = rgb_degamma[i].g;
2170 points->blue[i] = rgb_degamma[i].b;
2174 kvfree(rgb_degamma);
2175 } else if (trans == TRANSFER_FUNCTION_HLG) {
2176 rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
2177 sizeof(*rgb_degamma),
2180 goto rgb_degamma_alloc_fail;
2182 build_hlg_degamma(rgb_degamma,
2186 for (i = 0; i <= MAX_HW_POINTS ; i++) {
2187 points->red[i] = rgb_degamma[i].r;
2188 points->green[i] = rgb_degamma[i].g;
2189 points->blue[i] = rgb_degamma[i].b;
2192 kvfree(rgb_degamma);
2194 points->end_exponent = 0;
2195 points->x_point_at_y1_red = 1;
2196 points->x_point_at_y1_green = 1;
2197 points->x_point_at_y1_blue = 1;
2199 rgb_degamma_alloc_fail: