Commit | Line | Data |
---|---|---|
ec7e6bb8 LSL |
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 "dc.h" | |
27 | #include "opp.h" | |
28 | #include "color_gamma.h" | |
29 | ||
792474b7 | 30 | |
ec7e6bb8 LSL |
31 | #define NUM_PTS_IN_REGION 16 |
32 | #define NUM_REGIONS 32 | |
792474b7 | 33 | #define MAX_HW_POINTS (NUM_PTS_IN_REGION*NUM_REGIONS) |
ec7e6bb8 LSL |
34 | |
35 | static struct hw_x_point coordinates_x[MAX_HW_POINTS + 2]; | |
792474b7 | 36 | |
ec7e6bb8 | 37 | static struct fixed31_32 pq_table[MAX_HW_POINTS + 2]; |
44c6f2e5 | 38 | static struct fixed31_32 de_pq_table[MAX_HW_POINTS + 2]; |
792474b7 | 39 | |
ec7e6bb8 | 40 | static bool pq_initialized; /* = false; */ |
792474b7 | 41 | static bool de_pq_initialized; /* = false; */ |
ec7e6bb8 LSL |
42 | |
43 | /* one-time setup of X points */ | |
44 | void setup_x_points_distribution(void) | |
45 | { | |
eb0e5154 | 46 | struct fixed31_32 region_size = dc_fixpt_from_int(128); |
ec7e6bb8 LSL |
47 | int32_t segment; |
48 | uint32_t seg_offset; | |
49 | uint32_t index; | |
50 | struct fixed31_32 increment; | |
51 | ||
792474b7 VP |
52 | coordinates_x[MAX_HW_POINTS].x = region_size; |
53 | coordinates_x[MAX_HW_POINTS + 1].x = region_size; | |
ec7e6bb8 LSL |
54 | |
55 | for (segment = 6; segment > (6 - NUM_REGIONS); segment--) { | |
eb0e5154 DL |
56 | region_size = dc_fixpt_div_int(region_size, 2); |
57 | increment = dc_fixpt_div_int(region_size, | |
ec7e6bb8 LSL |
58 | NUM_PTS_IN_REGION); |
59 | seg_offset = (segment + (NUM_REGIONS - 7)) * NUM_PTS_IN_REGION; | |
60 | coordinates_x[seg_offset].x = region_size; | |
61 | ||
62 | for (index = seg_offset + 1; | |
63 | index < seg_offset + NUM_PTS_IN_REGION; | |
64 | index++) { | |
eb0e5154 | 65 | coordinates_x[index].x = dc_fixpt_add |
ec7e6bb8 LSL |
66 | (coordinates_x[index-1].x, increment); |
67 | } | |
68 | } | |
69 | } | |
70 | ||
71 | static void compute_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y) | |
72 | { | |
73 | /* consts for PQ gamma formula. */ | |
74 | const struct fixed31_32 m1 = | |
eb0e5154 | 75 | dc_fixpt_from_fraction(159301758, 1000000000); |
ec7e6bb8 | 76 | const struct fixed31_32 m2 = |
eb0e5154 | 77 | dc_fixpt_from_fraction(7884375, 100000); |
ec7e6bb8 | 78 | const struct fixed31_32 c1 = |
eb0e5154 | 79 | dc_fixpt_from_fraction(8359375, 10000000); |
ec7e6bb8 | 80 | const struct fixed31_32 c2 = |
eb0e5154 | 81 | dc_fixpt_from_fraction(188515625, 10000000); |
ec7e6bb8 | 82 | const struct fixed31_32 c3 = |
eb0e5154 | 83 | dc_fixpt_from_fraction(186875, 10000); |
ec7e6bb8 LSL |
84 | |
85 | struct fixed31_32 l_pow_m1; | |
86 | struct fixed31_32 base; | |
87 | ||
eb0e5154 DL |
88 | if (dc_fixpt_lt(in_x, dc_fixpt_zero)) |
89 | in_x = dc_fixpt_zero; | |
ec7e6bb8 | 90 | |
eb0e5154 DL |
91 | l_pow_m1 = dc_fixpt_pow(in_x, m1); |
92 | base = dc_fixpt_div( | |
93 | dc_fixpt_add(c1, | |
94 | (dc_fixpt_mul(c2, l_pow_m1))), | |
95 | dc_fixpt_add(dc_fixpt_one, | |
96 | (dc_fixpt_mul(c3, l_pow_m1)))); | |
97 | *out_y = dc_fixpt_pow(base, m2); | |
ec7e6bb8 LSL |
98 | } |
99 | ||
792474b7 VP |
100 | static void compute_de_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y) |
101 | { | |
102 | /* consts for dePQ gamma formula. */ | |
103 | const struct fixed31_32 m1 = | |
eb0e5154 | 104 | dc_fixpt_from_fraction(159301758, 1000000000); |
792474b7 | 105 | const struct fixed31_32 m2 = |
eb0e5154 | 106 | dc_fixpt_from_fraction(7884375, 100000); |
792474b7 | 107 | const struct fixed31_32 c1 = |
eb0e5154 | 108 | dc_fixpt_from_fraction(8359375, 10000000); |
792474b7 | 109 | const struct fixed31_32 c2 = |
eb0e5154 | 110 | dc_fixpt_from_fraction(188515625, 10000000); |
792474b7 | 111 | const struct fixed31_32 c3 = |
eb0e5154 | 112 | dc_fixpt_from_fraction(186875, 10000); |
792474b7 VP |
113 | |
114 | struct fixed31_32 l_pow_m1; | |
115 | struct fixed31_32 base, div; | |
116 | ||
117 | ||
eb0e5154 DL |
118 | if (dc_fixpt_lt(in_x, dc_fixpt_zero)) |
119 | in_x = dc_fixpt_zero; | |
792474b7 | 120 | |
eb0e5154 DL |
121 | l_pow_m1 = dc_fixpt_pow(in_x, |
122 | dc_fixpt_div(dc_fixpt_one, m2)); | |
123 | base = dc_fixpt_sub(l_pow_m1, c1); | |
792474b7 | 124 | |
eb0e5154 DL |
125 | if (dc_fixpt_lt(base, dc_fixpt_zero)) |
126 | base = dc_fixpt_zero; | |
792474b7 | 127 | |
eb0e5154 | 128 | div = dc_fixpt_sub(c2, dc_fixpt_mul(c3, l_pow_m1)); |
792474b7 | 129 | |
eb0e5154 DL |
130 | *out_y = dc_fixpt_pow(dc_fixpt_div(base, div), |
131 | dc_fixpt_div(dc_fixpt_one, m1)); | |
792474b7 VP |
132 | |
133 | } | |
ec7e6bb8 LSL |
134 | /* one-time pre-compute PQ values - only for sdr_white_level 80 */ |
135 | void precompute_pq(void) | |
136 | { | |
137 | int i; | |
138 | struct fixed31_32 x; | |
139 | const struct hw_x_point *coord_x = coordinates_x + 32; | |
140 | struct fixed31_32 scaling_factor = | |
eb0e5154 | 141 | dc_fixpt_from_fraction(80, 10000); |
ec7e6bb8 LSL |
142 | |
143 | /* pow function has problems with arguments too small */ | |
144 | for (i = 0; i < 32; i++) | |
eb0e5154 | 145 | pq_table[i] = dc_fixpt_zero; |
ec7e6bb8 LSL |
146 | |
147 | for (i = 32; i <= MAX_HW_POINTS; i++) { | |
eb0e5154 | 148 | x = dc_fixpt_mul(coord_x->x, scaling_factor); |
ec7e6bb8 LSL |
149 | compute_pq(x, &pq_table[i]); |
150 | ++coord_x; | |
151 | } | |
152 | } | |
153 | ||
792474b7 VP |
154 | /* one-time pre-compute dePQ values - only for max pixel value 125 FP16 */ |
155 | void precompute_de_pq(void) | |
156 | { | |
157 | int i; | |
158 | struct fixed31_32 y; | |
44c6f2e5 VP |
159 | uint32_t begin_index, end_index; |
160 | ||
eb0e5154 | 161 | struct fixed31_32 scaling_factor = dc_fixpt_from_int(125); |
792474b7 | 162 | |
44c6f2e5 VP |
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 | |
165 | */ | |
166 | begin_index = 13 * NUM_PTS_IN_REGION; | |
167 | end_index = begin_index + 12 * NUM_PTS_IN_REGION; | |
792474b7 | 168 | |
44c6f2e5 | 169 | for (i = 0; i <= begin_index; i++) |
eb0e5154 | 170 | de_pq_table[i] = dc_fixpt_zero; |
44c6f2e5 VP |
171 | |
172 | for (; i <= end_index; i++) { | |
173 | compute_de_pq(coordinates_x[i].x, &y); | |
eb0e5154 | 174 | de_pq_table[i] = dc_fixpt_mul(y, scaling_factor); |
792474b7 | 175 | } |
44c6f2e5 VP |
176 | |
177 | for (; i <= MAX_HW_POINTS; i++) | |
178 | de_pq_table[i] = de_pq_table[i-1]; | |
792474b7 | 179 | } |
ec7e6bb8 LSL |
180 | struct dividers { |
181 | struct fixed31_32 divider1; | |
182 | struct fixed31_32 divider2; | |
183 | struct fixed31_32 divider3; | |
184 | }; | |
185 | ||
792474b7 | 186 | static void build_coefficients(struct gamma_coefficients *coefficients, bool is_2_4) |
ec7e6bb8 | 187 | { |
55a01d40 KK |
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}; | |
ec7e6bb8 | 193 | |
55a01d40 KK |
194 | uint32_t i = 0; |
195 | uint32_t index = is_2_4 == true ? 0:1; | |
ec7e6bb8 LSL |
196 | |
197 | do { | |
eb0e5154 | 198 | coefficients->a0[i] = dc_fixpt_from_fraction( |
792474b7 | 199 | numerator01[index], 10000000); |
eb0e5154 | 200 | coefficients->a1[i] = dc_fixpt_from_fraction( |
792474b7 | 201 | numerator02[index], 1000); |
eb0e5154 | 202 | coefficients->a2[i] = dc_fixpt_from_fraction( |
792474b7 | 203 | numerator03[index], 1000); |
eb0e5154 | 204 | coefficients->a3[i] = dc_fixpt_from_fraction( |
792474b7 | 205 | numerator04[index], 1000); |
eb0e5154 | 206 | coefficients->user_gamma[i] = dc_fixpt_from_fraction( |
792474b7 | 207 | numerator05[index], 1000); |
ec7e6bb8 LSL |
208 | |
209 | ++i; | |
210 | } while (i != ARRAY_SIZE(coefficients->a0)); | |
211 | } | |
212 | ||
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) | |
220 | { | |
eb0e5154 | 221 | const struct fixed31_32 one = dc_fixpt_from_int(1); |
ec7e6bb8 | 222 | |
eb0e5154 | 223 | if (dc_fixpt_lt(one, arg)) |
ec7e6bb8 LSL |
224 | return one; |
225 | ||
eb0e5154 DL |
226 | if (dc_fixpt_le(arg, dc_fixpt_neg(a0))) |
227 | return dc_fixpt_sub( | |
ec7e6bb8 | 228 | a2, |
eb0e5154 DL |
229 | dc_fixpt_mul( |
230 | dc_fixpt_add( | |
ec7e6bb8 LSL |
231 | one, |
232 | a3), | |
eb0e5154 DL |
233 | dc_fixpt_pow( |
234 | dc_fixpt_neg(arg), | |
235 | dc_fixpt_recip(gamma)))); | |
236 | else if (dc_fixpt_le(a0, arg)) | |
237 | return dc_fixpt_sub( | |
238 | dc_fixpt_mul( | |
239 | dc_fixpt_add( | |
ec7e6bb8 LSL |
240 | one, |
241 | a3), | |
eb0e5154 | 242 | dc_fixpt_pow( |
ec7e6bb8 | 243 | arg, |
eb0e5154 | 244 | dc_fixpt_recip(gamma))), |
ec7e6bb8 LSL |
245 | a2); |
246 | else | |
eb0e5154 | 247 | return dc_fixpt_mul( |
ec7e6bb8 LSL |
248 | arg, |
249 | a1); | |
250 | } | |
251 | ||
792474b7 VP |
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) | |
259 | { | |
260 | struct fixed31_32 linear; | |
261 | ||
eb0e5154 DL |
262 | a0 = dc_fixpt_mul(a0, a1); |
263 | if (dc_fixpt_le(arg, dc_fixpt_neg(a0))) | |
792474b7 | 264 | |
eb0e5154 DL |
265 | linear = dc_fixpt_neg( |
266 | dc_fixpt_pow( | |
267 | dc_fixpt_div( | |
268 | dc_fixpt_sub(a2, arg), | |
269 | dc_fixpt_add( | |
270 | dc_fixpt_one, a3)), gamma)); | |
792474b7 | 271 | |
eb0e5154 DL |
272 | else if (dc_fixpt_le(dc_fixpt_neg(a0), arg) && |
273 | dc_fixpt_le(arg, a0)) | |
274 | linear = dc_fixpt_div(arg, a1); | |
792474b7 | 275 | else |
eb0e5154 DL |
276 | linear = dc_fixpt_pow( |
277 | dc_fixpt_div( | |
278 | dc_fixpt_add(a2, arg), | |
279 | dc_fixpt_add( | |
280 | dc_fixpt_one, a3)), gamma); | |
792474b7 VP |
281 | |
282 | return linear; | |
283 | } | |
284 | ||
ec7e6bb8 LSL |
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) | |
289 | { | |
290 | return translate_from_linear_space( | |
291 | arg, | |
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]); | |
297 | } | |
298 | ||
792474b7 VP |
299 | |
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) | |
304 | { | |
305 | return translate_to_linear_space( | |
306 | arg, | |
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]); | |
312 | } | |
313 | ||
314 | ||
ec7e6bb8 LSL |
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) | |
324 | { | |
325 | const uint32_t max_number = ramp->num_entries + 3; | |
326 | ||
327 | struct fixed31_32 left, right; | |
328 | ||
329 | uint32_t i = *index_to_start; | |
330 | ||
331 | while (i < max_number) { | |
332 | if (channel == CHANNEL_NAME_RED) { | |
333 | left = axis_x[i].r; | |
334 | ||
335 | if (i < max_number - 1) | |
336 | right = axis_x[i + 1].r; | |
337 | else | |
338 | right = axis_x[max_number - 1].r; | |
339 | } else if (channel == CHANNEL_NAME_GREEN) { | |
340 | left = axis_x[i].g; | |
341 | ||
342 | if (i < max_number - 1) | |
343 | right = axis_x[i + 1].g; | |
344 | else | |
345 | right = axis_x[max_number - 1].g; | |
346 | } else { | |
347 | left = axis_x[i].b; | |
348 | ||
349 | if (i < max_number - 1) | |
350 | right = axis_x[i + 1].b; | |
351 | else | |
352 | right = axis_x[max_number - 1].b; | |
353 | } | |
354 | ||
eb0e5154 DL |
355 | if (dc_fixpt_le(left, hw_point) && |
356 | dc_fixpt_le(hw_point, right)) { | |
ec7e6bb8 LSL |
357 | *index_to_start = i; |
358 | *index_left = i; | |
359 | ||
360 | if (i < max_number - 1) | |
361 | *index_right = i + 1; | |
362 | else | |
363 | *index_right = max_number - 1; | |
364 | ||
365 | *pos = HW_POINT_POSITION_MIDDLE; | |
366 | ||
367 | return true; | |
368 | } else if ((i == *index_to_start) && | |
eb0e5154 | 369 | dc_fixpt_le(hw_point, left)) { |
ec7e6bb8 LSL |
370 | *index_to_start = i; |
371 | *index_left = i; | |
372 | *index_right = i; | |
373 | ||
374 | *pos = HW_POINT_POSITION_LEFT; | |
375 | ||
376 | return true; | |
377 | } else if ((i == max_number - 1) && | |
eb0e5154 | 378 | dc_fixpt_le(right, hw_point)) { |
ec7e6bb8 LSL |
379 | *index_to_start = i; |
380 | *index_left = i; | |
381 | *index_right = i; | |
382 | ||
383 | *pos = HW_POINT_POSITION_RIGHT; | |
384 | ||
385 | return true; | |
386 | } | |
387 | ||
388 | ++i; | |
389 | } | |
390 | ||
391 | return false; | |
392 | } | |
393 | ||
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) | |
401 | { | |
402 | uint32_t i = 0; | |
403 | ||
404 | while (i <= number_of_points) { | |
405 | struct fixed31_32 coord_x; | |
406 | ||
407 | uint32_t index_to_start = 0; | |
408 | uint32_t index_left = 0; | |
409 | uint32_t index_right = 0; | |
410 | ||
411 | enum hw_point_position hw_pos; | |
412 | ||
413 | struct gamma_point *point; | |
414 | ||
415 | struct fixed31_32 left_pos; | |
416 | struct fixed31_32 right_pos; | |
417 | ||
ec7e6bb8 LSL |
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; | |
422 | else | |
423 | coord_x = coordinates_x[i].regamma_y_blue; | |
424 | ||
425 | if (!find_software_points( | |
426 | ramp, axis_x, coord_x, channel, | |
427 | &index_to_start, &index_left, &index_right, &hw_pos)) { | |
428 | BREAK_TO_DEBUGGER(); | |
429 | return false; | |
430 | } | |
431 | ||
432 | if (index_left >= ramp->num_entries + 3) { | |
433 | BREAK_TO_DEBUGGER(); | |
434 | return false; | |
435 | } | |
436 | ||
437 | if (index_right >= ramp->num_entries + 3) { | |
438 | BREAK_TO_DEBUGGER(); | |
439 | return false; | |
440 | } | |
441 | ||
442 | if (channel == CHANNEL_NAME_RED) { | |
443 | point = &coeff[i].r; | |
444 | ||
445 | left_pos = axis_x[index_left].r; | |
446 | right_pos = axis_x[index_right].r; | |
447 | } else if (channel == CHANNEL_NAME_GREEN) { | |
448 | point = &coeff[i].g; | |
449 | ||
450 | left_pos = axis_x[index_left].g; | |
451 | right_pos = axis_x[index_right].g; | |
452 | } else { | |
453 | point = &coeff[i].b; | |
454 | ||
455 | left_pos = axis_x[index_left].b; | |
456 | right_pos = axis_x[index_right].b; | |
457 | } | |
458 | ||
459 | if (hw_pos == HW_POINT_POSITION_MIDDLE) | |
eb0e5154 DL |
460 | point->coeff = dc_fixpt_div( |
461 | dc_fixpt_sub( | |
ec7e6bb8 LSL |
462 | coord_x, |
463 | left_pos), | |
eb0e5154 | 464 | dc_fixpt_sub( |
ec7e6bb8 LSL |
465 | right_pos, |
466 | left_pos)); | |
467 | else if (hw_pos == HW_POINT_POSITION_LEFT) | |
eb0e5154 | 468 | point->coeff = dc_fixpt_zero; |
ec7e6bb8 | 469 | else if (hw_pos == HW_POINT_POSITION_RIGHT) |
eb0e5154 | 470 | point->coeff = dc_fixpt_from_int(2); |
ec7e6bb8 LSL |
471 | else { |
472 | BREAK_TO_DEBUGGER(); | |
473 | return false; | |
474 | } | |
475 | ||
476 | point->left_index = index_left; | |
477 | point->right_index = index_right; | |
478 | point->pos = hw_pos; | |
479 | ||
480 | ++i; | |
481 | } | |
482 | ||
483 | return true; | |
484 | } | |
485 | ||
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, | |
490 | uint32_t max_index) | |
491 | { | |
492 | const struct gamma_point *point; | |
493 | ||
494 | struct fixed31_32 result; | |
495 | ||
496 | if (channel == CHANNEL_NAME_RED) | |
497 | point = &coeff->r; | |
498 | else if (channel == CHANNEL_NAME_GREEN) | |
499 | point = &coeff->g; | |
500 | else | |
501 | point = &coeff->b; | |
502 | ||
503 | if ((point->left_index < 0) || (point->left_index > max_index)) { | |
504 | BREAK_TO_DEBUGGER(); | |
eb0e5154 | 505 | return dc_fixpt_zero; |
ec7e6bb8 LSL |
506 | } |
507 | ||
508 | if ((point->right_index < 0) || (point->right_index > max_index)) { | |
509 | BREAK_TO_DEBUGGER(); | |
eb0e5154 | 510 | return dc_fixpt_zero; |
ec7e6bb8 LSL |
511 | } |
512 | ||
513 | if (point->pos == HW_POINT_POSITION_MIDDLE) | |
514 | if (channel == CHANNEL_NAME_RED) | |
eb0e5154 DL |
515 | result = dc_fixpt_add( |
516 | dc_fixpt_mul( | |
ec7e6bb8 | 517 | point->coeff, |
eb0e5154 | 518 | dc_fixpt_sub( |
ec7e6bb8 LSL |
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) | |
eb0e5154 DL |
523 | result = dc_fixpt_add( |
524 | dc_fixpt_mul( | |
ec7e6bb8 | 525 | point->coeff, |
eb0e5154 | 526 | dc_fixpt_sub( |
ec7e6bb8 LSL |
527 | rgb[point->right_index].g, |
528 | rgb[point->left_index].g)), | |
529 | rgb[point->left_index].g); | |
530 | else | |
eb0e5154 DL |
531 | result = dc_fixpt_add( |
532 | dc_fixpt_mul( | |
ec7e6bb8 | 533 | point->coeff, |
eb0e5154 | 534 | dc_fixpt_sub( |
ec7e6bb8 LSL |
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) { | |
539 | BREAK_TO_DEBUGGER(); | |
eb0e5154 | 540 | result = dc_fixpt_zero; |
ec7e6bb8 LSL |
541 | } else { |
542 | BREAK_TO_DEBUGGER(); | |
eb0e5154 | 543 | result = dc_fixpt_one; |
ec7e6bb8 LSL |
544 | } |
545 | ||
546 | return result; | |
547 | } | |
548 | ||
792474b7 | 549 | static void build_pq(struct pwl_float_data_ex *rgb_regamma, |
ec7e6bb8 LSL |
550 | uint32_t hw_points_num, |
551 | const struct hw_x_point *coordinate_x, | |
552 | uint32_t sdr_white_level) | |
553 | { | |
554 | uint32_t i, start_index; | |
555 | ||
556 | struct pwl_float_data_ex *rgb = rgb_regamma; | |
557 | const struct hw_x_point *coord_x = coordinate_x; | |
558 | struct fixed31_32 x; | |
559 | struct fixed31_32 output; | |
560 | struct fixed31_32 scaling_factor = | |
eb0e5154 | 561 | dc_fixpt_from_fraction(sdr_white_level, 10000); |
ec7e6bb8 LSL |
562 | |
563 | if (!pq_initialized && sdr_white_level == 80) { | |
564 | precompute_pq(); | |
565 | pq_initialized = true; | |
566 | } | |
567 | ||
568 | /* TODO: start index is from segment 2^-24, skipping first segment | |
569 | * due to x values too small for power calculations | |
570 | */ | |
571 | start_index = 32; | |
572 | rgb += start_index; | |
573 | coord_x += start_index; | |
574 | ||
ec7e6bb8 LSL |
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. | |
577 | * FP 1.0 = 80nits | |
578 | */ | |
579 | if (sdr_white_level == 80) { | |
580 | output = pq_table[i]; | |
581 | } else { | |
eb0e5154 | 582 | x = dc_fixpt_mul(coord_x->x, scaling_factor); |
ec7e6bb8 LSL |
583 | compute_pq(x, &output); |
584 | } | |
585 | ||
586 | /* should really not happen? */ | |
eb0e5154 DL |
587 | if (dc_fixpt_lt(output, dc_fixpt_zero)) |
588 | output = dc_fixpt_zero; | |
589 | else if (dc_fixpt_lt(dc_fixpt_one, output)) | |
590 | output = dc_fixpt_one; | |
ec7e6bb8 LSL |
591 | |
592 | rgb->r = output; | |
593 | rgb->g = output; | |
594 | rgb->b = output; | |
595 | ||
596 | ++coord_x; | |
597 | ++rgb; | |
598 | } | |
599 | } | |
600 | ||
792474b7 | 601 | static void build_de_pq(struct pwl_float_data_ex *de_pq, |
ec7e6bb8 LSL |
602 | uint32_t hw_points_num, |
603 | const struct hw_x_point *coordinate_x) | |
604 | { | |
605 | uint32_t i; | |
792474b7 VP |
606 | struct fixed31_32 output; |
607 | ||
eb0e5154 | 608 | struct fixed31_32 scaling_factor = dc_fixpt_from_int(125); |
792474b7 VP |
609 | |
610 | if (!de_pq_initialized) { | |
611 | precompute_de_pq(); | |
612 | de_pq_initialized = true; | |
613 | } | |
614 | ||
615 | ||
616 | for (i = 0; i <= hw_points_num; i++) { | |
617 | output = de_pq_table[i]; | |
618 | /* should really not happen? */ | |
eb0e5154 DL |
619 | if (dc_fixpt_lt(output, dc_fixpt_zero)) |
620 | output = dc_fixpt_zero; | |
621 | else if (dc_fixpt_lt(scaling_factor, output)) | |
792474b7 | 622 | output = scaling_factor; |
44c6f2e5 VP |
623 | de_pq[i].r = output; |
624 | de_pq[i].g = output; | |
625 | de_pq[i].b = output; | |
792474b7 VP |
626 | } |
627 | } | |
628 | ||
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) | |
632 | { | |
633 | uint32_t i; | |
ec7e6bb8 LSL |
634 | |
635 | struct gamma_coefficients coeff; | |
636 | struct pwl_float_data_ex *rgb = rgb_regamma; | |
637 | const struct hw_x_point *coord_x = coordinate_x; | |
638 | ||
792474b7 | 639 | build_coefficients(&coeff, is_2_4); |
ec7e6bb8 LSL |
640 | |
641 | i = 0; | |
642 | ||
643 | while (i != hw_points_num + 1) { | |
792474b7 | 644 | /*TODO use y vs r,g,b*/ |
ec7e6bb8 LSL |
645 | rgb->r = translate_from_linear_space_ex( |
646 | coord_x->x, &coeff, 0); | |
792474b7 VP |
647 | rgb->g = rgb->r; |
648 | rgb->b = rgb->r; | |
649 | ++coord_x; | |
650 | ++rgb; | |
651 | ++i; | |
652 | } | |
653 | } | |
654 | ||
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) | |
658 | { | |
659 | uint32_t i; | |
792474b7 | 660 | struct gamma_coefficients coeff; |
44c6f2e5 | 661 | uint32_t begin_index, end_index; |
792474b7 VP |
662 | |
663 | build_coefficients(&coeff, is_2_4); | |
792474b7 VP |
664 | i = 0; |
665 | ||
44c6f2e5 VP |
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 | |
668 | */ | |
669 | begin_index = 13 * NUM_PTS_IN_REGION; | |
670 | end_index = begin_index + 12 * NUM_PTS_IN_REGION; | |
671 | ||
672 | while (i != begin_index) { | |
eb0e5154 DL |
673 | curve[i].r = dc_fixpt_zero; |
674 | curve[i].g = dc_fixpt_zero; | |
675 | curve[i].b = dc_fixpt_zero; | |
44c6f2e5 VP |
676 | i++; |
677 | } | |
678 | ||
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; | |
684 | i++; | |
685 | } | |
792474b7 | 686 | while (i != hw_points_num + 1) { |
eb0e5154 DL |
687 | curve[i].r = dc_fixpt_one; |
688 | curve[i].g = dc_fixpt_one; | |
689 | curve[i].b = dc_fixpt_one; | |
44c6f2e5 | 690 | i++; |
ec7e6bb8 LSL |
691 | } |
692 | } | |
693 | ||
55a01d40 | 694 | static void scale_gamma(struct pwl_float_data *pwl_rgb, |
ec7e6bb8 LSL |
695 | const struct dc_gamma *ramp, |
696 | struct dividers dividers) | |
697 | { | |
eb0e5154 DL |
698 | const struct fixed31_32 max_driver = dc_fixpt_from_int(0xFFFF); |
699 | const struct fixed31_32 max_os = dc_fixpt_from_int(0xFF00); | |
ec7e6bb8 LSL |
700 | struct fixed31_32 scaler = max_os; |
701 | uint32_t i; | |
702 | struct pwl_float_data *rgb = pwl_rgb; | |
703 | struct pwl_float_data *rgb_last = rgb + ramp->num_entries - 1; | |
704 | ||
705 | i = 0; | |
706 | ||
707 | do { | |
eb0e5154 DL |
708 | if (dc_fixpt_lt(max_os, ramp->entries.red[i]) || |
709 | dc_fixpt_lt(max_os, ramp->entries.green[i]) || | |
710 | dc_fixpt_lt(max_os, ramp->entries.blue[i])) { | |
ec7e6bb8 LSL |
711 | scaler = max_driver; |
712 | break; | |
713 | } | |
714 | ++i; | |
715 | } while (i != ramp->num_entries); | |
716 | ||
717 | i = 0; | |
718 | ||
719 | do { | |
eb0e5154 | 720 | rgb->r = dc_fixpt_div( |
ec7e6bb8 | 721 | ramp->entries.red[i], scaler); |
eb0e5154 | 722 | rgb->g = dc_fixpt_div( |
ec7e6bb8 | 723 | ramp->entries.green[i], scaler); |
eb0e5154 | 724 | rgb->b = dc_fixpt_div( |
ec7e6bb8 LSL |
725 | ramp->entries.blue[i], scaler); |
726 | ||
727 | ++rgb; | |
728 | ++i; | |
729 | } while (i != ramp->num_entries); | |
730 | ||
eb0e5154 | 731 | rgb->r = dc_fixpt_mul(rgb_last->r, |
ec7e6bb8 | 732 | dividers.divider1); |
eb0e5154 | 733 | rgb->g = dc_fixpt_mul(rgb_last->g, |
ec7e6bb8 | 734 | dividers.divider1); |
eb0e5154 | 735 | rgb->b = dc_fixpt_mul(rgb_last->b, |
ec7e6bb8 LSL |
736 | dividers.divider1); |
737 | ||
738 | ++rgb; | |
739 | ||
eb0e5154 | 740 | rgb->r = dc_fixpt_mul(rgb_last->r, |
ec7e6bb8 | 741 | dividers.divider2); |
eb0e5154 | 742 | rgb->g = dc_fixpt_mul(rgb_last->g, |
ec7e6bb8 | 743 | dividers.divider2); |
eb0e5154 | 744 | rgb->b = dc_fixpt_mul(rgb_last->b, |
ec7e6bb8 LSL |
745 | dividers.divider2); |
746 | ||
747 | ++rgb; | |
748 | ||
eb0e5154 | 749 | rgb->r = dc_fixpt_mul(rgb_last->r, |
ec7e6bb8 | 750 | dividers.divider3); |
eb0e5154 | 751 | rgb->g = dc_fixpt_mul(rgb_last->g, |
ec7e6bb8 | 752 | dividers.divider3); |
eb0e5154 | 753 | rgb->b = dc_fixpt_mul(rgb_last->b, |
ec7e6bb8 | 754 | dividers.divider3); |
ec7e6bb8 LSL |
755 | } |
756 | ||
55a01d40 | 757 | static void scale_gamma_dx(struct pwl_float_data *pwl_rgb, |
ec7e6bb8 LSL |
758 | const struct dc_gamma *ramp, |
759 | struct dividers dividers) | |
760 | { | |
761 | uint32_t i; | |
eb0e5154 DL |
762 | struct fixed31_32 min = dc_fixpt_zero; |
763 | struct fixed31_32 max = dc_fixpt_one; | |
ec7e6bb8 | 764 | |
eb0e5154 DL |
765 | struct fixed31_32 delta = dc_fixpt_zero; |
766 | struct fixed31_32 offset = dc_fixpt_zero; | |
ec7e6bb8 LSL |
767 | |
768 | for (i = 0 ; i < ramp->num_entries; i++) { | |
eb0e5154 | 769 | if (dc_fixpt_lt(ramp->entries.red[i], min)) |
ec7e6bb8 LSL |
770 | min = ramp->entries.red[i]; |
771 | ||
eb0e5154 | 772 | if (dc_fixpt_lt(ramp->entries.green[i], min)) |
ec7e6bb8 LSL |
773 | min = ramp->entries.green[i]; |
774 | ||
eb0e5154 | 775 | if (dc_fixpt_lt(ramp->entries.blue[i], min)) |
ec7e6bb8 LSL |
776 | min = ramp->entries.blue[i]; |
777 | ||
eb0e5154 | 778 | if (dc_fixpt_lt(max, ramp->entries.red[i])) |
ec7e6bb8 LSL |
779 | max = ramp->entries.red[i]; |
780 | ||
eb0e5154 | 781 | if (dc_fixpt_lt(max, ramp->entries.green[i])) |
ec7e6bb8 LSL |
782 | max = ramp->entries.green[i]; |
783 | ||
eb0e5154 | 784 | if (dc_fixpt_lt(max, ramp->entries.blue[i])) |
ec7e6bb8 LSL |
785 | max = ramp->entries.blue[i]; |
786 | } | |
787 | ||
eb0e5154 DL |
788 | if (dc_fixpt_lt(min, dc_fixpt_zero)) |
789 | delta = dc_fixpt_neg(min); | |
ec7e6bb8 | 790 | |
eb0e5154 | 791 | offset = dc_fixpt_add(min, max); |
ec7e6bb8 LSL |
792 | |
793 | for (i = 0 ; i < ramp->num_entries; i++) { | |
eb0e5154 DL |
794 | pwl_rgb[i].r = dc_fixpt_div( |
795 | dc_fixpt_add( | |
ec7e6bb8 | 796 | ramp->entries.red[i], delta), offset); |
eb0e5154 DL |
797 | pwl_rgb[i].g = dc_fixpt_div( |
798 | dc_fixpt_add( | |
ec7e6bb8 | 799 | ramp->entries.green[i], delta), offset); |
eb0e5154 DL |
800 | pwl_rgb[i].b = dc_fixpt_div( |
801 | dc_fixpt_add( | |
ec7e6bb8 LSL |
802 | ramp->entries.blue[i], delta), offset); |
803 | ||
804 | } | |
805 | ||
eb0e5154 | 806 | pwl_rgb[i].r = dc_fixpt_sub(dc_fixpt_mul_int( |
ec7e6bb8 | 807 | pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r); |
eb0e5154 | 808 | pwl_rgb[i].g = dc_fixpt_sub(dc_fixpt_mul_int( |
ec7e6bb8 | 809 | pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g); |
eb0e5154 | 810 | pwl_rgb[i].b = dc_fixpt_sub(dc_fixpt_mul_int( |
ec7e6bb8 LSL |
811 | pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b); |
812 | ++i; | |
eb0e5154 | 813 | pwl_rgb[i].r = dc_fixpt_sub(dc_fixpt_mul_int( |
ec7e6bb8 | 814 | pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r); |
eb0e5154 | 815 | pwl_rgb[i].g = dc_fixpt_sub(dc_fixpt_mul_int( |
ec7e6bb8 | 816 | pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g); |
eb0e5154 | 817 | pwl_rgb[i].b = dc_fixpt_sub(dc_fixpt_mul_int( |
ec7e6bb8 | 818 | pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b); |
55a01d40 | 819 | } |
ec7e6bb8 | 820 | |
55a01d40 KK |
821 | /* todo: all these scale_gamma functions are inherently the same but |
822 | * take different structures as params or different format for ramp | |
823 | * values. We could probably implement it in a more generic fashion | |
824 | */ | |
825 | static void scale_user_regamma_ramp(struct pwl_float_data *pwl_rgb, | |
826 | const struct regamma_ramp *ramp, | |
827 | struct dividers dividers) | |
828 | { | |
829 | unsigned short max_driver = 0xFFFF; | |
830 | unsigned short max_os = 0xFF00; | |
831 | unsigned short scaler = max_os; | |
832 | uint32_t i; | |
833 | struct pwl_float_data *rgb = pwl_rgb; | |
834 | struct pwl_float_data *rgb_last = rgb + GAMMA_RGB_256_ENTRIES - 1; | |
835 | ||
836 | i = 0; | |
837 | do { | |
838 | if (ramp->gamma[i] > max_os || | |
839 | ramp->gamma[i + 256] > max_os || | |
840 | ramp->gamma[i + 512] > max_os) { | |
841 | scaler = max_driver; | |
842 | break; | |
843 | } | |
844 | i++; | |
845 | } while (i != GAMMA_RGB_256_ENTRIES); | |
846 | ||
847 | i = 0; | |
848 | do { | |
eb0e5154 | 849 | rgb->r = dc_fixpt_from_fraction( |
55a01d40 | 850 | ramp->gamma[i], scaler); |
eb0e5154 | 851 | rgb->g = dc_fixpt_from_fraction( |
55a01d40 | 852 | ramp->gamma[i + 256], scaler); |
eb0e5154 | 853 | rgb->b = dc_fixpt_from_fraction( |
55a01d40 KK |
854 | ramp->gamma[i + 512], scaler); |
855 | ||
856 | ++rgb; | |
857 | ++i; | |
858 | } while (i != GAMMA_RGB_256_ENTRIES); | |
859 | ||
eb0e5154 | 860 | rgb->r = dc_fixpt_mul(rgb_last->r, |
55a01d40 | 861 | dividers.divider1); |
eb0e5154 | 862 | rgb->g = dc_fixpt_mul(rgb_last->g, |
55a01d40 | 863 | dividers.divider1); |
eb0e5154 | 864 | rgb->b = dc_fixpt_mul(rgb_last->b, |
55a01d40 KK |
865 | dividers.divider1); |
866 | ||
867 | ++rgb; | |
868 | ||
eb0e5154 | 869 | rgb->r = dc_fixpt_mul(rgb_last->r, |
55a01d40 | 870 | dividers.divider2); |
eb0e5154 | 871 | rgb->g = dc_fixpt_mul(rgb_last->g, |
55a01d40 | 872 | dividers.divider2); |
eb0e5154 | 873 | rgb->b = dc_fixpt_mul(rgb_last->b, |
55a01d40 KK |
874 | dividers.divider2); |
875 | ||
876 | ++rgb; | |
877 | ||
eb0e5154 | 878 | rgb->r = dc_fixpt_mul(rgb_last->r, |
55a01d40 | 879 | dividers.divider3); |
eb0e5154 | 880 | rgb->g = dc_fixpt_mul(rgb_last->g, |
55a01d40 | 881 | dividers.divider3); |
eb0e5154 | 882 | rgb->b = dc_fixpt_mul(rgb_last->b, |
55a01d40 | 883 | dividers.divider3); |
ec7e6bb8 LSL |
884 | } |
885 | ||
886 | /* | |
887 | * RS3+ color transform DDI - 1D LUT adjustment is composed with regamma here | |
888 | * Input is evenly distributed in the output color space as specified in | |
889 | * SetTimings | |
890 | * | |
891 | * Interpolation details: | |
892 | * 1D LUT has 4096 values which give curve correction in 0-1 float range | |
893 | * for evenly spaced points in 0-1 range. lut1D[index] gives correction | |
894 | * for index/4095. | |
895 | * First we find index for which: | |
896 | * index/4095 < regamma_y < (index+1)/4095 => | |
897 | * index < 4095*regamma_y < index + 1 | |
898 | * norm_y = 4095*regamma_y, and index is just truncating to nearest integer | |
899 | * lut1 = lut1D[index], lut2 = lut1D[index+1] | |
900 | * | |
901 | *adjustedY is then linearly interpolating regamma Y between lut1 and lut2 | |
902 | */ | |
903 | static void apply_lut_1d( | |
904 | const struct dc_gamma *ramp, | |
905 | uint32_t num_hw_points, | |
906 | struct dc_transfer_func_distributed_points *tf_pts) | |
907 | { | |
908 | int i = 0; | |
909 | int color = 0; | |
910 | struct fixed31_32 *regamma_y; | |
911 | struct fixed31_32 norm_y; | |
912 | struct fixed31_32 lut1; | |
913 | struct fixed31_32 lut2; | |
914 | const int max_lut_index = 4095; | |
915 | const struct fixed31_32 max_lut_index_f = | |
f3ba7a2f | 916 | dc_fixpt_from_int(max_lut_index); |
ec7e6bb8 LSL |
917 | int32_t index = 0, index_next = 0; |
918 | struct fixed31_32 index_f; | |
919 | struct fixed31_32 delta_lut; | |
920 | struct fixed31_32 delta_index; | |
921 | ||
922 | if (ramp->type != GAMMA_CS_TFM_1D) | |
923 | return; // this is not expected | |
924 | ||
925 | for (i = 0; i < num_hw_points; i++) { | |
926 | for (color = 0; color < 3; color++) { | |
927 | if (color == 0) | |
928 | regamma_y = &tf_pts->red[i]; | |
929 | else if (color == 1) | |
930 | regamma_y = &tf_pts->green[i]; | |
931 | else | |
932 | regamma_y = &tf_pts->blue[i]; | |
933 | ||
eb0e5154 | 934 | norm_y = dc_fixpt_mul(max_lut_index_f, |
ec7e6bb8 | 935 | *regamma_y); |
eb0e5154 | 936 | index = dc_fixpt_floor(norm_y); |
f3ba7a2f | 937 | index_f = dc_fixpt_from_int(index); |
ec7e6bb8 LSL |
938 | |
939 | if (index < 0 || index > max_lut_index) | |
940 | continue; | |
941 | ||
942 | index_next = (index == max_lut_index) ? index : index+1; | |
943 | ||
944 | if (color == 0) { | |
945 | lut1 = ramp->entries.red[index]; | |
946 | lut2 = ramp->entries.red[index_next]; | |
947 | } else if (color == 1) { | |
948 | lut1 = ramp->entries.green[index]; | |
949 | lut2 = ramp->entries.green[index_next]; | |
950 | } else { | |
951 | lut1 = ramp->entries.blue[index]; | |
952 | lut2 = ramp->entries.blue[index_next]; | |
953 | } | |
954 | ||
955 | // we have everything now, so interpolate | |
eb0e5154 DL |
956 | delta_lut = dc_fixpt_sub(lut2, lut1); |
957 | delta_index = dc_fixpt_sub(norm_y, index_f); | |
ec7e6bb8 | 958 | |
eb0e5154 DL |
959 | *regamma_y = dc_fixpt_add(lut1, |
960 | dc_fixpt_mul(delta_index, delta_lut)); | |
ec7e6bb8 LSL |
961 | } |
962 | } | |
963 | } | |
964 | ||
965 | static void build_evenly_distributed_points( | |
966 | struct gamma_pixel *points, | |
967 | uint32_t numberof_points, | |
968 | struct dividers dividers) | |
969 | { | |
970 | struct gamma_pixel *p = points; | |
971 | struct gamma_pixel *p_last = p + numberof_points - 1; | |
972 | ||
973 | uint32_t i = 0; | |
974 | ||
975 | do { | |
eb0e5154 | 976 | struct fixed31_32 value = dc_fixpt_from_fraction(i, |
ec7e6bb8 LSL |
977 | numberof_points - 1); |
978 | ||
979 | p->r = value; | |
980 | p->g = value; | |
981 | p->b = value; | |
982 | ||
983 | ++p; | |
984 | ++i; | |
985 | } while (i != numberof_points); | |
986 | ||
eb0e5154 DL |
987 | p->r = dc_fixpt_div(p_last->r, dividers.divider1); |
988 | p->g = dc_fixpt_div(p_last->g, dividers.divider1); | |
989 | p->b = dc_fixpt_div(p_last->b, dividers.divider1); | |
ec7e6bb8 LSL |
990 | |
991 | ++p; | |
992 | ||
eb0e5154 DL |
993 | p->r = dc_fixpt_div(p_last->r, dividers.divider2); |
994 | p->g = dc_fixpt_div(p_last->g, dividers.divider2); | |
995 | p->b = dc_fixpt_div(p_last->b, dividers.divider2); | |
ec7e6bb8 LSL |
996 | |
997 | ++p; | |
998 | ||
eb0e5154 DL |
999 | p->r = dc_fixpt_div(p_last->r, dividers.divider3); |
1000 | p->g = dc_fixpt_div(p_last->g, dividers.divider3); | |
1001 | p->b = dc_fixpt_div(p_last->b, dividers.divider3); | |
ec7e6bb8 LSL |
1002 | } |
1003 | ||
1004 | static inline void copy_rgb_regamma_to_coordinates_x( | |
1005 | struct hw_x_point *coordinates_x, | |
1006 | uint32_t hw_points_num, | |
1007 | const struct pwl_float_data_ex *rgb_ex) | |
1008 | { | |
1009 | struct hw_x_point *coords = coordinates_x; | |
1010 | uint32_t i = 0; | |
1011 | const struct pwl_float_data_ex *rgb_regamma = rgb_ex; | |
1012 | ||
55a01d40 | 1013 | while (i <= hw_points_num + 1) { |
ec7e6bb8 LSL |
1014 | coords->regamma_y_red = rgb_regamma->r; |
1015 | coords->regamma_y_green = rgb_regamma->g; | |
1016 | coords->regamma_y_blue = rgb_regamma->b; | |
1017 | ||
1018 | ++coords; | |
1019 | ++rgb_regamma; | |
1020 | ++i; | |
1021 | } | |
1022 | } | |
1023 | ||
1024 | static bool calculate_interpolated_hardware_curve( | |
1025 | const struct dc_gamma *ramp, | |
1026 | struct pixel_gamma_point *coeff128, | |
1027 | struct pwl_float_data *rgb_user, | |
1028 | const struct hw_x_point *coordinates_x, | |
1029 | const struct gamma_pixel *axis_x, | |
1030 | uint32_t number_of_points, | |
1031 | struct dc_transfer_func_distributed_points *tf_pts) | |
1032 | { | |
1033 | ||
1034 | const struct pixel_gamma_point *coeff = coeff128; | |
1035 | uint32_t max_entries = 3 - 1; | |
1036 | ||
1037 | uint32_t i = 0; | |
1038 | ||
1039 | for (i = 0; i < 3; i++) { | |
1040 | if (!build_custom_gamma_mapping_coefficients_worker( | |
1041 | ramp, coeff128, coordinates_x, axis_x, i, | |
1042 | number_of_points)) | |
1043 | return false; | |
1044 | } | |
1045 | ||
1046 | i = 0; | |
1047 | max_entries += ramp->num_entries; | |
1048 | ||
1049 | /* TODO: float point case */ | |
1050 | ||
1051 | while (i <= number_of_points) { | |
1052 | tf_pts->red[i] = calculate_mapped_value( | |
1053 | rgb_user, coeff, CHANNEL_NAME_RED, max_entries); | |
1054 | tf_pts->green[i] = calculate_mapped_value( | |
1055 | rgb_user, coeff, CHANNEL_NAME_GREEN, max_entries); | |
1056 | tf_pts->blue[i] = calculate_mapped_value( | |
1057 | rgb_user, coeff, CHANNEL_NAME_BLUE, max_entries); | |
1058 | ||
1059 | ++coeff; | |
1060 | ++i; | |
1061 | } | |
1062 | ||
1063 | return true; | |
1064 | } | |
1065 | ||
55a01d40 KK |
1066 | /* The "old" interpolation uses a complicated scheme to build an array of |
1067 | * coefficients while also using an array of 0-255 normalized to 0-1 | |
1068 | * Then there's another loop using both of the above + new scaled user ramp | |
1069 | * and we concatenate them. It also searches for points of interpolation and | |
1070 | * uses enums for positions. | |
1071 | * | |
1072 | * This function uses a different approach: | |
1073 | * user ramp is always applied on X with 0/255, 1/255, 2/255, ..., 255/255 | |
1074 | * To find index for hwX , we notice the following: | |
1075 | * i/255 <= hwX < (i+1)/255 <=> i <= 255*hwX < i+1 | |
1076 | * See apply_lut_1d which is the same principle, but on 4K entry 1D LUT | |
1077 | * | |
1078 | * Once the index is known, combined Y is simply: | |
1079 | * user_ramp(index) + (hwX-index/255)*(user_ramp(index+1) - user_ramp(index) | |
1080 | * | |
1081 | * We should switch to this method in all cases, it's simpler and faster | |
1082 | * ToDo one day - for now this only applies to ADL regamma to avoid regression | |
1083 | * for regular use cases (sRGB and PQ) | |
1084 | */ | |
1085 | static void interpolate_user_regamma(uint32_t hw_points_num, | |
1086 | struct pwl_float_data *rgb_user, | |
1087 | bool apply_degamma, | |
1088 | struct dc_transfer_func_distributed_points *tf_pts) | |
1089 | { | |
1090 | uint32_t i; | |
1091 | uint32_t color = 0; | |
1092 | int32_t index; | |
1093 | int32_t index_next; | |
1094 | struct fixed31_32 *tf_point; | |
1095 | struct fixed31_32 hw_x; | |
1096 | struct fixed31_32 norm_factor = | |
f3ba7a2f | 1097 | dc_fixpt_from_int(255); |
55a01d40 KK |
1098 | struct fixed31_32 norm_x; |
1099 | struct fixed31_32 index_f; | |
1100 | struct fixed31_32 lut1; | |
1101 | struct fixed31_32 lut2; | |
1102 | struct fixed31_32 delta_lut; | |
1103 | struct fixed31_32 delta_index; | |
1104 | ||
1105 | i = 0; | |
1106 | /* fixed_pt library has problems handling too small values */ | |
1107 | while (i != 32) { | |
eb0e5154 DL |
1108 | tf_pts->red[i] = dc_fixpt_zero; |
1109 | tf_pts->green[i] = dc_fixpt_zero; | |
1110 | tf_pts->blue[i] = dc_fixpt_zero; | |
55a01d40 KK |
1111 | ++i; |
1112 | } | |
1113 | while (i <= hw_points_num + 1) { | |
1114 | for (color = 0; color < 3; color++) { | |
1115 | if (color == 0) | |
1116 | tf_point = &tf_pts->red[i]; | |
1117 | else if (color == 1) | |
1118 | tf_point = &tf_pts->green[i]; | |
1119 | else | |
1120 | tf_point = &tf_pts->blue[i]; | |
1121 | ||
1122 | if (apply_degamma) { | |
1123 | if (color == 0) | |
1124 | hw_x = coordinates_x[i].regamma_y_red; | |
1125 | else if (color == 1) | |
1126 | hw_x = coordinates_x[i].regamma_y_green; | |
1127 | else | |
1128 | hw_x = coordinates_x[i].regamma_y_blue; | |
1129 | } else | |
1130 | hw_x = coordinates_x[i].x; | |
1131 | ||
eb0e5154 DL |
1132 | norm_x = dc_fixpt_mul(norm_factor, hw_x); |
1133 | index = dc_fixpt_floor(norm_x); | |
55a01d40 KK |
1134 | if (index < 0 || index > 255) |
1135 | continue; | |
1136 | ||
f3ba7a2f | 1137 | index_f = dc_fixpt_from_int(index); |
55a01d40 KK |
1138 | index_next = (index == 255) ? index : index + 1; |
1139 | ||
1140 | if (color == 0) { | |
1141 | lut1 = rgb_user[index].r; | |
1142 | lut2 = rgb_user[index_next].r; | |
1143 | } else if (color == 1) { | |
1144 | lut1 = rgb_user[index].g; | |
1145 | lut2 = rgb_user[index_next].g; | |
1146 | } else { | |
1147 | lut1 = rgb_user[index].b; | |
1148 | lut2 = rgb_user[index_next].b; | |
1149 | } | |
1150 | ||
1151 | // we have everything now, so interpolate | |
eb0e5154 DL |
1152 | delta_lut = dc_fixpt_sub(lut2, lut1); |
1153 | delta_index = dc_fixpt_sub(norm_x, index_f); | |
55a01d40 | 1154 | |
eb0e5154 DL |
1155 | *tf_point = dc_fixpt_add(lut1, |
1156 | dc_fixpt_mul(delta_index, delta_lut)); | |
55a01d40 KK |
1157 | } |
1158 | ++i; | |
1159 | } | |
1160 | } | |
1161 | ||
ec7e6bb8 LSL |
1162 | static void build_new_custom_resulted_curve( |
1163 | uint32_t hw_points_num, | |
1164 | struct dc_transfer_func_distributed_points *tf_pts) | |
1165 | { | |
1166 | uint32_t i; | |
1167 | ||
1168 | i = 0; | |
1169 | ||
1170 | while (i != hw_points_num + 1) { | |
eb0e5154 DL |
1171 | tf_pts->red[i] = dc_fixpt_clamp( |
1172 | tf_pts->red[i], dc_fixpt_zero, | |
1173 | dc_fixpt_one); | |
1174 | tf_pts->green[i] = dc_fixpt_clamp( | |
1175 | tf_pts->green[i], dc_fixpt_zero, | |
1176 | dc_fixpt_one); | |
1177 | tf_pts->blue[i] = dc_fixpt_clamp( | |
1178 | tf_pts->blue[i], dc_fixpt_zero, | |
1179 | dc_fixpt_one); | |
ec7e6bb8 LSL |
1180 | |
1181 | ++i; | |
1182 | } | |
1183 | } | |
1184 | ||
55a01d40 KK |
1185 | static void apply_degamma_for_user_regamma(struct pwl_float_data_ex *rgb_regamma, |
1186 | uint32_t hw_points_num) | |
1187 | { | |
1188 | uint32_t i; | |
1189 | ||
1190 | struct gamma_coefficients coeff; | |
1191 | struct pwl_float_data_ex *rgb = rgb_regamma; | |
1192 | const struct hw_x_point *coord_x = coordinates_x; | |
1193 | ||
1194 | build_coefficients(&coeff, true); | |
1195 | ||
1196 | i = 0; | |
1197 | while (i != hw_points_num + 1) { | |
1198 | rgb->r = translate_from_linear_space_ex( | |
1199 | coord_x->x, &coeff, 0); | |
1200 | rgb->g = rgb->r; | |
1201 | rgb->b = rgb->r; | |
1202 | ++coord_x; | |
1203 | ++rgb; | |
1204 | ++i; | |
1205 | } | |
1206 | } | |
1207 | ||
ec7e6bb8 LSL |
1208 | static bool map_regamma_hw_to_x_user( |
1209 | const struct dc_gamma *ramp, | |
1210 | struct pixel_gamma_point *coeff128, | |
1211 | struct pwl_float_data *rgb_user, | |
1212 | struct hw_x_point *coords_x, | |
1213 | const struct gamma_pixel *axis_x, | |
1214 | const struct pwl_float_data_ex *rgb_regamma, | |
1215 | uint32_t hw_points_num, | |
1216 | struct dc_transfer_func_distributed_points *tf_pts, | |
1217 | bool mapUserRamp) | |
1218 | { | |
1219 | /* setup to spare calculated ideal regamma values */ | |
1220 | ||
1221 | int i = 0; | |
1222 | struct hw_x_point *coords = coords_x; | |
1223 | const struct pwl_float_data_ex *regamma = rgb_regamma; | |
1224 | ||
1225 | if (mapUserRamp) { | |
1226 | copy_rgb_regamma_to_coordinates_x(coords, | |
1227 | hw_points_num, | |
1228 | rgb_regamma); | |
1229 | ||
1230 | calculate_interpolated_hardware_curve( | |
1231 | ramp, coeff128, rgb_user, coords, axis_x, | |
1232 | hw_points_num, tf_pts); | |
1233 | } else { | |
1234 | /* just copy current rgb_regamma into tf_pts */ | |
1235 | while (i <= hw_points_num) { | |
1236 | tf_pts->red[i] = regamma->r; | |
1237 | tf_pts->green[i] = regamma->g; | |
1238 | tf_pts->blue[i] = regamma->b; | |
1239 | ||
1240 | ++regamma; | |
1241 | ++i; | |
1242 | } | |
1243 | } | |
1244 | ||
55a01d40 | 1245 | /* this should be named differently, all it does is clamp to 0-1 */ |
ec7e6bb8 LSL |
1246 | build_new_custom_resulted_curve(hw_points_num, tf_pts); |
1247 | ||
1248 | return true; | |
1249 | } | |
1250 | ||
792474b7 VP |
1251 | #define _EXTRA_POINTS 3 |
1252 | ||
ec7e6bb8 LSL |
1253 | bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf, |
1254 | const struct dc_gamma *ramp, bool mapUserRamp) | |
1255 | { | |
1256 | struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts; | |
1257 | struct dividers dividers; | |
1258 | ||
1259 | struct pwl_float_data *rgb_user = NULL; | |
1260 | struct pwl_float_data_ex *rgb_regamma = NULL; | |
1261 | struct gamma_pixel *axix_x = NULL; | |
792474b7 | 1262 | struct pixel_gamma_point *coeff = NULL; |
ec7e6bb8 LSL |
1263 | enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB; |
1264 | bool ret = false; | |
1265 | ||
1266 | if (output_tf->type == TF_TYPE_BYPASS) | |
1267 | return false; | |
1268 | ||
1269 | /* we can use hardcoded curve for plain SRGB TF */ | |
1270 | if (output_tf->type == TF_TYPE_PREDEFINED && | |
1271 | output_tf->tf == TRANSFER_FUNCTION_SRGB && | |
1272 | (!mapUserRamp && ramp->type == GAMMA_RGB_256)) | |
1273 | return true; | |
1274 | ||
1275 | output_tf->type = TF_TYPE_DISTRIBUTED_POINTS; | |
1276 | ||
f7dbe918 MD |
1277 | rgb_user = kvzalloc(sizeof(*rgb_user) * (ramp->num_entries + _EXTRA_POINTS), |
1278 | GFP_KERNEL); | |
ec7e6bb8 LSL |
1279 | if (!rgb_user) |
1280 | goto rgb_user_alloc_fail; | |
f7dbe918 MD |
1281 | rgb_regamma = kvzalloc(sizeof(*rgb_regamma) * (MAX_HW_POINTS + _EXTRA_POINTS), |
1282 | GFP_KERNEL); | |
ec7e6bb8 LSL |
1283 | if (!rgb_regamma) |
1284 | goto rgb_regamma_alloc_fail; | |
f7dbe918 MD |
1285 | axix_x = kvzalloc(sizeof(*axix_x) * (ramp->num_entries + 3), |
1286 | GFP_KERNEL); | |
ec7e6bb8 LSL |
1287 | if (!axix_x) |
1288 | goto axix_x_alloc_fail; | |
f7dbe918 | 1289 | coeff = kvzalloc(sizeof(*coeff) * (MAX_HW_POINTS + _EXTRA_POINTS), GFP_KERNEL); |
792474b7 VP |
1290 | if (!coeff) |
1291 | goto coeff_alloc_fail; | |
ec7e6bb8 | 1292 | |
eb0e5154 DL |
1293 | dividers.divider1 = dc_fixpt_from_fraction(3, 2); |
1294 | dividers.divider2 = dc_fixpt_from_int(2); | |
1295 | dividers.divider3 = dc_fixpt_from_fraction(5, 2); | |
ec7e6bb8 LSL |
1296 | |
1297 | tf = output_tf->tf; | |
1298 | ||
1299 | build_evenly_distributed_points( | |
1300 | axix_x, | |
1301 | ramp->num_entries, | |
1302 | dividers); | |
1303 | ||
1304 | if (ramp->type == GAMMA_RGB_256 && mapUserRamp) | |
1305 | scale_gamma(rgb_user, ramp, dividers); | |
1306 | else if (ramp->type == GAMMA_RGB_FLOAT_1024) | |
1307 | scale_gamma_dx(rgb_user, ramp, dividers); | |
1308 | ||
1309 | if (tf == TRANSFER_FUNCTION_PQ) { | |
1310 | tf_pts->end_exponent = 7; | |
1311 | tf_pts->x_point_at_y1_red = 125; | |
1312 | tf_pts->x_point_at_y1_green = 125; | |
1313 | tf_pts->x_point_at_y1_blue = 125; | |
1314 | ||
792474b7 | 1315 | build_pq(rgb_regamma, |
ec7e6bb8 LSL |
1316 | MAX_HW_POINTS, |
1317 | coordinates_x, | |
1318 | output_tf->sdr_ref_white_level); | |
1319 | } else { | |
1320 | tf_pts->end_exponent = 0; | |
1321 | tf_pts->x_point_at_y1_red = 1; | |
1322 | tf_pts->x_point_at_y1_green = 1; | |
1323 | tf_pts->x_point_at_y1_blue = 1; | |
1324 | ||
792474b7 | 1325 | build_regamma(rgb_regamma, |
ec7e6bb8 | 1326 | MAX_HW_POINTS, |
792474b7 | 1327 | coordinates_x, tf == TRANSFER_FUNCTION_SRGB ? true:false); |
ec7e6bb8 LSL |
1328 | } |
1329 | ||
792474b7 | 1330 | map_regamma_hw_to_x_user(ramp, coeff, rgb_user, |
ec7e6bb8 LSL |
1331 | coordinates_x, axix_x, rgb_regamma, |
1332 | MAX_HW_POINTS, tf_pts, | |
1333 | (mapUserRamp || ramp->type != GAMMA_RGB_256) && | |
1334 | ramp->type != GAMMA_CS_TFM_1D); | |
1335 | ||
1336 | if (ramp->type == GAMMA_CS_TFM_1D) | |
1337 | apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts); | |
1338 | ||
1339 | ret = true; | |
1340 | ||
f7dbe918 | 1341 | kvfree(coeff); |
792474b7 | 1342 | coeff_alloc_fail: |
f7dbe918 | 1343 | kvfree(axix_x); |
ec7e6bb8 | 1344 | axix_x_alloc_fail: |
f7dbe918 | 1345 | kvfree(rgb_regamma); |
ec7e6bb8 | 1346 | rgb_regamma_alloc_fail: |
f7dbe918 | 1347 | kvfree(rgb_user); |
ec7e6bb8 LSL |
1348 | rgb_user_alloc_fail: |
1349 | return ret; | |
1350 | } | |
1351 | ||
55a01d40 KK |
1352 | bool calculate_user_regamma_coeff(struct dc_transfer_func *output_tf, |
1353 | const struct regamma_lut *regamma) | |
1354 | { | |
1355 | struct gamma_coefficients coeff; | |
1356 | const struct hw_x_point *coord_x = coordinates_x; | |
1357 | uint32_t i = 0; | |
1358 | ||
1359 | do { | |
eb0e5154 | 1360 | coeff.a0[i] = dc_fixpt_from_fraction( |
55a01d40 | 1361 | regamma->coeff.A0[i], 10000000); |
eb0e5154 | 1362 | coeff.a1[i] = dc_fixpt_from_fraction( |
55a01d40 | 1363 | regamma->coeff.A1[i], 1000); |
eb0e5154 | 1364 | coeff.a2[i] = dc_fixpt_from_fraction( |
55a01d40 | 1365 | regamma->coeff.A2[i], 1000); |
eb0e5154 | 1366 | coeff.a3[i] = dc_fixpt_from_fraction( |
55a01d40 | 1367 | regamma->coeff.A3[i], 1000); |
eb0e5154 | 1368 | coeff.user_gamma[i] = dc_fixpt_from_fraction( |
55a01d40 KK |
1369 | regamma->coeff.gamma[i], 1000); |
1370 | ||
1371 | ++i; | |
1372 | } while (i != 3); | |
1373 | ||
1374 | i = 0; | |
1375 | /* fixed_pt library has problems handling too small values */ | |
1376 | while (i != 32) { | |
eb0e5154 DL |
1377 | output_tf->tf_pts.red[i] = dc_fixpt_zero; |
1378 | output_tf->tf_pts.green[i] = dc_fixpt_zero; | |
1379 | output_tf->tf_pts.blue[i] = dc_fixpt_zero; | |
55a01d40 KK |
1380 | ++coord_x; |
1381 | ++i; | |
1382 | } | |
1383 | while (i != MAX_HW_POINTS + 1) { | |
1384 | output_tf->tf_pts.red[i] = translate_from_linear_space_ex( | |
1385 | coord_x->x, &coeff, 0); | |
1386 | output_tf->tf_pts.green[i] = translate_from_linear_space_ex( | |
1387 | coord_x->x, &coeff, 1); | |
1388 | output_tf->tf_pts.blue[i] = translate_from_linear_space_ex( | |
1389 | coord_x->x, &coeff, 2); | |
1390 | ++coord_x; | |
1391 | ++i; | |
1392 | } | |
1393 | ||
1394 | // this function just clamps output to 0-1 | |
1395 | build_new_custom_resulted_curve(MAX_HW_POINTS, &output_tf->tf_pts); | |
1396 | output_tf->type = TF_TYPE_DISTRIBUTED_POINTS; | |
1397 | ||
1398 | return true; | |
1399 | } | |
1400 | ||
1401 | bool calculate_user_regamma_ramp(struct dc_transfer_func *output_tf, | |
1402 | const struct regamma_lut *regamma) | |
1403 | { | |
1404 | struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts; | |
1405 | struct dividers dividers; | |
1406 | ||
1407 | struct pwl_float_data *rgb_user = NULL; | |
1408 | struct pwl_float_data_ex *rgb_regamma = NULL; | |
1409 | bool ret = false; | |
1410 | ||
1411 | if (regamma == NULL) | |
1412 | return false; | |
1413 | ||
1414 | output_tf->type = TF_TYPE_DISTRIBUTED_POINTS; | |
1415 | ||
6396bb22 KC |
1416 | rgb_user = kcalloc(GAMMA_RGB_256_ENTRIES + _EXTRA_POINTS, |
1417 | sizeof(*rgb_user), | |
1418 | GFP_KERNEL); | |
55a01d40 KK |
1419 | if (!rgb_user) |
1420 | goto rgb_user_alloc_fail; | |
1421 | ||
6396bb22 KC |
1422 | rgb_regamma = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS, |
1423 | sizeof(*rgb_regamma), | |
1424 | GFP_KERNEL); | |
55a01d40 KK |
1425 | if (!rgb_regamma) |
1426 | goto rgb_regamma_alloc_fail; | |
1427 | ||
eb0e5154 DL |
1428 | dividers.divider1 = dc_fixpt_from_fraction(3, 2); |
1429 | dividers.divider2 = dc_fixpt_from_int(2); | |
1430 | dividers.divider3 = dc_fixpt_from_fraction(5, 2); | |
55a01d40 KK |
1431 | |
1432 | scale_user_regamma_ramp(rgb_user, ®amma->ramp, dividers); | |
1433 | ||
1434 | if (regamma->flags.bits.applyDegamma == 1) { | |
1435 | apply_degamma_for_user_regamma(rgb_regamma, MAX_HW_POINTS); | |
1436 | copy_rgb_regamma_to_coordinates_x(coordinates_x, | |
1437 | MAX_HW_POINTS, rgb_regamma); | |
1438 | } | |
1439 | ||
1440 | interpolate_user_regamma(MAX_HW_POINTS, rgb_user, | |
1441 | regamma->flags.bits.applyDegamma, tf_pts); | |
1442 | ||
1443 | // no custom HDR curves! | |
1444 | tf_pts->end_exponent = 0; | |
1445 | tf_pts->x_point_at_y1_red = 1; | |
1446 | tf_pts->x_point_at_y1_green = 1; | |
1447 | tf_pts->x_point_at_y1_blue = 1; | |
1448 | ||
1449 | // this function just clamps output to 0-1 | |
1450 | build_new_custom_resulted_curve(MAX_HW_POINTS, tf_pts); | |
1451 | ||
1452 | ret = true; | |
1453 | ||
1454 | kfree(rgb_regamma); | |
1455 | rgb_regamma_alloc_fail: | |
1fafef9d | 1456 | kvfree(rgb_user); |
55a01d40 KK |
1457 | rgb_user_alloc_fail: |
1458 | return ret; | |
1459 | } | |
1460 | ||
792474b7 VP |
1461 | bool mod_color_calculate_degamma_params(struct dc_transfer_func *input_tf, |
1462 | const struct dc_gamma *ramp, bool mapUserRamp) | |
1463 | { | |
1464 | struct dc_transfer_func_distributed_points *tf_pts = &input_tf->tf_pts; | |
1465 | struct dividers dividers; | |
1466 | ||
1467 | struct pwl_float_data *rgb_user = NULL; | |
1468 | struct pwl_float_data_ex *curve = NULL; | |
1469 | struct gamma_pixel *axix_x = NULL; | |
1470 | struct pixel_gamma_point *coeff = NULL; | |
1471 | enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB; | |
1472 | bool ret = false; | |
1473 | ||
1474 | if (input_tf->type == TF_TYPE_BYPASS) | |
1475 | return false; | |
1476 | ||
1477 | /* we can use hardcoded curve for plain SRGB TF */ | |
1478 | if (input_tf->type == TF_TYPE_PREDEFINED && | |
1479 | input_tf->tf == TRANSFER_FUNCTION_SRGB && | |
1480 | (!mapUserRamp && ramp->type == GAMMA_RGB_256)) | |
1481 | return true; | |
1482 | ||
1483 | input_tf->type = TF_TYPE_DISTRIBUTED_POINTS; | |
1484 | ||
f7dbe918 MD |
1485 | rgb_user = kvzalloc(sizeof(*rgb_user) * (ramp->num_entries + _EXTRA_POINTS), |
1486 | GFP_KERNEL); | |
792474b7 VP |
1487 | if (!rgb_user) |
1488 | goto rgb_user_alloc_fail; | |
f7dbe918 MD |
1489 | curve = kvzalloc(sizeof(*curve) * (MAX_HW_POINTS + _EXTRA_POINTS), |
1490 | GFP_KERNEL); | |
792474b7 VP |
1491 | if (!curve) |
1492 | goto curve_alloc_fail; | |
f7dbe918 MD |
1493 | axix_x = kvzalloc(sizeof(*axix_x) * (ramp->num_entries + _EXTRA_POINTS), |
1494 | GFP_KERNEL); | |
792474b7 VP |
1495 | if (!axix_x) |
1496 | goto axix_x_alloc_fail; | |
f7dbe918 | 1497 | coeff = kvzalloc(sizeof(*coeff) * (MAX_HW_POINTS + _EXTRA_POINTS), GFP_KERNEL); |
792474b7 VP |
1498 | if (!coeff) |
1499 | goto coeff_alloc_fail; | |
1500 | ||
eb0e5154 DL |
1501 | dividers.divider1 = dc_fixpt_from_fraction(3, 2); |
1502 | dividers.divider2 = dc_fixpt_from_int(2); | |
1503 | dividers.divider3 = dc_fixpt_from_fraction(5, 2); | |
792474b7 VP |
1504 | |
1505 | tf = input_tf->tf; | |
1506 | ||
1507 | build_evenly_distributed_points( | |
1508 | axix_x, | |
1509 | ramp->num_entries, | |
1510 | dividers); | |
1511 | ||
1512 | if (ramp->type == GAMMA_RGB_256 && mapUserRamp) | |
1513 | scale_gamma(rgb_user, ramp, dividers); | |
1514 | else if (ramp->type == GAMMA_RGB_FLOAT_1024) | |
1515 | scale_gamma_dx(rgb_user, ramp, dividers); | |
1516 | ||
1517 | if (tf == TRANSFER_FUNCTION_PQ) | |
1518 | build_de_pq(curve, | |
44c6f2e5 VP |
1519 | MAX_HW_POINTS, |
1520 | coordinates_x); | |
792474b7 VP |
1521 | else |
1522 | build_degamma(curve, | |
44c6f2e5 VP |
1523 | MAX_HW_POINTS, |
1524 | coordinates_x, | |
792474b7 VP |
1525 | tf == TRANSFER_FUNCTION_SRGB ? true:false); |
1526 | ||
1527 | tf_pts->end_exponent = 0; | |
1528 | tf_pts->x_point_at_y1_red = 1; | |
1529 | tf_pts->x_point_at_y1_green = 1; | |
1530 | tf_pts->x_point_at_y1_blue = 1; | |
1531 | ||
1532 | map_regamma_hw_to_x_user(ramp, coeff, rgb_user, | |
44c6f2e5 VP |
1533 | coordinates_x, axix_x, curve, |
1534 | MAX_HW_POINTS, tf_pts, | |
792474b7 VP |
1535 | mapUserRamp); |
1536 | ||
1537 | ret = true; | |
1538 | ||
f7dbe918 | 1539 | kvfree(coeff); |
792474b7 | 1540 | coeff_alloc_fail: |
f7dbe918 | 1541 | kvfree(axix_x); |
792474b7 | 1542 | axix_x_alloc_fail: |
f7dbe918 | 1543 | kvfree(curve); |
792474b7 | 1544 | curve_alloc_fail: |
f7dbe918 | 1545 | kvfree(rgb_user); |
792474b7 VP |
1546 | rgb_user_alloc_fail: |
1547 | ||
1548 | return ret; | |
1549 | ||
1550 | } | |
1551 | ||
1552 | ||
ec7e6bb8 LSL |
1553 | bool mod_color_calculate_curve(enum dc_transfer_func_predefined trans, |
1554 | struct dc_transfer_func_distributed_points *points) | |
1555 | { | |
1556 | uint32_t i; | |
1557 | bool ret = false; | |
1558 | struct pwl_float_data_ex *rgb_regamma = NULL; | |
1559 | ||
9eee2137 VP |
1560 | if (trans == TRANSFER_FUNCTION_UNITY || |
1561 | trans == TRANSFER_FUNCTION_LINEAR) { | |
792474b7 VP |
1562 | points->end_exponent = 0; |
1563 | points->x_point_at_y1_red = 1; | |
1564 | points->x_point_at_y1_green = 1; | |
1565 | points->x_point_at_y1_blue = 1; | |
1566 | ||
44c6f2e5 | 1567 | for (i = 0; i <= MAX_HW_POINTS ; i++) { |
ec7e6bb8 LSL |
1568 | points->red[i] = coordinates_x[i].x; |
1569 | points->green[i] = coordinates_x[i].x; | |
1570 | points->blue[i] = coordinates_x[i].x; | |
1571 | } | |
1572 | ret = true; | |
1573 | } else if (trans == TRANSFER_FUNCTION_PQ) { | |
f7dbe918 MD |
1574 | rgb_regamma = kvzalloc(sizeof(*rgb_regamma) * |
1575 | (MAX_HW_POINTS + _EXTRA_POINTS), | |
1576 | GFP_KERNEL); | |
ec7e6bb8 LSL |
1577 | if (!rgb_regamma) |
1578 | goto rgb_regamma_alloc_fail; | |
792474b7 VP |
1579 | points->end_exponent = 7; |
1580 | points->x_point_at_y1_red = 125; | |
1581 | points->x_point_at_y1_green = 125; | |
1582 | points->x_point_at_y1_blue = 125; | |
1583 | ||
1584 | ||
1585 | build_pq(rgb_regamma, | |
1586 | MAX_HW_POINTS, | |
1587 | coordinates_x, | |
1588 | 80); | |
44c6f2e5 | 1589 | for (i = 0; i <= MAX_HW_POINTS ; i++) { |
792474b7 VP |
1590 | points->red[i] = rgb_regamma[i].r; |
1591 | points->green[i] = rgb_regamma[i].g; | |
1592 | points->blue[i] = rgb_regamma[i].b; | |
1593 | } | |
1594 | ret = true; | |
1595 | ||
f7dbe918 | 1596 | kvfree(rgb_regamma); |
792474b7 VP |
1597 | } else if (trans == TRANSFER_FUNCTION_SRGB || |
1598 | trans == TRANSFER_FUNCTION_BT709) { | |
f7dbe918 MD |
1599 | rgb_regamma = kvzalloc(sizeof(*rgb_regamma) * |
1600 | (MAX_HW_POINTS + _EXTRA_POINTS), | |
1601 | GFP_KERNEL); | |
792474b7 VP |
1602 | if (!rgb_regamma) |
1603 | goto rgb_regamma_alloc_fail; | |
ec7e6bb8 LSL |
1604 | points->end_exponent = 0; |
1605 | points->x_point_at_y1_red = 1; | |
1606 | points->x_point_at_y1_green = 1; | |
1607 | points->x_point_at_y1_blue = 1; | |
1608 | ||
792474b7 | 1609 | build_regamma(rgb_regamma, |
ec7e6bb8 | 1610 | MAX_HW_POINTS, |
792474b7 | 1611 | coordinates_x, trans == TRANSFER_FUNCTION_SRGB ? true:false); |
44c6f2e5 | 1612 | for (i = 0; i <= MAX_HW_POINTS ; i++) { |
ec7e6bb8 LSL |
1613 | points->red[i] = rgb_regamma[i].r; |
1614 | points->green[i] = rgb_regamma[i].g; | |
1615 | points->blue[i] = rgb_regamma[i].b; | |
1616 | } | |
1617 | ret = true; | |
1618 | ||
f7dbe918 | 1619 | kvfree(rgb_regamma); |
ec7e6bb8 LSL |
1620 | } |
1621 | rgb_regamma_alloc_fail: | |
1622 | return ret; | |
1623 | } | |
1624 | ||
1625 | ||
792474b7 VP |
1626 | bool mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans, |
1627 | struct dc_transfer_func_distributed_points *points) | |
1628 | { | |
1629 | uint32_t i; | |
1630 | bool ret = false; | |
1631 | struct pwl_float_data_ex *rgb_degamma = NULL; | |
1632 | ||
9eee2137 VP |
1633 | if (trans == TRANSFER_FUNCTION_UNITY || |
1634 | trans == TRANSFER_FUNCTION_LINEAR) { | |
792474b7 | 1635 | |
44c6f2e5 VP |
1636 | for (i = 0; i <= MAX_HW_POINTS ; i++) { |
1637 | points->red[i] = coordinates_x[i].x; | |
1638 | points->green[i] = coordinates_x[i].x; | |
1639 | points->blue[i] = coordinates_x[i].x; | |
792474b7 VP |
1640 | } |
1641 | ret = true; | |
1642 | } else if (trans == TRANSFER_FUNCTION_PQ) { | |
f7dbe918 MD |
1643 | rgb_degamma = kvzalloc(sizeof(*rgb_degamma) * |
1644 | (MAX_HW_POINTS + _EXTRA_POINTS), | |
1645 | GFP_KERNEL); | |
792474b7 VP |
1646 | if (!rgb_degamma) |
1647 | goto rgb_degamma_alloc_fail; | |
1648 | ||
1649 | ||
1650 | build_de_pq(rgb_degamma, | |
44c6f2e5 VP |
1651 | MAX_HW_POINTS, |
1652 | coordinates_x); | |
1653 | for (i = 0; i <= MAX_HW_POINTS ; i++) { | |
792474b7 VP |
1654 | points->red[i] = rgb_degamma[i].r; |
1655 | points->green[i] = rgb_degamma[i].g; | |
1656 | points->blue[i] = rgb_degamma[i].b; | |
1657 | } | |
1658 | ret = true; | |
1659 | ||
f7dbe918 | 1660 | kvfree(rgb_degamma); |
792474b7 VP |
1661 | } else if (trans == TRANSFER_FUNCTION_SRGB || |
1662 | trans == TRANSFER_FUNCTION_BT709) { | |
f7dbe918 MD |
1663 | rgb_degamma = kvzalloc(sizeof(*rgb_degamma) * |
1664 | (MAX_HW_POINTS + _EXTRA_POINTS), | |
1665 | GFP_KERNEL); | |
792474b7 VP |
1666 | if (!rgb_degamma) |
1667 | goto rgb_degamma_alloc_fail; | |
1668 | ||
1669 | build_degamma(rgb_degamma, | |
44c6f2e5 VP |
1670 | MAX_HW_POINTS, |
1671 | coordinates_x, trans == TRANSFER_FUNCTION_SRGB ? true:false); | |
1672 | for (i = 0; i <= MAX_HW_POINTS ; i++) { | |
792474b7 VP |
1673 | points->red[i] = rgb_degamma[i].r; |
1674 | points->green[i] = rgb_degamma[i].g; | |
1675 | points->blue[i] = rgb_degamma[i].b; | |
1676 | } | |
1677 | ret = true; | |
1678 | ||
f7dbe918 | 1679 | kvfree(rgb_degamma); |
792474b7 VP |
1680 | } |
1681 | points->end_exponent = 0; | |
1682 | points->x_point_at_y1_red = 1; | |
1683 | points->x_point_at_y1_green = 1; | |
1684 | points->x_point_at_y1_blue = 1; | |
1685 | ||
1686 | rgb_degamma_alloc_fail: | |
1687 | return ret; | |
1688 | } | |
1689 | ||
1690 |