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