1 | /* |
2 | * Copyright 2018 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 | #include "amdgpu.h" |
26 | #include "amdgpu_mode.h" |
27 | #include "amdgpu_dm.h" |
28 | #include "dc.h" |
29 | #include "modules/color/color_gamma.h" |
30 | #include "basics/conversion.h" |
31 | |
32 | /** |
33 | * DOC: overview |
34 | * |
35 | * The DC interface to HW gives us the following color management blocks |
36 | * per pipe (surface): |
37 | * |
38 | * - Input gamma LUT (de-normalized) |
39 | * - Input CSC (normalized) |
40 | * - Surface degamma LUT (normalized) |
41 | * - Surface CSC (normalized) |
42 | * - Surface regamma LUT (normalized) |
43 | * - Output CSC (normalized) |
44 | * |
45 | * But these aren't a direct mapping to DRM color properties. The current DRM |
46 | * interface exposes CRTC degamma, CRTC CTM and CRTC regamma while our hardware |
47 | * is essentially giving: |
48 | * |
49 | * Plane CTM -> Plane degamma -> Plane CTM -> Plane regamma -> Plane CTM |
50 | * |
51 | * The input gamma LUT block isn't really applicable here since it operates |
52 | * on the actual input data itself rather than the HW fp representation. The |
53 | * input and output CSC blocks are technically available to use as part of |
54 | * the DC interface but are typically used internally by DC for conversions |
55 | * between color spaces. These could be blended together with user |
56 | * adjustments in the future but for now these should remain untouched. |
57 | * |
58 | * The pipe blending also happens after these blocks so we don't actually |
59 | * support any CRTC props with correct blending with multiple planes - but we |
60 | * can still support CRTC color management properties in DM in most single |
61 | * plane cases correctly with clever management of the DC interface in DM. |
62 | * |
63 | * As per DRM documentation, blocks should be in hardware bypass when their |
64 | * respective property is set to NULL. A linear DGM/RGM LUT should also |
65 | * considered as putting the respective block into bypass mode. |
66 | * |
67 | * This means that the following |
68 | * configuration is assumed to be the default: |
69 | * |
70 | * Plane DGM Bypass -> Plane CTM Bypass -> Plane RGM Bypass -> ... |
71 | * CRTC DGM Bypass -> CRTC CTM Bypass -> CRTC RGM Bypass |
72 | */ |
73 | |
74 | #define MAX_DRM_LUT_VALUE 0xFFFF |
75 | |
76 | /** |
77 | * amdgpu_dm_init_color_mod - Initialize the color module. |
78 | * |
79 | * We're not using the full color module, only certain components. |
80 | * Only call setup functions for components that we need. |
81 | */ |
82 | void amdgpu_dm_init_color_mod(void) |
83 | { |
84 | setup_x_points_distribution(); |
85 | } |
86 | |
87 | /** |
88 | * __extract_blob_lut - Extracts the DRM lut and lut size from a blob. |
89 | * @blob: DRM color mgmt property blob |
90 | * @size: lut size |
91 | * |
92 | * Returns: |
93 | * DRM LUT or NULL |
94 | */ |
95 | static const struct drm_color_lut * |
96 | (const struct drm_property_blob *blob, uint32_t *size) |
97 | { |
98 | *size = blob ? drm_color_lut_size(blob) : 0; |
99 | return blob ? (struct drm_color_lut *)blob->data : NULL; |
100 | } |
101 | |
102 | /** |
103 | * __is_lut_linear - check if the given lut is a linear mapping of values |
104 | * @lut: given lut to check values |
105 | * @size: lut size |
106 | * |
107 | * It is considered linear if the lut represents: |
108 | * f(a) = (0xFF00/MAX_COLOR_LUT_ENTRIES-1)a; for integer a in [0, |
109 | * MAX_COLOR_LUT_ENTRIES) |
110 | * |
111 | * Returns: |
112 | * True if the given lut is a linear mapping of values, i.e. it acts like a |
113 | * bypass LUT. Otherwise, false. |
114 | */ |
115 | static bool __is_lut_linear(const struct drm_color_lut *lut, uint32_t size) |
116 | { |
117 | int i; |
118 | uint32_t expected; |
119 | int delta; |
120 | |
121 | for (i = 0; i < size; i++) { |
122 | /* All color values should equal */ |
123 | if ((lut[i].red != lut[i].green) || (lut[i].green != lut[i].blue)) |
124 | return false; |
125 | |
126 | expected = i * MAX_DRM_LUT_VALUE / (size-1); |
127 | |
128 | /* Allow a +/-1 error. */ |
129 | delta = lut[i].red - expected; |
130 | if (delta < -1 || 1 < delta) |
131 | return false; |
132 | } |
133 | return true; |
134 | } |
135 | |
136 | /** |
137 | * __drm_lut_to_dc_gamma - convert the drm_color_lut to dc_gamma. |
138 | * @lut: DRM lookup table for color conversion |
139 | * @gamma: DC gamma to set entries |
140 | * @is_legacy: legacy or atomic gamma |
141 | * |
142 | * The conversion depends on the size of the lut - whether or not it's legacy. |
143 | */ |
144 | static void __drm_lut_to_dc_gamma(const struct drm_color_lut *lut, |
145 | struct dc_gamma *gamma, bool is_legacy) |
146 | { |
147 | uint32_t r, g, b; |
148 | int i; |
149 | |
150 | if (is_legacy) { |
151 | for (i = 0; i < MAX_COLOR_LEGACY_LUT_ENTRIES; i++) { |
152 | r = drm_color_lut_extract(user_input: lut[i].red, bit_precision: 16); |
153 | g = drm_color_lut_extract(user_input: lut[i].green, bit_precision: 16); |
154 | b = drm_color_lut_extract(user_input: lut[i].blue, bit_precision: 16); |
155 | |
156 | gamma->entries.red[i] = dc_fixpt_from_int(arg: r); |
157 | gamma->entries.green[i] = dc_fixpt_from_int(arg: g); |
158 | gamma->entries.blue[i] = dc_fixpt_from_int(arg: b); |
159 | } |
160 | return; |
161 | } |
162 | |
163 | /* else */ |
164 | for (i = 0; i < MAX_COLOR_LUT_ENTRIES; i++) { |
165 | r = drm_color_lut_extract(user_input: lut[i].red, bit_precision: 16); |
166 | g = drm_color_lut_extract(user_input: lut[i].green, bit_precision: 16); |
167 | b = drm_color_lut_extract(user_input: lut[i].blue, bit_precision: 16); |
168 | |
169 | gamma->entries.red[i] = dc_fixpt_from_fraction(numerator: r, MAX_DRM_LUT_VALUE); |
170 | gamma->entries.green[i] = dc_fixpt_from_fraction(numerator: g, MAX_DRM_LUT_VALUE); |
171 | gamma->entries.blue[i] = dc_fixpt_from_fraction(numerator: b, MAX_DRM_LUT_VALUE); |
172 | } |
173 | } |
174 | |
175 | /** |
176 | * __drm_ctm_to_dc_matrix - converts a DRM CTM to a DC CSC float matrix |
177 | * @ctm: DRM color transformation matrix |
178 | * @matrix: DC CSC float matrix |
179 | * |
180 | * The matrix needs to be a 3x4 (12 entry) matrix. |
181 | */ |
182 | static void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm, |
183 | struct fixed31_32 *matrix) |
184 | { |
185 | int64_t val; |
186 | int i; |
187 | |
188 | /* |
189 | * DRM gives a 3x3 matrix, but DC wants 3x4. Assuming we're operating |
190 | * with homogeneous coordinates, augment the matrix with 0's. |
191 | * |
192 | * The format provided is S31.32, using signed-magnitude representation. |
193 | * Our fixed31_32 is also S31.32, but is using 2's complement. We have |
194 | * to convert from signed-magnitude to 2's complement. |
195 | */ |
196 | for (i = 0; i < 12; i++) { |
197 | /* Skip 4th element */ |
198 | if (i % 4 == 3) { |
199 | matrix[i] = dc_fixpt_zero; |
200 | continue; |
201 | } |
202 | |
203 | /* gamut_remap_matrix[i] = ctm[i - floor(i/4)] */ |
204 | val = ctm->matrix[i - (i / 4)]; |
205 | /* If negative, convert to 2's complement. */ |
206 | if (val & (1ULL << 63)) |
207 | val = -(val & ~(1ULL << 63)); |
208 | |
209 | matrix[i].value = val; |
210 | } |
211 | } |
212 | |
213 | /** |
214 | * __set_legacy_tf - Calculates the legacy transfer function |
215 | * @func: transfer function |
216 | * @lut: lookup table that defines the color space |
217 | * @lut_size: size of respective lut |
218 | * @has_rom: if ROM can be used for hardcoded curve |
219 | * |
220 | * Only for sRGB input space |
221 | * |
222 | * Returns: |
223 | * 0 in case of success, -ENOMEM if fails |
224 | */ |
225 | static int __set_legacy_tf(struct dc_transfer_func *func, |
226 | const struct drm_color_lut *lut, uint32_t lut_size, |
227 | bool has_rom) |
228 | { |
229 | struct dc_gamma *gamma = NULL; |
230 | struct calculate_buffer cal_buffer = {0}; |
231 | bool res; |
232 | |
233 | ASSERT(lut && lut_size == MAX_COLOR_LEGACY_LUT_ENTRIES); |
234 | |
235 | cal_buffer.buffer_index = -1; |
236 | |
237 | gamma = dc_create_gamma(); |
238 | if (!gamma) |
239 | return -ENOMEM; |
240 | |
241 | gamma->type = GAMMA_RGB_256; |
242 | gamma->num_entries = lut_size; |
243 | __drm_lut_to_dc_gamma(lut, gamma, is_legacy: true); |
244 | |
245 | res = mod_color_calculate_regamma_params(output_tf: func, ramp: gamma, mapUserRamp: true, canRomBeUsed: has_rom, |
246 | NULL, cal_buffer: &cal_buffer); |
247 | |
248 | dc_gamma_release(dc_gamma: &gamma); |
249 | |
250 | return res ? 0 : -ENOMEM; |
251 | } |
252 | |
253 | /** |
254 | * __set_output_tf - calculates the output transfer function based on expected input space. |
255 | * @func: transfer function |
256 | * @lut: lookup table that defines the color space |
257 | * @lut_size: size of respective lut |
258 | * @has_rom: if ROM can be used for hardcoded curve |
259 | * |
260 | * Returns: |
261 | * 0 in case of success. -ENOMEM if fails. |
262 | */ |
263 | static int __set_output_tf(struct dc_transfer_func *func, |
264 | const struct drm_color_lut *lut, uint32_t lut_size, |
265 | bool has_rom) |
266 | { |
267 | struct dc_gamma *gamma = NULL; |
268 | struct calculate_buffer cal_buffer = {0}; |
269 | bool res; |
270 | |
271 | ASSERT(lut && lut_size == MAX_COLOR_LUT_ENTRIES); |
272 | |
273 | cal_buffer.buffer_index = -1; |
274 | |
275 | gamma = dc_create_gamma(); |
276 | if (!gamma) |
277 | return -ENOMEM; |
278 | |
279 | gamma->num_entries = lut_size; |
280 | __drm_lut_to_dc_gamma(lut, gamma, is_legacy: false); |
281 | |
282 | if (func->tf == TRANSFER_FUNCTION_LINEAR) { |
283 | /* |
284 | * Color module doesn't like calculating regamma params |
285 | * on top of a linear input. But degamma params can be used |
286 | * instead to simulate this. |
287 | */ |
288 | gamma->type = GAMMA_CUSTOM; |
289 | res = mod_color_calculate_degamma_params(NULL, output_tf: func, |
290 | ramp: gamma, mapUserRamp: true); |
291 | } else { |
292 | /* |
293 | * Assume sRGB. The actual mapping will depend on whether the |
294 | * input was legacy or not. |
295 | */ |
296 | gamma->type = GAMMA_CS_TFM_1D; |
297 | res = mod_color_calculate_regamma_params(output_tf: func, ramp: gamma, mapUserRamp: false, |
298 | canRomBeUsed: has_rom, NULL, cal_buffer: &cal_buffer); |
299 | } |
300 | |
301 | dc_gamma_release(dc_gamma: &gamma); |
302 | |
303 | return res ? 0 : -ENOMEM; |
304 | } |
305 | |
306 | /** |
307 | * __set_input_tf - calculates the input transfer function based on expected |
308 | * input space. |
309 | * @func: transfer function |
310 | * @lut: lookup table that defines the color space |
311 | * @lut_size: size of respective lut. |
312 | * |
313 | * Returns: |
314 | * 0 in case of success. -ENOMEM if fails. |
315 | */ |
316 | static int __set_input_tf(struct dc_transfer_func *func, |
317 | const struct drm_color_lut *lut, uint32_t lut_size) |
318 | { |
319 | struct dc_gamma *gamma = NULL; |
320 | bool res; |
321 | |
322 | gamma = dc_create_gamma(); |
323 | if (!gamma) |
324 | return -ENOMEM; |
325 | |
326 | gamma->type = GAMMA_CUSTOM; |
327 | gamma->num_entries = lut_size; |
328 | |
329 | __drm_lut_to_dc_gamma(lut, gamma, is_legacy: false); |
330 | |
331 | res = mod_color_calculate_degamma_params(NULL, output_tf: func, ramp: gamma, mapUserRamp: true); |
332 | dc_gamma_release(dc_gamma: &gamma); |
333 | |
334 | return res ? 0 : -ENOMEM; |
335 | } |
336 | |
337 | /** |
338 | * amdgpu_dm_verify_lut_sizes - verifies if DRM luts match the hw supported sizes |
339 | * @crtc_state: the DRM CRTC state |
340 | * |
341 | * Verifies that the Degamma and Gamma LUTs attached to the &crtc_state |
342 | * are of the expected size. |
343 | * |
344 | * Returns: |
345 | * 0 on success. -EINVAL if any lut sizes are invalid. |
346 | */ |
347 | int amdgpu_dm_verify_lut_sizes(const struct drm_crtc_state *crtc_state) |
348 | { |
349 | const struct drm_color_lut *lut = NULL; |
350 | uint32_t size = 0; |
351 | |
352 | lut = __extract_blob_lut(blob: crtc_state->degamma_lut, size: &size); |
353 | if (lut && size != MAX_COLOR_LUT_ENTRIES) { |
354 | DRM_DEBUG_DRIVER( |
355 | "Invalid Degamma LUT size. Should be %u but got %u.\n" , |
356 | MAX_COLOR_LUT_ENTRIES, size); |
357 | return -EINVAL; |
358 | } |
359 | |
360 | lut = __extract_blob_lut(blob: crtc_state->gamma_lut, size: &size); |
361 | if (lut && size != MAX_COLOR_LUT_ENTRIES && |
362 | size != MAX_COLOR_LEGACY_LUT_ENTRIES) { |
363 | DRM_DEBUG_DRIVER( |
364 | "Invalid Gamma LUT size. Should be %u (or %u for legacy) but got %u.\n" , |
365 | MAX_COLOR_LUT_ENTRIES, MAX_COLOR_LEGACY_LUT_ENTRIES, |
366 | size); |
367 | return -EINVAL; |
368 | } |
369 | |
370 | return 0; |
371 | } |
372 | |
373 | /** |
374 | * amdgpu_dm_update_crtc_color_mgmt: Maps DRM color management to DC stream. |
375 | * @crtc: amdgpu_dm crtc state |
376 | * |
377 | * With no plane level color management properties we're free to use any |
378 | * of the HW blocks as long as the CRTC CTM always comes before the |
379 | * CRTC RGM and after the CRTC DGM. |
380 | * |
381 | * - The CRTC RGM block will be placed in the RGM LUT block if it is non-linear. |
382 | * - The CRTC DGM block will be placed in the DGM LUT block if it is non-linear. |
383 | * - The CRTC CTM will be placed in the gamut remap block if it is non-linear. |
384 | * |
385 | * The RGM block is typically more fully featured and accurate across |
386 | * all ASICs - DCE can't support a custom non-linear CRTC DGM. |
387 | * |
388 | * For supporting both plane level color management and CRTC level color |
389 | * management at once we have to either restrict the usage of CRTC properties |
390 | * or blend adjustments together. |
391 | * |
392 | * Returns: |
393 | * 0 on success. Error code if setup fails. |
394 | */ |
395 | int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc) |
396 | { |
397 | struct dc_stream_state *stream = crtc->stream; |
398 | struct amdgpu_device *adev = drm_to_adev(ddev: crtc->base.state->dev); |
399 | bool has_rom = adev->asic_type <= CHIP_RAVEN; |
400 | struct drm_color_ctm *ctm = NULL; |
401 | const struct drm_color_lut *degamma_lut, *regamma_lut; |
402 | uint32_t degamma_size, regamma_size; |
403 | bool has_regamma, has_degamma; |
404 | bool is_legacy; |
405 | int r; |
406 | |
407 | r = amdgpu_dm_verify_lut_sizes(crtc_state: &crtc->base); |
408 | if (r) |
409 | return r; |
410 | |
411 | degamma_lut = __extract_blob_lut(blob: crtc->base.degamma_lut, size: °amma_size); |
412 | regamma_lut = __extract_blob_lut(blob: crtc->base.gamma_lut, size: ®amma_size); |
413 | |
414 | has_degamma = |
415 | degamma_lut && !__is_lut_linear(lut: degamma_lut, size: degamma_size); |
416 | |
417 | has_regamma = |
418 | regamma_lut && !__is_lut_linear(lut: regamma_lut, size: regamma_size); |
419 | |
420 | is_legacy = regamma_size == MAX_COLOR_LEGACY_LUT_ENTRIES; |
421 | |
422 | /* Reset all adjustments. */ |
423 | crtc->cm_has_degamma = false; |
424 | crtc->cm_is_degamma_srgb = false; |
425 | |
426 | /* Setup regamma and degamma. */ |
427 | if (is_legacy) { |
428 | /* |
429 | * Legacy regamma forces us to use the sRGB RGM as a base. |
430 | * This also means we can't use linear DGM since DGM needs |
431 | * to use sRGB as a base as well, resulting in incorrect CRTC |
432 | * DGM and CRTC CTM. |
433 | * |
434 | * TODO: Just map this to the standard regamma interface |
435 | * instead since this isn't really right. One of the cases |
436 | * where this setup currently fails is trying to do an |
437 | * inverse color ramp in legacy userspace. |
438 | */ |
439 | crtc->cm_is_degamma_srgb = true; |
440 | stream->out_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS; |
441 | stream->out_transfer_func->tf = TRANSFER_FUNCTION_SRGB; |
442 | |
443 | r = __set_legacy_tf(func: stream->out_transfer_func, lut: regamma_lut, |
444 | lut_size: regamma_size, has_rom); |
445 | if (r) |
446 | return r; |
447 | } else if (has_regamma) { |
448 | /* If atomic regamma, CRTC RGM goes into RGM LUT. */ |
449 | stream->out_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS; |
450 | stream->out_transfer_func->tf = TRANSFER_FUNCTION_LINEAR; |
451 | |
452 | r = __set_output_tf(func: stream->out_transfer_func, lut: regamma_lut, |
453 | lut_size: regamma_size, has_rom); |
454 | if (r) |
455 | return r; |
456 | } else { |
457 | /* |
458 | * No CRTC RGM means we can just put the block into bypass |
459 | * since we don't have any plane level adjustments using it. |
460 | */ |
461 | stream->out_transfer_func->type = TF_TYPE_BYPASS; |
462 | stream->out_transfer_func->tf = TRANSFER_FUNCTION_LINEAR; |
463 | } |
464 | |
465 | /* |
466 | * CRTC DGM goes into DGM LUT. It would be nice to place it |
467 | * into the RGM since it's a more featured block but we'd |
468 | * have to place the CTM in the OCSC in that case. |
469 | */ |
470 | crtc->cm_has_degamma = has_degamma; |
471 | |
472 | /* Setup CRTC CTM. */ |
473 | if (crtc->base.ctm) { |
474 | ctm = (struct drm_color_ctm *)crtc->base.ctm->data; |
475 | |
476 | /* |
477 | * Gamut remapping must be used for gamma correction |
478 | * since it comes before the regamma correction. |
479 | * |
480 | * OCSC could be used for gamma correction, but we'd need to |
481 | * blend the adjustments together with the required output |
482 | * conversion matrix - so just use the gamut remap block |
483 | * for now. |
484 | */ |
485 | __drm_ctm_to_dc_matrix(ctm, matrix: stream->gamut_remap_matrix.matrix); |
486 | |
487 | stream->gamut_remap_matrix.enable_remap = true; |
488 | stream->csc_color_matrix.enable_adjustment = false; |
489 | } else { |
490 | /* Bypass CTM. */ |
491 | stream->gamut_remap_matrix.enable_remap = false; |
492 | stream->csc_color_matrix.enable_adjustment = false; |
493 | } |
494 | |
495 | return 0; |
496 | } |
497 | |
498 | /** |
499 | * amdgpu_dm_update_plane_color_mgmt: Maps DRM color management to DC plane. |
500 | * @crtc: amdgpu_dm crtc state |
501 | * @dc_plane_state: target DC surface |
502 | * |
503 | * Update the underlying dc_stream_state's input transfer function (ITF) in |
504 | * preparation for hardware commit. The transfer function used depends on |
505 | * the preparation done on the stream for color management. |
506 | * |
507 | * Returns: |
508 | * 0 on success. -ENOMEM if mem allocation fails. |
509 | */ |
510 | int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc, |
511 | struct dc_plane_state *dc_plane_state) |
512 | { |
513 | const struct drm_color_lut *degamma_lut; |
514 | enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB; |
515 | uint32_t degamma_size; |
516 | int r; |
517 | |
518 | /* Get the correct base transfer function for implicit degamma. */ |
519 | switch (dc_plane_state->format) { |
520 | case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: |
521 | case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb: |
522 | /* DC doesn't have a transfer function for BT601 specifically. */ |
523 | tf = TRANSFER_FUNCTION_BT709; |
524 | break; |
525 | default: |
526 | break; |
527 | } |
528 | |
529 | if (crtc->cm_has_degamma) { |
530 | degamma_lut = __extract_blob_lut(blob: crtc->base.degamma_lut, |
531 | size: °amma_size); |
532 | ASSERT(degamma_size == MAX_COLOR_LUT_ENTRIES); |
533 | |
534 | dc_plane_state->in_transfer_func->type = |
535 | TF_TYPE_DISTRIBUTED_POINTS; |
536 | |
537 | /* |
538 | * This case isn't fully correct, but also fairly |
539 | * uncommon. This is userspace trying to use a |
540 | * legacy gamma LUT + atomic degamma LUT |
541 | * at the same time. |
542 | * |
543 | * Legacy gamma requires the input to be in linear |
544 | * space, so that means we need to apply an sRGB |
545 | * degamma. But color module also doesn't support |
546 | * a user ramp in this case so the degamma will |
547 | * be lost. |
548 | * |
549 | * Even if we did support it, it's still not right: |
550 | * |
551 | * Input -> CRTC DGM -> sRGB DGM -> CRTC CTM -> |
552 | * sRGB RGM -> CRTC RGM -> Output |
553 | * |
554 | * The CSC will be done in the wrong space since |
555 | * we're applying an sRGB DGM on top of the CRTC |
556 | * DGM. |
557 | * |
558 | * TODO: Don't use the legacy gamma interface and just |
559 | * map these to the atomic one instead. |
560 | */ |
561 | if (crtc->cm_is_degamma_srgb) |
562 | dc_plane_state->in_transfer_func->tf = tf; |
563 | else |
564 | dc_plane_state->in_transfer_func->tf = |
565 | TRANSFER_FUNCTION_LINEAR; |
566 | |
567 | r = __set_input_tf(func: dc_plane_state->in_transfer_func, |
568 | lut: degamma_lut, lut_size: degamma_size); |
569 | if (r) |
570 | return r; |
571 | } else if (crtc->cm_is_degamma_srgb) { |
572 | /* |
573 | * For legacy gamma support we need the regamma input |
574 | * in linear space. Assume that the input is sRGB. |
575 | */ |
576 | dc_plane_state->in_transfer_func->type = TF_TYPE_PREDEFINED; |
577 | dc_plane_state->in_transfer_func->tf = tf; |
578 | |
579 | if (tf != TRANSFER_FUNCTION_SRGB && |
580 | !mod_color_calculate_degamma_params(NULL, |
581 | output_tf: dc_plane_state->in_transfer_func, NULL, mapUserRamp: false)) |
582 | return -ENOMEM; |
583 | } else { |
584 | /* ...Otherwise we can just bypass the DGM block. */ |
585 | dc_plane_state->in_transfer_func->type = TF_TYPE_BYPASS; |
586 | dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_LINEAR; |
587 | } |
588 | |
589 | return 0; |
590 | } |
591 | |