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.
28 #include "color_gamma.h"
31 #define NUM_PTS_IN_REGION 16
32 #define NUM_REGIONS 32
33 #define MAX_HW_POINTS (NUM_PTS_IN_REGION*NUM_REGIONS)
35 static struct hw_x_point coordinates_x
[MAX_HW_POINTS
+ 2];
37 static struct fixed31_32 pq_table
[MAX_HW_POINTS
+ 2];
38 static struct fixed31_32 de_pq_table
[MAX_HW_POINTS
+ 2];
40 static bool pq_initialized
; /* = false; */
41 static bool de_pq_initialized
; /* = false; */
43 /* one-time setup of X points */
44 void setup_x_points_distribution(void)
46 struct fixed31_32 region_size
= dal_fixed31_32_from_int(128);
50 struct fixed31_32 increment
;
52 coordinates_x
[MAX_HW_POINTS
].x
= region_size
;
53 coordinates_x
[MAX_HW_POINTS
+ 1].x
= region_size
;
55 for (segment
= 6; segment
> (6 - NUM_REGIONS
); segment
--) {
56 region_size
= dal_fixed31_32_div_int(region_size
, 2);
57 increment
= dal_fixed31_32_div_int(region_size
,
59 seg_offset
= (segment
+ (NUM_REGIONS
- 7)) * NUM_PTS_IN_REGION
;
60 coordinates_x
[seg_offset
].x
= region_size
;
62 for (index
= seg_offset
+ 1;
63 index
< seg_offset
+ NUM_PTS_IN_REGION
;
65 coordinates_x
[index
].x
= dal_fixed31_32_add
66 (coordinates_x
[index
-1].x
, increment
);
71 static void compute_pq(struct fixed31_32 in_x
, struct fixed31_32
*out_y
)
73 /* consts for PQ gamma formula. */
74 const struct fixed31_32 m1
=
75 dal_fixed31_32_from_fraction(159301758, 1000000000);
76 const struct fixed31_32 m2
=
77 dal_fixed31_32_from_fraction(7884375, 100000);
78 const struct fixed31_32 c1
=
79 dal_fixed31_32_from_fraction(8359375, 10000000);
80 const struct fixed31_32 c2
=
81 dal_fixed31_32_from_fraction(188515625, 10000000);
82 const struct fixed31_32 c3
=
83 dal_fixed31_32_from_fraction(186875, 10000);
85 struct fixed31_32 l_pow_m1
;
86 struct fixed31_32 base
;
88 if (dal_fixed31_32_lt(in_x
, dal_fixed31_32_zero
))
89 in_x
= dal_fixed31_32_zero
;
91 l_pow_m1
= dal_fixed31_32_pow(in_x
, m1
);
92 base
= dal_fixed31_32_div(
93 dal_fixed31_32_add(c1
,
94 (dal_fixed31_32_mul(c2
, l_pow_m1
))),
95 dal_fixed31_32_add(dal_fixed31_32_one
,
96 (dal_fixed31_32_mul(c3
, l_pow_m1
))));
97 *out_y
= dal_fixed31_32_pow(base
, m2
);
100 static void compute_de_pq(struct fixed31_32 in_x
, struct fixed31_32
*out_y
)
102 /* consts for dePQ gamma formula. */
103 const struct fixed31_32 m1
=
104 dal_fixed31_32_from_fraction(159301758, 1000000000);
105 const struct fixed31_32 m2
=
106 dal_fixed31_32_from_fraction(7884375, 100000);
107 const struct fixed31_32 c1
=
108 dal_fixed31_32_from_fraction(8359375, 10000000);
109 const struct fixed31_32 c2
=
110 dal_fixed31_32_from_fraction(188515625, 10000000);
111 const struct fixed31_32 c3
=
112 dal_fixed31_32_from_fraction(186875, 10000);
114 struct fixed31_32 l_pow_m1
;
115 struct fixed31_32 base
, div
;
118 if (dal_fixed31_32_lt(in_x
, dal_fixed31_32_zero
))
119 in_x
= dal_fixed31_32_zero
;
121 l_pow_m1
= dal_fixed31_32_pow(in_x
,
122 dal_fixed31_32_div(dal_fixed31_32_one
, m2
));
123 base
= dal_fixed31_32_sub(l_pow_m1
, c1
);
125 if (dal_fixed31_32_lt(base
, dal_fixed31_32_zero
))
126 base
= dal_fixed31_32_zero
;
128 div
= dal_fixed31_32_sub(c2
, dal_fixed31_32_mul(c3
, l_pow_m1
));
130 *out_y
= dal_fixed31_32_pow(dal_fixed31_32_div(base
, div
),
131 dal_fixed31_32_div(dal_fixed31_32_one
, m1
));
134 /* one-time pre-compute PQ values - only for sdr_white_level 80 */
135 void precompute_pq(void)
139 const struct hw_x_point
*coord_x
= coordinates_x
+ 32;
140 struct fixed31_32 scaling_factor
=
141 dal_fixed31_32_from_fraction(80, 10000);
143 /* pow function has problems with arguments too small */
144 for (i
= 0; i
< 32; i
++)
145 pq_table
[i
] = dal_fixed31_32_zero
;
147 for (i
= 32; i
<= MAX_HW_POINTS
; i
++) {
148 x
= dal_fixed31_32_mul(coord_x
->x
, scaling_factor
);
149 compute_pq(x
, &pq_table
[i
]);
154 /* one-time pre-compute dePQ values - only for max pixel value 125 FP16 */
155 void precompute_de_pq(void)
159 uint32_t begin_index
, end_index
;
161 struct fixed31_32 scaling_factor
= dal_fixed31_32_from_int(125);
163 /* X points is 2^-25 to 2^7
164 * De-gamma X is 2^-12 to 2^0 – we are skipping first -12-(-25) = 13 regions
166 begin_index
= 13 * NUM_PTS_IN_REGION
;
167 end_index
= begin_index
+ 12 * NUM_PTS_IN_REGION
;
169 for (i
= 0; i
<= begin_index
; i
++)
170 de_pq_table
[i
] = dal_fixed31_32_zero
;
172 for (; i
<= end_index
; i
++) {
173 compute_de_pq(coordinates_x
[i
].x
, &y
);
174 de_pq_table
[i
] = dal_fixed31_32_mul(y
, scaling_factor
);
177 for (; i
<= MAX_HW_POINTS
; i
++)
178 de_pq_table
[i
] = de_pq_table
[i
-1];
181 struct fixed31_32 divider1
;
182 struct fixed31_32 divider2
;
183 struct fixed31_32 divider3
;
186 static void build_coefficients(struct gamma_coefficients
*coefficients
, bool is_2_4
)
188 static const int32_t numerator01
[] = { 31308, 180000};
189 static const int32_t numerator02
[] = { 12920, 4500};
190 static const int32_t numerator03
[] = { 55, 99};
191 static const int32_t numerator04
[] = { 55, 99};
192 static const int32_t numerator05
[] = { 2400, 2200};
195 uint32_t index
= is_2_4
== true ? 0:1;
198 coefficients
->a0
[i
] = dal_fixed31_32_from_fraction(
199 numerator01
[index
], 10000000);
200 coefficients
->a1
[i
] = dal_fixed31_32_from_fraction(
201 numerator02
[index
], 1000);
202 coefficients
->a2
[i
] = dal_fixed31_32_from_fraction(
203 numerator03
[index
], 1000);
204 coefficients
->a3
[i
] = dal_fixed31_32_from_fraction(
205 numerator04
[index
], 1000);
206 coefficients
->user_gamma
[i
] = dal_fixed31_32_from_fraction(
207 numerator05
[index
], 1000);
210 } while (i
!= ARRAY_SIZE(coefficients
->a0
));
213 static struct fixed31_32
translate_from_linear_space(
214 struct fixed31_32 arg
,
215 struct fixed31_32 a0
,
216 struct fixed31_32 a1
,
217 struct fixed31_32 a2
,
218 struct fixed31_32 a3
,
219 struct fixed31_32 gamma
)
221 const struct fixed31_32 one
= dal_fixed31_32_from_int(1);
223 if (dal_fixed31_32_lt(one
, arg
))
226 if (dal_fixed31_32_le(arg
, dal_fixed31_32_neg(a0
)))
227 return dal_fixed31_32_sub(
234 dal_fixed31_32_neg(arg
),
235 dal_fixed31_32_recip(gamma
))));
236 else if (dal_fixed31_32_le(a0
, arg
))
237 return dal_fixed31_32_sub(
244 dal_fixed31_32_recip(gamma
))),
247 return dal_fixed31_32_mul(
252 static struct fixed31_32
translate_to_linear_space(
253 struct fixed31_32 arg
,
254 struct fixed31_32 a0
,
255 struct fixed31_32 a1
,
256 struct fixed31_32 a2
,
257 struct fixed31_32 a3
,
258 struct fixed31_32 gamma
)
260 struct fixed31_32 linear
;
262 a0
= dal_fixed31_32_mul(a0
, a1
);
263 if (dal_fixed31_32_le(arg
, dal_fixed31_32_neg(a0
)))
265 linear
= dal_fixed31_32_neg(
268 dal_fixed31_32_sub(a2
, arg
),
270 dal_fixed31_32_one
, a3
)), gamma
));
272 else if (dal_fixed31_32_le(dal_fixed31_32_neg(a0
), arg
) &&
273 dal_fixed31_32_le(arg
, a0
))
274 linear
= dal_fixed31_32_div(arg
, a1
);
276 linear
= dal_fixed31_32_pow(
278 dal_fixed31_32_add(a2
, arg
),
280 dal_fixed31_32_one
, a3
)), gamma
);
285 static inline struct fixed31_32
translate_from_linear_space_ex(
286 struct fixed31_32 arg
,
287 struct gamma_coefficients
*coeff
,
288 uint32_t color_index
)
290 return translate_from_linear_space(
292 coeff
->a0
[color_index
],
293 coeff
->a1
[color_index
],
294 coeff
->a2
[color_index
],
295 coeff
->a3
[color_index
],
296 coeff
->user_gamma
[color_index
]);
300 static inline struct fixed31_32
translate_to_linear_space_ex(
301 struct fixed31_32 arg
,
302 struct gamma_coefficients
*coeff
,
303 uint32_t color_index
)
305 return translate_to_linear_space(
307 coeff
->a0
[color_index
],
308 coeff
->a1
[color_index
],
309 coeff
->a2
[color_index
],
310 coeff
->a3
[color_index
],
311 coeff
->user_gamma
[color_index
]);
315 static bool find_software_points(
316 const struct dc_gamma
*ramp
,
317 const struct gamma_pixel
*axis_x
,
318 struct fixed31_32 hw_point
,
319 enum channel_name channel
,
320 uint32_t *index_to_start
,
321 uint32_t *index_left
,
322 uint32_t *index_right
,
323 enum hw_point_position
*pos
)
325 const uint32_t max_number
= ramp
->num_entries
+ 3;
327 struct fixed31_32 left
, right
;
329 uint32_t i
= *index_to_start
;
331 while (i
< max_number
) {
332 if (channel
== CHANNEL_NAME_RED
) {
335 if (i
< max_number
- 1)
336 right
= axis_x
[i
+ 1].r
;
338 right
= axis_x
[max_number
- 1].r
;
339 } else if (channel
== CHANNEL_NAME_GREEN
) {
342 if (i
< max_number
- 1)
343 right
= axis_x
[i
+ 1].g
;
345 right
= axis_x
[max_number
- 1].g
;
349 if (i
< max_number
- 1)
350 right
= axis_x
[i
+ 1].b
;
352 right
= axis_x
[max_number
- 1].b
;
355 if (dal_fixed31_32_le(left
, hw_point
) &&
356 dal_fixed31_32_le(hw_point
, right
)) {
360 if (i
< max_number
- 1)
361 *index_right
= i
+ 1;
363 *index_right
= max_number
- 1;
365 *pos
= HW_POINT_POSITION_MIDDLE
;
368 } else if ((i
== *index_to_start
) &&
369 dal_fixed31_32_le(hw_point
, left
)) {
374 *pos
= HW_POINT_POSITION_LEFT
;
377 } else if ((i
== max_number
- 1) &&
378 dal_fixed31_32_le(right
, hw_point
)) {
383 *pos
= HW_POINT_POSITION_RIGHT
;
394 static bool build_custom_gamma_mapping_coefficients_worker(
395 const struct dc_gamma
*ramp
,
396 struct pixel_gamma_point
*coeff
,
397 const struct hw_x_point
*coordinates_x
,
398 const struct gamma_pixel
*axis_x
,
399 enum channel_name channel
,
400 uint32_t number_of_points
)
404 while (i
<= number_of_points
) {
405 struct fixed31_32 coord_x
;
407 uint32_t index_to_start
= 0;
408 uint32_t index_left
= 0;
409 uint32_t index_right
= 0;
411 enum hw_point_position hw_pos
;
413 struct gamma_point
*point
;
415 struct fixed31_32 left_pos
;
416 struct fixed31_32 right_pos
;
418 if (channel
== CHANNEL_NAME_RED
)
419 coord_x
= coordinates_x
[i
].regamma_y_red
;
420 else if (channel
== CHANNEL_NAME_GREEN
)
421 coord_x
= coordinates_x
[i
].regamma_y_green
;
423 coord_x
= coordinates_x
[i
].regamma_y_blue
;
425 if (!find_software_points(
426 ramp
, axis_x
, coord_x
, channel
,
427 &index_to_start
, &index_left
, &index_right
, &hw_pos
)) {
432 if (index_left
>= ramp
->num_entries
+ 3) {
437 if (index_right
>= ramp
->num_entries
+ 3) {
442 if (channel
== CHANNEL_NAME_RED
) {
445 left_pos
= axis_x
[index_left
].r
;
446 right_pos
= axis_x
[index_right
].r
;
447 } else if (channel
== CHANNEL_NAME_GREEN
) {
450 left_pos
= axis_x
[index_left
].g
;
451 right_pos
= axis_x
[index_right
].g
;
455 left_pos
= axis_x
[index_left
].b
;
456 right_pos
= axis_x
[index_right
].b
;
459 if (hw_pos
== HW_POINT_POSITION_MIDDLE
)
460 point
->coeff
= dal_fixed31_32_div(
467 else if (hw_pos
== HW_POINT_POSITION_LEFT
)
468 point
->coeff
= dal_fixed31_32_zero
;
469 else if (hw_pos
== HW_POINT_POSITION_RIGHT
)
470 point
->coeff
= dal_fixed31_32_from_int(2);
476 point
->left_index
= index_left
;
477 point
->right_index
= index_right
;
486 static struct fixed31_32
calculate_mapped_value(
487 struct pwl_float_data
*rgb
,
488 const struct pixel_gamma_point
*coeff
,
489 enum channel_name channel
,
492 const struct gamma_point
*point
;
494 struct fixed31_32 result
;
496 if (channel
== CHANNEL_NAME_RED
)
498 else if (channel
== CHANNEL_NAME_GREEN
)
503 if ((point
->left_index
< 0) || (point
->left_index
> max_index
)) {
505 return dal_fixed31_32_zero
;
508 if ((point
->right_index
< 0) || (point
->right_index
> max_index
)) {
510 return dal_fixed31_32_zero
;
513 if (point
->pos
== HW_POINT_POSITION_MIDDLE
)
514 if (channel
== CHANNEL_NAME_RED
)
515 result
= dal_fixed31_32_add(
519 rgb
[point
->right_index
].r
,
520 rgb
[point
->left_index
].r
)),
521 rgb
[point
->left_index
].r
);
522 else if (channel
== CHANNEL_NAME_GREEN
)
523 result
= dal_fixed31_32_add(
527 rgb
[point
->right_index
].g
,
528 rgb
[point
->left_index
].g
)),
529 rgb
[point
->left_index
].g
);
531 result
= dal_fixed31_32_add(
535 rgb
[point
->right_index
].b
,
536 rgb
[point
->left_index
].b
)),
537 rgb
[point
->left_index
].b
);
538 else if (point
->pos
== HW_POINT_POSITION_LEFT
) {
540 result
= dal_fixed31_32_zero
;
543 result
= dal_fixed31_32_one
;
549 static void build_pq(struct pwl_float_data_ex
*rgb_regamma
,
550 uint32_t hw_points_num
,
551 const struct hw_x_point
*coordinate_x
,
552 uint32_t sdr_white_level
)
554 uint32_t i
, start_index
;
556 struct pwl_float_data_ex
*rgb
= rgb_regamma
;
557 const struct hw_x_point
*coord_x
= coordinate_x
;
559 struct fixed31_32 output
;
560 struct fixed31_32 scaling_factor
=
561 dal_fixed31_32_from_fraction(sdr_white_level
, 10000);
563 if (!pq_initialized
&& sdr_white_level
== 80) {
565 pq_initialized
= true;
568 /* TODO: start index is from segment 2^-24, skipping first segment
569 * due to x values too small for power calculations
573 coord_x
+= start_index
;
575 for (i
= start_index
; i
<= hw_points_num
; i
++) {
576 /* Multiply 0.008 as regamma is 0-1 and FP16 input is 0-125.
579 if (sdr_white_level
== 80) {
580 output
= pq_table
[i
];
582 x
= dal_fixed31_32_mul(coord_x
->x
, scaling_factor
);
583 compute_pq(x
, &output
);
586 /* should really not happen? */
587 if (dal_fixed31_32_lt(output
, dal_fixed31_32_zero
))
588 output
= dal_fixed31_32_zero
;
589 else if (dal_fixed31_32_lt(dal_fixed31_32_one
, output
))
590 output
= dal_fixed31_32_one
;
601 static void build_de_pq(struct pwl_float_data_ex
*de_pq
,
602 uint32_t hw_points_num
,
603 const struct hw_x_point
*coordinate_x
)
606 struct fixed31_32 output
;
608 struct fixed31_32 scaling_factor
= dal_fixed31_32_from_int(125);
610 if (!de_pq_initialized
) {
612 de_pq_initialized
= true;
616 for (i
= 0; i
<= hw_points_num
; i
++) {
617 output
= de_pq_table
[i
];
618 /* should really not happen? */
619 if (dal_fixed31_32_lt(output
, dal_fixed31_32_zero
))
620 output
= dal_fixed31_32_zero
;
621 else if (dal_fixed31_32_lt(scaling_factor
, output
))
622 output
= scaling_factor
;
629 static void build_regamma(struct pwl_float_data_ex
*rgb_regamma
,
630 uint32_t hw_points_num
,
631 const struct hw_x_point
*coordinate_x
, bool is_2_4
)
635 struct gamma_coefficients coeff
;
636 struct pwl_float_data_ex
*rgb
= rgb_regamma
;
637 const struct hw_x_point
*coord_x
= coordinate_x
;
639 build_coefficients(&coeff
, is_2_4
);
643 while (i
!= hw_points_num
+ 1) {
644 /*TODO use y vs r,g,b*/
645 rgb
->r
= translate_from_linear_space_ex(
646 coord_x
->x
, &coeff
, 0);
655 static void build_degamma(struct pwl_float_data_ex
*curve
,
656 uint32_t hw_points_num
,
657 const struct hw_x_point
*coordinate_x
, bool is_2_4
)
660 struct gamma_coefficients coeff
;
661 uint32_t begin_index
, end_index
;
663 build_coefficients(&coeff
, is_2_4
);
666 /* X points is 2^-25 to 2^7
667 * De-gamma X is 2^-12 to 2^0 – we are skipping first -12-(-25) = 13 regions
669 begin_index
= 13 * NUM_PTS_IN_REGION
;
670 end_index
= begin_index
+ 12 * NUM_PTS_IN_REGION
;
672 while (i
!= begin_index
) {
673 curve
[i
].r
= dal_fixed31_32_zero
;
674 curve
[i
].g
= dal_fixed31_32_zero
;
675 curve
[i
].b
= dal_fixed31_32_zero
;
679 while (i
!= end_index
) {
680 curve
[i
].r
= translate_to_linear_space_ex(
681 coordinate_x
[i
].x
, &coeff
, 0);
682 curve
[i
].g
= curve
[i
].r
;
683 curve
[i
].b
= curve
[i
].r
;
686 while (i
!= hw_points_num
+ 1) {
687 curve
[i
].r
= dal_fixed31_32_one
;
688 curve
[i
].g
= dal_fixed31_32_one
;
689 curve
[i
].b
= dal_fixed31_32_one
;
694 static bool scale_gamma(struct pwl_float_data
*pwl_rgb
,
695 const struct dc_gamma
*ramp
,
696 struct dividers dividers
)
698 const struct fixed31_32 max_driver
= dal_fixed31_32_from_int(0xFFFF);
699 const struct fixed31_32 max_os
= dal_fixed31_32_from_int(0xFF00);
700 struct fixed31_32 scaler
= max_os
;
702 struct pwl_float_data
*rgb
= pwl_rgb
;
703 struct pwl_float_data
*rgb_last
= rgb
+ ramp
->num_entries
- 1;
708 if (dal_fixed31_32_lt(max_os
, ramp
->entries
.red
[i
]) ||
709 dal_fixed31_32_lt(max_os
, ramp
->entries
.green
[i
]) ||
710 dal_fixed31_32_lt(max_os
, ramp
->entries
.blue
[i
])) {
715 } while (i
!= ramp
->num_entries
);
720 rgb
->r
= dal_fixed31_32_div(
721 ramp
->entries
.red
[i
], scaler
);
722 rgb
->g
= dal_fixed31_32_div(
723 ramp
->entries
.green
[i
], scaler
);
724 rgb
->b
= dal_fixed31_32_div(
725 ramp
->entries
.blue
[i
], scaler
);
729 } while (i
!= ramp
->num_entries
);
731 rgb
->r
= dal_fixed31_32_mul(rgb_last
->r
,
733 rgb
->g
= dal_fixed31_32_mul(rgb_last
->g
,
735 rgb
->b
= dal_fixed31_32_mul(rgb_last
->b
,
740 rgb
->r
= dal_fixed31_32_mul(rgb_last
->r
,
742 rgb
->g
= dal_fixed31_32_mul(rgb_last
->g
,
744 rgb
->b
= dal_fixed31_32_mul(rgb_last
->b
,
749 rgb
->r
= dal_fixed31_32_mul(rgb_last
->r
,
751 rgb
->g
= dal_fixed31_32_mul(rgb_last
->g
,
753 rgb
->b
= dal_fixed31_32_mul(rgb_last
->b
,
759 static bool scale_gamma_dx(struct pwl_float_data
*pwl_rgb
,
760 const struct dc_gamma
*ramp
,
761 struct dividers dividers
)
764 struct fixed31_32 min
= dal_fixed31_32_zero
;
765 struct fixed31_32 max
= dal_fixed31_32_one
;
767 struct fixed31_32 delta
= dal_fixed31_32_zero
;
768 struct fixed31_32 offset
= dal_fixed31_32_zero
;
770 for (i
= 0 ; i
< ramp
->num_entries
; i
++) {
771 if (dal_fixed31_32_lt(ramp
->entries
.red
[i
], min
))
772 min
= ramp
->entries
.red
[i
];
774 if (dal_fixed31_32_lt(ramp
->entries
.green
[i
], min
))
775 min
= ramp
->entries
.green
[i
];
777 if (dal_fixed31_32_lt(ramp
->entries
.blue
[i
], min
))
778 min
= ramp
->entries
.blue
[i
];
780 if (dal_fixed31_32_lt(max
, ramp
->entries
.red
[i
]))
781 max
= ramp
->entries
.red
[i
];
783 if (dal_fixed31_32_lt(max
, ramp
->entries
.green
[i
]))
784 max
= ramp
->entries
.green
[i
];
786 if (dal_fixed31_32_lt(max
, ramp
->entries
.blue
[i
]))
787 max
= ramp
->entries
.blue
[i
];
790 if (dal_fixed31_32_lt(min
, dal_fixed31_32_zero
))
791 delta
= dal_fixed31_32_neg(min
);
793 offset
= dal_fixed31_32_add(min
, max
);
795 for (i
= 0 ; i
< ramp
->num_entries
; i
++) {
796 pwl_rgb
[i
].r
= dal_fixed31_32_div(
798 ramp
->entries
.red
[i
], delta
), offset
);
799 pwl_rgb
[i
].g
= dal_fixed31_32_div(
801 ramp
->entries
.green
[i
], delta
), offset
);
802 pwl_rgb
[i
].b
= dal_fixed31_32_div(
804 ramp
->entries
.blue
[i
], delta
), offset
);
808 pwl_rgb
[i
].r
= dal_fixed31_32_sub(dal_fixed31_32_mul_int(
809 pwl_rgb
[i
-1].r
, 2), pwl_rgb
[i
-2].r
);
810 pwl_rgb
[i
].g
= dal_fixed31_32_sub(dal_fixed31_32_mul_int(
811 pwl_rgb
[i
-1].g
, 2), pwl_rgb
[i
-2].g
);
812 pwl_rgb
[i
].b
= dal_fixed31_32_sub(dal_fixed31_32_mul_int(
813 pwl_rgb
[i
-1].b
, 2), pwl_rgb
[i
-2].b
);
815 pwl_rgb
[i
].r
= dal_fixed31_32_sub(dal_fixed31_32_mul_int(
816 pwl_rgb
[i
-1].r
, 2), pwl_rgb
[i
-2].r
);
817 pwl_rgb
[i
].g
= dal_fixed31_32_sub(dal_fixed31_32_mul_int(
818 pwl_rgb
[i
-1].g
, 2), pwl_rgb
[i
-2].g
);
819 pwl_rgb
[i
].b
= dal_fixed31_32_sub(dal_fixed31_32_mul_int(
820 pwl_rgb
[i
-1].b
, 2), pwl_rgb
[i
-2].b
);
826 * RS3+ color transform DDI - 1D LUT adjustment is composed with regamma here
827 * Input is evenly distributed in the output color space as specified in
830 * Interpolation details:
831 * 1D LUT has 4096 values which give curve correction in 0-1 float range
832 * for evenly spaced points in 0-1 range. lut1D[index] gives correction
834 * First we find index for which:
835 * index/4095 < regamma_y < (index+1)/4095 =>
836 * index < 4095*regamma_y < index + 1
837 * norm_y = 4095*regamma_y, and index is just truncating to nearest integer
838 * lut1 = lut1D[index], lut2 = lut1D[index+1]
840 *adjustedY is then linearly interpolating regamma Y between lut1 and lut2
842 static void apply_lut_1d(
843 const struct dc_gamma
*ramp
,
844 uint32_t num_hw_points
,
845 struct dc_transfer_func_distributed_points
*tf_pts
)
849 struct fixed31_32
*regamma_y
;
850 struct fixed31_32 norm_y
;
851 struct fixed31_32 lut1
;
852 struct fixed31_32 lut2
;
853 const int max_lut_index
= 4095;
854 const struct fixed31_32 max_lut_index_f
=
855 dal_fixed31_32_from_int_nonconst(max_lut_index
);
856 int32_t index
= 0, index_next
= 0;
857 struct fixed31_32 index_f
;
858 struct fixed31_32 delta_lut
;
859 struct fixed31_32 delta_index
;
861 if (ramp
->type
!= GAMMA_CS_TFM_1D
)
862 return; // this is not expected
864 for (i
= 0; i
< num_hw_points
; i
++) {
865 for (color
= 0; color
< 3; color
++) {
867 regamma_y
= &tf_pts
->red
[i
];
869 regamma_y
= &tf_pts
->green
[i
];
871 regamma_y
= &tf_pts
->blue
[i
];
873 norm_y
= dal_fixed31_32_mul(max_lut_index_f
,
875 index
= dal_fixed31_32_floor(norm_y
);
876 index_f
= dal_fixed31_32_from_int_nonconst(index
);
878 if (index
< 0 || index
> max_lut_index
)
881 index_next
= (index
== max_lut_index
) ? index
: index
+1;
884 lut1
= ramp
->entries
.red
[index
];
885 lut2
= ramp
->entries
.red
[index_next
];
886 } else if (color
== 1) {
887 lut1
= ramp
->entries
.green
[index
];
888 lut2
= ramp
->entries
.green
[index_next
];
890 lut1
= ramp
->entries
.blue
[index
];
891 lut2
= ramp
->entries
.blue
[index_next
];
894 // we have everything now, so interpolate
895 delta_lut
= dal_fixed31_32_sub(lut2
, lut1
);
896 delta_index
= dal_fixed31_32_sub(norm_y
, index_f
);
898 *regamma_y
= dal_fixed31_32_add(lut1
,
899 dal_fixed31_32_mul(delta_index
, delta_lut
));
904 static void build_evenly_distributed_points(
905 struct gamma_pixel
*points
,
906 uint32_t numberof_points
,
907 struct dividers dividers
)
909 struct gamma_pixel
*p
= points
;
910 struct gamma_pixel
*p_last
= p
+ numberof_points
- 1;
915 struct fixed31_32 value
= dal_fixed31_32_from_fraction(i
,
916 numberof_points
- 1);
924 } while (i
!= numberof_points
);
926 p
->r
= dal_fixed31_32_div(p_last
->r
, dividers
.divider1
);
927 p
->g
= dal_fixed31_32_div(p_last
->g
, dividers
.divider1
);
928 p
->b
= dal_fixed31_32_div(p_last
->b
, dividers
.divider1
);
932 p
->r
= dal_fixed31_32_div(p_last
->r
, dividers
.divider2
);
933 p
->g
= dal_fixed31_32_div(p_last
->g
, dividers
.divider2
);
934 p
->b
= dal_fixed31_32_div(p_last
->b
, dividers
.divider2
);
938 p
->r
= dal_fixed31_32_div(p_last
->r
, dividers
.divider3
);
939 p
->g
= dal_fixed31_32_div(p_last
->g
, dividers
.divider3
);
940 p
->b
= dal_fixed31_32_div(p_last
->b
, dividers
.divider3
);
943 static inline void copy_rgb_regamma_to_coordinates_x(
944 struct hw_x_point
*coordinates_x
,
945 uint32_t hw_points_num
,
946 const struct pwl_float_data_ex
*rgb_ex
)
948 struct hw_x_point
*coords
= coordinates_x
;
950 const struct pwl_float_data_ex
*rgb_regamma
= rgb_ex
;
952 while (i
<= hw_points_num
) {
953 coords
->regamma_y_red
= rgb_regamma
->r
;
954 coords
->regamma_y_green
= rgb_regamma
->g
;
955 coords
->regamma_y_blue
= rgb_regamma
->b
;
963 static bool calculate_interpolated_hardware_curve(
964 const struct dc_gamma
*ramp
,
965 struct pixel_gamma_point
*coeff128
,
966 struct pwl_float_data
*rgb_user
,
967 const struct hw_x_point
*coordinates_x
,
968 const struct gamma_pixel
*axis_x
,
969 uint32_t number_of_points
,
970 struct dc_transfer_func_distributed_points
*tf_pts
)
973 const struct pixel_gamma_point
*coeff
= coeff128
;
974 uint32_t max_entries
= 3 - 1;
978 for (i
= 0; i
< 3; i
++) {
979 if (!build_custom_gamma_mapping_coefficients_worker(
980 ramp
, coeff128
, coordinates_x
, axis_x
, i
,
986 max_entries
+= ramp
->num_entries
;
988 /* TODO: float point case */
990 while (i
<= number_of_points
) {
991 tf_pts
->red
[i
] = calculate_mapped_value(
992 rgb_user
, coeff
, CHANNEL_NAME_RED
, max_entries
);
993 tf_pts
->green
[i
] = calculate_mapped_value(
994 rgb_user
, coeff
, CHANNEL_NAME_GREEN
, max_entries
);
995 tf_pts
->blue
[i
] = calculate_mapped_value(
996 rgb_user
, coeff
, CHANNEL_NAME_BLUE
, max_entries
);
1005 static void build_new_custom_resulted_curve(
1006 uint32_t hw_points_num
,
1007 struct dc_transfer_func_distributed_points
*tf_pts
)
1013 while (i
!= hw_points_num
+ 1) {
1014 tf_pts
->red
[i
] = dal_fixed31_32_clamp(
1015 tf_pts
->red
[i
], dal_fixed31_32_zero
,
1016 dal_fixed31_32_one
);
1017 tf_pts
->green
[i
] = dal_fixed31_32_clamp(
1018 tf_pts
->green
[i
], dal_fixed31_32_zero
,
1019 dal_fixed31_32_one
);
1020 tf_pts
->blue
[i
] = dal_fixed31_32_clamp(
1021 tf_pts
->blue
[i
], dal_fixed31_32_zero
,
1022 dal_fixed31_32_one
);
1028 static bool map_regamma_hw_to_x_user(
1029 const struct dc_gamma
*ramp
,
1030 struct pixel_gamma_point
*coeff128
,
1031 struct pwl_float_data
*rgb_user
,
1032 struct hw_x_point
*coords_x
,
1033 const struct gamma_pixel
*axis_x
,
1034 const struct pwl_float_data_ex
*rgb_regamma
,
1035 uint32_t hw_points_num
,
1036 struct dc_transfer_func_distributed_points
*tf_pts
,
1039 /* setup to spare calculated ideal regamma values */
1042 struct hw_x_point
*coords
= coords_x
;
1043 const struct pwl_float_data_ex
*regamma
= rgb_regamma
;
1046 copy_rgb_regamma_to_coordinates_x(coords
,
1050 calculate_interpolated_hardware_curve(
1051 ramp
, coeff128
, rgb_user
, coords
, axis_x
,
1052 hw_points_num
, tf_pts
);
1054 /* just copy current rgb_regamma into tf_pts */
1055 while (i
<= hw_points_num
) {
1056 tf_pts
->red
[i
] = regamma
->r
;
1057 tf_pts
->green
[i
] = regamma
->g
;
1058 tf_pts
->blue
[i
] = regamma
->b
;
1065 build_new_custom_resulted_curve(hw_points_num
, tf_pts
);
1070 #define _EXTRA_POINTS 3
1072 bool mod_color_calculate_regamma_params(struct dc_transfer_func
*output_tf
,
1073 const struct dc_gamma
*ramp
, bool mapUserRamp
)
1075 struct dc_transfer_func_distributed_points
*tf_pts
= &output_tf
->tf_pts
;
1076 struct dividers dividers
;
1078 struct pwl_float_data
*rgb_user
= NULL
;
1079 struct pwl_float_data_ex
*rgb_regamma
= NULL
;
1080 struct gamma_pixel
*axix_x
= NULL
;
1081 struct pixel_gamma_point
*coeff
= NULL
;
1082 enum dc_transfer_func_predefined tf
= TRANSFER_FUNCTION_SRGB
;
1085 if (output_tf
->type
== TF_TYPE_BYPASS
)
1088 /* we can use hardcoded curve for plain SRGB TF */
1089 if (output_tf
->type
== TF_TYPE_PREDEFINED
&&
1090 output_tf
->tf
== TRANSFER_FUNCTION_SRGB
&&
1091 (!mapUserRamp
&& ramp
->type
== GAMMA_RGB_256
))
1094 output_tf
->type
= TF_TYPE_DISTRIBUTED_POINTS
;
1096 rgb_user
= kzalloc(sizeof(*rgb_user
) * (ramp
->num_entries
+ _EXTRA_POINTS
),
1099 goto rgb_user_alloc_fail
;
1100 rgb_regamma
= kzalloc(sizeof(*rgb_regamma
) * (MAX_HW_POINTS
+ _EXTRA_POINTS
),
1103 goto rgb_regamma_alloc_fail
;
1104 axix_x
= kzalloc(sizeof(*axix_x
) * (ramp
->num_entries
+ 3),
1107 goto axix_x_alloc_fail
;
1108 coeff
= kzalloc(sizeof(*coeff
) * (MAX_HW_POINTS
+ _EXTRA_POINTS
), GFP_KERNEL
);
1110 goto coeff_alloc_fail
;
1112 dividers
.divider1
= dal_fixed31_32_from_fraction(3, 2);
1113 dividers
.divider2
= dal_fixed31_32_from_int(2);
1114 dividers
.divider3
= dal_fixed31_32_from_fraction(5, 2);
1118 build_evenly_distributed_points(
1123 if (ramp
->type
== GAMMA_RGB_256
&& mapUserRamp
)
1124 scale_gamma(rgb_user
, ramp
, dividers
);
1125 else if (ramp
->type
== GAMMA_RGB_FLOAT_1024
)
1126 scale_gamma_dx(rgb_user
, ramp
, dividers
);
1128 if (tf
== TRANSFER_FUNCTION_PQ
) {
1129 tf_pts
->end_exponent
= 7;
1130 tf_pts
->x_point_at_y1_red
= 125;
1131 tf_pts
->x_point_at_y1_green
= 125;
1132 tf_pts
->x_point_at_y1_blue
= 125;
1134 build_pq(rgb_regamma
,
1137 output_tf
->sdr_ref_white_level
);
1139 tf_pts
->end_exponent
= 0;
1140 tf_pts
->x_point_at_y1_red
= 1;
1141 tf_pts
->x_point_at_y1_green
= 1;
1142 tf_pts
->x_point_at_y1_blue
= 1;
1144 build_regamma(rgb_regamma
,
1146 coordinates_x
, tf
== TRANSFER_FUNCTION_SRGB
? true:false);
1149 map_regamma_hw_to_x_user(ramp
, coeff
, rgb_user
,
1150 coordinates_x
, axix_x
, rgb_regamma
,
1151 MAX_HW_POINTS
, tf_pts
,
1152 (mapUserRamp
|| ramp
->type
!= GAMMA_RGB_256
) &&
1153 ramp
->type
!= GAMMA_CS_TFM_1D
);
1155 if (ramp
->type
== GAMMA_CS_TFM_1D
)
1156 apply_lut_1d(ramp
, MAX_HW_POINTS
, tf_pts
);
1165 rgb_regamma_alloc_fail
:
1167 rgb_user_alloc_fail
:
1171 bool mod_color_calculate_degamma_params(struct dc_transfer_func
*input_tf
,
1172 const struct dc_gamma
*ramp
, bool mapUserRamp
)
1174 struct dc_transfer_func_distributed_points
*tf_pts
= &input_tf
->tf_pts
;
1175 struct dividers dividers
;
1177 struct pwl_float_data
*rgb_user
= NULL
;
1178 struct pwl_float_data_ex
*curve
= NULL
;
1179 struct gamma_pixel
*axix_x
= NULL
;
1180 struct pixel_gamma_point
*coeff
= NULL
;
1181 enum dc_transfer_func_predefined tf
= TRANSFER_FUNCTION_SRGB
;
1184 if (input_tf
->type
== TF_TYPE_BYPASS
)
1187 /* we can use hardcoded curve for plain SRGB TF */
1188 if (input_tf
->type
== TF_TYPE_PREDEFINED
&&
1189 input_tf
->tf
== TRANSFER_FUNCTION_SRGB
&&
1190 (!mapUserRamp
&& ramp
->type
== GAMMA_RGB_256
))
1193 input_tf
->type
= TF_TYPE_DISTRIBUTED_POINTS
;
1195 rgb_user
= kzalloc(sizeof(*rgb_user
) * (ramp
->num_entries
+ _EXTRA_POINTS
),
1198 goto rgb_user_alloc_fail
;
1199 curve
= kzalloc(sizeof(*curve
) * (MAX_HW_POINTS
+ _EXTRA_POINTS
),
1202 goto curve_alloc_fail
;
1203 axix_x
= kzalloc(sizeof(*axix_x
) * (ramp
->num_entries
+ _EXTRA_POINTS
),
1206 goto axix_x_alloc_fail
;
1207 coeff
= kzalloc(sizeof(*coeff
) * (MAX_HW_POINTS
+ _EXTRA_POINTS
), GFP_KERNEL
);
1209 goto coeff_alloc_fail
;
1211 dividers
.divider1
= dal_fixed31_32_from_fraction(3, 2);
1212 dividers
.divider2
= dal_fixed31_32_from_int(2);
1213 dividers
.divider3
= dal_fixed31_32_from_fraction(5, 2);
1217 build_evenly_distributed_points(
1222 if (ramp
->type
== GAMMA_RGB_256
&& mapUserRamp
)
1223 scale_gamma(rgb_user
, ramp
, dividers
);
1224 else if (ramp
->type
== GAMMA_RGB_FLOAT_1024
)
1225 scale_gamma_dx(rgb_user
, ramp
, dividers
);
1227 if (tf
== TRANSFER_FUNCTION_PQ
)
1232 build_degamma(curve
,
1235 tf
== TRANSFER_FUNCTION_SRGB
? true:false);
1237 tf_pts
->end_exponent
= 0;
1238 tf_pts
->x_point_at_y1_red
= 1;
1239 tf_pts
->x_point_at_y1_green
= 1;
1240 tf_pts
->x_point_at_y1_blue
= 1;
1242 map_regamma_hw_to_x_user(ramp
, coeff
, rgb_user
,
1243 coordinates_x
, axix_x
, curve
,
1244 MAX_HW_POINTS
, tf_pts
,
1256 rgb_user_alloc_fail
:
1263 bool mod_color_calculate_curve(enum dc_transfer_func_predefined trans
,
1264 struct dc_transfer_func_distributed_points
*points
)
1268 struct pwl_float_data_ex
*rgb_regamma
= NULL
;
1270 if (trans
== TRANSFER_FUNCTION_UNITY
||
1271 trans
== TRANSFER_FUNCTION_LINEAR
) {
1272 points
->end_exponent
= 0;
1273 points
->x_point_at_y1_red
= 1;
1274 points
->x_point_at_y1_green
= 1;
1275 points
->x_point_at_y1_blue
= 1;
1277 for (i
= 0; i
<= MAX_HW_POINTS
; i
++) {
1278 points
->red
[i
] = coordinates_x
[i
].x
;
1279 points
->green
[i
] = coordinates_x
[i
].x
;
1280 points
->blue
[i
] = coordinates_x
[i
].x
;
1283 } else if (trans
== TRANSFER_FUNCTION_PQ
) {
1284 rgb_regamma
= kzalloc(sizeof(*rgb_regamma
) * (MAX_HW_POINTS
+
1285 _EXTRA_POINTS
), GFP_KERNEL
);
1287 goto rgb_regamma_alloc_fail
;
1288 points
->end_exponent
= 7;
1289 points
->x_point_at_y1_red
= 125;
1290 points
->x_point_at_y1_green
= 125;
1291 points
->x_point_at_y1_blue
= 125;
1294 build_pq(rgb_regamma
,
1298 for (i
= 0; i
<= MAX_HW_POINTS
; i
++) {
1299 points
->red
[i
] = rgb_regamma
[i
].r
;
1300 points
->green
[i
] = rgb_regamma
[i
].g
;
1301 points
->blue
[i
] = rgb_regamma
[i
].b
;
1306 } else if (trans
== TRANSFER_FUNCTION_SRGB
||
1307 trans
== TRANSFER_FUNCTION_BT709
) {
1308 rgb_regamma
= kzalloc(sizeof(*rgb_regamma
) * (MAX_HW_POINTS
+
1309 _EXTRA_POINTS
), GFP_KERNEL
);
1311 goto rgb_regamma_alloc_fail
;
1312 points
->end_exponent
= 0;
1313 points
->x_point_at_y1_red
= 1;
1314 points
->x_point_at_y1_green
= 1;
1315 points
->x_point_at_y1_blue
= 1;
1317 build_regamma(rgb_regamma
,
1319 coordinates_x
, trans
== TRANSFER_FUNCTION_SRGB
? true:false);
1320 for (i
= 0; i
<= MAX_HW_POINTS
; i
++) {
1321 points
->red
[i
] = rgb_regamma
[i
].r
;
1322 points
->green
[i
] = rgb_regamma
[i
].g
;
1323 points
->blue
[i
] = rgb_regamma
[i
].b
;
1329 rgb_regamma_alloc_fail
:
1334 bool mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans
,
1335 struct dc_transfer_func_distributed_points
*points
)
1339 struct pwl_float_data_ex
*rgb_degamma
= NULL
;
1341 if (trans
== TRANSFER_FUNCTION_UNITY
||
1342 trans
== TRANSFER_FUNCTION_LINEAR
) {
1344 for (i
= 0; i
<= MAX_HW_POINTS
; i
++) {
1345 points
->red
[i
] = coordinates_x
[i
].x
;
1346 points
->green
[i
] = coordinates_x
[i
].x
;
1347 points
->blue
[i
] = coordinates_x
[i
].x
;
1350 } else if (trans
== TRANSFER_FUNCTION_PQ
) {
1351 rgb_degamma
= kzalloc(sizeof(*rgb_degamma
) * (MAX_HW_POINTS
+
1352 _EXTRA_POINTS
), GFP_KERNEL
);
1354 goto rgb_degamma_alloc_fail
;
1357 build_de_pq(rgb_degamma
,
1360 for (i
= 0; i
<= MAX_HW_POINTS
; i
++) {
1361 points
->red
[i
] = rgb_degamma
[i
].r
;
1362 points
->green
[i
] = rgb_degamma
[i
].g
;
1363 points
->blue
[i
] = rgb_degamma
[i
].b
;
1368 } else if (trans
== TRANSFER_FUNCTION_SRGB
||
1369 trans
== TRANSFER_FUNCTION_BT709
) {
1370 rgb_degamma
= kzalloc(sizeof(*rgb_degamma
) * (MAX_HW_POINTS
+
1371 _EXTRA_POINTS
), GFP_KERNEL
);
1373 goto rgb_degamma_alloc_fail
;
1375 build_degamma(rgb_degamma
,
1377 coordinates_x
, trans
== TRANSFER_FUNCTION_SRGB
? true:false);
1378 for (i
= 0; i
<= MAX_HW_POINTS
; i
++) {
1379 points
->red
[i
] = rgb_degamma
[i
].r
;
1380 points
->green
[i
] = rgb_degamma
[i
].g
;
1381 points
->blue
[i
] = rgb_degamma
[i
].b
;
1387 points
->end_exponent
= 0;
1388 points
->x_point_at_y1_red
= 1;
1389 points
->x_point_at_y1_green
= 1;
1390 points
->x_point_at_y1_blue
= 1;
1392 rgb_degamma_alloc_fail
: