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 | |
30 | /* When calculating LUT values the first region and at least one subsequent |
31 | * region are calculated with full precision. These defines are a demarcation |
32 | * of where the second region starts and ends. |
33 | * These are hardcoded values to avoid recalculating them in loops. |
34 | */ |
35 | #define PRECISE_LUT_REGION_START 224 |
36 | #define PRECISE_LUT_REGION_END 239 |
37 | |
38 | static struct hw_x_point coordinates_x[MAX_HW_POINTS + 2]; |
39 | |
40 | // Hardcoded table that depends on setup_x_points_distribution and sdr_level=80 |
41 | // If x points are changed, then PQ Y points will be misaligned and a new |
42 | // table would need to be generated. Or use old method that calls compute_pq. |
43 | // The last point is above PQ formula range (0-125 in normalized FP16) |
44 | // The value for the last point (128) is such that interpolation from |
45 | // 120 to 128 will give 1.0 for X = 125.0 |
46 | // first couple points are 0 - HW LUT is mirrored around zero, so making first |
47 | // segment 0 to 0 will effectively clip it, and these are very low PQ codes |
48 | // min nonzero value below (216825) is a little under 12-bit PQ code 1. |
49 | static const unsigned long long pq_divider = 1000000000; |
50 | static const unsigned long long pq_numerator[MAX_HW_POINTS + 1] = { |
51 | 0, 0, 0, 0, 216825, 222815, |
52 | 228691, 234460, 240128, 245702, 251187, 256587, |
53 | 261908, 267152, 272324, 277427, 282465, 292353, |
54 | 302011, 311456, 320704, 329768, 338661, 347394, |
55 | 355975, 364415, 372721, 380900, 388959, 396903, |
56 | 404739, 412471, 420104, 435089, 449727, 464042, |
57 | 478060, 491800, 505281, 518520, 531529, 544324, |
58 | 556916, 569316, 581533, 593576, 605454, 617175, |
59 | 628745, 651459, 673643, 695337, 716578, 737395, |
60 | 757817, 777869, 797572, 816947, 836012, 854782, |
61 | 873274, 891500, 909474, 927207, 944709, 979061, |
62 | 1012601, 1045391, 1077485, 1108931, 1139770, 1170042, |
63 | 1199778, 1229011, 1257767, 1286071, 1313948, 1341416, |
64 | 1368497, 1395207, 1421563, 1473272, 1523733, 1573041, |
65 | 1621279, 1668520, 1714828, 1760262, 1804874, 1848710, |
66 | 1891814, 1934223, 1975973, 2017096, 2057622, 2097578, |
67 | 2136989, 2214269, 2289629, 2363216, 2435157, 2505564, |
68 | 2574539, 2642169, 2708536, 2773711, 2837760, 2900742, |
69 | 2962712, 3023719, 3083810, 3143025, 3201405, 3315797, |
70 | 3427246, 3535974, 3642181, 3746038, 3847700, 3947305, |
71 | 4044975, 4140823, 4234949, 4327445, 4418394, 4507872, |
72 | 4595951, 4682694, 4768161, 4935487, 5098326, 5257022, |
73 | 5411878, 5563161, 5711107, 5855928, 5997812, 6136929, |
74 | 6273436, 6407471, 6539163, 6668629, 6795976, 6921304, |
75 | 7044703, 7286050, 7520623, 7748950, 7971492, 8188655, |
76 | 8400800, 8608247, 8811286, 9010175, 9205149, 9396421, |
77 | 9584186, 9768620, 9949889, 10128140, 10303513, 10646126, |
78 | 10978648, 11301874, 11616501, 11923142, 12222340, 12514578, |
79 | 12800290, 13079866, 13353659, 13621988, 13885144, 14143394, |
80 | 14396982, 14646132, 14891052, 15368951, 15832050, 16281537, |
81 | 16718448, 17143696, 17558086, 17962337, 18357092, 18742927, |
82 | 19120364, 19489877, 19851894, 20206810, 20554983, 20896745, |
83 | 21232399, 21886492, 22519276, 23132491, 23727656, 24306104, |
84 | 24869013, 25417430, 25952292, 26474438, 26984626, 27483542, |
85 | 27971811, 28450000, 28918632, 29378184, 29829095, 30706591, |
86 | 31554022, 32373894, 33168387, 33939412, 34688657, 35417620, |
87 | 36127636, 36819903, 37495502, 38155408, 38800507, 39431607, |
88 | 40049446, 40654702, 41247996, 42400951, 43512407, 44585892, |
89 | 45624474, 46630834, 47607339, 48556082, 49478931, 50377558, |
90 | 51253467, 52108015, 52942436, 53757848, 54555277, 55335659, |
91 | 56099856, 57582802, 59009766, 60385607, 61714540, 63000246, |
92 | 64245964, 65454559, 66628579, 67770304, 68881781, 69964856, |
93 | 71021203, 72052340, 73059655, 74044414, 75007782, 76874537, |
94 | 78667536, 80393312, 82057522, 83665098, 85220372, 86727167, |
95 | 88188883, 89608552, 90988895, 92332363, 93641173, 94917336, |
96 | 96162685, 97378894, 98567496, 100867409, 103072439, 105191162, |
97 | 107230989, 109198368, 111098951, 112937723, 114719105, 116447036, |
98 | 118125045, 119756307, 121343688, 122889787, 124396968, 125867388, |
99 | 127303021, 130077030, 132731849, 135278464, 137726346, 140083726, |
100 | 142357803, 144554913, 146680670, 148740067, 150737572, 152677197, |
101 | 154562560, 156396938, 158183306, 159924378, 161622632, 164899602, |
102 | 168030318, 171028513, 173906008, 176673051, 179338593, 181910502, |
103 | 184395731, 186800463, 189130216, 191389941, 193584098, 195716719, |
104 | 197791463, 199811660, 201780351, 205574133, 209192504, 212652233, |
105 | 215967720, 219151432, 222214238, 225165676, 228014163, 230767172, |
106 | 233431363, 236012706, 238516569, 240947800, 243310793, 245609544, |
107 | 247847696, 252155270, 256257056, 260173059, 263920427, 267513978, |
108 | 270966613, 274289634, 277493001, 280585542, 283575118, 286468763, |
109 | 289272796, 291992916, 294634284, 297201585, 299699091, 304500003, |
110 | 309064541, 313416043, 317574484, 321557096, 325378855, 329052864, |
111 | 332590655, 336002433, 339297275, 342483294, 345567766, 348557252, |
112 | 351457680, 354274432, 357012407, 362269536, 367260561, 372012143, |
113 | 376547060, 380884936, 385042798, 389035522, 392876185, 396576344, |
114 | 400146265, 403595112, 406931099, 410161619, 413293351, 416332348, |
115 | 419284117, 424945627, 430313203, 435416697, 440281572, 444929733, |
116 | 449380160, 453649415, 457752035, 461700854, 465507260, 469181407, |
117 | 472732388, 476168376, 479496748, 482724188, 485856764, 491858986, |
118 | 497542280, 502939446, 508078420, 512983199, 517674549, 522170569, |
119 | 526487126, 530638214, 534636233, 538492233, 542216094, 545816693, |
120 | 549302035, 552679362, 555955249, 562226134, 568156709, 573782374, |
121 | 579133244, 584235153, 589110430, 593778512, 598256421, 602559154, |
122 | 606699989, 610690741, 614541971, 618263157, 621862836, 625348729, |
123 | 628727839, 635190643, 641295921, 647081261, 652578597, 657815287, |
124 | 662814957, 667598146, 672182825, 676584810, 680818092, 684895111, |
125 | 688826974, 692623643, 696294085, 699846401, 703287935, 709864782, |
126 | 716071394, 721947076, 727525176, 732834238, 737898880, 742740485, |
127 | 747377745, 751827095, 756103063, 760218552, 764185078, 768012958, |
128 | 771711474, 775289005, 778753144, 785368225, 791604988, 797503949, |
129 | 803099452, 808420859, 813493471, 818339244, 822977353, 827424644, |
130 | 831695997, 835804619, 839762285, 843579541, 847265867, 850829815, |
131 | 854279128, 860861356, 867061719, 872921445, 878475444, 883753534, |
132 | 888781386, 893581259, 898172578, 902572393, 906795754, 910856010, |
133 | 914765057, 918533538, 922171018, 925686119, 929086644, 935571664, |
134 | 941675560, 947439782, 952899395, 958084324, 963020312, 967729662, |
135 | 972231821, 976543852, 980680801, 984656009, 988481353, 992167459, |
136 | 995723865, 999159168, 1002565681}; |
137 | |
138 | // these are helpers for calculations to reduce stack usage |
139 | // do not depend on these being preserved across calls |
140 | |
141 | /* Helper to optimize gamma calculation, only use in translate_from_linear, in |
142 | * particular the dc_fixpt_pow function which is very expensive |
143 | * The idea is that our regions for X points are exponential and currently they all use |
144 | * the same number of points (NUM_PTS_IN_REGION) and in each region every point |
145 | * is exactly 2x the one at the same index in the previous region. In other words |
146 | * X[i] = 2 * X[i-NUM_PTS_IN_REGION] for i>=16 |
147 | * The other fact is that (2x)^gamma = 2^gamma * x^gamma |
148 | * So we compute and save x^gamma for the first 16 regions, and for every next region |
149 | * just multiply with 2^gamma which can be computed once, and save the result so we |
150 | * recursively compute all the values. |
151 | */ |
152 | |
153 | /* |
154 | * Regamma coefficients are used for both regamma and degamma. Degamma |
155 | * coefficients are calculated in our formula using the regamma coefficients. |
156 | */ |
157 | /*sRGB 709 2.2 2.4 P3*/ |
158 | static const int32_t numerator01[] = { 31308, 180000, 0, 0, 0}; |
159 | static const int32_t numerator02[] = { 12920, 4500, 0, 0, 0}; |
160 | static const int32_t numerator03[] = { 55, 99, 0, 0, 0}; |
161 | static const int32_t numerator04[] = { 55, 99, 0, 0, 0}; |
162 | static const int32_t numerator05[] = { 2400, 2222, 2200, 2400, 2600}; |
163 | |
164 | /* one-time setup of X points */ |
165 | void setup_x_points_distribution(void) |
166 | { |
167 | struct fixed31_32 region_size = dc_fixpt_from_int(arg: 128); |
168 | int32_t segment; |
169 | uint32_t seg_offset; |
170 | uint32_t index; |
171 | struct fixed31_32 increment; |
172 | |
173 | coordinates_x[MAX_HW_POINTS].x = region_size; |
174 | coordinates_x[MAX_HW_POINTS + 1].x = region_size; |
175 | |
176 | for (segment = 6; segment > (6 - NUM_REGIONS); segment--) { |
177 | region_size = dc_fixpt_div_int(arg1: region_size, arg2: 2); |
178 | increment = dc_fixpt_div_int(arg1: region_size, |
179 | NUM_PTS_IN_REGION); |
180 | seg_offset = (segment + (NUM_REGIONS - 7)) * NUM_PTS_IN_REGION; |
181 | coordinates_x[seg_offset].x = region_size; |
182 | |
183 | for (index = seg_offset + 1; |
184 | index < seg_offset + NUM_PTS_IN_REGION; |
185 | index++) { |
186 | coordinates_x[index].x = dc_fixpt_add |
187 | (arg1: coordinates_x[index-1].x, arg2: increment); |
188 | } |
189 | } |
190 | } |
191 | |
192 | void log_x_points_distribution(struct dal_logger *logger) |
193 | { |
194 | int i = 0; |
195 | |
196 | if (logger != NULL) { |
197 | LOG_GAMMA_WRITE("Log X Distribution\n" ); |
198 | |
199 | for (i = 0; i < MAX_HW_POINTS; i++) |
200 | LOG_GAMMA_WRITE("%llu\n" , coordinates_x[i].x.value); |
201 | } |
202 | } |
203 | |
204 | static void compute_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y) |
205 | { |
206 | /* consts for PQ gamma formula. */ |
207 | const struct fixed31_32 m1 = |
208 | dc_fixpt_from_fraction(numerator: 159301758, denominator: 1000000000); |
209 | const struct fixed31_32 m2 = |
210 | dc_fixpt_from_fraction(numerator: 7884375, denominator: 100000); |
211 | const struct fixed31_32 c1 = |
212 | dc_fixpt_from_fraction(numerator: 8359375, denominator: 10000000); |
213 | const struct fixed31_32 c2 = |
214 | dc_fixpt_from_fraction(numerator: 188515625, denominator: 10000000); |
215 | const struct fixed31_32 c3 = |
216 | dc_fixpt_from_fraction(numerator: 186875, denominator: 10000); |
217 | |
218 | struct fixed31_32 l_pow_m1; |
219 | struct fixed31_32 base; |
220 | |
221 | if (dc_fixpt_lt(arg1: in_x, arg2: dc_fixpt_zero)) |
222 | in_x = dc_fixpt_zero; |
223 | |
224 | l_pow_m1 = dc_fixpt_pow(arg1: in_x, arg2: m1); |
225 | base = dc_fixpt_div( |
226 | arg1: dc_fixpt_add(arg1: c1, |
227 | arg2: (dc_fixpt_mul(arg1: c2, arg2: l_pow_m1))), |
228 | arg2: dc_fixpt_add(arg1: dc_fixpt_one, |
229 | arg2: (dc_fixpt_mul(arg1: c3, arg2: l_pow_m1)))); |
230 | *out_y = dc_fixpt_pow(arg1: base, arg2: m2); |
231 | } |
232 | |
233 | static void compute_de_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y) |
234 | { |
235 | /* consts for dePQ gamma formula. */ |
236 | const struct fixed31_32 m1 = |
237 | dc_fixpt_from_fraction(numerator: 159301758, denominator: 1000000000); |
238 | const struct fixed31_32 m2 = |
239 | dc_fixpt_from_fraction(numerator: 7884375, denominator: 100000); |
240 | const struct fixed31_32 c1 = |
241 | dc_fixpt_from_fraction(numerator: 8359375, denominator: 10000000); |
242 | const struct fixed31_32 c2 = |
243 | dc_fixpt_from_fraction(numerator: 188515625, denominator: 10000000); |
244 | const struct fixed31_32 c3 = |
245 | dc_fixpt_from_fraction(numerator: 186875, denominator: 10000); |
246 | |
247 | struct fixed31_32 l_pow_m1; |
248 | struct fixed31_32 base, div; |
249 | struct fixed31_32 base2; |
250 | |
251 | |
252 | if (dc_fixpt_lt(arg1: in_x, arg2: dc_fixpt_zero)) |
253 | in_x = dc_fixpt_zero; |
254 | |
255 | l_pow_m1 = dc_fixpt_pow(arg1: in_x, |
256 | arg2: dc_fixpt_div(arg1: dc_fixpt_one, arg2: m2)); |
257 | base = dc_fixpt_sub(arg1: l_pow_m1, arg2: c1); |
258 | |
259 | div = dc_fixpt_sub(arg1: c2, arg2: dc_fixpt_mul(arg1: c3, arg2: l_pow_m1)); |
260 | |
261 | base2 = dc_fixpt_div(arg1: base, arg2: div); |
262 | // avoid complex numbers |
263 | if (dc_fixpt_lt(arg1: base2, arg2: dc_fixpt_zero)) |
264 | base2 = dc_fixpt_sub(arg1: dc_fixpt_zero, arg2: base2); |
265 | |
266 | |
267 | *out_y = dc_fixpt_pow(arg1: base2, arg2: dc_fixpt_div(arg1: dc_fixpt_one, arg2: m1)); |
268 | |
269 | } |
270 | |
271 | |
272 | /* de gamma, non-linear to linear */ |
273 | static void compute_hlg_eotf(struct fixed31_32 in_x, |
274 | struct fixed31_32 *out_y, |
275 | uint32_t sdr_white_level, uint32_t max_luminance_nits) |
276 | { |
277 | struct fixed31_32 a; |
278 | struct fixed31_32 b; |
279 | struct fixed31_32 c; |
280 | struct fixed31_32 threshold; |
281 | struct fixed31_32 x; |
282 | |
283 | struct fixed31_32 scaling_factor = |
284 | dc_fixpt_from_fraction(numerator: max_luminance_nits, denominator: sdr_white_level); |
285 | a = dc_fixpt_from_fraction(numerator: 17883277, denominator: 100000000); |
286 | b = dc_fixpt_from_fraction(numerator: 28466892, denominator: 100000000); |
287 | c = dc_fixpt_from_fraction(numerator: 55991073, denominator: 100000000); |
288 | threshold = dc_fixpt_from_fraction(numerator: 1, denominator: 2); |
289 | |
290 | if (dc_fixpt_lt(arg1: in_x, arg2: threshold)) { |
291 | x = dc_fixpt_mul(arg1: in_x, arg2: in_x); |
292 | x = dc_fixpt_div_int(arg1: x, arg2: 3); |
293 | } else { |
294 | x = dc_fixpt_sub(arg1: in_x, arg2: c); |
295 | x = dc_fixpt_div(arg1: x, arg2: a); |
296 | x = dc_fixpt_exp(arg: x); |
297 | x = dc_fixpt_add(arg1: x, arg2: b); |
298 | x = dc_fixpt_div_int(arg1: x, arg2: 12); |
299 | } |
300 | *out_y = dc_fixpt_mul(arg1: x, arg2: scaling_factor); |
301 | |
302 | } |
303 | |
304 | /* re gamma, linear to non-linear */ |
305 | static void compute_hlg_oetf(struct fixed31_32 in_x, struct fixed31_32 *out_y, |
306 | uint32_t sdr_white_level, uint32_t max_luminance_nits) |
307 | { |
308 | struct fixed31_32 a; |
309 | struct fixed31_32 b; |
310 | struct fixed31_32 c; |
311 | struct fixed31_32 threshold; |
312 | struct fixed31_32 x; |
313 | |
314 | struct fixed31_32 scaling_factor = |
315 | dc_fixpt_from_fraction(numerator: sdr_white_level, denominator: max_luminance_nits); |
316 | a = dc_fixpt_from_fraction(numerator: 17883277, denominator: 100000000); |
317 | b = dc_fixpt_from_fraction(numerator: 28466892, denominator: 100000000); |
318 | c = dc_fixpt_from_fraction(numerator: 55991073, denominator: 100000000); |
319 | threshold = dc_fixpt_from_fraction(numerator: 1, denominator: 12); |
320 | x = dc_fixpt_mul(arg1: in_x, arg2: scaling_factor); |
321 | |
322 | |
323 | if (dc_fixpt_lt(arg1: x, arg2: threshold)) { |
324 | x = dc_fixpt_mul(arg1: x, arg2: dc_fixpt_from_fraction(numerator: 3, denominator: 1)); |
325 | *out_y = dc_fixpt_pow(arg1: x, arg2: dc_fixpt_half); |
326 | } else { |
327 | x = dc_fixpt_mul(arg1: x, arg2: dc_fixpt_from_fraction(numerator: 12, denominator: 1)); |
328 | x = dc_fixpt_sub(arg1: x, arg2: b); |
329 | x = dc_fixpt_log(arg: x); |
330 | x = dc_fixpt_mul(arg1: a, arg2: x); |
331 | *out_y = dc_fixpt_add(arg1: x, arg2: c); |
332 | } |
333 | } |
334 | |
335 | |
336 | /* one-time pre-compute PQ values - only for sdr_white_level 80 */ |
337 | void precompute_pq(void) |
338 | { |
339 | int i; |
340 | struct fixed31_32 *pq_table = mod_color_get_table(type: type_pq_table); |
341 | |
342 | for (i = 0; i <= MAX_HW_POINTS; i++) |
343 | pq_table[i] = dc_fixpt_from_fraction(numerator: pq_numerator[i], denominator: pq_divider); |
344 | |
345 | /* below is old method that uses run-time calculation in fixed pt space */ |
346 | /* pow function has problems with arguments too small */ |
347 | /* |
348 | struct fixed31_32 x; |
349 | const struct hw_x_point *coord_x = coordinates_x + 32; |
350 | struct fixed31_32 scaling_factor = |
351 | dc_fixpt_from_fraction(80, 10000); |
352 | |
353 | for (i = 0; i < 32; i++) |
354 | pq_table[i] = dc_fixpt_zero; |
355 | |
356 | for (i = 32; i <= MAX_HW_POINTS; i++) { |
357 | x = dc_fixpt_mul(coord_x->x, scaling_factor); |
358 | compute_pq(x, &pq_table[i]); |
359 | ++coord_x; |
360 | } |
361 | */ |
362 | } |
363 | |
364 | /* one-time pre-compute dePQ values - only for max pixel value 125 FP16 */ |
365 | void precompute_de_pq(void) |
366 | { |
367 | int i; |
368 | struct fixed31_32 y; |
369 | uint32_t begin_index, end_index; |
370 | |
371 | struct fixed31_32 scaling_factor = dc_fixpt_from_int(arg: 125); |
372 | struct fixed31_32 *de_pq_table = mod_color_get_table(type: type_de_pq_table); |
373 | /* X points is 2^-25 to 2^7 |
374 | * De-gamma X is 2^-12 to 2^0 – we are skipping first -12-(-25) = 13 regions |
375 | */ |
376 | begin_index = 13 * NUM_PTS_IN_REGION; |
377 | end_index = begin_index + 12 * NUM_PTS_IN_REGION; |
378 | |
379 | for (i = 0; i <= begin_index; i++) |
380 | de_pq_table[i] = dc_fixpt_zero; |
381 | |
382 | for (; i <= end_index; i++) { |
383 | compute_de_pq(in_x: coordinates_x[i].x, out_y: &y); |
384 | de_pq_table[i] = dc_fixpt_mul(arg1: y, arg2: scaling_factor); |
385 | } |
386 | |
387 | for (; i <= MAX_HW_POINTS; i++) |
388 | de_pq_table[i] = de_pq_table[i-1]; |
389 | } |
390 | struct dividers { |
391 | struct fixed31_32 divider1; |
392 | struct fixed31_32 divider2; |
393 | struct fixed31_32 divider3; |
394 | }; |
395 | |
396 | |
397 | static bool build_coefficients(struct gamma_coefficients *coefficients, |
398 | enum dc_transfer_func_predefined type) |
399 | { |
400 | |
401 | uint32_t i = 0; |
402 | uint32_t index = 0; |
403 | bool ret = true; |
404 | |
405 | if (type == TRANSFER_FUNCTION_SRGB) |
406 | index = 0; |
407 | else if (type == TRANSFER_FUNCTION_BT709) |
408 | index = 1; |
409 | else if (type == TRANSFER_FUNCTION_GAMMA22) |
410 | index = 2; |
411 | else if (type == TRANSFER_FUNCTION_GAMMA24) |
412 | index = 3; |
413 | else if (type == TRANSFER_FUNCTION_GAMMA26) |
414 | index = 4; |
415 | else { |
416 | ret = false; |
417 | goto release; |
418 | } |
419 | |
420 | do { |
421 | coefficients->a0[i] = dc_fixpt_from_fraction( |
422 | numerator: numerator01[index], denominator: 10000000); |
423 | coefficients->a1[i] = dc_fixpt_from_fraction( |
424 | numerator: numerator02[index], denominator: 1000); |
425 | coefficients->a2[i] = dc_fixpt_from_fraction( |
426 | numerator: numerator03[index], denominator: 1000); |
427 | coefficients->a3[i] = dc_fixpt_from_fraction( |
428 | numerator: numerator04[index], denominator: 1000); |
429 | coefficients->user_gamma[i] = dc_fixpt_from_fraction( |
430 | numerator: numerator05[index], denominator: 1000); |
431 | |
432 | ++i; |
433 | } while (i != ARRAY_SIZE(coefficients->a0)); |
434 | release: |
435 | return ret; |
436 | } |
437 | |
438 | static struct fixed31_32 translate_from_linear_space( |
439 | struct translate_from_linear_space_args *args) |
440 | { |
441 | const struct fixed31_32 one = dc_fixpt_from_int(arg: 1); |
442 | |
443 | struct fixed31_32 scratch_1, scratch_2; |
444 | struct calculate_buffer *cal_buffer = args->cal_buffer; |
445 | |
446 | if (dc_fixpt_le(arg1: one, arg2: args->arg)) |
447 | return one; |
448 | |
449 | if (dc_fixpt_le(arg1: args->arg, arg2: dc_fixpt_neg(arg: args->a0))) { |
450 | scratch_1 = dc_fixpt_add(arg1: one, arg2: args->a3); |
451 | scratch_2 = dc_fixpt_pow( |
452 | arg1: dc_fixpt_neg(arg: args->arg), |
453 | arg2: dc_fixpt_recip(arg: args->gamma)); |
454 | scratch_1 = dc_fixpt_mul(arg1: scratch_1, arg2: scratch_2); |
455 | scratch_1 = dc_fixpt_sub(arg1: args->a2, arg2: scratch_1); |
456 | |
457 | return scratch_1; |
458 | } else if (dc_fixpt_le(arg1: args->a0, arg2: args->arg)) { |
459 | if (cal_buffer->buffer_index == 0) { |
460 | cal_buffer->gamma_of_2 = dc_fixpt_pow(arg1: dc_fixpt_from_int(arg: 2), |
461 | arg2: dc_fixpt_recip(arg: args->gamma)); |
462 | } |
463 | scratch_1 = dc_fixpt_add(arg1: one, arg2: args->a3); |
464 | /* In the first region (first 16 points) and in the |
465 | * region delimited by START/END we calculate with |
466 | * full precision to avoid error accumulation. |
467 | */ |
468 | if ((cal_buffer->buffer_index >= PRECISE_LUT_REGION_START && |
469 | cal_buffer->buffer_index <= PRECISE_LUT_REGION_END) || |
470 | (cal_buffer->buffer_index < 16)) |
471 | scratch_2 = dc_fixpt_pow(arg1: args->arg, |
472 | arg2: dc_fixpt_recip(arg: args->gamma)); |
473 | else |
474 | scratch_2 = dc_fixpt_mul(arg1: cal_buffer->gamma_of_2, |
475 | arg2: cal_buffer->buffer[cal_buffer->buffer_index%16]); |
476 | |
477 | if (cal_buffer->buffer_index != -1) { |
478 | cal_buffer->buffer[cal_buffer->buffer_index%16] = scratch_2; |
479 | cal_buffer->buffer_index++; |
480 | } |
481 | |
482 | scratch_1 = dc_fixpt_mul(arg1: scratch_1, arg2: scratch_2); |
483 | scratch_1 = dc_fixpt_sub(arg1: scratch_1, arg2: args->a2); |
484 | |
485 | return scratch_1; |
486 | } else |
487 | return dc_fixpt_mul(arg1: args->arg, arg2: args->a1); |
488 | } |
489 | |
490 | |
491 | static struct fixed31_32 translate_from_linear_space_long( |
492 | struct translate_from_linear_space_args *args) |
493 | { |
494 | const struct fixed31_32 one = dc_fixpt_from_int(arg: 1); |
495 | |
496 | if (dc_fixpt_lt(arg1: one, arg2: args->arg)) |
497 | return one; |
498 | |
499 | if (dc_fixpt_le(arg1: args->arg, arg2: dc_fixpt_neg(arg: args->a0))) |
500 | return dc_fixpt_sub( |
501 | arg1: args->a2, |
502 | arg2: dc_fixpt_mul( |
503 | arg1: dc_fixpt_add( |
504 | arg1: one, |
505 | arg2: args->a3), |
506 | arg2: dc_fixpt_pow( |
507 | arg1: dc_fixpt_neg(arg: args->arg), |
508 | arg2: dc_fixpt_recip(arg: args->gamma)))); |
509 | else if (dc_fixpt_le(arg1: args->a0, arg2: args->arg)) |
510 | return dc_fixpt_sub( |
511 | arg1: dc_fixpt_mul( |
512 | arg1: dc_fixpt_add( |
513 | arg1: one, |
514 | arg2: args->a3), |
515 | arg2: dc_fixpt_pow( |
516 | arg1: args->arg, |
517 | arg2: dc_fixpt_recip(arg: args->gamma))), |
518 | arg2: args->a2); |
519 | else |
520 | return dc_fixpt_mul(arg1: args->arg, arg2: args->a1); |
521 | } |
522 | |
523 | static struct fixed31_32 calculate_gamma22(struct fixed31_32 arg, bool use_eetf, struct calculate_buffer *cal_buffer) |
524 | { |
525 | struct fixed31_32 gamma = dc_fixpt_from_fraction(numerator: 22, denominator: 10); |
526 | struct translate_from_linear_space_args scratch_gamma_args; |
527 | |
528 | scratch_gamma_args.arg = arg; |
529 | scratch_gamma_args.a0 = dc_fixpt_zero; |
530 | scratch_gamma_args.a1 = dc_fixpt_zero; |
531 | scratch_gamma_args.a2 = dc_fixpt_zero; |
532 | scratch_gamma_args.a3 = dc_fixpt_zero; |
533 | scratch_gamma_args.cal_buffer = cal_buffer; |
534 | scratch_gamma_args.gamma = gamma; |
535 | |
536 | if (use_eetf) |
537 | return translate_from_linear_space_long(args: &scratch_gamma_args); |
538 | |
539 | return translate_from_linear_space(args: &scratch_gamma_args); |
540 | } |
541 | |
542 | |
543 | static struct fixed31_32 translate_to_linear_space( |
544 | struct fixed31_32 arg, |
545 | struct fixed31_32 a0, |
546 | struct fixed31_32 a1, |
547 | struct fixed31_32 a2, |
548 | struct fixed31_32 a3, |
549 | struct fixed31_32 gamma) |
550 | { |
551 | struct fixed31_32 linear; |
552 | |
553 | a0 = dc_fixpt_mul(arg1: a0, arg2: a1); |
554 | if (dc_fixpt_le(arg1: arg, arg2: dc_fixpt_neg(arg: a0))) |
555 | |
556 | linear = dc_fixpt_neg( |
557 | arg: dc_fixpt_pow( |
558 | arg1: dc_fixpt_div( |
559 | arg1: dc_fixpt_sub(arg1: a2, arg2: arg), |
560 | arg2: dc_fixpt_add( |
561 | arg1: dc_fixpt_one, arg2: a3)), arg2: gamma)); |
562 | |
563 | else if (dc_fixpt_le(arg1: dc_fixpt_neg(arg: a0), arg2: arg) && |
564 | dc_fixpt_le(arg1: arg, arg2: a0)) |
565 | linear = dc_fixpt_div(arg1: arg, arg2: a1); |
566 | else |
567 | linear = dc_fixpt_pow( |
568 | arg1: dc_fixpt_div( |
569 | arg1: dc_fixpt_add(arg1: a2, arg2: arg), |
570 | arg2: dc_fixpt_add( |
571 | arg1: dc_fixpt_one, arg2: a3)), arg2: gamma); |
572 | |
573 | return linear; |
574 | } |
575 | |
576 | static struct fixed31_32 translate_from_linear_space_ex( |
577 | struct fixed31_32 arg, |
578 | struct gamma_coefficients *coeff, |
579 | uint32_t color_index, |
580 | struct calculate_buffer *cal_buffer) |
581 | { |
582 | struct translate_from_linear_space_args scratch_gamma_args; |
583 | |
584 | scratch_gamma_args.arg = arg; |
585 | scratch_gamma_args.a0 = coeff->a0[color_index]; |
586 | scratch_gamma_args.a1 = coeff->a1[color_index]; |
587 | scratch_gamma_args.a2 = coeff->a2[color_index]; |
588 | scratch_gamma_args.a3 = coeff->a3[color_index]; |
589 | scratch_gamma_args.gamma = coeff->user_gamma[color_index]; |
590 | scratch_gamma_args.cal_buffer = cal_buffer; |
591 | |
592 | return translate_from_linear_space(args: &scratch_gamma_args); |
593 | } |
594 | |
595 | |
596 | static inline struct fixed31_32 translate_to_linear_space_ex( |
597 | struct fixed31_32 arg, |
598 | struct gamma_coefficients *coeff, |
599 | uint32_t color_index) |
600 | { |
601 | return translate_to_linear_space( |
602 | arg, |
603 | a0: coeff->a0[color_index], |
604 | a1: coeff->a1[color_index], |
605 | a2: coeff->a2[color_index], |
606 | a3: coeff->a3[color_index], |
607 | gamma: coeff->user_gamma[color_index]); |
608 | } |
609 | |
610 | |
611 | static bool find_software_points( |
612 | const struct dc_gamma *ramp, |
613 | const struct gamma_pixel *axis_x, |
614 | struct fixed31_32 hw_point, |
615 | enum channel_name channel, |
616 | uint32_t *index_to_start, |
617 | uint32_t *index_left, |
618 | uint32_t *index_right, |
619 | enum hw_point_position *pos) |
620 | { |
621 | const uint32_t max_number = ramp->num_entries + 3; |
622 | |
623 | struct fixed31_32 left, right; |
624 | |
625 | uint32_t i = *index_to_start; |
626 | |
627 | while (i < max_number) { |
628 | if (channel == CHANNEL_NAME_RED) { |
629 | left = axis_x[i].r; |
630 | |
631 | if (i < max_number - 1) |
632 | right = axis_x[i + 1].r; |
633 | else |
634 | right = axis_x[max_number - 1].r; |
635 | } else if (channel == CHANNEL_NAME_GREEN) { |
636 | left = axis_x[i].g; |
637 | |
638 | if (i < max_number - 1) |
639 | right = axis_x[i + 1].g; |
640 | else |
641 | right = axis_x[max_number - 1].g; |
642 | } else { |
643 | left = axis_x[i].b; |
644 | |
645 | if (i < max_number - 1) |
646 | right = axis_x[i + 1].b; |
647 | else |
648 | right = axis_x[max_number - 1].b; |
649 | } |
650 | |
651 | if (dc_fixpt_le(arg1: left, arg2: hw_point) && |
652 | dc_fixpt_le(arg1: hw_point, arg2: right)) { |
653 | *index_to_start = i; |
654 | *index_left = i; |
655 | |
656 | if (i < max_number - 1) |
657 | *index_right = i + 1; |
658 | else |
659 | *index_right = max_number - 1; |
660 | |
661 | *pos = HW_POINT_POSITION_MIDDLE; |
662 | |
663 | return true; |
664 | } else if ((i == *index_to_start) && |
665 | dc_fixpt_le(arg1: hw_point, arg2: left)) { |
666 | *index_to_start = i; |
667 | *index_left = i; |
668 | *index_right = i; |
669 | |
670 | *pos = HW_POINT_POSITION_LEFT; |
671 | |
672 | return true; |
673 | } else if ((i == max_number - 1) && |
674 | dc_fixpt_le(arg1: right, arg2: hw_point)) { |
675 | *index_to_start = i; |
676 | *index_left = i; |
677 | *index_right = i; |
678 | |
679 | *pos = HW_POINT_POSITION_RIGHT; |
680 | |
681 | return true; |
682 | } |
683 | |
684 | ++i; |
685 | } |
686 | |
687 | return false; |
688 | } |
689 | |
690 | static bool build_custom_gamma_mapping_coefficients_worker( |
691 | const struct dc_gamma *ramp, |
692 | struct pixel_gamma_point *coeff, |
693 | const struct hw_x_point *coordinates_x, |
694 | const struct gamma_pixel *axis_x, |
695 | enum channel_name channel, |
696 | uint32_t number_of_points) |
697 | { |
698 | uint32_t i = 0; |
699 | |
700 | while (i <= number_of_points) { |
701 | struct fixed31_32 coord_x; |
702 | |
703 | uint32_t index_to_start = 0; |
704 | uint32_t index_left = 0; |
705 | uint32_t index_right = 0; |
706 | |
707 | enum hw_point_position hw_pos; |
708 | |
709 | struct gamma_point *point; |
710 | |
711 | struct fixed31_32 left_pos; |
712 | struct fixed31_32 right_pos; |
713 | |
714 | if (channel == CHANNEL_NAME_RED) |
715 | coord_x = coordinates_x[i].regamma_y_red; |
716 | else if (channel == CHANNEL_NAME_GREEN) |
717 | coord_x = coordinates_x[i].regamma_y_green; |
718 | else |
719 | coord_x = coordinates_x[i].regamma_y_blue; |
720 | |
721 | if (!find_software_points( |
722 | ramp, axis_x, hw_point: coord_x, channel, |
723 | index_to_start: &index_to_start, index_left: &index_left, index_right: &index_right, pos: &hw_pos)) { |
724 | BREAK_TO_DEBUGGER(); |
725 | return false; |
726 | } |
727 | |
728 | if (index_left >= ramp->num_entries + 3) { |
729 | BREAK_TO_DEBUGGER(); |
730 | return false; |
731 | } |
732 | |
733 | if (index_right >= ramp->num_entries + 3) { |
734 | BREAK_TO_DEBUGGER(); |
735 | return false; |
736 | } |
737 | |
738 | if (channel == CHANNEL_NAME_RED) { |
739 | point = &coeff[i].r; |
740 | |
741 | left_pos = axis_x[index_left].r; |
742 | right_pos = axis_x[index_right].r; |
743 | } else if (channel == CHANNEL_NAME_GREEN) { |
744 | point = &coeff[i].g; |
745 | |
746 | left_pos = axis_x[index_left].g; |
747 | right_pos = axis_x[index_right].g; |
748 | } else { |
749 | point = &coeff[i].b; |
750 | |
751 | left_pos = axis_x[index_left].b; |
752 | right_pos = axis_x[index_right].b; |
753 | } |
754 | |
755 | if (hw_pos == HW_POINT_POSITION_MIDDLE) |
756 | point->coeff = dc_fixpt_div( |
757 | arg1: dc_fixpt_sub( |
758 | arg1: coord_x, |
759 | arg2: left_pos), |
760 | arg2: dc_fixpt_sub( |
761 | arg1: right_pos, |
762 | arg2: left_pos)); |
763 | else if (hw_pos == HW_POINT_POSITION_LEFT) |
764 | point->coeff = dc_fixpt_zero; |
765 | else if (hw_pos == HW_POINT_POSITION_RIGHT) |
766 | point->coeff = dc_fixpt_from_int(arg: 2); |
767 | else { |
768 | BREAK_TO_DEBUGGER(); |
769 | return false; |
770 | } |
771 | |
772 | point->left_index = index_left; |
773 | point->right_index = index_right; |
774 | point->pos = hw_pos; |
775 | |
776 | ++i; |
777 | } |
778 | |
779 | return true; |
780 | } |
781 | |
782 | static struct fixed31_32 calculate_mapped_value( |
783 | struct pwl_float_data *rgb, |
784 | const struct pixel_gamma_point *coeff, |
785 | enum channel_name channel, |
786 | uint32_t max_index) |
787 | { |
788 | const struct gamma_point *point; |
789 | |
790 | struct fixed31_32 result; |
791 | |
792 | if (channel == CHANNEL_NAME_RED) |
793 | point = &coeff->r; |
794 | else if (channel == CHANNEL_NAME_GREEN) |
795 | point = &coeff->g; |
796 | else |
797 | point = &coeff->b; |
798 | |
799 | if ((point->left_index < 0) || (point->left_index > max_index)) { |
800 | BREAK_TO_DEBUGGER(); |
801 | return dc_fixpt_zero; |
802 | } |
803 | |
804 | if ((point->right_index < 0) || (point->right_index > max_index)) { |
805 | BREAK_TO_DEBUGGER(); |
806 | return dc_fixpt_zero; |
807 | } |
808 | |
809 | if (point->pos == HW_POINT_POSITION_MIDDLE) |
810 | if (channel == CHANNEL_NAME_RED) |
811 | result = dc_fixpt_add( |
812 | arg1: dc_fixpt_mul( |
813 | arg1: point->coeff, |
814 | arg2: dc_fixpt_sub( |
815 | arg1: rgb[point->right_index].r, |
816 | arg2: rgb[point->left_index].r)), |
817 | arg2: rgb[point->left_index].r); |
818 | else if (channel == CHANNEL_NAME_GREEN) |
819 | result = dc_fixpt_add( |
820 | arg1: dc_fixpt_mul( |
821 | arg1: point->coeff, |
822 | arg2: dc_fixpt_sub( |
823 | arg1: rgb[point->right_index].g, |
824 | arg2: rgb[point->left_index].g)), |
825 | arg2: rgb[point->left_index].g); |
826 | else |
827 | result = dc_fixpt_add( |
828 | arg1: dc_fixpt_mul( |
829 | arg1: point->coeff, |
830 | arg2: dc_fixpt_sub( |
831 | arg1: rgb[point->right_index].b, |
832 | arg2: rgb[point->left_index].b)), |
833 | arg2: rgb[point->left_index].b); |
834 | else if (point->pos == HW_POINT_POSITION_LEFT) { |
835 | BREAK_TO_DEBUGGER(); |
836 | result = dc_fixpt_zero; |
837 | } else { |
838 | result = dc_fixpt_one; |
839 | } |
840 | |
841 | return result; |
842 | } |
843 | |
844 | static void build_pq(struct pwl_float_data_ex *rgb_regamma, |
845 | uint32_t hw_points_num, |
846 | const struct hw_x_point *coordinate_x, |
847 | uint32_t sdr_white_level) |
848 | { |
849 | uint32_t i, start_index; |
850 | |
851 | struct pwl_float_data_ex *rgb = rgb_regamma; |
852 | const struct hw_x_point *coord_x = coordinate_x; |
853 | struct fixed31_32 x; |
854 | struct fixed31_32 output; |
855 | struct fixed31_32 scaling_factor = |
856 | dc_fixpt_from_fraction(numerator: sdr_white_level, denominator: 10000); |
857 | struct fixed31_32 *pq_table = mod_color_get_table(type: type_pq_table); |
858 | |
859 | if (!mod_color_is_table_init(type: type_pq_table) && sdr_white_level == 80) { |
860 | precompute_pq(); |
861 | mod_color_set_table_init_state(type: type_pq_table, state: true); |
862 | } |
863 | |
864 | /* TODO: start index is from segment 2^-24, skipping first segment |
865 | * due to x values too small for power calculations |
866 | */ |
867 | start_index = 32; |
868 | rgb += start_index; |
869 | coord_x += start_index; |
870 | |
871 | for (i = start_index; i <= hw_points_num; i++) { |
872 | /* Multiply 0.008 as regamma is 0-1 and FP16 input is 0-125. |
873 | * FP 1.0 = 80nits |
874 | */ |
875 | if (sdr_white_level == 80) { |
876 | output = pq_table[i]; |
877 | } else { |
878 | x = dc_fixpt_mul(arg1: coord_x->x, arg2: scaling_factor); |
879 | compute_pq(in_x: x, out_y: &output); |
880 | } |
881 | |
882 | /* should really not happen? */ |
883 | if (dc_fixpt_lt(arg1: output, arg2: dc_fixpt_zero)) |
884 | output = dc_fixpt_zero; |
885 | |
886 | rgb->r = output; |
887 | rgb->g = output; |
888 | rgb->b = output; |
889 | |
890 | ++coord_x; |
891 | ++rgb; |
892 | } |
893 | } |
894 | |
895 | static void build_de_pq(struct pwl_float_data_ex *de_pq, |
896 | uint32_t hw_points_num, |
897 | const struct hw_x_point *coordinate_x) |
898 | { |
899 | uint32_t i; |
900 | struct fixed31_32 output; |
901 | struct fixed31_32 *de_pq_table = mod_color_get_table(type: type_de_pq_table); |
902 | struct fixed31_32 scaling_factor = dc_fixpt_from_int(arg: 125); |
903 | |
904 | if (!mod_color_is_table_init(type: type_de_pq_table)) { |
905 | precompute_de_pq(); |
906 | mod_color_set_table_init_state(type: type_de_pq_table, state: true); |
907 | } |
908 | |
909 | |
910 | for (i = 0; i <= hw_points_num; i++) { |
911 | output = de_pq_table[i]; |
912 | /* should really not happen? */ |
913 | if (dc_fixpt_lt(arg1: output, arg2: dc_fixpt_zero)) |
914 | output = dc_fixpt_zero; |
915 | else if (dc_fixpt_lt(arg1: scaling_factor, arg2: output)) |
916 | output = scaling_factor; |
917 | de_pq[i].r = output; |
918 | de_pq[i].g = output; |
919 | de_pq[i].b = output; |
920 | } |
921 | } |
922 | |
923 | static bool build_regamma(struct pwl_float_data_ex *rgb_regamma, |
924 | uint32_t hw_points_num, |
925 | const struct hw_x_point *coordinate_x, |
926 | enum dc_transfer_func_predefined type, |
927 | struct calculate_buffer *cal_buffer) |
928 | { |
929 | uint32_t i; |
930 | bool ret = false; |
931 | |
932 | struct gamma_coefficients *coeff; |
933 | struct pwl_float_data_ex *rgb = rgb_regamma; |
934 | const struct hw_x_point *coord_x = coordinate_x; |
935 | |
936 | coeff = kvzalloc(size: sizeof(*coeff), GFP_KERNEL); |
937 | if (!coeff) |
938 | goto release; |
939 | |
940 | if (!build_coefficients(coefficients: coeff, type)) |
941 | goto release; |
942 | |
943 | memset(cal_buffer->buffer, 0, NUM_PTS_IN_REGION * sizeof(struct fixed31_32)); |
944 | cal_buffer->buffer_index = 0; // see variable definition for more info |
945 | |
946 | i = 0; |
947 | while (i <= hw_points_num) { |
948 | /* TODO use y vs r,g,b */ |
949 | rgb->r = translate_from_linear_space_ex( |
950 | arg: coord_x->x, coeff, color_index: 0, cal_buffer); |
951 | rgb->g = rgb->r; |
952 | rgb->b = rgb->r; |
953 | ++coord_x; |
954 | ++rgb; |
955 | ++i; |
956 | } |
957 | cal_buffer->buffer_index = -1; |
958 | ret = true; |
959 | release: |
960 | kvfree(addr: coeff); |
961 | return ret; |
962 | } |
963 | |
964 | static void hermite_spline_eetf(struct fixed31_32 input_x, |
965 | struct fixed31_32 max_display, |
966 | struct fixed31_32 min_display, |
967 | struct fixed31_32 max_content, |
968 | struct fixed31_32 *out_x) |
969 | { |
970 | struct fixed31_32 min_lum_pq; |
971 | struct fixed31_32 max_lum_pq; |
972 | struct fixed31_32 max_content_pq; |
973 | struct fixed31_32 ks; |
974 | struct fixed31_32 E1; |
975 | struct fixed31_32 E2; |
976 | struct fixed31_32 E3; |
977 | struct fixed31_32 t; |
978 | struct fixed31_32 t2; |
979 | struct fixed31_32 t3; |
980 | struct fixed31_32 two; |
981 | struct fixed31_32 three; |
982 | struct fixed31_32 temp1; |
983 | struct fixed31_32 temp2; |
984 | struct fixed31_32 a = dc_fixpt_from_fraction(numerator: 15, denominator: 10); |
985 | struct fixed31_32 b = dc_fixpt_from_fraction(numerator: 5, denominator: 10); |
986 | struct fixed31_32 epsilon = dc_fixpt_from_fraction(numerator: 1, denominator: 1000000); // dc_fixpt_epsilon is a bit too small |
987 | |
988 | if (dc_fixpt_eq(arg1: max_content, arg2: dc_fixpt_zero)) { |
989 | *out_x = dc_fixpt_zero; |
990 | return; |
991 | } |
992 | |
993 | compute_pq(in_x: input_x, out_y: &E1); |
994 | compute_pq(in_x: dc_fixpt_div(arg1: min_display, arg2: max_content), out_y: &min_lum_pq); |
995 | compute_pq(in_x: dc_fixpt_div(arg1: max_display, arg2: max_content), out_y: &max_lum_pq); |
996 | compute_pq(in_x: dc_fixpt_one, out_y: &max_content_pq); // always 1? DAL2 code is weird |
997 | a = dc_fixpt_div(arg1: dc_fixpt_add(arg1: dc_fixpt_one, arg2: b), arg2: max_content_pq); // (1+b)/maxContent |
998 | ks = dc_fixpt_sub(arg1: dc_fixpt_mul(arg1: a, arg2: max_lum_pq), arg2: b); // a * max_lum_pq - b |
999 | |
1000 | if (dc_fixpt_lt(arg1: E1, arg2: ks)) |
1001 | E2 = E1; |
1002 | else if (dc_fixpt_le(arg1: ks, arg2: E1) && dc_fixpt_le(arg1: E1, arg2: dc_fixpt_one)) { |
1003 | if (dc_fixpt_lt(arg1: epsilon, arg2: dc_fixpt_sub(arg1: dc_fixpt_one, arg2: ks))) |
1004 | // t = (E1 - ks) / (1 - ks) |
1005 | t = dc_fixpt_div(arg1: dc_fixpt_sub(arg1: E1, arg2: ks), |
1006 | arg2: dc_fixpt_sub(arg1: dc_fixpt_one, arg2: ks)); |
1007 | else |
1008 | t = dc_fixpt_zero; |
1009 | |
1010 | two = dc_fixpt_from_int(arg: 2); |
1011 | three = dc_fixpt_from_int(arg: 3); |
1012 | |
1013 | t2 = dc_fixpt_mul(arg1: t, arg2: t); |
1014 | t3 = dc_fixpt_mul(arg1: t2, arg2: t); |
1015 | temp1 = dc_fixpt_mul(arg1: two, arg2: t3); |
1016 | temp2 = dc_fixpt_mul(arg1: three, arg2: t2); |
1017 | |
1018 | // (2t^3 - 3t^2 + 1) * ks |
1019 | E2 = dc_fixpt_mul(arg1: ks, arg2: dc_fixpt_add(arg1: dc_fixpt_one, |
1020 | arg2: dc_fixpt_sub(arg1: temp1, arg2: temp2))); |
1021 | |
1022 | // (-2t^3 + 3t^2) * max_lum_pq |
1023 | E2 = dc_fixpt_add(arg1: E2, arg2: dc_fixpt_mul(arg1: max_lum_pq, |
1024 | arg2: dc_fixpt_sub(arg1: temp2, arg2: temp1))); |
1025 | |
1026 | temp1 = dc_fixpt_mul(arg1: two, arg2: t2); |
1027 | temp2 = dc_fixpt_sub(arg1: dc_fixpt_one, arg2: ks); |
1028 | |
1029 | // (t^3 - 2t^2 + t) * (1-ks) |
1030 | E2 = dc_fixpt_add(arg1: E2, arg2: dc_fixpt_mul(arg1: temp2, |
1031 | arg2: dc_fixpt_add(arg1: t, arg2: dc_fixpt_sub(arg1: t3, arg2: temp1)))); |
1032 | } else |
1033 | E2 = dc_fixpt_one; |
1034 | |
1035 | temp1 = dc_fixpt_sub(arg1: dc_fixpt_one, arg2: E2); |
1036 | temp2 = dc_fixpt_mul(arg1: temp1, arg2: temp1); |
1037 | temp2 = dc_fixpt_mul(arg1: temp2, arg2: temp2); |
1038 | // temp2 = (1-E2)^4 |
1039 | |
1040 | E3 = dc_fixpt_add(arg1: E2, arg2: dc_fixpt_mul(arg1: min_lum_pq, arg2: temp2)); |
1041 | compute_de_pq(in_x: E3, out_y: out_x); |
1042 | |
1043 | *out_x = dc_fixpt_div(arg1: *out_x, arg2: dc_fixpt_div(arg1: max_display, arg2: max_content)); |
1044 | } |
1045 | |
1046 | static bool build_freesync_hdr(struct pwl_float_data_ex *rgb_regamma, |
1047 | uint32_t hw_points_num, |
1048 | const struct hw_x_point *coordinate_x, |
1049 | const struct hdr_tm_params *fs_params, |
1050 | struct calculate_buffer *cal_buffer) |
1051 | { |
1052 | uint32_t i; |
1053 | struct pwl_float_data_ex *rgb = rgb_regamma; |
1054 | const struct hw_x_point *coord_x = coordinate_x; |
1055 | const struct hw_x_point *prv_coord_x = coord_x; |
1056 | struct fixed31_32 scaledX = dc_fixpt_zero; |
1057 | struct fixed31_32 scaledX1 = dc_fixpt_zero; |
1058 | struct fixed31_32 max_display; |
1059 | struct fixed31_32 min_display; |
1060 | struct fixed31_32 max_content; |
1061 | struct fixed31_32 clip = dc_fixpt_one; |
1062 | struct fixed31_32 output; |
1063 | bool use_eetf = false; |
1064 | bool is_clipped = false; |
1065 | struct fixed31_32 sdr_white_level; |
1066 | struct fixed31_32 coordX_diff; |
1067 | struct fixed31_32 out_dist_max; |
1068 | struct fixed31_32 bright_norm; |
1069 | |
1070 | if (fs_params->max_content == 0 || |
1071 | fs_params->max_display == 0) |
1072 | return false; |
1073 | |
1074 | max_display = dc_fixpt_from_int(arg: fs_params->max_display); |
1075 | min_display = dc_fixpt_from_fraction(numerator: fs_params->min_display, denominator: 10000); |
1076 | max_content = dc_fixpt_from_int(arg: fs_params->max_content); |
1077 | sdr_white_level = dc_fixpt_from_int(arg: fs_params->sdr_white_level); |
1078 | |
1079 | if (fs_params->min_display > 1000) // cap at 0.1 at the bottom |
1080 | min_display = dc_fixpt_from_fraction(numerator: 1, denominator: 10); |
1081 | if (fs_params->max_display < 100) // cap at 100 at the top |
1082 | max_display = dc_fixpt_from_int(arg: 100); |
1083 | |
1084 | // only max used, we don't adjust min luminance |
1085 | if (fs_params->max_content > fs_params->max_display) |
1086 | use_eetf = true; |
1087 | else |
1088 | max_content = max_display; |
1089 | |
1090 | if (!use_eetf) |
1091 | cal_buffer->buffer_index = 0; // see var definition for more info |
1092 | rgb += 32; // first 32 points have problems with fixed point, too small |
1093 | coord_x += 32; |
1094 | |
1095 | for (i = 32; i <= hw_points_num; i++) { |
1096 | if (!is_clipped) { |
1097 | if (use_eetf) { |
1098 | /* max content is equal 1 */ |
1099 | scaledX1 = dc_fixpt_div(arg1: coord_x->x, |
1100 | arg2: dc_fixpt_div(arg1: max_content, arg2: sdr_white_level)); |
1101 | hermite_spline_eetf(input_x: scaledX1, max_display, min_display, |
1102 | max_content, out_x: &scaledX); |
1103 | } else |
1104 | scaledX = dc_fixpt_div(arg1: coord_x->x, |
1105 | arg2: dc_fixpt_div(arg1: max_display, arg2: sdr_white_level)); |
1106 | |
1107 | if (dc_fixpt_lt(arg1: scaledX, arg2: clip)) { |
1108 | if (dc_fixpt_lt(arg1: scaledX, arg2: dc_fixpt_zero)) |
1109 | output = dc_fixpt_zero; |
1110 | else |
1111 | output = calculate_gamma22(arg: scaledX, use_eetf, cal_buffer); |
1112 | |
1113 | // Ensure output respects reasonable boundaries |
1114 | output = dc_fixpt_clamp(arg: output, min_value: dc_fixpt_zero, max_value: dc_fixpt_one); |
1115 | |
1116 | rgb->r = output; |
1117 | rgb->g = output; |
1118 | rgb->b = output; |
1119 | } else { |
1120 | /* Here clipping happens for the first time */ |
1121 | is_clipped = true; |
1122 | |
1123 | /* The next few lines implement the equation |
1124 | * output = prev_out + |
1125 | * (coord_x->x - prev_coord_x->x) * |
1126 | * (1.0 - prev_out) / |
1127 | * (maxDisp/sdr_white_level - prevCoordX) |
1128 | * |
1129 | * This equation interpolates the first point |
1130 | * after max_display/80 so that the slope from |
1131 | * hw_x_before_max and hw_x_after_max is such |
1132 | * that we hit Y=1.0 at max_display/80. |
1133 | */ |
1134 | |
1135 | coordX_diff = dc_fixpt_sub(arg1: coord_x->x, arg2: prv_coord_x->x); |
1136 | out_dist_max = dc_fixpt_sub(arg1: dc_fixpt_one, arg2: output); |
1137 | bright_norm = dc_fixpt_div(arg1: max_display, arg2: sdr_white_level); |
1138 | |
1139 | output = dc_fixpt_add( |
1140 | arg1: output, arg2: dc_fixpt_mul( |
1141 | arg1: coordX_diff, arg2: dc_fixpt_div( |
1142 | arg1: out_dist_max, |
1143 | arg2: dc_fixpt_sub(arg1: bright_norm, arg2: prv_coord_x->x) |
1144 | ) |
1145 | ) |
1146 | ); |
1147 | |
1148 | /* Relaxing the maximum boundary to 1.07 (instead of 1.0) |
1149 | * because the last point in the curve must be such that |
1150 | * the maximum display pixel brightness interpolates to |
1151 | * exactly 1.0. The worst case scenario was calculated |
1152 | * around 1.057, so the limit of 1.07 leaves some safety |
1153 | * margin. |
1154 | */ |
1155 | output = dc_fixpt_clamp(arg: output, min_value: dc_fixpt_zero, |
1156 | max_value: dc_fixpt_from_fraction(numerator: 107, denominator: 100)); |
1157 | |
1158 | rgb->r = output; |
1159 | rgb->g = output; |
1160 | rgb->b = output; |
1161 | } |
1162 | } else { |
1163 | /* Every other clipping after the first |
1164 | * one is dealt with here |
1165 | */ |
1166 | rgb->r = clip; |
1167 | rgb->g = clip; |
1168 | rgb->b = clip; |
1169 | } |
1170 | |
1171 | prv_coord_x = coord_x; |
1172 | ++coord_x; |
1173 | ++rgb; |
1174 | } |
1175 | cal_buffer->buffer_index = -1; |
1176 | |
1177 | return true; |
1178 | } |
1179 | |
1180 | static bool build_degamma(struct pwl_float_data_ex *curve, |
1181 | uint32_t hw_points_num, |
1182 | const struct hw_x_point *coordinate_x, enum dc_transfer_func_predefined type) |
1183 | { |
1184 | uint32_t i; |
1185 | struct gamma_coefficients coeff; |
1186 | uint32_t begin_index, end_index; |
1187 | bool ret = false; |
1188 | |
1189 | if (!build_coefficients(coefficients: &coeff, type)) |
1190 | goto release; |
1191 | |
1192 | i = 0; |
1193 | |
1194 | /* X points is 2^-25 to 2^7 |
1195 | * De-gamma X is 2^-12 to 2^0 – we are skipping first -12-(-25) = 13 regions |
1196 | */ |
1197 | begin_index = 13 * NUM_PTS_IN_REGION; |
1198 | end_index = begin_index + 12 * NUM_PTS_IN_REGION; |
1199 | |
1200 | while (i != begin_index) { |
1201 | curve[i].r = dc_fixpt_zero; |
1202 | curve[i].g = dc_fixpt_zero; |
1203 | curve[i].b = dc_fixpt_zero; |
1204 | i++; |
1205 | } |
1206 | |
1207 | while (i != end_index) { |
1208 | curve[i].r = translate_to_linear_space_ex( |
1209 | arg: coordinate_x[i].x, coeff: &coeff, color_index: 0); |
1210 | curve[i].g = curve[i].r; |
1211 | curve[i].b = curve[i].r; |
1212 | i++; |
1213 | } |
1214 | while (i != hw_points_num + 1) { |
1215 | curve[i].r = dc_fixpt_one; |
1216 | curve[i].g = dc_fixpt_one; |
1217 | curve[i].b = dc_fixpt_one; |
1218 | i++; |
1219 | } |
1220 | ret = true; |
1221 | release: |
1222 | return ret; |
1223 | } |
1224 | |
1225 | |
1226 | |
1227 | |
1228 | |
1229 | static void build_hlg_degamma(struct pwl_float_data_ex *degamma, |
1230 | uint32_t hw_points_num, |
1231 | const struct hw_x_point *coordinate_x, |
1232 | uint32_t sdr_white_level, uint32_t max_luminance_nits) |
1233 | { |
1234 | uint32_t i; |
1235 | |
1236 | struct pwl_float_data_ex *rgb = degamma; |
1237 | const struct hw_x_point *coord_x = coordinate_x; |
1238 | |
1239 | i = 0; |
1240 | // check when i == 434 |
1241 | while (i != hw_points_num + 1) { |
1242 | compute_hlg_eotf(in_x: coord_x->x, out_y: &rgb->r, sdr_white_level, max_luminance_nits); |
1243 | rgb->g = rgb->r; |
1244 | rgb->b = rgb->r; |
1245 | ++coord_x; |
1246 | ++rgb; |
1247 | ++i; |
1248 | } |
1249 | } |
1250 | |
1251 | |
1252 | static void build_hlg_regamma(struct pwl_float_data_ex *regamma, |
1253 | uint32_t hw_points_num, |
1254 | const struct hw_x_point *coordinate_x, |
1255 | uint32_t sdr_white_level, uint32_t max_luminance_nits) |
1256 | { |
1257 | uint32_t i; |
1258 | |
1259 | struct pwl_float_data_ex *rgb = regamma; |
1260 | const struct hw_x_point *coord_x = coordinate_x; |
1261 | |
1262 | i = 0; |
1263 | |
1264 | // when i == 471 |
1265 | while (i != hw_points_num + 1) { |
1266 | compute_hlg_oetf(in_x: coord_x->x, out_y: &rgb->r, sdr_white_level, max_luminance_nits); |
1267 | rgb->g = rgb->r; |
1268 | rgb->b = rgb->r; |
1269 | ++coord_x; |
1270 | ++rgb; |
1271 | ++i; |
1272 | } |
1273 | } |
1274 | |
1275 | static void scale_gamma(struct pwl_float_data *pwl_rgb, |
1276 | const struct dc_gamma *ramp, |
1277 | struct dividers dividers) |
1278 | { |
1279 | const struct fixed31_32 max_driver = dc_fixpt_from_int(arg: 0xFFFF); |
1280 | const struct fixed31_32 max_os = dc_fixpt_from_int(arg: 0xFF00); |
1281 | struct fixed31_32 scaler = max_os; |
1282 | uint32_t i; |
1283 | struct pwl_float_data *rgb = pwl_rgb; |
1284 | struct pwl_float_data *rgb_last = rgb + ramp->num_entries - 1; |
1285 | |
1286 | i = 0; |
1287 | |
1288 | do { |
1289 | if (dc_fixpt_lt(arg1: max_os, arg2: ramp->entries.red[i]) || |
1290 | dc_fixpt_lt(arg1: max_os, arg2: ramp->entries.green[i]) || |
1291 | dc_fixpt_lt(arg1: max_os, arg2: ramp->entries.blue[i])) { |
1292 | scaler = max_driver; |
1293 | break; |
1294 | } |
1295 | ++i; |
1296 | } while (i != ramp->num_entries); |
1297 | |
1298 | i = 0; |
1299 | |
1300 | do { |
1301 | rgb->r = dc_fixpt_div( |
1302 | arg1: ramp->entries.red[i], arg2: scaler); |
1303 | rgb->g = dc_fixpt_div( |
1304 | arg1: ramp->entries.green[i], arg2: scaler); |
1305 | rgb->b = dc_fixpt_div( |
1306 | arg1: ramp->entries.blue[i], arg2: scaler); |
1307 | |
1308 | ++rgb; |
1309 | ++i; |
1310 | } while (i != ramp->num_entries); |
1311 | |
1312 | rgb->r = dc_fixpt_mul(arg1: rgb_last->r, |
1313 | arg2: dividers.divider1); |
1314 | rgb->g = dc_fixpt_mul(arg1: rgb_last->g, |
1315 | arg2: dividers.divider1); |
1316 | rgb->b = dc_fixpt_mul(arg1: rgb_last->b, |
1317 | arg2: dividers.divider1); |
1318 | |
1319 | ++rgb; |
1320 | |
1321 | rgb->r = dc_fixpt_mul(arg1: rgb_last->r, |
1322 | arg2: dividers.divider2); |
1323 | rgb->g = dc_fixpt_mul(arg1: rgb_last->g, |
1324 | arg2: dividers.divider2); |
1325 | rgb->b = dc_fixpt_mul(arg1: rgb_last->b, |
1326 | arg2: dividers.divider2); |
1327 | |
1328 | ++rgb; |
1329 | |
1330 | rgb->r = dc_fixpt_mul(arg1: rgb_last->r, |
1331 | arg2: dividers.divider3); |
1332 | rgb->g = dc_fixpt_mul(arg1: rgb_last->g, |
1333 | arg2: dividers.divider3); |
1334 | rgb->b = dc_fixpt_mul(arg1: rgb_last->b, |
1335 | arg2: dividers.divider3); |
1336 | } |
1337 | |
1338 | static void scale_gamma_dx(struct pwl_float_data *pwl_rgb, |
1339 | const struct dc_gamma *ramp, |
1340 | struct dividers dividers) |
1341 | { |
1342 | uint32_t i; |
1343 | struct fixed31_32 min = dc_fixpt_zero; |
1344 | struct fixed31_32 max = dc_fixpt_one; |
1345 | |
1346 | struct fixed31_32 delta = dc_fixpt_zero; |
1347 | struct fixed31_32 offset = dc_fixpt_zero; |
1348 | |
1349 | for (i = 0 ; i < ramp->num_entries; i++) { |
1350 | if (dc_fixpt_lt(arg1: ramp->entries.red[i], arg2: min)) |
1351 | min = ramp->entries.red[i]; |
1352 | |
1353 | if (dc_fixpt_lt(arg1: ramp->entries.green[i], arg2: min)) |
1354 | min = ramp->entries.green[i]; |
1355 | |
1356 | if (dc_fixpt_lt(arg1: ramp->entries.blue[i], arg2: min)) |
1357 | min = ramp->entries.blue[i]; |
1358 | |
1359 | if (dc_fixpt_lt(arg1: max, arg2: ramp->entries.red[i])) |
1360 | max = ramp->entries.red[i]; |
1361 | |
1362 | if (dc_fixpt_lt(arg1: max, arg2: ramp->entries.green[i])) |
1363 | max = ramp->entries.green[i]; |
1364 | |
1365 | if (dc_fixpt_lt(arg1: max, arg2: ramp->entries.blue[i])) |
1366 | max = ramp->entries.blue[i]; |
1367 | } |
1368 | |
1369 | if (dc_fixpt_lt(arg1: min, arg2: dc_fixpt_zero)) |
1370 | delta = dc_fixpt_neg(arg: min); |
1371 | |
1372 | offset = dc_fixpt_add(arg1: min, arg2: max); |
1373 | |
1374 | for (i = 0 ; i < ramp->num_entries; i++) { |
1375 | pwl_rgb[i].r = dc_fixpt_div( |
1376 | arg1: dc_fixpt_add( |
1377 | arg1: ramp->entries.red[i], arg2: delta), arg2: offset); |
1378 | pwl_rgb[i].g = dc_fixpt_div( |
1379 | arg1: dc_fixpt_add( |
1380 | arg1: ramp->entries.green[i], arg2: delta), arg2: offset); |
1381 | pwl_rgb[i].b = dc_fixpt_div( |
1382 | arg1: dc_fixpt_add( |
1383 | arg1: ramp->entries.blue[i], arg2: delta), arg2: offset); |
1384 | |
1385 | } |
1386 | |
1387 | pwl_rgb[i].r = dc_fixpt_sub(arg1: dc_fixpt_mul_int( |
1388 | arg1: pwl_rgb[i-1].r, arg2: 2), arg2: pwl_rgb[i-2].r); |
1389 | pwl_rgb[i].g = dc_fixpt_sub(arg1: dc_fixpt_mul_int( |
1390 | arg1: pwl_rgb[i-1].g, arg2: 2), arg2: pwl_rgb[i-2].g); |
1391 | pwl_rgb[i].b = dc_fixpt_sub(arg1: dc_fixpt_mul_int( |
1392 | arg1: pwl_rgb[i-1].b, arg2: 2), arg2: pwl_rgb[i-2].b); |
1393 | ++i; |
1394 | pwl_rgb[i].r = dc_fixpt_sub(arg1: dc_fixpt_mul_int( |
1395 | arg1: pwl_rgb[i-1].r, arg2: 2), arg2: pwl_rgb[i-2].r); |
1396 | pwl_rgb[i].g = dc_fixpt_sub(arg1: dc_fixpt_mul_int( |
1397 | arg1: pwl_rgb[i-1].g, arg2: 2), arg2: pwl_rgb[i-2].g); |
1398 | pwl_rgb[i].b = dc_fixpt_sub(arg1: dc_fixpt_mul_int( |
1399 | arg1: pwl_rgb[i-1].b, arg2: 2), arg2: pwl_rgb[i-2].b); |
1400 | } |
1401 | |
1402 | /* todo: all these scale_gamma functions are inherently the same but |
1403 | * take different structures as params or different format for ramp |
1404 | * values. We could probably implement it in a more generic fashion |
1405 | */ |
1406 | static void scale_user_regamma_ramp(struct pwl_float_data *pwl_rgb, |
1407 | const struct regamma_ramp *ramp, |
1408 | struct dividers dividers) |
1409 | { |
1410 | unsigned short max_driver = 0xFFFF; |
1411 | unsigned short max_os = 0xFF00; |
1412 | unsigned short scaler = max_os; |
1413 | uint32_t i; |
1414 | struct pwl_float_data *rgb = pwl_rgb; |
1415 | struct pwl_float_data *rgb_last = rgb + GAMMA_RGB_256_ENTRIES - 1; |
1416 | |
1417 | i = 0; |
1418 | do { |
1419 | if (ramp->gamma[i] > max_os || |
1420 | ramp->gamma[i + 256] > max_os || |
1421 | ramp->gamma[i + 512] > max_os) { |
1422 | scaler = max_driver; |
1423 | break; |
1424 | } |
1425 | i++; |
1426 | } while (i != GAMMA_RGB_256_ENTRIES); |
1427 | |
1428 | i = 0; |
1429 | do { |
1430 | rgb->r = dc_fixpt_from_fraction( |
1431 | numerator: ramp->gamma[i], denominator: scaler); |
1432 | rgb->g = dc_fixpt_from_fraction( |
1433 | numerator: ramp->gamma[i + 256], denominator: scaler); |
1434 | rgb->b = dc_fixpt_from_fraction( |
1435 | numerator: ramp->gamma[i + 512], denominator: scaler); |
1436 | |
1437 | ++rgb; |
1438 | ++i; |
1439 | } while (i != GAMMA_RGB_256_ENTRIES); |
1440 | |
1441 | rgb->r = dc_fixpt_mul(arg1: rgb_last->r, |
1442 | arg2: dividers.divider1); |
1443 | rgb->g = dc_fixpt_mul(arg1: rgb_last->g, |
1444 | arg2: dividers.divider1); |
1445 | rgb->b = dc_fixpt_mul(arg1: rgb_last->b, |
1446 | arg2: dividers.divider1); |
1447 | |
1448 | ++rgb; |
1449 | |
1450 | rgb->r = dc_fixpt_mul(arg1: rgb_last->r, |
1451 | arg2: dividers.divider2); |
1452 | rgb->g = dc_fixpt_mul(arg1: rgb_last->g, |
1453 | arg2: dividers.divider2); |
1454 | rgb->b = dc_fixpt_mul(arg1: rgb_last->b, |
1455 | arg2: dividers.divider2); |
1456 | |
1457 | ++rgb; |
1458 | |
1459 | rgb->r = dc_fixpt_mul(arg1: rgb_last->r, |
1460 | arg2: dividers.divider3); |
1461 | rgb->g = dc_fixpt_mul(arg1: rgb_last->g, |
1462 | arg2: dividers.divider3); |
1463 | rgb->b = dc_fixpt_mul(arg1: rgb_last->b, |
1464 | arg2: dividers.divider3); |
1465 | } |
1466 | |
1467 | /* |
1468 | * RS3+ color transform DDI - 1D LUT adjustment is composed with regamma here |
1469 | * Input is evenly distributed in the output color space as specified in |
1470 | * SetTimings |
1471 | * |
1472 | * Interpolation details: |
1473 | * 1D LUT has 4096 values which give curve correction in 0-1 float range |
1474 | * for evenly spaced points in 0-1 range. lut1D[index] gives correction |
1475 | * for index/4095. |
1476 | * First we find index for which: |
1477 | * index/4095 < regamma_y < (index+1)/4095 => |
1478 | * index < 4095*regamma_y < index + 1 |
1479 | * norm_y = 4095*regamma_y, and index is just truncating to nearest integer |
1480 | * lut1 = lut1D[index], lut2 = lut1D[index+1] |
1481 | * |
1482 | * adjustedY is then linearly interpolating regamma Y between lut1 and lut2 |
1483 | * |
1484 | * Custom degamma on Linux uses the same interpolation math, so is handled here |
1485 | */ |
1486 | static void apply_lut_1d( |
1487 | const struct dc_gamma *ramp, |
1488 | uint32_t num_hw_points, |
1489 | struct dc_transfer_func_distributed_points *tf_pts) |
1490 | { |
1491 | int i = 0; |
1492 | int color = 0; |
1493 | struct fixed31_32 *regamma_y; |
1494 | struct fixed31_32 norm_y; |
1495 | struct fixed31_32 lut1; |
1496 | struct fixed31_32 lut2; |
1497 | const int max_lut_index = 4095; |
1498 | const struct fixed31_32 penult_lut_index_f = |
1499 | dc_fixpt_from_int(arg: max_lut_index-1); |
1500 | const struct fixed31_32 max_lut_index_f = |
1501 | dc_fixpt_from_int(arg: max_lut_index); |
1502 | int32_t index = 0, index_next = 0; |
1503 | struct fixed31_32 index_f; |
1504 | struct fixed31_32 delta_lut; |
1505 | struct fixed31_32 delta_index; |
1506 | |
1507 | if (ramp->type != GAMMA_CS_TFM_1D && ramp->type != GAMMA_CUSTOM) |
1508 | return; // this is not expected |
1509 | |
1510 | for (i = 0; i < num_hw_points; i++) { |
1511 | for (color = 0; color < 3; color++) { |
1512 | if (color == 0) |
1513 | regamma_y = &tf_pts->red[i]; |
1514 | else if (color == 1) |
1515 | regamma_y = &tf_pts->green[i]; |
1516 | else |
1517 | regamma_y = &tf_pts->blue[i]; |
1518 | |
1519 | norm_y = dc_fixpt_mul(arg1: max_lut_index_f, |
1520 | arg2: *regamma_y); |
1521 | index = dc_fixpt_floor(arg: norm_y); |
1522 | index_f = dc_fixpt_from_int(arg: index); |
1523 | |
1524 | if (index < 0) |
1525 | continue; |
1526 | |
1527 | if (index <= max_lut_index) |
1528 | index_next = (index == max_lut_index) ? index : index+1; |
1529 | else { |
1530 | /* Here we are dealing with the last point in the curve, |
1531 | * which in some cases might exceed the range given by |
1532 | * max_lut_index. So we interpolate the value using |
1533 | * max_lut_index and max_lut_index - 1. |
1534 | */ |
1535 | index = max_lut_index - 1; |
1536 | index_next = max_lut_index; |
1537 | index_f = penult_lut_index_f; |
1538 | } |
1539 | |
1540 | if (color == 0) { |
1541 | lut1 = ramp->entries.red[index]; |
1542 | lut2 = ramp->entries.red[index_next]; |
1543 | } else if (color == 1) { |
1544 | lut1 = ramp->entries.green[index]; |
1545 | lut2 = ramp->entries.green[index_next]; |
1546 | } else { |
1547 | lut1 = ramp->entries.blue[index]; |
1548 | lut2 = ramp->entries.blue[index_next]; |
1549 | } |
1550 | |
1551 | // we have everything now, so interpolate |
1552 | delta_lut = dc_fixpt_sub(arg1: lut2, arg2: lut1); |
1553 | delta_index = dc_fixpt_sub(arg1: norm_y, arg2: index_f); |
1554 | |
1555 | *regamma_y = dc_fixpt_add(arg1: lut1, |
1556 | arg2: dc_fixpt_mul(arg1: delta_index, arg2: delta_lut)); |
1557 | } |
1558 | } |
1559 | } |
1560 | |
1561 | static void build_evenly_distributed_points( |
1562 | struct gamma_pixel *points, |
1563 | uint32_t numberof_points, |
1564 | struct dividers dividers) |
1565 | { |
1566 | struct gamma_pixel *p = points; |
1567 | struct gamma_pixel *p_last; |
1568 | |
1569 | uint32_t i = 0; |
1570 | |
1571 | // This function should not gets called with 0 as a parameter |
1572 | ASSERT(numberof_points > 0); |
1573 | p_last = p + numberof_points - 1; |
1574 | |
1575 | do { |
1576 | struct fixed31_32 value = dc_fixpt_from_fraction(numerator: i, |
1577 | denominator: numberof_points - 1); |
1578 | |
1579 | p->r = value; |
1580 | p->g = value; |
1581 | p->b = value; |
1582 | |
1583 | ++p; |
1584 | ++i; |
1585 | } while (i < numberof_points); |
1586 | |
1587 | p->r = dc_fixpt_div(arg1: p_last->r, arg2: dividers.divider1); |
1588 | p->g = dc_fixpt_div(arg1: p_last->g, arg2: dividers.divider1); |
1589 | p->b = dc_fixpt_div(arg1: p_last->b, arg2: dividers.divider1); |
1590 | |
1591 | ++p; |
1592 | |
1593 | p->r = dc_fixpt_div(arg1: p_last->r, arg2: dividers.divider2); |
1594 | p->g = dc_fixpt_div(arg1: p_last->g, arg2: dividers.divider2); |
1595 | p->b = dc_fixpt_div(arg1: p_last->b, arg2: dividers.divider2); |
1596 | |
1597 | ++p; |
1598 | |
1599 | p->r = dc_fixpt_div(arg1: p_last->r, arg2: dividers.divider3); |
1600 | p->g = dc_fixpt_div(arg1: p_last->g, arg2: dividers.divider3); |
1601 | p->b = dc_fixpt_div(arg1: p_last->b, arg2: dividers.divider3); |
1602 | } |
1603 | |
1604 | static inline void copy_rgb_regamma_to_coordinates_x( |
1605 | struct hw_x_point *coordinates_x, |
1606 | uint32_t hw_points_num, |
1607 | const struct pwl_float_data_ex *rgb_ex) |
1608 | { |
1609 | struct hw_x_point *coords = coordinates_x; |
1610 | uint32_t i = 0; |
1611 | const struct pwl_float_data_ex *rgb_regamma = rgb_ex; |
1612 | |
1613 | while (i <= hw_points_num + 1) { |
1614 | coords->regamma_y_red = rgb_regamma->r; |
1615 | coords->regamma_y_green = rgb_regamma->g; |
1616 | coords->regamma_y_blue = rgb_regamma->b; |
1617 | |
1618 | ++coords; |
1619 | ++rgb_regamma; |
1620 | ++i; |
1621 | } |
1622 | } |
1623 | |
1624 | static bool calculate_interpolated_hardware_curve( |
1625 | const struct dc_gamma *ramp, |
1626 | struct pixel_gamma_point *coeff128, |
1627 | struct pwl_float_data *rgb_user, |
1628 | const struct hw_x_point *coordinates_x, |
1629 | const struct gamma_pixel *axis_x, |
1630 | uint32_t number_of_points, |
1631 | struct dc_transfer_func_distributed_points *tf_pts) |
1632 | { |
1633 | |
1634 | const struct pixel_gamma_point *coeff = coeff128; |
1635 | uint32_t max_entries = 3 - 1; |
1636 | |
1637 | uint32_t i = 0; |
1638 | |
1639 | for (i = 0; i < 3; i++) { |
1640 | if (!build_custom_gamma_mapping_coefficients_worker( |
1641 | ramp, coeff: coeff128, coordinates_x, axis_x, channel: i, |
1642 | number_of_points)) |
1643 | return false; |
1644 | } |
1645 | |
1646 | i = 0; |
1647 | max_entries += ramp->num_entries; |
1648 | |
1649 | /* TODO: float point case */ |
1650 | |
1651 | while (i <= number_of_points) { |
1652 | tf_pts->red[i] = calculate_mapped_value( |
1653 | rgb: rgb_user, coeff, channel: CHANNEL_NAME_RED, max_index: max_entries); |
1654 | tf_pts->green[i] = calculate_mapped_value( |
1655 | rgb: rgb_user, coeff, channel: CHANNEL_NAME_GREEN, max_index: max_entries); |
1656 | tf_pts->blue[i] = calculate_mapped_value( |
1657 | rgb: rgb_user, coeff, channel: CHANNEL_NAME_BLUE, max_index: max_entries); |
1658 | |
1659 | ++coeff; |
1660 | ++i; |
1661 | } |
1662 | |
1663 | return true; |
1664 | } |
1665 | |
1666 | /* The "old" interpolation uses a complicated scheme to build an array of |
1667 | * coefficients while also using an array of 0-255 normalized to 0-1 |
1668 | * Then there's another loop using both of the above + new scaled user ramp |
1669 | * and we concatenate them. It also searches for points of interpolation and |
1670 | * uses enums for positions. |
1671 | * |
1672 | * This function uses a different approach: |
1673 | * user ramp is always applied on X with 0/255, 1/255, 2/255, ..., 255/255 |
1674 | * To find index for hwX , we notice the following: |
1675 | * i/255 <= hwX < (i+1)/255 <=> i <= 255*hwX < i+1 |
1676 | * See apply_lut_1d which is the same principle, but on 4K entry 1D LUT |
1677 | * |
1678 | * Once the index is known, combined Y is simply: |
1679 | * user_ramp(index) + (hwX-index/255)*(user_ramp(index+1) - user_ramp(index) |
1680 | * |
1681 | * We should switch to this method in all cases, it's simpler and faster |
1682 | * ToDo one day - for now this only applies to ADL regamma to avoid regression |
1683 | * for regular use cases (sRGB and PQ) |
1684 | */ |
1685 | static void interpolate_user_regamma(uint32_t hw_points_num, |
1686 | struct pwl_float_data *rgb_user, |
1687 | bool apply_degamma, |
1688 | struct dc_transfer_func_distributed_points *tf_pts) |
1689 | { |
1690 | uint32_t i; |
1691 | uint32_t color = 0; |
1692 | int32_t index; |
1693 | int32_t index_next; |
1694 | struct fixed31_32 *tf_point; |
1695 | struct fixed31_32 hw_x; |
1696 | struct fixed31_32 norm_factor = |
1697 | dc_fixpt_from_int(arg: 255); |
1698 | struct fixed31_32 norm_x; |
1699 | struct fixed31_32 index_f; |
1700 | struct fixed31_32 lut1; |
1701 | struct fixed31_32 lut2; |
1702 | struct fixed31_32 delta_lut; |
1703 | struct fixed31_32 delta_index; |
1704 | const struct fixed31_32 one = dc_fixpt_from_int(arg: 1); |
1705 | |
1706 | i = 0; |
1707 | /* fixed_pt library has problems handling too small values */ |
1708 | while (i != 32) { |
1709 | tf_pts->red[i] = dc_fixpt_zero; |
1710 | tf_pts->green[i] = dc_fixpt_zero; |
1711 | tf_pts->blue[i] = dc_fixpt_zero; |
1712 | ++i; |
1713 | } |
1714 | while (i <= hw_points_num + 1) { |
1715 | for (color = 0; color < 3; color++) { |
1716 | if (color == 0) |
1717 | tf_point = &tf_pts->red[i]; |
1718 | else if (color == 1) |
1719 | tf_point = &tf_pts->green[i]; |
1720 | else |
1721 | tf_point = &tf_pts->blue[i]; |
1722 | |
1723 | if (apply_degamma) { |
1724 | if (color == 0) |
1725 | hw_x = coordinates_x[i].regamma_y_red; |
1726 | else if (color == 1) |
1727 | hw_x = coordinates_x[i].regamma_y_green; |
1728 | else |
1729 | hw_x = coordinates_x[i].regamma_y_blue; |
1730 | } else |
1731 | hw_x = coordinates_x[i].x; |
1732 | |
1733 | if (dc_fixpt_le(arg1: one, arg2: hw_x)) |
1734 | hw_x = one; |
1735 | |
1736 | norm_x = dc_fixpt_mul(arg1: norm_factor, arg2: hw_x); |
1737 | index = dc_fixpt_floor(arg: norm_x); |
1738 | if (index < 0 || index > 255) |
1739 | continue; |
1740 | |
1741 | index_f = dc_fixpt_from_int(arg: index); |
1742 | index_next = (index == 255) ? index : index + 1; |
1743 | |
1744 | if (color == 0) { |
1745 | lut1 = rgb_user[index].r; |
1746 | lut2 = rgb_user[index_next].r; |
1747 | } else if (color == 1) { |
1748 | lut1 = rgb_user[index].g; |
1749 | lut2 = rgb_user[index_next].g; |
1750 | } else { |
1751 | lut1 = rgb_user[index].b; |
1752 | lut2 = rgb_user[index_next].b; |
1753 | } |
1754 | |
1755 | // we have everything now, so interpolate |
1756 | delta_lut = dc_fixpt_sub(arg1: lut2, arg2: lut1); |
1757 | delta_index = dc_fixpt_sub(arg1: norm_x, arg2: index_f); |
1758 | |
1759 | *tf_point = dc_fixpt_add(arg1: lut1, |
1760 | arg2: dc_fixpt_mul(arg1: delta_index, arg2: delta_lut)); |
1761 | } |
1762 | ++i; |
1763 | } |
1764 | } |
1765 | |
1766 | static void build_new_custom_resulted_curve( |
1767 | uint32_t hw_points_num, |
1768 | struct dc_transfer_func_distributed_points *tf_pts) |
1769 | { |
1770 | uint32_t i = 0; |
1771 | |
1772 | while (i != hw_points_num + 1) { |
1773 | tf_pts->red[i] = dc_fixpt_clamp( |
1774 | arg: tf_pts->red[i], min_value: dc_fixpt_zero, |
1775 | max_value: dc_fixpt_one); |
1776 | tf_pts->green[i] = dc_fixpt_clamp( |
1777 | arg: tf_pts->green[i], min_value: dc_fixpt_zero, |
1778 | max_value: dc_fixpt_one); |
1779 | tf_pts->blue[i] = dc_fixpt_clamp( |
1780 | arg: tf_pts->blue[i], min_value: dc_fixpt_zero, |
1781 | max_value: dc_fixpt_one); |
1782 | |
1783 | ++i; |
1784 | } |
1785 | } |
1786 | |
1787 | static void apply_degamma_for_user_regamma(struct pwl_float_data_ex *rgb_regamma, |
1788 | uint32_t hw_points_num, struct calculate_buffer *cal_buffer) |
1789 | { |
1790 | uint32_t i; |
1791 | |
1792 | struct gamma_coefficients coeff; |
1793 | struct pwl_float_data_ex *rgb = rgb_regamma; |
1794 | const struct hw_x_point *coord_x = coordinates_x; |
1795 | |
1796 | build_coefficients(coefficients: &coeff, type: TRANSFER_FUNCTION_SRGB); |
1797 | |
1798 | i = 0; |
1799 | while (i != hw_points_num + 1) { |
1800 | rgb->r = translate_from_linear_space_ex( |
1801 | arg: coord_x->x, coeff: &coeff, color_index: 0, cal_buffer); |
1802 | rgb->g = rgb->r; |
1803 | rgb->b = rgb->r; |
1804 | ++coord_x; |
1805 | ++rgb; |
1806 | ++i; |
1807 | } |
1808 | } |
1809 | |
1810 | static bool map_regamma_hw_to_x_user( |
1811 | const struct dc_gamma *ramp, |
1812 | struct pixel_gamma_point *coeff128, |
1813 | struct pwl_float_data *rgb_user, |
1814 | struct hw_x_point *coords_x, |
1815 | const struct gamma_pixel *axis_x, |
1816 | const struct pwl_float_data_ex *rgb_regamma, |
1817 | uint32_t hw_points_num, |
1818 | struct dc_transfer_func_distributed_points *tf_pts, |
1819 | bool map_user_ramp, |
1820 | bool do_clamping) |
1821 | { |
1822 | /* setup to spare calculated ideal regamma values */ |
1823 | |
1824 | int i = 0; |
1825 | struct hw_x_point *coords = coords_x; |
1826 | const struct pwl_float_data_ex *regamma = rgb_regamma; |
1827 | |
1828 | if (ramp && map_user_ramp) { |
1829 | copy_rgb_regamma_to_coordinates_x(coordinates_x: coords, |
1830 | hw_points_num, |
1831 | rgb_ex: rgb_regamma); |
1832 | |
1833 | calculate_interpolated_hardware_curve( |
1834 | ramp, coeff128, rgb_user, coordinates_x: coords, axis_x, |
1835 | number_of_points: hw_points_num, tf_pts); |
1836 | } else { |
1837 | /* just copy current rgb_regamma into tf_pts */ |
1838 | while (i <= hw_points_num) { |
1839 | tf_pts->red[i] = regamma->r; |
1840 | tf_pts->green[i] = regamma->g; |
1841 | tf_pts->blue[i] = regamma->b; |
1842 | |
1843 | ++regamma; |
1844 | ++i; |
1845 | } |
1846 | } |
1847 | |
1848 | if (do_clamping) { |
1849 | /* this should be named differently, all it does is clamp to 0-1 */ |
1850 | build_new_custom_resulted_curve(hw_points_num, tf_pts); |
1851 | } |
1852 | |
1853 | return true; |
1854 | } |
1855 | |
1856 | #define 3 |
1857 | |
1858 | bool calculate_user_regamma_coeff(struct dc_transfer_func *output_tf, |
1859 | const struct regamma_lut *regamma, |
1860 | struct calculate_buffer *cal_buffer, |
1861 | const struct dc_gamma *ramp) |
1862 | { |
1863 | struct gamma_coefficients coeff; |
1864 | const struct hw_x_point *coord_x = coordinates_x; |
1865 | uint32_t i = 0; |
1866 | |
1867 | do { |
1868 | coeff.a0[i] = dc_fixpt_from_fraction( |
1869 | numerator: regamma->coeff.A0[i], denominator: 10000000); |
1870 | coeff.a1[i] = dc_fixpt_from_fraction( |
1871 | numerator: regamma->coeff.A1[i], denominator: 1000); |
1872 | coeff.a2[i] = dc_fixpt_from_fraction( |
1873 | numerator: regamma->coeff.A2[i], denominator: 1000); |
1874 | coeff.a3[i] = dc_fixpt_from_fraction( |
1875 | numerator: regamma->coeff.A3[i], denominator: 1000); |
1876 | coeff.user_gamma[i] = dc_fixpt_from_fraction( |
1877 | numerator: regamma->coeff.gamma[i], denominator: 1000); |
1878 | |
1879 | ++i; |
1880 | } while (i != 3); |
1881 | |
1882 | i = 0; |
1883 | /* fixed_pt library has problems handling too small values */ |
1884 | while (i != 32) { |
1885 | output_tf->tf_pts.red[i] = dc_fixpt_zero; |
1886 | output_tf->tf_pts.green[i] = dc_fixpt_zero; |
1887 | output_tf->tf_pts.blue[i] = dc_fixpt_zero; |
1888 | ++coord_x; |
1889 | ++i; |
1890 | } |
1891 | while (i != MAX_HW_POINTS + 1) { |
1892 | output_tf->tf_pts.red[i] = translate_from_linear_space_ex( |
1893 | arg: coord_x->x, coeff: &coeff, color_index: 0, cal_buffer); |
1894 | output_tf->tf_pts.green[i] = translate_from_linear_space_ex( |
1895 | arg: coord_x->x, coeff: &coeff, color_index: 1, cal_buffer); |
1896 | output_tf->tf_pts.blue[i] = translate_from_linear_space_ex( |
1897 | arg: coord_x->x, coeff: &coeff, color_index: 2, cal_buffer); |
1898 | ++coord_x; |
1899 | ++i; |
1900 | } |
1901 | |
1902 | if (ramp && ramp->type == GAMMA_CS_TFM_1D) |
1903 | apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts: &output_tf->tf_pts); |
1904 | |
1905 | // this function just clamps output to 0-1 |
1906 | build_new_custom_resulted_curve(MAX_HW_POINTS, tf_pts: &output_tf->tf_pts); |
1907 | output_tf->type = TF_TYPE_DISTRIBUTED_POINTS; |
1908 | |
1909 | return true; |
1910 | } |
1911 | |
1912 | bool calculate_user_regamma_ramp(struct dc_transfer_func *output_tf, |
1913 | const struct regamma_lut *regamma, |
1914 | struct calculate_buffer *cal_buffer, |
1915 | const struct dc_gamma *ramp) |
1916 | { |
1917 | struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts; |
1918 | struct dividers dividers; |
1919 | |
1920 | struct pwl_float_data *rgb_user = NULL; |
1921 | struct pwl_float_data_ex *rgb_regamma = NULL; |
1922 | bool ret = false; |
1923 | |
1924 | if (regamma == NULL) |
1925 | return false; |
1926 | |
1927 | output_tf->type = TF_TYPE_DISTRIBUTED_POINTS; |
1928 | |
1929 | rgb_user = kcalloc(n: GAMMA_RGB_256_ENTRIES + _EXTRA_POINTS, |
1930 | size: sizeof(*rgb_user), |
1931 | GFP_KERNEL); |
1932 | if (!rgb_user) |
1933 | goto rgb_user_alloc_fail; |
1934 | |
1935 | rgb_regamma = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS, |
1936 | size: sizeof(*rgb_regamma), |
1937 | GFP_KERNEL); |
1938 | if (!rgb_regamma) |
1939 | goto rgb_regamma_alloc_fail; |
1940 | |
1941 | dividers.divider1 = dc_fixpt_from_fraction(numerator: 3, denominator: 2); |
1942 | dividers.divider2 = dc_fixpt_from_int(arg: 2); |
1943 | dividers.divider3 = dc_fixpt_from_fraction(numerator: 5, denominator: 2); |
1944 | |
1945 | scale_user_regamma_ramp(pwl_rgb: rgb_user, ramp: ®amma->ramp, dividers); |
1946 | |
1947 | if (regamma->flags.bits.applyDegamma == 1) { |
1948 | apply_degamma_for_user_regamma(rgb_regamma, MAX_HW_POINTS, cal_buffer); |
1949 | copy_rgb_regamma_to_coordinates_x(coordinates_x, |
1950 | MAX_HW_POINTS, rgb_ex: rgb_regamma); |
1951 | } |
1952 | |
1953 | interpolate_user_regamma(MAX_HW_POINTS, rgb_user, |
1954 | apply_degamma: regamma->flags.bits.applyDegamma, tf_pts); |
1955 | |
1956 | // no custom HDR curves! |
1957 | tf_pts->end_exponent = 0; |
1958 | tf_pts->x_point_at_y1_red = 1; |
1959 | tf_pts->x_point_at_y1_green = 1; |
1960 | tf_pts->x_point_at_y1_blue = 1; |
1961 | |
1962 | if (ramp && ramp->type == GAMMA_CS_TFM_1D) |
1963 | apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts: &output_tf->tf_pts); |
1964 | |
1965 | // this function just clamps output to 0-1 |
1966 | build_new_custom_resulted_curve(MAX_HW_POINTS, tf_pts); |
1967 | |
1968 | ret = true; |
1969 | |
1970 | kfree(objp: rgb_regamma); |
1971 | rgb_regamma_alloc_fail: |
1972 | kfree(objp: rgb_user); |
1973 | rgb_user_alloc_fail: |
1974 | return ret; |
1975 | } |
1976 | |
1977 | bool mod_color_calculate_degamma_params(struct dc_color_caps *dc_caps, |
1978 | struct dc_transfer_func *input_tf, |
1979 | const struct dc_gamma *ramp, bool map_user_ramp) |
1980 | { |
1981 | struct dc_transfer_func_distributed_points *tf_pts = &input_tf->tf_pts; |
1982 | struct dividers dividers; |
1983 | struct pwl_float_data *rgb_user = NULL; |
1984 | struct pwl_float_data_ex *curve = NULL; |
1985 | struct gamma_pixel *axis_x = NULL; |
1986 | struct pixel_gamma_point *coeff = NULL; |
1987 | enum dc_transfer_func_predefined tf; |
1988 | uint32_t i; |
1989 | bool ret = false; |
1990 | |
1991 | if (input_tf->type == TF_TYPE_BYPASS) |
1992 | return false; |
1993 | |
1994 | /* we can use hardcoded curve for plain SRGB TF |
1995 | * If linear, it's bypass if no user ramp |
1996 | */ |
1997 | if (input_tf->type == TF_TYPE_PREDEFINED) { |
1998 | if ((input_tf->tf == TRANSFER_FUNCTION_SRGB || |
1999 | input_tf->tf == TRANSFER_FUNCTION_LINEAR) && |
2000 | !map_user_ramp) |
2001 | return true; |
2002 | |
2003 | if (dc_caps != NULL && |
2004 | dc_caps->dpp.dcn_arch == 1) { |
2005 | |
2006 | if (input_tf->tf == TRANSFER_FUNCTION_PQ && |
2007 | dc_caps->dpp.dgam_rom_caps.pq == 1) |
2008 | return true; |
2009 | |
2010 | if (input_tf->tf == TRANSFER_FUNCTION_GAMMA22 && |
2011 | dc_caps->dpp.dgam_rom_caps.gamma2_2 == 1) |
2012 | return true; |
2013 | |
2014 | // HLG OOTF not accounted for |
2015 | if (input_tf->tf == TRANSFER_FUNCTION_HLG && |
2016 | dc_caps->dpp.dgam_rom_caps.hlg == 1) |
2017 | return true; |
2018 | } |
2019 | } |
2020 | |
2021 | input_tf->type = TF_TYPE_DISTRIBUTED_POINTS; |
2022 | |
2023 | if (map_user_ramp && ramp && ramp->type == GAMMA_RGB_256) { |
2024 | rgb_user = kvcalloc(n: ramp->num_entries + _EXTRA_POINTS, |
2025 | size: sizeof(*rgb_user), |
2026 | GFP_KERNEL); |
2027 | if (!rgb_user) |
2028 | goto rgb_user_alloc_fail; |
2029 | |
2030 | axis_x = kvcalloc(n: ramp->num_entries + _EXTRA_POINTS, size: sizeof(*axis_x), |
2031 | GFP_KERNEL); |
2032 | if (!axis_x) |
2033 | goto axis_x_alloc_fail; |
2034 | |
2035 | dividers.divider1 = dc_fixpt_from_fraction(numerator: 3, denominator: 2); |
2036 | dividers.divider2 = dc_fixpt_from_int(arg: 2); |
2037 | dividers.divider3 = dc_fixpt_from_fraction(numerator: 5, denominator: 2); |
2038 | |
2039 | build_evenly_distributed_points( |
2040 | points: axis_x, |
2041 | numberof_points: ramp->num_entries, |
2042 | dividers); |
2043 | |
2044 | scale_gamma(pwl_rgb: rgb_user, ramp, dividers); |
2045 | } |
2046 | |
2047 | curve = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, size: sizeof(*curve), |
2048 | GFP_KERNEL); |
2049 | if (!curve) |
2050 | goto curve_alloc_fail; |
2051 | |
2052 | coeff = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, size: sizeof(*coeff), |
2053 | GFP_KERNEL); |
2054 | if (!coeff) |
2055 | goto coeff_alloc_fail; |
2056 | |
2057 | tf = input_tf->tf; |
2058 | |
2059 | if (tf == TRANSFER_FUNCTION_PQ) |
2060 | build_de_pq(de_pq: curve, |
2061 | MAX_HW_POINTS, |
2062 | coordinate_x: coordinates_x); |
2063 | else if (tf == TRANSFER_FUNCTION_SRGB || |
2064 | tf == TRANSFER_FUNCTION_BT709 || |
2065 | tf == TRANSFER_FUNCTION_GAMMA22 || |
2066 | tf == TRANSFER_FUNCTION_GAMMA24 || |
2067 | tf == TRANSFER_FUNCTION_GAMMA26) |
2068 | build_degamma(curve, |
2069 | MAX_HW_POINTS, |
2070 | coordinate_x: coordinates_x, |
2071 | type: tf); |
2072 | else if (tf == TRANSFER_FUNCTION_HLG) |
2073 | build_hlg_degamma(degamma: curve, |
2074 | MAX_HW_POINTS, |
2075 | coordinate_x: coordinates_x, |
2076 | sdr_white_level: 80, max_luminance_nits: 1000); |
2077 | else if (tf == TRANSFER_FUNCTION_LINEAR) { |
2078 | // just copy coordinates_x into curve |
2079 | i = 0; |
2080 | while (i != MAX_HW_POINTS + 1) { |
2081 | curve[i].r = coordinates_x[i].x; |
2082 | curve[i].g = curve[i].r; |
2083 | curve[i].b = curve[i].r; |
2084 | i++; |
2085 | } |
2086 | } else |
2087 | goto invalid_tf_fail; |
2088 | |
2089 | tf_pts->end_exponent = 0; |
2090 | tf_pts->x_point_at_y1_red = 1; |
2091 | tf_pts->x_point_at_y1_green = 1; |
2092 | tf_pts->x_point_at_y1_blue = 1; |
2093 | |
2094 | if (input_tf->tf == TRANSFER_FUNCTION_PQ) { |
2095 | /* just copy current rgb_regamma into tf_pts */ |
2096 | struct pwl_float_data_ex *curvePt = curve; |
2097 | int i = 0; |
2098 | |
2099 | while (i <= MAX_HW_POINTS) { |
2100 | tf_pts->red[i] = curvePt->r; |
2101 | tf_pts->green[i] = curvePt->g; |
2102 | tf_pts->blue[i] = curvePt->b; |
2103 | ++curvePt; |
2104 | ++i; |
2105 | } |
2106 | } else { |
2107 | // clamps to 0-1 |
2108 | map_regamma_hw_to_x_user(ramp, coeff128: coeff, rgb_user, |
2109 | coords_x: coordinates_x, axis_x, rgb_regamma: curve, |
2110 | MAX_HW_POINTS, tf_pts, |
2111 | map_user_ramp: map_user_ramp && ramp && ramp->type == GAMMA_RGB_256, |
2112 | do_clamping: true); |
2113 | } |
2114 | |
2115 | |
2116 | |
2117 | if (ramp && ramp->type == GAMMA_CUSTOM) |
2118 | apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts); |
2119 | |
2120 | ret = true; |
2121 | |
2122 | invalid_tf_fail: |
2123 | kvfree(addr: coeff); |
2124 | coeff_alloc_fail: |
2125 | kvfree(addr: curve); |
2126 | curve_alloc_fail: |
2127 | kvfree(addr: axis_x); |
2128 | axis_x_alloc_fail: |
2129 | kvfree(addr: rgb_user); |
2130 | rgb_user_alloc_fail: |
2131 | |
2132 | return ret; |
2133 | } |
2134 | |
2135 | static bool calculate_curve(enum dc_transfer_func_predefined trans, |
2136 | struct dc_transfer_func_distributed_points *points, |
2137 | struct pwl_float_data_ex *rgb_regamma, |
2138 | const struct hdr_tm_params *fs_params, |
2139 | uint32_t sdr_ref_white_level, |
2140 | struct calculate_buffer *cal_buffer) |
2141 | { |
2142 | uint32_t i; |
2143 | bool ret = false; |
2144 | |
2145 | if (trans == TRANSFER_FUNCTION_UNITY || |
2146 | trans == TRANSFER_FUNCTION_LINEAR) { |
2147 | points->end_exponent = 0; |
2148 | points->x_point_at_y1_red = 1; |
2149 | points->x_point_at_y1_green = 1; |
2150 | points->x_point_at_y1_blue = 1; |
2151 | |
2152 | for (i = 0; i <= MAX_HW_POINTS ; i++) { |
2153 | rgb_regamma[i].r = coordinates_x[i].x; |
2154 | rgb_regamma[i].g = coordinates_x[i].x; |
2155 | rgb_regamma[i].b = coordinates_x[i].x; |
2156 | } |
2157 | |
2158 | ret = true; |
2159 | } else if (trans == TRANSFER_FUNCTION_PQ) { |
2160 | points->end_exponent = 7; |
2161 | points->x_point_at_y1_red = 125; |
2162 | points->x_point_at_y1_green = 125; |
2163 | points->x_point_at_y1_blue = 125; |
2164 | |
2165 | build_pq(rgb_regamma, |
2166 | MAX_HW_POINTS, |
2167 | coordinate_x: coordinates_x, |
2168 | sdr_white_level: sdr_ref_white_level); |
2169 | |
2170 | ret = true; |
2171 | } else if (trans == TRANSFER_FUNCTION_GAMMA22 && |
2172 | fs_params != NULL && fs_params->skip_tm == 0) { |
2173 | build_freesync_hdr(rgb_regamma, |
2174 | MAX_HW_POINTS, |
2175 | coordinate_x: coordinates_x, |
2176 | fs_params, |
2177 | cal_buffer); |
2178 | |
2179 | ret = true; |
2180 | } else if (trans == TRANSFER_FUNCTION_HLG) { |
2181 | points->end_exponent = 4; |
2182 | points->x_point_at_y1_red = 12; |
2183 | points->x_point_at_y1_green = 12; |
2184 | points->x_point_at_y1_blue = 12; |
2185 | |
2186 | build_hlg_regamma(regamma: rgb_regamma, |
2187 | MAX_HW_POINTS, |
2188 | coordinate_x: coordinates_x, |
2189 | sdr_white_level: 80, max_luminance_nits: 1000); |
2190 | |
2191 | ret = true; |
2192 | } else { |
2193 | // trans == TRANSFER_FUNCTION_SRGB |
2194 | // trans == TRANSFER_FUNCTION_BT709 |
2195 | // trans == TRANSFER_FUNCTION_GAMMA22 |
2196 | // trans == TRANSFER_FUNCTION_GAMMA24 |
2197 | // trans == TRANSFER_FUNCTION_GAMMA26 |
2198 | points->end_exponent = 0; |
2199 | points->x_point_at_y1_red = 1; |
2200 | points->x_point_at_y1_green = 1; |
2201 | points->x_point_at_y1_blue = 1; |
2202 | |
2203 | build_regamma(rgb_regamma, |
2204 | MAX_HW_POINTS, |
2205 | coordinate_x: coordinates_x, |
2206 | type: trans, |
2207 | cal_buffer); |
2208 | |
2209 | ret = true; |
2210 | } |
2211 | |
2212 | return ret; |
2213 | } |
2214 | |
2215 | bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf, |
2216 | const struct dc_gamma *ramp, |
2217 | bool map_user_ramp, |
2218 | bool can_rom_be_used, |
2219 | const struct hdr_tm_params *fs_params, |
2220 | struct calculate_buffer *cal_buffer) |
2221 | { |
2222 | struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts; |
2223 | struct dividers dividers; |
2224 | |
2225 | struct pwl_float_data *rgb_user = NULL; |
2226 | struct pwl_float_data_ex *rgb_regamma = NULL; |
2227 | struct gamma_pixel *axis_x = NULL; |
2228 | struct pixel_gamma_point *coeff = NULL; |
2229 | enum dc_transfer_func_predefined tf; |
2230 | bool do_clamping = true; |
2231 | bool ret = false; |
2232 | |
2233 | if (output_tf->type == TF_TYPE_BYPASS) |
2234 | return false; |
2235 | |
2236 | /* we can use hardcoded curve for plain SRGB TF */ |
2237 | if (output_tf->type == TF_TYPE_PREDEFINED && can_rom_be_used == true && |
2238 | output_tf->tf == TRANSFER_FUNCTION_SRGB) { |
2239 | if (ramp == NULL) |
2240 | return true; |
2241 | if ((ramp->is_identity && ramp->type != GAMMA_CS_TFM_1D) || |
2242 | (!map_user_ramp && ramp->type == GAMMA_RGB_256)) |
2243 | return true; |
2244 | } |
2245 | |
2246 | output_tf->type = TF_TYPE_DISTRIBUTED_POINTS; |
2247 | |
2248 | if (ramp && ramp->type != GAMMA_CS_TFM_1D && |
2249 | (map_user_ramp || ramp->type != GAMMA_RGB_256)) { |
2250 | rgb_user = kvcalloc(n: ramp->num_entries + _EXTRA_POINTS, |
2251 | size: sizeof(*rgb_user), |
2252 | GFP_KERNEL); |
2253 | if (!rgb_user) |
2254 | goto rgb_user_alloc_fail; |
2255 | |
2256 | axis_x = kvcalloc(n: ramp->num_entries + 3, size: sizeof(*axis_x), |
2257 | GFP_KERNEL); |
2258 | if (!axis_x) |
2259 | goto axis_x_alloc_fail; |
2260 | |
2261 | dividers.divider1 = dc_fixpt_from_fraction(numerator: 3, denominator: 2); |
2262 | dividers.divider2 = dc_fixpt_from_int(arg: 2); |
2263 | dividers.divider3 = dc_fixpt_from_fraction(numerator: 5, denominator: 2); |
2264 | |
2265 | build_evenly_distributed_points( |
2266 | points: axis_x, |
2267 | numberof_points: ramp->num_entries, |
2268 | dividers); |
2269 | |
2270 | if (ramp->type == GAMMA_RGB_256 && map_user_ramp) |
2271 | scale_gamma(pwl_rgb: rgb_user, ramp, dividers); |
2272 | else if (ramp->type == GAMMA_RGB_FLOAT_1024) |
2273 | scale_gamma_dx(pwl_rgb: rgb_user, ramp, dividers); |
2274 | } |
2275 | |
2276 | rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, |
2277 | size: sizeof(*rgb_regamma), |
2278 | GFP_KERNEL); |
2279 | if (!rgb_regamma) |
2280 | goto rgb_regamma_alloc_fail; |
2281 | |
2282 | coeff = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, size: sizeof(*coeff), |
2283 | GFP_KERNEL); |
2284 | if (!coeff) |
2285 | goto coeff_alloc_fail; |
2286 | |
2287 | tf = output_tf->tf; |
2288 | |
2289 | ret = calculate_curve(trans: tf, |
2290 | points: tf_pts, |
2291 | rgb_regamma, |
2292 | fs_params, |
2293 | sdr_ref_white_level: output_tf->sdr_ref_white_level, |
2294 | cal_buffer); |
2295 | |
2296 | if (ret) { |
2297 | do_clamping = !(output_tf->tf == TRANSFER_FUNCTION_PQ) && |
2298 | !(output_tf->tf == TRANSFER_FUNCTION_GAMMA22 && |
2299 | fs_params != NULL && fs_params->skip_tm == 0); |
2300 | |
2301 | map_regamma_hw_to_x_user(ramp, coeff128: coeff, rgb_user, |
2302 | coords_x: coordinates_x, axis_x, rgb_regamma, |
2303 | MAX_HW_POINTS, tf_pts, |
2304 | map_user_ramp: (map_user_ramp || (ramp && ramp->type != GAMMA_RGB_256)) && |
2305 | (ramp && ramp->type != GAMMA_CS_TFM_1D), |
2306 | do_clamping); |
2307 | |
2308 | if (ramp && ramp->type == GAMMA_CS_TFM_1D) |
2309 | apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts); |
2310 | } |
2311 | |
2312 | kvfree(addr: coeff); |
2313 | coeff_alloc_fail: |
2314 | kvfree(addr: rgb_regamma); |
2315 | rgb_regamma_alloc_fail: |
2316 | kvfree(addr: axis_x); |
2317 | axis_x_alloc_fail: |
2318 | kvfree(addr: rgb_user); |
2319 | rgb_user_alloc_fail: |
2320 | return ret; |
2321 | } |
2322 | |